From e7db65bbab17d55aa893682a6614527844cfe0bd Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 24 Sep 2014 20:17:59 +0200 Subject: [PATCH 001/171] use provided request encoding to decode base64 basic authentication header and fallback to ISO-8859-1, if no encoding was provided --- .../web/filter/BasicAuthenticationFilter.java | 53 +++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/web/filter/BasicAuthenticationFilter.java b/scm-core/src/main/java/sonia/scm/web/filter/BasicAuthenticationFilter.java index 38532ff3ac..ba3bcbe12d 100644 --- a/scm-core/src/main/java/sonia/scm/web/filter/BasicAuthenticationFilter.java +++ b/scm-core/src/main/java/sonia/scm/web/filter/BasicAuthenticationFilter.java @@ -35,6 +35,7 @@ package sonia.scm.web.filter; //~--- non-JDK imports -------------------------------------------------------- +import com.google.common.base.Objects; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; @@ -59,6 +60,7 @@ import sonia.scm.web.security.WebSecurityContext; import com.sun.jersey.core.util.Base64; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.util.Set; @@ -87,6 +89,9 @@ public class BasicAuthenticationFilter extends AutoLoginFilter /** marker for failed authentication */ private static final String ATTRIBUTE_FAILED_AUTH = "sonia.scm.auth.failed"; + /** default encoding to decode basic authentication header */ + private static final String DEFAULT_ENCODING = "ISO-8859-1"; + /** the logger for BasicAuthenticationFilter */ private static final Logger logger = LoggerFactory.getLogger(BasicAuthenticationFilter.class); @@ -271,13 +276,14 @@ public class BasicAuthenticationFilter extends AutoLoginFilter * @param authentication * * @return + * + * @throws IOException */ private User authenticate(HttpServletRequest request, HttpServletResponse response, Subject subject, String authentication) + throws IOException { - String token = authentication.substring(6); - - token = new String(Base64.decode(token.getBytes())); + String token = decodeAuthenticationHeader(request, authentication); int index = token.indexOf(CREDENTIAL_SEPARATOR); User user = null; @@ -331,6 +337,47 @@ public class BasicAuthenticationFilter extends AutoLoginFilter return user; } + /** + * Decode base64 of the basic authentication header. The method tries to use + * the charset provided by the request, if the request does not send an + * contain an encoding the method will be fallback to ISO-8859-1. + * + * + * @param request http request + * @param authentication base64 encoded basic authentication string + * + * @return decoded basic authentication header + * + * @see issue 627 + * @see Stackoverflow Basic Authentication + * + * @throws UnsupportedEncodingException + */ + private String decodeAuthenticationHeader(HttpServletRequest request, + String authentication) + throws UnsupportedEncodingException + { + + String encoding = Objects.firstNonNull(request.getCharacterEncoding(), + DEFAULT_ENCODING); + + String token = authentication.substring(6); + + try + { + token = new String(Base64.decode(token.getBytes(encoding))); + } + catch (UnsupportedEncodingException ex) + { + logger.warn( + "encoding {} is not supported, use {} for decoding basic auth", + encoding, DEFAULT_ENCODING); + token = new String(Base64.decode(token.getBytes(DEFAULT_ENCODING))); + } + + return token; + } + //~--- fields --------------------------------------------------------------- /** scm main configuration */ From 8e3e42ebf5b725563f5601469d69db330f3e019d Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 15 Oct 2014 08:44:26 +0200 Subject: [PATCH 002/171] use ISO-8859-1 to decode basic authentication header --- .../web/filter/BasicAuthenticationFilter.java | 71 ++++++++----------- 1 file changed, 28 insertions(+), 43 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/web/filter/BasicAuthenticationFilter.java b/scm-core/src/main/java/sonia/scm/web/filter/BasicAuthenticationFilter.java index ba3bcbe12d..ebafcb8059 100644 --- a/scm-core/src/main/java/sonia/scm/web/filter/BasicAuthenticationFilter.java +++ b/scm-core/src/main/java/sonia/scm/web/filter/BasicAuthenticationFilter.java @@ -35,7 +35,7 @@ package sonia.scm.web.filter; //~--- non-JDK imports -------------------------------------------------------- -import com.google.common.base.Objects; +import com.google.common.base.Charsets; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; @@ -62,6 +62,8 @@ import com.sun.jersey.core.util.Base64; import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; + import java.util.Set; import javax.servlet.FilterChain; @@ -90,7 +92,7 @@ public class BasicAuthenticationFilter extends AutoLoginFilter private static final String ATTRIBUTE_FAILED_AUTH = "sonia.scm.auth.failed"; /** default encoding to decode basic authentication header */ - private static final String DEFAULT_ENCODING = "ISO-8859-1"; + private static final Charset DEFAULT_ENCODING = Charsets.ISO_8859_1; /** the logger for BasicAuthenticationFilter */ private static final Logger logger = @@ -127,6 +129,30 @@ public class BasicAuthenticationFilter extends AutoLoginFilter //~--- methods -------------------------------------------------------------- + /** + * Decode base64 of the basic authentication header. The method will use + * ISO-8859-1 to encode the base64 authentication header. + * + * + * @param request http request + * @param authentication base64 encoded basic authentication string + * + * @return decoded basic authentication header + * + * @see issue 627 + * @see Stackoverflow Basic Authentication + * + * @throws UnsupportedEncodingException + */ + protected String decodeAuthenticationHeader(HttpServletRequest request, + String authentication) + throws UnsupportedEncodingException + { + String token = authentication.substring(6); + + return new String(Base64.decode(token), DEFAULT_ENCODING); + } + /** * Method description * @@ -337,47 +363,6 @@ public class BasicAuthenticationFilter extends AutoLoginFilter return user; } - /** - * Decode base64 of the basic authentication header. The method tries to use - * the charset provided by the request, if the request does not send an - * contain an encoding the method will be fallback to ISO-8859-1. - * - * - * @param request http request - * @param authentication base64 encoded basic authentication string - * - * @return decoded basic authentication header - * - * @see issue 627 - * @see Stackoverflow Basic Authentication - * - * @throws UnsupportedEncodingException - */ - private String decodeAuthenticationHeader(HttpServletRequest request, - String authentication) - throws UnsupportedEncodingException - { - - String encoding = Objects.firstNonNull(request.getCharacterEncoding(), - DEFAULT_ENCODING); - - String token = authentication.substring(6); - - try - { - token = new String(Base64.decode(token.getBytes(encoding))); - } - catch (UnsupportedEncodingException ex) - { - logger.warn( - "encoding {} is not supported, use {} for decoding basic auth", - encoding, DEFAULT_ENCODING); - token = new String(Base64.decode(token.getBytes(DEFAULT_ENCODING))); - } - - return token; - } - //~--- fields --------------------------------------------------------------- /** scm main configuration */ From 8e608d2439ac8b0ec784820c21f87bc8ead48019 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Fri, 17 Oct 2014 15:43:28 +0200 Subject: [PATCH 003/171] created small user-agent detection framework to choose the right encoding for basic authentication --- .../main/java/sonia/scm/web/UserAgent.java | 270 ++++++++++++++++++ .../java/sonia/scm/web/UserAgentParser.java | 155 ++++++++++ .../java/sonia/scm/web/UserAgentProvider.java | 55 ++++ .../web/filter/BasicAuthenticationFilter.java | 69 +++-- .../sonia/scm/web/UserAgentParserTest.java | 190 ++++++++++++ .../scm/web/GitBasicAuthenticationFilter.java | 5 +- .../sonia/scm/web/GitUserAgentProvider.java | 96 +++++++ .../scm/web/GitUserAgentProviderTest.java | 83 ++++++ .../scm/web/HgBasicAuthenticationFilter.java | 5 +- .../sonia/scm/web/HgUserAgentProvider.java | 83 ++++++ .../scm/web/HgUserAgentProviderTest.java | 91 ++++++ .../scm/web/SvnBasicAuthenticationFilter.java | 5 +- .../sonia/scm/web/SvnUserAgentProvider.java | 96 +++++++ .../scm/web/SvnUserAgentProviderTest.java | 98 +++++++ .../main/java/sonia/scm/ScmServletModule.java | 4 + .../sonia/scm/plugin/DefaultPluginLoader.java | 9 +- .../scm/web/BrowserUserAgentProvider.java | 128 +++++++++ .../scm/web/BrowserUserAgentProviderTest.java | 114 ++++++++ 18 files changed, 1527 insertions(+), 29 deletions(-) create mode 100644 scm-core/src/main/java/sonia/scm/web/UserAgent.java create mode 100644 scm-core/src/main/java/sonia/scm/web/UserAgentParser.java create mode 100644 scm-core/src/main/java/sonia/scm/web/UserAgentProvider.java create mode 100644 scm-core/src/test/java/sonia/scm/web/UserAgentParserTest.java create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitUserAgentProvider.java create mode 100644 scm-plugins/scm-git-plugin/src/test/java/sonia/scm/web/GitUserAgentProviderTest.java create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgUserAgentProvider.java create mode 100644 scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgUserAgentProviderTest.java create mode 100644 scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnUserAgentProvider.java create mode 100644 scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/web/SvnUserAgentProviderTest.java create mode 100644 scm-webapp/src/main/java/sonia/scm/web/BrowserUserAgentProvider.java create mode 100644 scm-webapp/src/test/java/sonia/scm/web/BrowserUserAgentProviderTest.java diff --git a/scm-core/src/main/java/sonia/scm/web/UserAgent.java b/scm-core/src/main/java/sonia/scm/web/UserAgent.java new file mode 100644 index 0000000000..edefc16744 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/web/UserAgent.java @@ -0,0 +1,270 @@ +/** +* 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; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Charsets; +import com.google.common.base.Objects; + +import static com.google.common.base.Preconditions.*; + +//~--- JDK imports ------------------------------------------------------------ + +import java.nio.charset.Charset; + +/** + * + * @author Sebastian Sdorra + * @since 1.42 + */ +public final class UserAgent +{ + + /** + * Constructs ... + * + * + * @param name + * @param browser + * @param basicAuthenticationCharset + */ + private UserAgent(String name, boolean browser, + Charset basicAuthenticationCharset) + { + this.name = checkNotNull(name); + this.browser = browser; + this.basicAuthenticationCharset = checkNotNull(basicAuthenticationCharset); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param name + * + * @return + */ + public static Builder builder(String name) + { + return new Builder(name); + } + + /** + * Method description + * + * + * @param obj + * + * @return + */ + @Override + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final UserAgent other = (UserAgent) obj; + + return Objects.equal(name, other.name) + && Objects.equal(browser, other.browser) + && Objects.equal(basicAuthenticationCharset, basicAuthenticationCharset); + } + + /** + * Method description + * + * + * @return + */ + @Override + public int hashCode() + { + return Objects.hashCode(name, browser, basicAuthenticationCharset); + } + + /** + * Method description + * + * + * @return + */ + @Override + public String toString() + { + //J- + return Objects.toStringHelper(this) + .add("name", name) + .add("browser", browser) + .add("basicAuthenticationCharset", basicAuthenticationCharset) + .toString(); + //J+ + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + public Charset getBasicAuthenticationCharset() + { + return basicAuthenticationCharset; + } + + /** + * Method description + * + * + * @return + */ + public String getName() + { + return name; + } + + /** + * Method description + * + * + * @return + */ + public boolean isBrowser() + { + return browser; + } + + //~--- inner classes -------------------------------------------------------- + + /** + * Class description + * + * + * @version Enter version here..., 14/10/15 + * @author Enter your name here... + */ + public static class Builder + { + + /** + * Constructs ... + * + * + * @param name + */ + public Builder(String name) + { + this.name = name; + } + + //~--- methods ------------------------------------------------------------ + + /** + * Method description + * + * + * @param basicAuthenticationCharset + * + * @return + */ + public Builder basicAuthenticationCharset( + Charset basicAuthenticationCharset) + { + this.basicAuthenticationCharset = + checkNotNull(basicAuthenticationCharset); + + return this; + } + + /** + * Method description + * + * + * @param browser + * + * @return + */ + public Builder browser(boolean browser) + { + this.browser = browser; + + return this; + } + + /** + * Method description + * + * + * @return + */ + public UserAgent build() + { + return new UserAgent(name, browser, basicAuthenticationCharset); + } + + //~--- fields ------------------------------------------------------------- + + /** Field description */ + private final String name; + + /** Field description */ + private boolean browser = true; + + /** Field description */ + private Charset basicAuthenticationCharset = Charsets.ISO_8859_1; + } + + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private final Charset basicAuthenticationCharset; + + /** Field description */ + private final boolean browser; + + /** Field description */ + private final String name; +} diff --git a/scm-core/src/main/java/sonia/scm/web/UserAgentParser.java b/scm-core/src/main/java/sonia/scm/web/UserAgentParser.java new file mode 100644 index 0000000000..2cc1375d58 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/web/UserAgentParser.java @@ -0,0 +1,155 @@ +/** +* 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; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Strings; +import com.google.inject.Inject; +import com.google.inject.Singleton; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sonia.scm.cache.Cache; +import sonia.scm.cache.CacheManager; +import sonia.scm.util.HttpUtil; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.Locale; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; + +/** + * + * @author Sebastian Sdorra + */ +@Singleton +public final class UserAgentParser +{ + + /** Field description */ + @VisibleForTesting + static final String CACHE_NAME = "sonia.scm.user-agent"; + + /** Field description */ + @VisibleForTesting + static final UserAgent UNKNOWN = UserAgent.builder("UNKNOWN").build(); + + /** Field description */ + private static final Logger logger = + LoggerFactory.getLogger(UserAgentParser.class); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param providers + * @param cacheManager + */ + @Inject + public UserAgentParser(Set providers, + CacheManager cacheManager) + { + this.providers = providers; + this.cache = cacheManager.getCache(String.class, UserAgent.class, + CACHE_NAME); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param request + * + * @return + */ + public UserAgent parse(HttpServletRequest request) + { + return parse(request.getHeader(HttpUtil.HEADER_USERAGENT)); + } + + /** + * Method description + * + * + * @param userAgent + * + * @return + */ + public UserAgent parse(String userAgent) + { + String uas = Strings.nullToEmpty(userAgent).toLowerCase(Locale.ENGLISH); + UserAgent ua = cache.get(uas); + + if (ua == null) + { + for (UserAgentProvider provider : providers) + { + ua = provider.parseUserAgent(uas); + + if (ua != null) + { + break; + } + } + + if (ua == null) + { + ua = UNKNOWN; + } + + // cache.put(uas, ua); + } + + logger.trace("return user-agent {} for {}", ua, userAgent); + + return ua; + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private final Cache cache; + + /** Field description */ + private final Set providers; +} diff --git a/scm-core/src/main/java/sonia/scm/web/UserAgentProvider.java b/scm-core/src/main/java/sonia/scm/web/UserAgentProvider.java new file mode 100644 index 0000000000..f7645dca74 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/web/UserAgentProvider.java @@ -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.web; + +import sonia.scm.plugin.ExtensionPoint; + +/** + * + * @author Sebastian Sdorra + */ +@ExtensionPoint(multi = true) +public interface UserAgentProvider +{ + + /** + * Method description + * + * + * @param userAgentString + * + * @return + */ + public UserAgent parseUserAgent(String userAgentString); +} diff --git a/scm-core/src/main/java/sonia/scm/web/filter/BasicAuthenticationFilter.java b/scm-core/src/main/java/sonia/scm/web/filter/BasicAuthenticationFilter.java index ebafcb8059..2432888273 100644 --- a/scm-core/src/main/java/sonia/scm/web/filter/BasicAuthenticationFilter.java +++ b/scm-core/src/main/java/sonia/scm/web/filter/BasicAuthenticationFilter.java @@ -52,6 +52,7 @@ import sonia.scm.SCMContext; import sonia.scm.config.ScmConfiguration; import sonia.scm.user.User; import sonia.scm.util.HttpUtil; +import sonia.scm.web.UserAgentParser; import sonia.scm.util.Util; import sonia.scm.web.security.WebSecurityContext; @@ -70,6 +71,7 @@ import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import sonia.scm.web.UserAgent; /** * @@ -92,7 +94,7 @@ public class BasicAuthenticationFilter extends AutoLoginFilter private static final String ATTRIBUTE_FAILED_AUTH = "sonia.scm.auth.failed"; /** default encoding to decode basic authentication header */ - private static final Charset DEFAULT_ENCODING = Charsets.ISO_8859_1; + public static final Charset DEFAULT_ENCODING = Charsets.ISO_8859_1; /** the logger for BasicAuthenticationFilter */ private static final Logger logger = @@ -101,11 +103,13 @@ public class BasicAuthenticationFilter extends AutoLoginFilter //~--- constructors --------------------------------------------------------- /** - * Constructs ... + * Constructs a new basic authenticaton filter * * * @param securityContextProvider - * @deprecated use the constructor with out arguments instead. + * @deprecated use + * {@link #BasicAuthenticationFilter(ScmConfiguration, Set, UserAgentParser) + * instead */ @Deprecated public BasicAuthenticationFilter( @@ -118,21 +122,43 @@ public class BasicAuthenticationFilter extends AutoLoginFilter * @param autoLoginModules auto login modules * * @since 1.21 + * + * @deprecated use + * {@link #BasicAuthenticationFilter(ScmConfiguration, Set, UserAgentParser) + * instead */ - @Inject + @Deprecated public BasicAuthenticationFilter(ScmConfiguration configuration, Set autoLoginModules) + { + this(configuration, autoLoginModules, null); + } + + /** + * Constructs a new basic authentication filter + * + * @param configuration scm-manager global configuration + * @param autoLoginModules auto login modules + * @param userAgentParser parser for user-agent header + * + * @since 1.42 + */ + @Inject + public BasicAuthenticationFilter(ScmConfiguration configuration, + Set autoLoginModules, UserAgentParser userAgentParser) { super(autoLoginModules); this.configuration = configuration; + this.userAgentParser = userAgentParser; } //~--- methods -------------------------------------------------------------- /** * Decode base64 of the basic authentication header. The method will use - * ISO-8859-1 to encode the base64 authentication header. - * + * the charset provided by the {@link UserAgent}, if the + * {@link UserAgentParser} is not available the method will be fall back to + * ISO-8859-1. * * @param request http request * @param authentication base64 encoded basic authentication string @@ -148,9 +174,14 @@ public class BasicAuthenticationFilter extends AutoLoginFilter String authentication) throws UnsupportedEncodingException { - String token = authentication.substring(6); + Charset encoding = DEFAULT_ENCODING; - return new String(Base64.decode(token), DEFAULT_ENCODING); + if (userAgentParser != null) + { + encoding = userAgentParser.parse(request).getBasicAuthenticationCharset(); + } + + return new String(Base64.decode(authentication), encoding); } /** @@ -179,11 +210,7 @@ public class BasicAuthenticationFilter extends AutoLoginFilter if (Util.startWithIgnoreCase(authentication, AUTHORIZATION_BASIC_PREFIX)) { - if (logger.isTraceEnabled()) - { - logger.trace( - "found basic authorization header, start authentication"); - } + logger.trace("found basic authorization header, start authentication"); user = authenticate(request, response, subject, authentication); @@ -202,10 +229,7 @@ public class BasicAuthenticationFilter extends AutoLoginFilter else if ((configuration != null) && configuration.isAnonymousAccessEnabled()) { - if (logger.isTraceEnabled()) - { - logger.trace("anonymous access granted"); - } + logger.trace("anonymous access granted"); user = SCMContext.ANONYMOUS; } @@ -213,10 +237,7 @@ public class BasicAuthenticationFilter extends AutoLoginFilter if (user == null) { - if (logger.isTraceEnabled()) - { - logger.trace("could not find user send unauthorized"); - } + logger.trace("could not find user send unauthorized"); handleUnauthorized(request, response, chain); } @@ -309,7 +330,8 @@ public class BasicAuthenticationFilter extends AutoLoginFilter HttpServletResponse response, Subject subject, String authentication) throws IOException { - String token = decodeAuthenticationHeader(request, authentication); + String token = decodeAuthenticationHeader(request, + authentication.substring(6)); int index = token.indexOf(CREDENTIAL_SEPARATOR); User user = null; @@ -367,4 +389,7 @@ public class BasicAuthenticationFilter extends AutoLoginFilter /** scm main configuration */ protected ScmConfiguration configuration; + + /** Field description */ + protected UserAgentParser userAgentParser; } diff --git a/scm-core/src/test/java/sonia/scm/web/UserAgentParserTest.java b/scm-core/src/test/java/sonia/scm/web/UserAgentParserTest.java new file mode 100644 index 0000000000..acd73ca497 --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/web/UserAgentParserTest.java @@ -0,0 +1,190 @@ +/** +* 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; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Charsets; +import com.google.common.collect.Sets; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import sonia.scm.cache.Cache; +import sonia.scm.cache.CacheManager; +import sonia.scm.util.HttpUtil; + +import static org.hamcrest.Matchers.*; + +import static org.junit.Assert.*; + +import static org.mockito.Mockito.*; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; + +/** + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +public class UserAgentParserTest +{ + + /** Field description */ + private static final String UA_1 = "mozilla/5.0"; + + /** Field description */ + private static final String UA_2 = "wget/1.5.3"; + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + */ + @Before + public void prepare() + { + Set providers = Sets.newHashSet(provider1, provider2); + + when(cacheManager.getCache(String.class, UserAgent.class, + UserAgentParser.CACHE_NAME)).thenReturn(cache); + parser = new UserAgentParser(providers, cacheManager); + } + + /** + * Method description + * + */ + @Test + public void testDefaultValues() + { + UserAgent ua = parser.parse(UA_1); + + assertEquals(Charsets.ISO_8859_1, ua.getBasicAuthenticationCharset()); + assertTrue(ua.isBrowser()); + } + + /** + * Method description + * + */ + @Test + public void testParse() + { + UserAgent ua = UserAgent.builder("UA1").build(); + + when(provider1.parseUserAgent(UA_1)).thenReturn(ua); + + UserAgent ua2 = UserAgent.builder("UA2").build(); + + when(provider2.parseUserAgent(UA_2)).thenReturn(ua2); + + assertEquals(ua, parser.parse(UA_1)); + assertEquals(ua2, parser.parse(UA_2)); + } + + /** + * Method description + * + */ + @Test + public void testParseHttpServletRequest() + { + when(request.getHeader(HttpUtil.HEADER_USERAGENT)).thenReturn(UA_2); + + UserAgent ua = UserAgent.builder("UA2").build(); + + when(provider1.parseUserAgent(UA_2)).thenReturn(ua); + assertEquals(ua, parser.parse(request)); + } + + /** + * Method description + * + */ + @Test + public void testParseNotFound() + { + assertEquals(UserAgentParser.UNKNOWN, parser.parse(UA_1)); + assertEquals(UserAgentParser.UNKNOWN, parser.parse(UA_2)); + } + + /** + * Method description + * + */ + @Test + public void testParseWithCache() + { + UserAgent ua = UserAgent.builder("UA").build(); + + when(cache.get(UA_1)).thenReturn(ua); + assertEquals(ua, parser.parse(UA_1)); + assertEquals(UserAgentParser.UNKNOWN, parser.parse(UA_2)); + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + @Mock + private Cache cache; + + /** Field description */ + @Mock + private CacheManager cacheManager; + + /** Field description */ + private UserAgentParser parser; + + /** Field description */ + @Mock + private UserAgentProvider provider1; + + /** Field description */ + @Mock + private UserAgentProvider provider2; + + /** Field description */ + @Mock + private HttpServletRequest request; +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitBasicAuthenticationFilter.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitBasicAuthenticationFilter.java index 35047fe1f6..b15f75f297 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitBasicAuthenticationFilter.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitBasicAuthenticationFilter.java @@ -67,12 +67,13 @@ public class GitBasicAuthenticationFilter extends BasicAuthenticationFilter * * @param configuration * @param autoLoginModules + * @param userAgentParser */ @Inject public GitBasicAuthenticationFilter(ScmConfiguration configuration, - Set autoLoginModules) + Set autoLoginModules, UserAgentParser userAgentParser) { - super(configuration, autoLoginModules); + super(configuration, autoLoginModules, userAgentParser); } //~--- methods -------------------------------------------------------------- diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitUserAgentProvider.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitUserAgentProvider.java new file mode 100644 index 0000000000..5960951f55 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitUserAgentProvider.java @@ -0,0 +1,96 @@ +/** + * 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; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Charsets; + +import sonia.scm.plugin.ext.Extension; + +/** + * + * @author Sebastian Sdorra + */ +@Extension +public class GitUserAgentProvider implements UserAgentProvider +{ + + /** Field description */ + @VisibleForTesting + static final UserAgent GIT = UserAgent.builder("Git").browser( + false).basicAuthenticationCharset( + Charsets.UTF_8).build(); + + /** Field description */ + @VisibleForTesting + static final UserAgent MSYSGIT = UserAgent.builder("msysGit").browser( + false).basicAuthenticationCharset( + Charsets.UTF_8).build(); + + /** Field description */ + private static final String PREFIX = "git/"; + + /** Field description */ + private static final String SUFFIX = "msysgit"; + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param userAgentString + * + * @return + */ + @Override + public UserAgent parseUserAgent(String userAgentString) + { + UserAgent ua = null; + + if (userAgentString.startsWith(PREFIX)) + { + if (userAgentString.contains(SUFFIX)) + { + ua = MSYSGIT; + } + else + { + ua = GIT; + } + } + + return ua; + } +} diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/web/GitUserAgentProviderTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/web/GitUserAgentProviderTest.java new file mode 100644 index 0000000000..b16580c4a5 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/web/GitUserAgentProviderTest.java @@ -0,0 +1,83 @@ +/** + * 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; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Strings; + +import org.junit.Test; + +import static org.junit.Assert.*; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.Locale; + +/** + * + * @author Sebastian Sdorra + */ +public class GitUserAgentProviderTest +{ + + /** + * Method description + * + */ + @Test + public void testParseUserAgent() + { + assertEquals(GitUserAgentProvider.GIT, parse("git/1.7.9.5")); + assertEquals(GitUserAgentProvider.MSYSGIT, parse("git/1.8.3.msysgit.0")); + assertNull(parse("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36")); + } + + /** + * Method description + * + * + * @param v + * + * @return + */ + private UserAgent parse(String v) + { + return provider.parseUserAgent( + Strings.nullToEmpty(v).toLowerCase(Locale.ENGLISH)); + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private final GitUserAgentProvider provider = new GitUserAgentProvider(); +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgBasicAuthenticationFilter.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgBasicAuthenticationFilter.java index 6d3c819e13..744a82bace 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgBasicAuthenticationFilter.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgBasicAuthenticationFilter.java @@ -63,12 +63,13 @@ public class HgBasicAuthenticationFilter extends BasicAuthenticationFilter * * @param configuration * @param autoLoginModules + * @param userAgentParser */ @Inject public HgBasicAuthenticationFilter(ScmConfiguration configuration, - Set autoLoginModules) + Set autoLoginModules, UserAgentParser userAgentParser) { - super(configuration, autoLoginModules); + super(configuration, autoLoginModules, userAgentParser); } //~--- methods -------------------------------------------------------------- diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgUserAgentProvider.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgUserAgentProvider.java new file mode 100644 index 0000000000..e9b9ef2deb --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgUserAgentProvider.java @@ -0,0 +1,83 @@ +/** + * 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; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.annotations.VisibleForTesting; + +import sonia.scm.plugin.ext.Extension; + +//~--- JDK imports ------------------------------------------------------------ + +import java.nio.charset.Charset; + +/** + * + * @author Sebastian Sdorra + */ +@Extension +public class HgUserAgentProvider implements UserAgentProvider +{ + + /** mercurial seems to use system encoding */ + @VisibleForTesting + static UserAgent HG = UserAgent.builder("Mercurial").browser( + false).basicAuthenticationCharset( + Charset.defaultCharset()).build(); + + /** Field description */ + private static final String PREFIX = "mercurial"; + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param userAgentString + * + * @return + */ + @Override + public UserAgent parseUserAgent(String userAgentString) + { + UserAgent ua = null; + + if (userAgentString.startsWith(PREFIX)) + { + ua = HG; + } + + return ua; + } +} diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgUserAgentProviderTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgUserAgentProviderTest.java new file mode 100644 index 0000000000..0a3e1cd03c --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgUserAgentProviderTest.java @@ -0,0 +1,91 @@ +/** + * 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; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Strings; + +import org.junit.Test; + +import static org.junit.Assert.*; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.Locale; + +/** + * + * @author Sebastian Sdorra + */ +public class HgUserAgentProviderTest +{ + + /** Field description */ + private static final String UA_1 = "mercurial/proto-1.0"; + + /** Field description */ + private static final String UA_2 = + "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36"; + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + */ + @Test + public void testParseUserAgent() + { + assertEquals(HgUserAgentProvider.HG, parse(UA_1)); + assertNull(parse(UA_2)); + } + + /** + * Method description + * + * + * @param v + * + * @return + */ + private UserAgent parse(String v) + { + return provider.parseUserAgent( + Strings.nullToEmpty(v).toLowerCase(Locale.ENGLISH)); + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private final HgUserAgentProvider provider = new HgUserAgentProvider(); +} diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnBasicAuthenticationFilter.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnBasicAuthenticationFilter.java index b94b6b5d56..67d1306719 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnBasicAuthenticationFilter.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnBasicAuthenticationFilter.java @@ -65,12 +65,13 @@ public class SvnBasicAuthenticationFilter extends BasicAuthenticationFilter * * @param configuration * @param autoLoginModules + * @param userAgentParser */ @Inject public SvnBasicAuthenticationFilter(ScmConfiguration configuration, - Set autoLoginModules) + Set autoLoginModules, UserAgentParser userAgentParser) { - super(configuration, autoLoginModules); + super(configuration, autoLoginModules, userAgentParser); } //~--- methods -------------------------------------------------------------- diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnUserAgentProvider.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnUserAgentProvider.java new file mode 100644 index 0000000000..1d34d000a2 --- /dev/null +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnUserAgentProvider.java @@ -0,0 +1,96 @@ +/** + * 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; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Charsets; + +import sonia.scm.plugin.ext.Extension; + +/** + * + * @author Sebastian Sdorra + */ +@Extension +public final class SvnUserAgentProvider implements UserAgentProvider +{ + + /** ua prefix */ + private static final String PREFIX = "svn"; + + /** ua suffix */ + private static final String SUFFIX = "tortoisesvn"; + + /** TortoiseSVN */ + @VisibleForTesting + static final UserAgent TORTOISE_SVN = + UserAgent.builder("TortoiseSVN").browser(false) + .basicAuthenticationCharset(Charsets.UTF_8).build(); + + /** Subversion cli client */ + @VisibleForTesting + static final UserAgent SVN = + UserAgent.builder("Subversion").browser(false) + .basicAuthenticationCharset(Charsets.UTF_8).build(); + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param userAgentString + * + * @return + */ + @Override + public UserAgent parseUserAgent(String userAgentString) + { + UserAgent ua = null; + + if (userAgentString.startsWith(PREFIX)) + { + if (userAgentString.contains(SUFFIX)) + { + ua = TORTOISE_SVN; + } + else + { + ua = SVN; + } + } + + return ua; + } +} diff --git a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/web/SvnUserAgentProviderTest.java b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/web/SvnUserAgentProviderTest.java new file mode 100644 index 0000000000..2604079e64 --- /dev/null +++ b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/web/SvnUserAgentProviderTest.java @@ -0,0 +1,98 @@ +/** + * 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; + +//~--- non-JDK imports -------------------------------------------------------- + +import org.junit.Test; + + +import static org.junit.Assert.*; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.Locale; + +/** + * + * @author Sebastian Sdorra + */ +public class SvnUserAgentProviderTest +{ + + /** Field description */ + private static final String UA_1 = + "SVN/1.8.8 (x64-microsoft-windows) serf/1.3.4 TortoiseSVN-1.8.6.25419"; + + /** Field description */ + private static final String UA_2 = "SVN/1.5.4 (r33841) neon/0.28.3"; + + /** Field description */ + private static final String UA_3 = "SVN/1.6.3 (r38063) neon/0.28.4"; + + /** Field description */ + private static final String UA_4 = + "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0;Google Wireless Transcoder;)"; + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + */ + @Test + public void testParseUserAgent() + { + assertEquals(SvnUserAgentProvider.TORTOISE_SVN, parse(UA_1)); + assertEquals(SvnUserAgentProvider.SVN, parse(UA_2)); + assertEquals(SvnUserAgentProvider.SVN, parse(UA_3)); + assertNull(parse(UA_4)); + } + + /** + * Method description + * + * + * @param ua + * + * @return + */ + private UserAgent parse(String ua) + { + return suap.parseUserAgent(ua.toLowerCase(Locale.ENGLISH)); + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private final SvnUserAgentProvider suap = new SvnUserAgentProvider(); +} diff --git a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java index 0f7fbbf822..30e8a2e1e8 100644 --- a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java +++ b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java @@ -156,6 +156,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; +import sonia.scm.web.UserAgentParser; /** * @@ -341,6 +342,9 @@ public class ScmServletModule extends ServletModule // bind new hook api bind(HookContextFactory.class); bind(HookEventFacade.class); + + // bind user-agent parser + bind(UserAgentParser.class); // bind debug logging filter if ("true".equalsIgnoreCase(System.getProperty(SYSTEM_PROPERTY_DEBUG_HTTP))) diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginLoader.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginLoader.java index 2d8e3fbc07..e28c4d8584 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginLoader.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginLoader.java @@ -79,6 +79,7 @@ import javax.servlet.ServletContext; import javax.servlet.ServletContextListener; import javax.xml.bind.JAXB; +import sonia.scm.web.BrowserUserAgentProvider; /** * @@ -575,7 +576,13 @@ public class DefaultPluginLoader implements PluginLoader new AnnotatedClass( Extensions.createExtension(), DefaultAuthenticationHandler.class - ) + ) + ); + extensions.add( + new AnnotatedClass( + Extensions.createExtension(), + BrowserUserAgentProvider.class + ) ); //J+ } diff --git a/scm-webapp/src/main/java/sonia/scm/web/BrowserUserAgentProvider.java b/scm-webapp/src/main/java/sonia/scm/web/BrowserUserAgentProvider.java new file mode 100644 index 0000000000..48a13e9760 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/web/BrowserUserAgentProvider.java @@ -0,0 +1,128 @@ +/** + * 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; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Charsets; + +import sonia.scm.plugin.ext.Extension; + +/** + * + * @author Sebastian Sdorra + */ +@Extension +public class BrowserUserAgentProvider implements UserAgentProvider +{ + + /** Field description */ + @VisibleForTesting + static final UserAgent CHROME = UserAgent.builder( + "Chrome").basicAuthenticationCharset( + Charsets.UTF_8).build(); + + /** Field description */ + private static final String CHROME_PATTERN = "chrome"; + + /** Field description */ + @VisibleForTesting + static final UserAgent FIREFOX = UserAgent.builder("Firefox").build(); + + /** Field description */ + private static final String FIREFOX_PATTERN = "firefox"; + + /** Field description */ + @VisibleForTesting + static final UserAgent MSIE = UserAgent.builder("Internet Explorer").build(); + + /** Field description */ + private static final String MSIE_PATTERN = "msie"; + + /** Field description */ + @VisibleForTesting // todo check charset + static final UserAgent SAFARI = UserAgent.builder("Safari").build(); + + /** Field description */ + private static final String OPERA_PATTERN = "opera"; + + /** Field description */ + private static final String SAFARI_PATTERN = "safari"; + + /** Field description */ + @VisibleForTesting // todo check charset + static final UserAgent OPERA = UserAgent.builder( + "Opera").basicAuthenticationCharset( + Charsets.UTF_8).build(); + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param userAgentString + * + * @return + */ + @Override + public UserAgent parseUserAgent(String userAgentString) + { + UserAgent ua = null; + + if (userAgentString.contains(CHROME_PATTERN)) + { + ua = CHROME; + } + else if (userAgentString.contains(FIREFOX_PATTERN)) + { + ua = FIREFOX; + } + else if (userAgentString.contains(OPERA_PATTERN)) + { + ua = OPERA; + } + else if (userAgentString.contains(MSIE_PATTERN)) + { + ua = MSIE; + } + else if (userAgentString.contains(SAFARI_PATTERN)) + { + ua = SAFARI; + } + + return ua; + } +} diff --git a/scm-webapp/src/test/java/sonia/scm/web/BrowserUserAgentProviderTest.java b/scm-webapp/src/test/java/sonia/scm/web/BrowserUserAgentProviderTest.java new file mode 100644 index 0000000000..c01b76a313 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/web/BrowserUserAgentProviderTest.java @@ -0,0 +1,114 @@ +/** + * 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; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Strings; + +import org.junit.Test; + + +import static org.junit.Assert.*; + + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.Locale; + +/** + * + * @author Sebastian Sdorra + */ +public class BrowserUserAgentProviderTest +{ + + /** Field description */ + private static final String CHROME = + "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36"; + + /** Field description */ + private static final String FIREFOX = + "Mozilla/5.0 (Windows; U; Windows NT 5.2; en-GB; rv:1.8.1.18) Gecko/20081029 Firefox/2.0.0.18"; + + /** Field description */ + private static final String MSIE = + "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; )"; + + /** Field description */ + private static final String OPERA = + "Opera/9.80 (Windows NT 5.1; U; cs) Presto/2.2.15 Version/10.00"; + + /** Field description */ + private static final String SAFARI = + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/6.1.3 Safari/537.75.14"; + + /** Field description */ + private static final String WGET = "Wget/1.5.3"; + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + */ + @Test + public void testParseUserAgent() + { + assertEquals(BrowserUserAgentProvider.MSIE, parse(MSIE)); + assertEquals(BrowserUserAgentProvider.FIREFOX, parse(FIREFOX)); + assertEquals(BrowserUserAgentProvider.OPERA, parse(OPERA)); + assertEquals(BrowserUserAgentProvider.CHROME, parse(CHROME)); + assertEquals(BrowserUserAgentProvider.SAFARI, parse(SAFARI)); + assertNull(parse(WGET)); + } + + /** + * Method description + * + * + * @param v + * + * @return + */ + private UserAgent parse(String v) + { + return provider.parseUserAgent( + Strings.nullToEmpty(v).toLowerCase(Locale.ENGLISH)); + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private final BrowserUserAgentProvider provider = + new BrowserUserAgentProvider(); +} From c27bd93d8d6cb4cdec3a2903c8ff4afcf8c02ca0 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 23 Dec 2014 11:58:23 +0100 Subject: [PATCH 004/171] update jgit to version 3.5.3.201412180710-r in order to fix CVE-2014-9390 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d182d59185..556c788068 100644 --- a/pom.xml +++ b/pom.xml @@ -439,7 +439,7 @@ 1.2.3 - 3.4.1.201406201815-r + 3.5.3.201412180710-r 1.8.5-scm2 From 48dd5f227ac6659742b78f19b90cb250cc73dd19 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 23 Dec 2014 12:01:29 +0100 Subject: [PATCH 005/171] update jersey to version 1.18.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 556c788068..1ca80d4bd5 100644 --- a/pom.xml +++ b/pom.xml @@ -430,7 +430,7 @@ 1.1.2 2.5 3.0 - 1.18.2 + 1.18.3 2.6.6 2.3.20 7.6.16.v20140903 From 7fad6bcf30f16d4bde20b6c4c17bc1a14c3b31ce Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 23 Dec 2014 12:01:48 +0100 Subject: [PATCH 006/171] update junit to version 4.12 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1ca80d4bd5..f0708726b4 100644 --- a/pom.xml +++ b/pom.xml @@ -423,7 +423,7 @@ 1.10.8 1.3 - 4.11 + 4.12 1.7.7 From 2a8655108561eda053d79fb771438fbd682b4783 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 23 Dec 2014 12:03:06 +0100 Subject: [PATCH 007/171] update mockito to version 1.10.17 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f0708726b4..6e5fbb52f2 100644 --- a/pom.xml +++ b/pom.xml @@ -421,7 +421,7 @@ - 1.10.8 + 1.10.17 1.3 4.12 From 0a5eb89f5a78bca7dcef6b7c8585d3bbb5cc42e3 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 23 Dec 2014 12:03:29 +0100 Subject: [PATCH 008/171] update slf4j to version 1.7.9 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6e5fbb52f2..f4bfae6be6 100644 --- a/pom.xml +++ b/pom.xml @@ -426,7 +426,7 @@ 4.12 - 1.7.7 + 1.7.9 1.1.2 2.5 3.0 From 430b2de1fb5f53a685e20b19110f0b18a3483f13 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 23 Dec 2014 12:24:56 +0100 Subject: [PATCH 009/171] [maven-release-plugin] prepare release 1.44 --- maven/pom.xml | 4 ++-- maven/scm-maven-plugin/pom.xml | 4 ++-- maven/scm-plugin-archetype/pom.xml | 4 ++-- pom.xml | 4 ++-- scm-clients/pom.xml | 6 +++--- scm-clients/scm-cli-client/pom.xml | 6 +++--- scm-clients/scm-client-api/pom.xml | 4 ++-- scm-clients/scm-client-impl/pom.xml | 8 ++++---- scm-core/pom.xml | 4 ++-- scm-dao-orientdb/pom.xml | 8 ++++---- scm-dao-xml/pom.xml | 8 ++++---- scm-plugin-backend/pom.xml | 6 +++--- scm-plugins/pom.xml | 8 ++++---- scm-plugins/scm-git-plugin/pom.xml | 6 +++--- scm-plugins/scm-hg-plugin/pom.xml | 6 +++--- scm-plugins/scm-svn-plugin/pom.xml | 6 +++--- scm-samples/pom.xml | 4 ++-- scm-samples/scm-sample-auth/pom.xml | 6 +++--- scm-samples/scm-sample-hello/pom.xml | 6 +++--- scm-server/pom.xml | 4 ++-- scm-test/pom.xml | 6 +++--- scm-webapp/pom.xml | 24 ++++++++++++------------ support/pom.xml | 4 ++-- support/scm-support-btrace/pom.xml | 6 +++--- 24 files changed, 76 insertions(+), 76 deletions(-) diff --git a/maven/pom.xml b/maven/pom.xml index 3520448e40..cef2fec953 100644 --- a/maven/pom.xml +++ b/maven/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.44-SNAPSHOT + 1.44 sonia.scm.maven scm-maven-plugins pom - 1.44-SNAPSHOT + 1.44 scm-maven-plugins diff --git a/maven/scm-maven-plugin/pom.xml b/maven/scm-maven-plugin/pom.xml index c66a350089..8ca33beb61 100644 --- a/maven/scm-maven-plugin/pom.xml +++ b/maven/scm-maven-plugin/pom.xml @@ -6,12 +6,12 @@ scm-maven-plugins sonia.scm.maven - 1.44-SNAPSHOT + 1.44 sonia.scm.maven scm-maven-plugin - 1.44-SNAPSHOT + 1.44 maven-plugin scm-maven-plugin diff --git a/maven/scm-plugin-archetype/pom.xml b/maven/scm-plugin-archetype/pom.xml index 451f9073c8..c318d1e564 100644 --- a/maven/scm-plugin-archetype/pom.xml +++ b/maven/scm-plugin-archetype/pom.xml @@ -6,12 +6,12 @@ scm-maven-plugins sonia.scm.maven - 1.44-SNAPSHOT + 1.44 sonia.scm.maven scm-plugin-archetype - 1.44-SNAPSHOT + 1.44 scm-plugin-archetype diff --git a/pom.xml b/pom.xml index f4bfae6be6..9e9ae05f0b 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ sonia.scm scm pom - 1.44-SNAPSHOT + 1.44 The easiest way to share your Git, Mercurial and Subversion repositories over http. @@ -36,7 +36,7 @@ scm:hg:http://bitbucket.org/sdorra/scm-manager scm:hg:https://bitbucket.org/sdorra/scm-manager http://bitbucket.org/sdorra/scm-manager - HEAD + 1.44 diff --git a/scm-clients/pom.xml b/scm-clients/pom.xml index 69c0aa44fc..4095d897a8 100644 --- a/scm-clients/pom.xml +++ b/scm-clients/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.44-SNAPSHOT + 1.44 sonia.scm.clients scm-clients pom - 1.44-SNAPSHOT + 1.44 scm-clients @@ -32,7 +32,7 @@ scm-core sonia.scm jar - 1.44-SNAPSHOT + 1.44 shiro-core diff --git a/scm-clients/scm-cli-client/pom.xml b/scm-clients/scm-cli-client/pom.xml index 5b76977406..ebe281dd81 100644 --- a/scm-clients/scm-cli-client/pom.xml +++ b/scm-clients/scm-cli-client/pom.xml @@ -6,12 +6,12 @@ scm-clients sonia.scm.clients - 1.44-SNAPSHOT + 1.44 sonia.scm.clients scm-cli-client - 1.44-SNAPSHOT + 1.44 scm-cli-client @@ -34,7 +34,7 @@ sonia.scm.clients scm-client-impl - 1.44-SNAPSHOT + 1.44 diff --git a/scm-clients/scm-client-api/pom.xml b/scm-clients/scm-client-api/pom.xml index d610d57455..9257e32b20 100644 --- a/scm-clients/scm-client-api/pom.xml +++ b/scm-clients/scm-client-api/pom.xml @@ -6,13 +6,13 @@ sonia.scm.clients scm-clients - 1.44-SNAPSHOT + 1.44 sonia.scm.clients scm-client-api jar - 1.44-SNAPSHOT + 1.44 scm-client-api diff --git a/scm-clients/scm-client-impl/pom.xml b/scm-clients/scm-client-impl/pom.xml index b0a640fd93..4742b00d7f 100644 --- a/scm-clients/scm-client-impl/pom.xml +++ b/scm-clients/scm-client-impl/pom.xml @@ -6,13 +6,13 @@ sonia.scm.clients scm-clients - 1.44-SNAPSHOT + 1.44 sonia.scm.clients scm-client-impl jar - 1.44-SNAPSHOT + 1.44 scm-client-impl @@ -36,7 +36,7 @@ sonia.scm.clients scm-client-api - 1.44-SNAPSHOT + 1.44 @@ -70,7 +70,7 @@ sonia.scm scm-test - 1.44-SNAPSHOT + 1.44 test diff --git a/scm-core/pom.xml b/scm-core/pom.xml index a12f622f03..cd6bbb0da9 100644 --- a/scm-core/pom.xml +++ b/scm-core/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.44-SNAPSHOT + 1.44 sonia.scm scm-core - 1.44-SNAPSHOT + 1.44 scm-core diff --git a/scm-dao-orientdb/pom.xml b/scm-dao-orientdb/pom.xml index 8a914f3f23..10f4bdacd0 100644 --- a/scm-dao-orientdb/pom.xml +++ b/scm-dao-orientdb/pom.xml @@ -6,12 +6,12 @@ sonia.scm scm - 1.44-SNAPSHOT + 1.44 sonia.scm scm-dao-orientdb - 1.44-SNAPSHOT + 1.44 scm-dao-orientdb @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.44-SNAPSHOT + 1.44 @@ -52,7 +52,7 @@ sonia.scm scm-test - 1.44-SNAPSHOT + 1.44 test diff --git a/scm-dao-xml/pom.xml b/scm-dao-xml/pom.xml index 7ecb0321d0..5dc3d0858f 100644 --- a/scm-dao-xml/pom.xml +++ b/scm-dao-xml/pom.xml @@ -6,12 +6,12 @@ sonia.scm scm - 1.44-SNAPSHOT + 1.44 sonia.scm scm-dao-xml - 1.44-SNAPSHOT + 1.44 scm-dao-xml @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.44-SNAPSHOT + 1.44 @@ -34,7 +34,7 @@ sonia.scm scm-test - 1.44-SNAPSHOT + 1.44 test diff --git a/scm-plugin-backend/pom.xml b/scm-plugin-backend/pom.xml index b953853a5a..d2288eaf8c 100644 --- a/scm-plugin-backend/pom.xml +++ b/scm-plugin-backend/pom.xml @@ -6,13 +6,13 @@ scm sonia.scm - 1.44-SNAPSHOT + 1.44 sonia.scm scm-plugin-backend war - 1.44-SNAPSHOT + 1.44 ${project.artifactId} @@ -62,7 +62,7 @@ sonia.scm scm-core - 1.44-SNAPSHOT + 1.44 diff --git a/scm-plugins/pom.xml b/scm-plugins/pom.xml index f8a7a3c7fa..bf3b1778be 100644 --- a/scm-plugins/pom.xml +++ b/scm-plugins/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.44-SNAPSHOT + 1.44 sonia.scm.plugins scm-plugins pom - 1.44-SNAPSHOT + 1.44 scm-plugins @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.44-SNAPSHOT + 1.44 @@ -59,7 +59,7 @@ sonia.scm.maven scm-maven-plugin - 1.44-SNAPSHOT + 1.44 process-resources diff --git a/scm-plugins/scm-git-plugin/pom.xml b/scm-plugins/scm-git-plugin/pom.xml index 705ce0c80b..014b0ec246 100644 --- a/scm-plugins/scm-git-plugin/pom.xml +++ b/scm-plugins/scm-git-plugin/pom.xml @@ -6,12 +6,12 @@ scm-plugins sonia.scm.plugins - 1.44-SNAPSHOT + 1.44 sonia.scm.plugins scm-git-plugin - 1.44-SNAPSHOT + 1.44 scm-git-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Git @@ -48,7 +48,7 @@ sonia.scm scm-test - 1.44-SNAPSHOT + 1.44 test diff --git a/scm-plugins/scm-hg-plugin/pom.xml b/scm-plugins/scm-hg-plugin/pom.xml index 59d8bc0409..55207b4920 100644 --- a/scm-plugins/scm-hg-plugin/pom.xml +++ b/scm-plugins/scm-hg-plugin/pom.xml @@ -6,12 +6,12 @@ sonia.scm.plugins scm-plugins - 1.44-SNAPSHOT + 1.44 sonia.scm.plugins scm-hg-plugin - 1.44-SNAPSHOT + 1.44 scm-hg-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Mercurial @@ -42,7 +42,7 @@ sonia.scm scm-test - 1.44-SNAPSHOT + 1.44 test diff --git a/scm-plugins/scm-svn-plugin/pom.xml b/scm-plugins/scm-svn-plugin/pom.xml index 60a5320020..67488cdbc2 100644 --- a/scm-plugins/scm-svn-plugin/pom.xml +++ b/scm-plugins/scm-svn-plugin/pom.xml @@ -6,12 +6,12 @@ scm-plugins sonia.scm.plugins - 1.44-SNAPSHOT + 1.44 sonia.scm.plugins scm-svn-plugin - 1.44-SNAPSHOT + 1.44 scm-svn-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Subversion @@ -48,7 +48,7 @@ sonia.scm scm-test - 1.44-SNAPSHOT + 1.44 test diff --git a/scm-samples/pom.xml b/scm-samples/pom.xml index 16dd38de33..75a990be9f 100644 --- a/scm-samples/pom.xml +++ b/scm-samples/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.44-SNAPSHOT + 1.44 sonia.scm.samples scm-samples pom - 1.44-SNAPSHOT + 1.44 scm-samples diff --git a/scm-samples/scm-sample-auth/pom.xml b/scm-samples/scm-sample-auth/pom.xml index d2464d632e..c70dd0e74d 100644 --- a/scm-samples/scm-sample-auth/pom.xml +++ b/scm-samples/scm-sample-auth/pom.xml @@ -6,12 +6,12 @@ scm-samples sonia.scm.samples - 1.44-SNAPSHOT + 1.44 sonia.scm.sample scm-sample-auth - 1.44-SNAPSHOT + 1.44 scm-sample-auth Sample Authentication Plugin https://bitbucket.org/sdorra/scm-manager @@ -28,7 +28,7 @@ sonia.scm scm-core - 1.44-SNAPSHOT + 1.44 diff --git a/scm-samples/scm-sample-hello/pom.xml b/scm-samples/scm-sample-hello/pom.xml index 9de39341be..464c7e0516 100644 --- a/scm-samples/scm-sample-hello/pom.xml +++ b/scm-samples/scm-sample-hello/pom.xml @@ -6,12 +6,12 @@ scm-samples sonia.scm.samples - 1.44-SNAPSHOT + 1.44 sonia.scm.sample scm-sample-hello - 1.44-SNAPSHOT + 1.44 scm-sample-hello A simple hello world plugin https://bitbucket.org/sdorra/scm-manager @@ -28,7 +28,7 @@ sonia.scm scm-core - 1.44-SNAPSHOT + 1.44 diff --git a/scm-server/pom.xml b/scm-server/pom.xml index aea132f962..bbff7f17b7 100644 --- a/scm-server/pom.xml +++ b/scm-server/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.44-SNAPSHOT + 1.44 sonia.scm scm-server - 1.44-SNAPSHOT + 1.44 scm-server jar diff --git a/scm-test/pom.xml b/scm-test/pom.xml index 2d90b3b830..75b58f4159 100644 --- a/scm-test/pom.xml +++ b/scm-test/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.44-SNAPSHOT + 1.44 sonia.scm scm-test - 1.44-SNAPSHOT + 1.44 scm-test @@ -25,7 +25,7 @@ sonia.scm scm-core - 1.44-SNAPSHOT + 1.44 diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index 2b5afe1548..1f10c95941 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.44-SNAPSHOT + 1.44 sonia.scm scm-webapp war - 1.44-SNAPSHOT + 1.44 scm-webapp @@ -38,31 +38,31 @@ sonia.scm scm-core - 1.44-SNAPSHOT + 1.44 sonia.scm scm-dao-xml - 1.44-SNAPSHOT + 1.44 sonia.scm.plugins scm-hg-plugin - 1.44-SNAPSHOT + 1.44 sonia.scm.plugins scm-svn-plugin - 1.44-SNAPSHOT + 1.44 sonia.scm.plugins scm-git-plugin - 1.44-SNAPSHOT + 1.44 @@ -280,7 +280,7 @@ sonia.scm scm-test - 1.44-SNAPSHOT + 1.44 test @@ -293,7 +293,7 @@ sonia.scm.plugins scm-git-plugin - 1.44-SNAPSHOT + 1.44 tests test @@ -301,7 +301,7 @@ sonia.scm.plugins scm-hg-plugin - 1.44-SNAPSHOT + 1.44 tests test @@ -309,7 +309,7 @@ sonia.scm.plugins scm-svn-plugin - 1.44-SNAPSHOT + 1.44 tests test @@ -535,7 +535,7 @@ sonia.scm scm-dao-orientdb - 1.44-SNAPSHOT + 1.44 diff --git a/support/pom.xml b/support/pom.xml index 3b85b68513..789cf26b89 100644 --- a/support/pom.xml +++ b/support/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.44-SNAPSHOT + 1.44 sonia.scm.support scm-support pom - 1.44-SNAPSHOT + 1.44 scm-support diff --git a/support/scm-support-btrace/pom.xml b/support/scm-support-btrace/pom.xml index 3aca5ee6b7..309dd9d9d5 100644 --- a/support/scm-support-btrace/pom.xml +++ b/support/scm-support-btrace/pom.xml @@ -4,12 +4,12 @@ sonia.scm.support scm-support - 1.44-SNAPSHOT + 1.44 sonia.scm scm-support-btrace - 1.44-SNAPSHOT + 1.44 jar scm-support-btrace @@ -18,7 +18,7 @@ sonia.scm scm-core - 1.44-SNAPSHOT + 1.44 From f731f7fc2a8f713df2b4c196673fde0d58cc36e4 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 23 Dec 2014 12:24:56 +0100 Subject: [PATCH 010/171] [maven-release-plugin] copy for tag 1.44 From 2254f05af8002b170d6ed92fe59cdb811f11fed9 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 23 Dec 2014 12:24:57 +0100 Subject: [PATCH 011/171] [maven-release-plugin] prepare for next development iteration --- maven/pom.xml | 4 ++-- maven/scm-maven-plugin/pom.xml | 4 ++-- maven/scm-plugin-archetype/pom.xml | 4 ++-- pom.xml | 4 ++-- scm-clients/pom.xml | 6 +++--- scm-clients/scm-cli-client/pom.xml | 6 +++--- scm-clients/scm-client-api/pom.xml | 4 ++-- scm-clients/scm-client-impl/pom.xml | 8 ++++---- scm-core/pom.xml | 4 ++-- scm-dao-orientdb/pom.xml | 8 ++++---- scm-dao-xml/pom.xml | 8 ++++---- scm-plugin-backend/pom.xml | 6 +++--- scm-plugins/pom.xml | 8 ++++---- scm-plugins/scm-git-plugin/pom.xml | 6 +++--- scm-plugins/scm-hg-plugin/pom.xml | 6 +++--- scm-plugins/scm-svn-plugin/pom.xml | 6 +++--- scm-samples/pom.xml | 4 ++-- scm-samples/scm-sample-auth/pom.xml | 6 +++--- scm-samples/scm-sample-hello/pom.xml | 6 +++--- scm-server/pom.xml | 4 ++-- scm-test/pom.xml | 6 +++--- scm-webapp/pom.xml | 24 ++++++++++++------------ support/pom.xml | 4 ++-- support/scm-support-btrace/pom.xml | 6 +++--- 24 files changed, 76 insertions(+), 76 deletions(-) diff --git a/maven/pom.xml b/maven/pom.xml index cef2fec953..6daff8cea8 100644 --- a/maven/pom.xml +++ b/maven/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.44 + 1.45-SNAPSHOT sonia.scm.maven scm-maven-plugins pom - 1.44 + 1.45-SNAPSHOT scm-maven-plugins diff --git a/maven/scm-maven-plugin/pom.xml b/maven/scm-maven-plugin/pom.xml index 8ca33beb61..318aea3aca 100644 --- a/maven/scm-maven-plugin/pom.xml +++ b/maven/scm-maven-plugin/pom.xml @@ -6,12 +6,12 @@ scm-maven-plugins sonia.scm.maven - 1.44 + 1.45-SNAPSHOT sonia.scm.maven scm-maven-plugin - 1.44 + 1.45-SNAPSHOT maven-plugin scm-maven-plugin diff --git a/maven/scm-plugin-archetype/pom.xml b/maven/scm-plugin-archetype/pom.xml index c318d1e564..bf73b724c1 100644 --- a/maven/scm-plugin-archetype/pom.xml +++ b/maven/scm-plugin-archetype/pom.xml @@ -6,12 +6,12 @@ scm-maven-plugins sonia.scm.maven - 1.44 + 1.45-SNAPSHOT sonia.scm.maven scm-plugin-archetype - 1.44 + 1.45-SNAPSHOT scm-plugin-archetype diff --git a/pom.xml b/pom.xml index 9e9ae05f0b..00ae706d05 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ sonia.scm scm pom - 1.44 + 1.45-SNAPSHOT The easiest way to share your Git, Mercurial and Subversion repositories over http. @@ -36,7 +36,7 @@ scm:hg:http://bitbucket.org/sdorra/scm-manager scm:hg:https://bitbucket.org/sdorra/scm-manager http://bitbucket.org/sdorra/scm-manager - 1.44 + HEAD diff --git a/scm-clients/pom.xml b/scm-clients/pom.xml index 4095d897a8..c47500616b 100644 --- a/scm-clients/pom.xml +++ b/scm-clients/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.44 + 1.45-SNAPSHOT sonia.scm.clients scm-clients pom - 1.44 + 1.45-SNAPSHOT scm-clients @@ -32,7 +32,7 @@ scm-core sonia.scm jar - 1.44 + 1.45-SNAPSHOT shiro-core diff --git a/scm-clients/scm-cli-client/pom.xml b/scm-clients/scm-cli-client/pom.xml index ebe281dd81..bd20c8285d 100644 --- a/scm-clients/scm-cli-client/pom.xml +++ b/scm-clients/scm-cli-client/pom.xml @@ -6,12 +6,12 @@ scm-clients sonia.scm.clients - 1.44 + 1.45-SNAPSHOT sonia.scm.clients scm-cli-client - 1.44 + 1.45-SNAPSHOT scm-cli-client @@ -34,7 +34,7 @@ sonia.scm.clients scm-client-impl - 1.44 + 1.45-SNAPSHOT diff --git a/scm-clients/scm-client-api/pom.xml b/scm-clients/scm-client-api/pom.xml index 9257e32b20..2c474990c1 100644 --- a/scm-clients/scm-client-api/pom.xml +++ b/scm-clients/scm-client-api/pom.xml @@ -6,13 +6,13 @@ sonia.scm.clients scm-clients - 1.44 + 1.45-SNAPSHOT sonia.scm.clients scm-client-api jar - 1.44 + 1.45-SNAPSHOT scm-client-api diff --git a/scm-clients/scm-client-impl/pom.xml b/scm-clients/scm-client-impl/pom.xml index 4742b00d7f..8c1c773507 100644 --- a/scm-clients/scm-client-impl/pom.xml +++ b/scm-clients/scm-client-impl/pom.xml @@ -6,13 +6,13 @@ sonia.scm.clients scm-clients - 1.44 + 1.45-SNAPSHOT sonia.scm.clients scm-client-impl jar - 1.44 + 1.45-SNAPSHOT scm-client-impl @@ -36,7 +36,7 @@ sonia.scm.clients scm-client-api - 1.44 + 1.45-SNAPSHOT @@ -70,7 +70,7 @@ sonia.scm scm-test - 1.44 + 1.45-SNAPSHOT test diff --git a/scm-core/pom.xml b/scm-core/pom.xml index cd6bbb0da9..30149cef7e 100644 --- a/scm-core/pom.xml +++ b/scm-core/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.44 + 1.45-SNAPSHOT sonia.scm scm-core - 1.44 + 1.45-SNAPSHOT scm-core diff --git a/scm-dao-orientdb/pom.xml b/scm-dao-orientdb/pom.xml index 10f4bdacd0..ed8f74b0d6 100644 --- a/scm-dao-orientdb/pom.xml +++ b/scm-dao-orientdb/pom.xml @@ -6,12 +6,12 @@ sonia.scm scm - 1.44 + 1.45-SNAPSHOT sonia.scm scm-dao-orientdb - 1.44 + 1.45-SNAPSHOT scm-dao-orientdb @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.44 + 1.45-SNAPSHOT @@ -52,7 +52,7 @@ sonia.scm scm-test - 1.44 + 1.45-SNAPSHOT test diff --git a/scm-dao-xml/pom.xml b/scm-dao-xml/pom.xml index 5dc3d0858f..67ce49d61e 100644 --- a/scm-dao-xml/pom.xml +++ b/scm-dao-xml/pom.xml @@ -6,12 +6,12 @@ sonia.scm scm - 1.44 + 1.45-SNAPSHOT sonia.scm scm-dao-xml - 1.44 + 1.45-SNAPSHOT scm-dao-xml @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.44 + 1.45-SNAPSHOT @@ -34,7 +34,7 @@ sonia.scm scm-test - 1.44 + 1.45-SNAPSHOT test diff --git a/scm-plugin-backend/pom.xml b/scm-plugin-backend/pom.xml index d2288eaf8c..d6ca0a3d5b 100644 --- a/scm-plugin-backend/pom.xml +++ b/scm-plugin-backend/pom.xml @@ -6,13 +6,13 @@ scm sonia.scm - 1.44 + 1.45-SNAPSHOT sonia.scm scm-plugin-backend war - 1.44 + 1.45-SNAPSHOT ${project.artifactId} @@ -62,7 +62,7 @@ sonia.scm scm-core - 1.44 + 1.45-SNAPSHOT diff --git a/scm-plugins/pom.xml b/scm-plugins/pom.xml index bf3b1778be..d2eb4e04e0 100644 --- a/scm-plugins/pom.xml +++ b/scm-plugins/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.44 + 1.45-SNAPSHOT sonia.scm.plugins scm-plugins pom - 1.44 + 1.45-SNAPSHOT scm-plugins @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.44 + 1.45-SNAPSHOT @@ -59,7 +59,7 @@ sonia.scm.maven scm-maven-plugin - 1.44 + 1.45-SNAPSHOT process-resources diff --git a/scm-plugins/scm-git-plugin/pom.xml b/scm-plugins/scm-git-plugin/pom.xml index 014b0ec246..bbacdb4cb9 100644 --- a/scm-plugins/scm-git-plugin/pom.xml +++ b/scm-plugins/scm-git-plugin/pom.xml @@ -6,12 +6,12 @@ scm-plugins sonia.scm.plugins - 1.44 + 1.45-SNAPSHOT sonia.scm.plugins scm-git-plugin - 1.44 + 1.45-SNAPSHOT scm-git-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Git @@ -48,7 +48,7 @@ sonia.scm scm-test - 1.44 + 1.45-SNAPSHOT test diff --git a/scm-plugins/scm-hg-plugin/pom.xml b/scm-plugins/scm-hg-plugin/pom.xml index 55207b4920..e37513a6a5 100644 --- a/scm-plugins/scm-hg-plugin/pom.xml +++ b/scm-plugins/scm-hg-plugin/pom.xml @@ -6,12 +6,12 @@ sonia.scm.plugins scm-plugins - 1.44 + 1.45-SNAPSHOT sonia.scm.plugins scm-hg-plugin - 1.44 + 1.45-SNAPSHOT scm-hg-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Mercurial @@ -42,7 +42,7 @@ sonia.scm scm-test - 1.44 + 1.45-SNAPSHOT test diff --git a/scm-plugins/scm-svn-plugin/pom.xml b/scm-plugins/scm-svn-plugin/pom.xml index 67488cdbc2..ca48107018 100644 --- a/scm-plugins/scm-svn-plugin/pom.xml +++ b/scm-plugins/scm-svn-plugin/pom.xml @@ -6,12 +6,12 @@ scm-plugins sonia.scm.plugins - 1.44 + 1.45-SNAPSHOT sonia.scm.plugins scm-svn-plugin - 1.44 + 1.45-SNAPSHOT scm-svn-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Subversion @@ -48,7 +48,7 @@ sonia.scm scm-test - 1.44 + 1.45-SNAPSHOT test diff --git a/scm-samples/pom.xml b/scm-samples/pom.xml index 75a990be9f..46867de76b 100644 --- a/scm-samples/pom.xml +++ b/scm-samples/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.44 + 1.45-SNAPSHOT sonia.scm.samples scm-samples pom - 1.44 + 1.45-SNAPSHOT scm-samples diff --git a/scm-samples/scm-sample-auth/pom.xml b/scm-samples/scm-sample-auth/pom.xml index c70dd0e74d..0ed3c5275b 100644 --- a/scm-samples/scm-sample-auth/pom.xml +++ b/scm-samples/scm-sample-auth/pom.xml @@ -6,12 +6,12 @@ scm-samples sonia.scm.samples - 1.44 + 1.45-SNAPSHOT sonia.scm.sample scm-sample-auth - 1.44 + 1.45-SNAPSHOT scm-sample-auth Sample Authentication Plugin https://bitbucket.org/sdorra/scm-manager @@ -28,7 +28,7 @@ sonia.scm scm-core - 1.44 + 1.45-SNAPSHOT diff --git a/scm-samples/scm-sample-hello/pom.xml b/scm-samples/scm-sample-hello/pom.xml index 464c7e0516..c3ff8dc498 100644 --- a/scm-samples/scm-sample-hello/pom.xml +++ b/scm-samples/scm-sample-hello/pom.xml @@ -6,12 +6,12 @@ scm-samples sonia.scm.samples - 1.44 + 1.45-SNAPSHOT sonia.scm.sample scm-sample-hello - 1.44 + 1.45-SNAPSHOT scm-sample-hello A simple hello world plugin https://bitbucket.org/sdorra/scm-manager @@ -28,7 +28,7 @@ sonia.scm scm-core - 1.44 + 1.45-SNAPSHOT diff --git a/scm-server/pom.xml b/scm-server/pom.xml index bbff7f17b7..b0939374cd 100644 --- a/scm-server/pom.xml +++ b/scm-server/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.44 + 1.45-SNAPSHOT sonia.scm scm-server - 1.44 + 1.45-SNAPSHOT scm-server jar diff --git a/scm-test/pom.xml b/scm-test/pom.xml index 75b58f4159..872902dbd1 100644 --- a/scm-test/pom.xml +++ b/scm-test/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.44 + 1.45-SNAPSHOT sonia.scm scm-test - 1.44 + 1.45-SNAPSHOT scm-test @@ -25,7 +25,7 @@ sonia.scm scm-core - 1.44 + 1.45-SNAPSHOT diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index 1f10c95941..6bdb76b94a 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.44 + 1.45-SNAPSHOT sonia.scm scm-webapp war - 1.44 + 1.45-SNAPSHOT scm-webapp @@ -38,31 +38,31 @@ sonia.scm scm-core - 1.44 + 1.45-SNAPSHOT sonia.scm scm-dao-xml - 1.44 + 1.45-SNAPSHOT sonia.scm.plugins scm-hg-plugin - 1.44 + 1.45-SNAPSHOT sonia.scm.plugins scm-svn-plugin - 1.44 + 1.45-SNAPSHOT sonia.scm.plugins scm-git-plugin - 1.44 + 1.45-SNAPSHOT @@ -280,7 +280,7 @@ sonia.scm scm-test - 1.44 + 1.45-SNAPSHOT test @@ -293,7 +293,7 @@ sonia.scm.plugins scm-git-plugin - 1.44 + 1.45-SNAPSHOT tests test @@ -301,7 +301,7 @@ sonia.scm.plugins scm-hg-plugin - 1.44 + 1.45-SNAPSHOT tests test @@ -309,7 +309,7 @@ sonia.scm.plugins scm-svn-plugin - 1.44 + 1.45-SNAPSHOT tests test @@ -535,7 +535,7 @@ sonia.scm scm-dao-orientdb - 1.44 + 1.45-SNAPSHOT diff --git a/support/pom.xml b/support/pom.xml index 789cf26b89..82e84ed222 100644 --- a/support/pom.xml +++ b/support/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.44 + 1.45-SNAPSHOT sonia.scm.support scm-support pom - 1.44 + 1.45-SNAPSHOT scm-support diff --git a/support/scm-support-btrace/pom.xml b/support/scm-support-btrace/pom.xml index 309dd9d9d5..9c3ad54f85 100644 --- a/support/scm-support-btrace/pom.xml +++ b/support/scm-support-btrace/pom.xml @@ -4,12 +4,12 @@ sonia.scm.support scm-support - 1.44 + 1.45-SNAPSHOT sonia.scm scm-support-btrace - 1.44 + 1.45-SNAPSHOT jar scm-support-btrace @@ -18,7 +18,7 @@ sonia.scm scm-core - 1.44 + 1.45-SNAPSHOT From f578b3d5b7d3ae4658d8fe0bd347864fa3093ef2 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 7 Jan 2015 08:09:03 +0100 Subject: [PATCH 012/171] increase timeout for directory import from 30 seconds to 5 minutes, to fix issue #662 --- .../resources/js/repository/sonia.repository.importwindow.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.importwindow.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.importwindow.js index 84e2b4009f..209b554e82 100644 --- a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.importwindow.js +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.importwindow.js @@ -534,6 +534,7 @@ Sonia.repository.ImportPanel = Ext.extend(Ext.Panel, { var lbox = this.showLoadingBox(); Ext.Ajax.request({ url: restUrl + 'import/repositories/' + this.repositoryType + '/directory.json', + timeout: 300000, // 5min method: 'POST', scope: this, success: function(response){ From 0609a7f45cd8a6c513ba02713cadd6ce069f97ac Mon Sep 17 00:00:00 2001 From: Stephan Christann Date: Thu, 15 Jan 2015 11:09:22 +0100 Subject: [PATCH 013/171] Usability of init script improved. --- scm-server/src/main/nativepkg/init-script | 26 ++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/scm-server/src/main/nativepkg/init-script b/scm-server/src/main/nativepkg/init-script index 5e6cac53f3..db2d995147 100644 --- a/scm-server/src/main/nativepkg/init-script +++ b/scm-server/src/main/nativepkg/init-script @@ -62,20 +62,33 @@ appname=ScmServerDaemon # See how we were called. start() { - /opt/scm-server/bin/scm-server start + if [ $(ps aux | grep java | grep ${appname} | wc -l) = 0 ] + then + echo "SCM-Server will now be started" + /opt/scm-server/bin/scm-server start + else + echo "SCM-Server already running" + status + fi } stop() { - if [ ! status = 0 ] + if [ ! $(ps aux | grep java | grep ${appname} | wc -l) = 0 ] then + echo "SCM-Server will now be stopped" /opt/scm-server/bin/scm-server stop else - echo "SCM-Server is not running" + echo "SCM-Server is not running" fi } status() { - ps auxwww | grep java | grep ${appname} || echo "SCM-Server is not running" + if [ "$(ps auxwww | grep java | grep ${appname} | awk '{ print $1 " PID:" $2 }')" ]; then + echo "SCM-Server is running" + ps auxwww | grep java | grep ${appname} | awk '{ print " PID: " $2 }' + else + echo "SCM-Server is not running" + fi } restart() { @@ -84,6 +97,7 @@ restart() { STAT=$( ps auxwww | grep java | grep ${appname} | wc -l ) while [ $STAT -ne 0 ] do + echo -n . sleep 3 if [ $SECONDS -gt 300 ] then @@ -92,7 +106,9 @@ restart() { fi STAT=$( ps auxwww | grep java | grep ${appname} | wc -l ) done + status start + status } # See how we were called. @@ -115,4 +131,4 @@ case "$1" in exit 1 esac -exit $RETVAL \ No newline at end of file +exit $RETVAL From e2b4862ad4f14a6ef87dad219f38ecfb87497747 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 18 Jan 2015 18:32:06 +0100 Subject: [PATCH 014/171] retrieve only new git commits, do not collect commits from existing branches --- pom.xml | 2 +- .../repository/GitHookChangesetCollector.java | 45 ++-- .../scm/web/CollectingPackParserListener.java | 195 ++++++++++++++++++ .../sonia/scm/web/GitReceivePackFactory.java | 4 +- 4 files changed, 229 insertions(+), 17 deletions(-) create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/CollectingPackParserListener.java diff --git a/pom.xml b/pom.xml index 00ae706d05..e4194b42ff 100644 --- a/pom.xml +++ b/pom.xml @@ -439,7 +439,7 @@ 1.2.3 - 3.5.3.201412180710-r + 3.5.3.201412180710-r-scm1 1.8.5-scm2 diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitHookChangesetCollector.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitHookChangesetCollector.java index 7ba8b7106b..471f2450c9 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitHookChangesetCollector.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitHookChangesetCollector.java @@ -49,6 +49,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.util.IOUtil; +import sonia.scm.web.CollectingPackParserListener; //~--- JDK imports ------------------------------------------------------------ @@ -56,6 +57,7 @@ import java.io.IOException; import java.util.List; + /** * * @author Sebastian Sdorra @@ -83,6 +85,7 @@ public class GitHookChangesetCollector { this.rpack = rpack; this.receiveCommands = receiveCommands; + this.listener = CollectingPackParserListener.get(rpack); } //~--- methods -------------------------------------------------------------- @@ -110,6 +113,16 @@ 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+ + if (rc.getType() != ReceiveCommand.Type.DELETE) { try @@ -163,16 +176,6 @@ public class GitHookChangesetCollector GitChangesetConverter converter, RevWalk walk, ReceiveCommand rc) throws IncorrectObjectTypeException, IOException { - //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()); @@ -201,14 +204,23 @@ public class GitHookChangesetCollector while (commit != null) { - // parse commit body to avoid npe - walk.parseBody(commit); + // only append new commits + if (listener.isNew(commit)) + { - Changeset changeset = converter.createChangeset(commit, branches); + // parse commit body to avoid npe + walk.parseBody(commit); - logger.trace("retrive commit {} for hook", changeset.getId()); + Changeset changeset = converter.createChangeset(commit, branches); - changesets.add(changeset); + logger.trace("retrieve commit {} for hook", changeset.getId()); + + changesets.add(changeset); + } + else + { + logger.trace("commit {} was already received", commit.getId()); + } commit = walk.next(); } @@ -216,6 +228,9 @@ public class GitHookChangesetCollector //~--- fields --------------------------------------------------------------- + /** listener to track new objects */ + private final CollectingPackParserListener listener; + /** Field description */ private final List receiveCommands; diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/CollectingPackParserListener.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/CollectingPackParserListener.java new file mode 100644 index 0000000000..fdb96d18d7 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/CollectingPackParserListener.java @@ -0,0 +1,195 @@ +/** + * 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.web; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; + +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectIdSubclassMap; +import org.eclipse.jgit.revwalk.RevObject; +import org.eclipse.jgit.transport.BaseReceivePack; +import org.eclipse.jgit.transport.BaseReceivePack.PackParserListener; +import org.eclipse.jgit.transport.PackParser; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.Set; + +/** + * Implementation of {@link PackParserListener} to collect every object which is + * pushed with the reveive pack. The listener is used to find out which object + * is new and which was already pushed. + * + * @author Sebastian Sdorra + */ +public class CollectingPackParserListener implements PackParserListener +{ + + /** + * the logger for CollectingPackParserListener + */ + private static final Logger logger = + LoggerFactory.getLogger(CollectingPackParserListener.class); + + //~--- get methods ---------------------------------------------------------- + + /** + * Returns the listener from the receive pack. + * + * + * @param pack receive pack + * + * @return listener + */ + public static CollectingPackParserListener get(BaseReceivePack pack) + { + PackParserListener listener = pack.getPackParserListener(); + + if (listener == null) + { + throw new IllegalArgumentException( + "receive pack does not contain a listener"); + } + + Preconditions.checkArgument( + listener instanceof CollectingPackParserListener, + "listener is not a CollectingPackParserListener"); + + return (CollectingPackParserListener) listener; + } + + //~--- set methods ---------------------------------------------------------- + + /** + * Applies the listener to the receive pack. + * + * + * @param pack receive pack + */ + public static void set(BaseReceivePack pack) + { + logger.trace("apply collecting listener to receive pack"); + pack.setPackParserListener(new CollectingPackParserListener()); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Collects all new object ids. + * + * + * @param parser pack parser + */ + @Override + public void after(PackParser parser) + { + logger.trace("retrieve new object ids from pack parser"); + + ObjectIdSubclassMap newObjectIdMap = parser.getNewObjectIds(); + + if (newObjectIdMap != null) + { + newObjectIds = ImmutableSet.copyOf(newObjectIdMap); + } + else + { + logger.warn("pack parser returned no newObjectIds"); + newObjectIds = ImmutableSet.of(); + } + + if (newObjectIds.isEmpty()) + { + logger.debug("new object ids are empty, we treat every commit as new"); + } + else + { + logger.debug("collected {} new object ids", newObjectIds.size()); + } + } + + /** + * Prepares the pack parser to retrieve the new object ids. + * + * + * @param parser pack parser + */ + @Override + public void before(PackParser parser) + { + logger.trace("prepare pack parser to collect new object ids"); + parser.setNeedNewObjectIds(true); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Returns {@code true} if the object is a new object. The method will also + * return {@code true}, if the pack parser does not return a list with new + * object ids. + * + * + * @param object rev object + * + * @return {@code true} if the object is new + */ + public boolean isNew(RevObject object) + { + ensureAfterWasCalled(); + + return newObjectIds.isEmpty() || newObjectIds.contains(object.getId()); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Throws an {@link IllegalStateException} if the after method was not called. + */ + private void ensureAfterWasCalled() + { + if (newObjectIds == null) + { + throw new IllegalStateException( "Pack parser seem not to be finished. " + + "The receive pack has not called the after method of the listener."); + } + } + + //~--- fields --------------------------------------------------------------- + + /** set of new object ids */ + private Set newObjectIds; +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitReceivePackFactory.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitReceivePackFactory.java index 535f0a2714..25bbe04cfc 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitReceivePackFactory.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitReceivePackFactory.java @@ -96,7 +96,9 @@ public class GitReceivePackFactory rpack.setPreReceiveHook(hook); rpack.setPostReceiveHook(hook); - + // apply collecting listener, to be able to check which commits are new + CollectingPackParserListener.set(rpack); + return rpack; } From dcbd1d597e822b31a0e3881c1194eea21b8dfad1 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 18 Jan 2015 21:26:39 +0100 Subject: [PATCH 015/171] avoid receiving duplicate git commits, during a push with multiple new branches --- .../java/sonia/scm/repository/Changeset.java | 9 +++- .../scm/repository/GitChangesetConverter.java | 17 +++++++ .../repository/GitHookChangesetCollector.java | 50 ++++++++++++------- 3 files changed, 55 insertions(+), 21 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/repository/Changeset.java b/scm-core/src/main/java/sonia/scm/repository/Changeset.java index dc12dc382f..fecfd74cc9 100644 --- a/scm-core/src/main/java/sonia/scm/repository/Changeset.java +++ b/scm-core/src/main/java/sonia/scm/repository/Changeset.java @@ -132,7 +132,9 @@ public class Changeset extends BasicPropertiesAware final Changeset other = (Changeset) obj; - return Objects.equal(id, other.id) && Objects.equal(date, other.date) + //J- + return Objects.equal(id, other.id) + && Objects.equal(date, other.date) && Objects.equal(author, other.author) && Objects.equal(description, other.description) && Objects.equal(parents, other.parents) @@ -140,6 +142,7 @@ public class Changeset extends BasicPropertiesAware && Objects.equal(branches, other.branches) && Objects.equal(modifications, other.modifications) && Objects.equal(properties, other.properties); + //J+ } /** @@ -206,7 +209,9 @@ public class Changeset extends BasicPropertiesAware } /** - * Returns the branches of the changeset. + * Returns the branches of the changeset. In the most cases a changeset is + * only related to one branch, but in the case of receive hooks it is possible + * that a changeset is related to more than a branch. * * * @return branches of the changeset diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitChangesetConverter.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitChangesetConverter.java index ab588816ce..a59e3b5754 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitChangesetConverter.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitChangesetConverter.java @@ -160,6 +160,23 @@ public class GitChangesetConverter implements Closeable return createChangeset(commit, branches); } + /** + * Method description + * + * + * @param commit + * @param branch + * + * @return + * + * @throws IOException + */ + public Changeset createChangeset(RevCommit commit, String branch) + throws IOException + { + return createChangeset(commit, Lists.newArrayList(branch)); + } + /** * Method description * diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitHookChangesetCollector.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitHookChangesetCollector.java index 471f2450c9..a13a08997c 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitHookChangesetCollector.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitHookChangesetCollector.java @@ -36,6 +36,7 @@ package sonia.scm.repository; //~--- non-JDK imports -------------------------------------------------------- import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.lib.ObjectId; @@ -56,7 +57,7 @@ import sonia.scm.web.CollectingPackParserListener; import java.io.IOException; import java.util.List; - +import java.util.Map; /** * @@ -98,7 +99,7 @@ public class GitHookChangesetCollector */ public List collectChangesets() { - List changesets = Lists.newArrayList(); + Map changesets = Maps.newLinkedHashMap(); org.eclipse.jgit.lib.Repository repository = rpack.getRepository(); @@ -157,7 +158,7 @@ public class GitHookChangesetCollector GitUtil.release(walk); } - return changesets; + return Lists.newArrayList(changesets.values()); } /** @@ -172,7 +173,7 @@ public class GitHookChangesetCollector * @throws IOException * @throws IncorrectObjectTypeException */ - private void collectChangesets(List changesets, + private void collectChangesets(Map changesets, GitChangesetConverter converter, RevWalk walk, ReceiveCommand rc) throws IncorrectObjectTypeException, IOException { @@ -190,7 +191,7 @@ public class GitHookChangesetCollector ObjectId oldId = rc.getOldId(); - if ((oldId != null) &&!oldId.equals(ObjectId.zeroId())) + if ((oldId != null) && !oldId.equals(ObjectId.zeroId())) { logger.trace("mark {} as uninteresting for rev walk", oldId.getName()); @@ -199,27 +200,38 @@ public class GitHookChangesetCollector RevCommit commit = walk.next(); - List branches = Lists.newArrayList(branch); - while (commit != null) { + String id = commit.getId().name(); + Changeset changeset = changesets.get(id); - // only append new commits - if (listener.isNew(commit)) + if (changeset != null) { - - // parse commit body to avoid npe - walk.parseBody(commit); - - Changeset changeset = converter.createChangeset(commit, branches); - - logger.trace("retrieve commit {} for hook", changeset.getId()); - - changesets.add(changeset); + logger.trace( + "commit {} already received durring this push, add branch {} to the commit", + commit, branch); + changeset.getBranches().add(branch); } else { - logger.trace("commit {} was already received", commit.getId()); + + // only append new commits + if (listener.isNew(commit)) + { + + // parse commit body to avoid npe + walk.parseBody(commit); + + changeset = converter.createChangeset(commit, branch); + + logger.trace("retrieve commit {} for hook", changeset.getId()); + + changesets.put(id, changeset); + } + else + { + logger.trace("commit {} was already received", commit.getId()); + } } commit = walk.next(); From 264cbbc9ad11b90596e8ffe6bab34233da89d3c7 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 18 Jan 2015 21:34:45 +0100 Subject: [PATCH 016/171] do not catch throwable --- scm-core/src/main/java/sonia/scm/BasicContextProvider.java | 2 +- .../src/main/java/sonia/scm/web/cgi/DefaultCGIExecutor.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/BasicContextProvider.java b/scm-core/src/main/java/sonia/scm/BasicContextProvider.java index 35dd2dcca0..f6507fc453 100644 --- a/scm-core/src/main/java/sonia/scm/BasicContextProvider.java +++ b/scm-core/src/main/java/sonia/scm/BasicContextProvider.java @@ -96,7 +96,7 @@ public class BasicContextProvider implements SCMContextProvider version = loadVersion(); stage = loadProjectStage(); } - catch (Throwable ex) + catch (Exception ex) { this.startupError = ex; diff --git a/scm-webapp/src/main/java/sonia/scm/web/cgi/DefaultCGIExecutor.java b/scm-webapp/src/main/java/sonia/scm/web/cgi/DefaultCGIExecutor.java index 52eead7956..95d6ce2575 100644 --- a/scm-webapp/src/main/java/sonia/scm/web/cgi/DefaultCGIExecutor.java +++ b/scm-webapp/src/main/java/sonia/scm/web/cgi/DefaultCGIExecutor.java @@ -190,7 +190,7 @@ public class DefaultCGIExecutor extends AbstractCGIExecutor p = Runtime.getRuntime().exec(execCmd, env.getEnvArray(), workDirectory); execute(p); } - catch (Throwable ex) + catch (IOException ex) { getExceptionHandler().handleException(request, response, ex); } From 31bb6dfe6d0e771861634dbbc9895f960fed7e88 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Mon, 19 Jan 2015 17:54:57 +0100 Subject: [PATCH 017/171] return a property for closed branches --- .../spi/javahg/AbstractChangesetCommand.java | 162 +++++++++--------- .../sonia/scm/styles/changesets-eager.style | 2 +- 2 files changed, 85 insertions(+), 79 deletions(-) diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/AbstractChangesetCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/AbstractChangesetCommand.java index b3e3c2f3a9..b7aa64b801 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/AbstractChangesetCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/AbstractChangesetCommand.java @@ -30,6 +30,7 @@ */ + package sonia.scm.repository.spi.javahg; //~--- non-JDK imports -------------------------------------------------------- @@ -87,6 +88,9 @@ public abstract class AbstractChangesetCommand extends AbstractCommand private static final String NULL_ID = "0000000000000000000000000000000000000000"; + /** changeset property for closed branch */ + private static final String PROPERTY_CLOSE = "hg.close"; + /** changeset property for parent1 revision */ private static final String PROPERTY_PARENT1_REVISION = "hg.p1.rev"; @@ -114,12 +118,80 @@ public abstract class AbstractChangesetCommand extends AbstractCommand //~--- methods -------------------------------------------------------------- + /** + * Method description + * + * + * @param stream + * + * @return + */ + protected List loadRevisionsFromStream(HgInputStream stream) + { + List revisions = Lists.newArrayList(); - //~--- get methods ---------------------------------------------------------- + try + { + while (stream.peek() != -1) + { + int rev = stream.revisionUpTo(' '); + if (rev >= 0) + { + revisions.add(rev); + } + } + } + catch (IOException ex) + { + throw new RuntimeIOException(ex); + } - //~--- methods -------------------------------------------------------------- + return revisions; + } + + /** + * Method description + * + * + * @param in + * + * @return + */ + protected List readListFromStream(HgInputStream in) + { + List changesets = Lists.newArrayList(); + + try + { + boolean found = in.find(CHANGESET_PATTERN); + + if (found) + { + while (!in.match(CHANGESET_PATTERN)) + { + + Changeset cset = createFromInputStream(in); + + if (cset != null) + { + changesets.add(cset); + } + } + + Utils.consumeAll(in); + } + + // If the pattern is not found there is no changsets + } + catch (IOException e) + { + throw new RuntimeIOException(e); + } + + return changesets; + } /** * Method description @@ -168,7 +240,16 @@ public abstract class AbstractChangesetCommand extends AbstractCommand changeset.getParents().add(p2); } - in.mustMatch(' '); // skip space part of {parents} + // skip space part of {parents} + in.mustMatch(' '); + + // read extras + String extraLine = in.textUpTo('\n'); + + if (extraLine.contains("close=1")) + { + changeset.getProperties().put(PROPERTY_CLOSE, "true"); + } Modifications modifications = changeset.getModifications(); @@ -204,39 +285,6 @@ public abstract class AbstractChangesetCommand extends AbstractCommand return changeset; } - /** - * Method description - * - * - * @param stream - * - * @return - */ - protected List loadRevisionsFromStream(HgInputStream stream) - { - List revisions = Lists.newArrayList(); - - try - { - while (stream.peek() != -1) - { - int rev = stream.revisionUpTo(' '); - - if (rev >= 0) - { - revisions.add(rev); - } - } - - } - catch (IOException ex) - { - throw new RuntimeIOException(ex); - } - - return revisions; - } - /** * Method description * @@ -268,48 +316,6 @@ public abstract class AbstractChangesetCommand extends AbstractCommand return in.nextAsText(40); } - /** - * Method description - * - * - * @param in - * - * @return - */ - protected List readListFromStream(HgInputStream in) - { - List changesets = Lists.newArrayList(); - - try - { - boolean found = in.find(CHANGESET_PATTERN); - - if (found) - { - while (!in.match(CHANGESET_PATTERN)) - { - - Changeset cset = createFromInputStream(in); - - if (cset != null) - { - changesets.add(cset); - } - } - - Utils.consumeAll(in); - } - - // If the pattern is not found there is no changsets - } - catch (IOException e) - { - throw new RuntimeIOException(e); - } - - return changesets; - } - //~--- get methods ---------------------------------------------------------- /** diff --git a/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/styles/changesets-eager.style b/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/styles/changesets-eager.style index 93b0b04e27..5c462fc459 100644 --- a/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/styles/changesets-eager.style +++ b/scm-plugins/scm-hg-plugin/src/main/resources/sonia/scm/styles/changesets-eager.style @@ -1,5 +1,5 @@ header = "%{pattern}" -changeset = "{rev}:{node}{author}\n{date|hgdate}\n{branch}\n{parents}{tags}{file_adds}{file_mods}{file_dels}\n{desc}\0" +changeset = "{rev}:{node}{author}\n{date|hgdate}\n{branch}\n{parents}{join(extras,',')}\n{tags}{file_adds}{file_mods}{file_dels}\n{desc}\0" tag = "t {tag}\n" file_add = "a {file_add}\n" file_mod = "m {file_mod}\n" From a3735e74173007e756e793dc9e1bf4b948515266 Mon Sep 17 00:00:00 2001 From: Stephan Christann Date: Thu, 22 Jan 2015 14:47:14 +0100 Subject: [PATCH 018/171] added missing shebang statement in create user script --- scm-server/src/main/nativepkg/create-user | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scm-server/src/main/nativepkg/create-user b/scm-server/src/main/nativepkg/create-user index 82398c513c..3ffe4972ce 100644 --- a/scm-server/src/main/nativepkg/create-user +++ b/scm-server/src/main/nativepkg/create-user @@ -1,5 +1,6 @@ +#!/bin/sh getent group scm >/dev/null || groupadd -r scm getent passwd scm >/dev/null || \ useradd -r -g scm -M -s /sbin/nologin \ -c "user for the scm-server process" scm -exit 0 \ No newline at end of file +exit 0 From 6145d3cb57f22620dba2681831916196b690e64c Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Fri, 23 Jan 2015 21:33:28 +0100 Subject: [PATCH 019/171] fix wrong logging --- .../sonia/scm/api/rest/resources/RepositoryImportResource.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java index c6c78b0136..fea00394fa 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java @@ -553,7 +553,8 @@ public class RepositoryImportResource } catch (RepositoryAllreadyExistExeption ex) { - logger.warn("a {} repository with the name {} already exists", ex); + logger.warn("a {} repository with the name {} already exists", type, + name); throw new WebApplicationException(Response.Status.CONFLICT); } From d707f0ee47c7b419d18409c056da1c82c8199e19 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Fri, 23 Jan 2015 21:44:04 +0100 Subject: [PATCH 020/171] servlet fields should be final --- .../sonia/scm/web/proxy/ProxyServlet.java | 2 +- .../java/sonia/scm/web/ScmGitServlet.java | 13 +++++++--- .../main/java/sonia/scm/web/HgCGIServlet.java | 26 ++++++++++++------- .../sonia/scm/web/HgHookCallbackServlet.java | 8 +++--- .../java/sonia/scm/web/SvnDAVServlet.java | 10 +++---- .../resources/AbstractResourceServlet.java | 2 +- .../java/sonia/scm/template/ErrorServlet.java | 4 +-- 7 files changed, 38 insertions(+), 27 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/web/proxy/ProxyServlet.java b/scm-core/src/main/java/sonia/scm/web/proxy/ProxyServlet.java index cc7758d7a9..6787a213f5 100644 --- a/scm-core/src/main/java/sonia/scm/web/proxy/ProxyServlet.java +++ b/scm-core/src/main/java/sonia/scm/web/proxy/ProxyServlet.java @@ -307,5 +307,5 @@ public class ProxyServlet extends HttpServlet //~--- fields --------------------------------------------------------------- /** Field description */ - private ProxyConfigurationProvider configurationProvider; + private final ProxyConfigurationProvider configurationProvider; } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/ScmGitServlet.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/ScmGitServlet.java index dbdd12809a..686e4dfbd7 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/ScmGitServlet.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/ScmGitServlet.java @@ -54,6 +54,7 @@ import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import sonia.scm.repository.RepositoryException; /** * @@ -170,7 +171,11 @@ public class ScmGitServlet extends GitServlet { repositoryViewer.handleRequest(request, response, scmRepository); } - catch (Exception ex) + catch (RepositoryException ex) + { + throw new ServletException("could not create repository view", ex); + } + catch (IOException ex) { throw new ServletException("could not create repository view", ex); } @@ -184,11 +189,11 @@ public class ScmGitServlet extends GitServlet //~--- fields --------------------------------------------------------------- /** Field description */ - private RepositoryProvider repositoryProvider; + private final RepositoryProvider repositoryProvider; /** Field description */ - private RepositoryRequestListenerUtil repositoryRequestListenerUtil; + private final RepositoryRequestListenerUtil repositoryRequestListenerUtil; /** Field description */ - private GitRepositoryViewer repositoryViewer; + private final GitRepositoryViewer repositoryViewer; } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java index a9346b3027..410d20847a 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java @@ -122,6 +122,7 @@ public class HgCGIServlet extends HttpServlet this.hookManager = hookManager; this.requestListenerUtil = requestListenerUtil; this.exceptionHandler = new HgCGIExceptionHandler(); + this.command = HgPythonScript.HGWEB.getFile(SCMContext.getContext()); } //~--- methods -------------------------------------------------------------- @@ -135,7 +136,7 @@ public class HgCGIServlet extends HttpServlet @Override public void init() throws ServletException { - command = HgPythonScript.HGWEB.getFile(SCMContext.getContext()); + super.init(); } @@ -176,7 +177,11 @@ public class HgCGIServlet extends HttpServlet { handleRequest(request, response, repository); } - catch (Exception ex) + catch (ServletException ex) + { + exceptionHandler.handleException(request, response, ex); + } + catch (IOException ex) { exceptionHandler.handleException(request, response, ex); } @@ -258,6 +263,7 @@ public class HgCGIServlet extends HttpServlet executor.getEnvironment().set(ENV_REPOSITORY_NAME, name); executor.getEnvironment().set(ENV_REPOSITORY_PATH, directory.getAbsolutePath()); + // add hook environment //J- HgEnvironment.prepareEnvironment( @@ -312,26 +318,26 @@ public class HgCGIServlet extends HttpServlet //~--- fields --------------------------------------------------------------- /** Field description */ - private CGIExecutorFactory cgiExecutorFactory; + private final CGIExecutorFactory cgiExecutorFactory; /** Field description */ - private File command; + private final File command; /** Field description */ - private ScmConfiguration configuration; + private final ScmConfiguration configuration; /** Field description */ - private HgCGIExceptionHandler exceptionHandler; + private final HgCGIExceptionHandler exceptionHandler; /** Field description */ - private HgRepositoryHandler handler; + private final HgRepositoryHandler handler; /** Field description */ - private HgHookManager hookManager; + private final HgHookManager hookManager; /** Field description */ - private RepositoryProvider repositoryProvider; + private final RepositoryProvider repositoryProvider; /** Field description */ - private RepositoryRequestListenerUtil requestListenerUtil; + private final RepositoryRequestListenerUtil requestListenerUtil; } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgHookCallbackServlet.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgHookCallbackServlet.java index ff4c9863c0..da24e94a16 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgHookCallbackServlet.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgHookCallbackServlet.java @@ -538,14 +538,14 @@ public class HgHookCallbackServlet extends HttpServlet //~--- fields --------------------------------------------------------------- /** Field description */ - private Provider contextProvider; + private final Provider contextProvider; /** Field description */ - private HgRepositoryHandler handler; + private final HgRepositoryHandler handler; /** Field description */ - private HookEventFacade hookEventFacade; + private final HookEventFacade hookEventFacade; /** Field description */ - private HgHookManager hookManager; + private final HgHookManager hookManager; } diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnDAVServlet.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnDAVServlet.java index 701de1d44b..db22857595 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnDAVServlet.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnDAVServlet.java @@ -284,21 +284,21 @@ public class SvnDAVServlet extends DAVServlet //~--- fields ------------------------------------------------------------- /** Field description */ - private RepositoryProvider repositoryProvider; + private final RepositoryProvider repositoryProvider; } //~--- fields --------------------------------------------------------------- /** Field description */ - private SvnCollectionRenderer collectionRenderer; + private final SvnCollectionRenderer collectionRenderer; /** Field description */ - private SvnRepositoryHandler handler; + private final SvnRepositoryHandler handler; /** Field description */ - private RepositoryProvider repositoryProvider; + private final RepositoryProvider repositoryProvider; /** Field description */ - private RepositoryRequestListenerUtil repositoryRequestListenerUtil; + private final RepositoryRequestListenerUtil repositoryRequestListenerUtil; } diff --git a/scm-webapp/src/main/java/sonia/scm/resources/AbstractResourceServlet.java b/scm-webapp/src/main/java/sonia/scm/resources/AbstractResourceServlet.java index a495456596..2488ba8d3f 100644 --- a/scm-webapp/src/main/java/sonia/scm/resources/AbstractResourceServlet.java +++ b/scm-webapp/src/main/java/sonia/scm/resources/AbstractResourceServlet.java @@ -167,5 +167,5 @@ public abstract class AbstractResourceServlet extends HttpServlet //~--- fields --------------------------------------------------------------- /** Field description */ - private ResourceManager resourceManager; + private final ResourceManager resourceManager; } diff --git a/scm-webapp/src/main/java/sonia/scm/template/ErrorServlet.java b/scm-webapp/src/main/java/sonia/scm/template/ErrorServlet.java index 7cfd6342ab..9672aea67b 100644 --- a/scm-webapp/src/main/java/sonia/scm/template/ErrorServlet.java +++ b/scm-webapp/src/main/java/sonia/scm/template/ErrorServlet.java @@ -185,8 +185,8 @@ public class ErrorServlet extends HttpServlet //~--- fields --------------------------------------------------------------- /** Field description */ - private SCMContextProvider context; + private final SCMContextProvider context; /** Field description */ - private TemplateEngineFactory templateEngineFactory; + private final TemplateEngineFactory templateEngineFactory; } From dd802cda69e39bf76d0e10c81efdda8dc327bcb8 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Fri, 23 Jan 2015 22:58:58 +0100 Subject: [PATCH 021/171] fix wrong logging --- .../sonia/scm/api/rest/resources/RepositoryImportResource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java index fea00394fa..65076528d6 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java @@ -693,7 +693,7 @@ public class RepositoryImportResource private void handleGenericCreationFailure(Exception ex, String type, String name) { - logger.error(String.format("could not create repository {} with type {}", + logger.error(String.format("could not create repository %s with type %s", type, name), ex); throw new WebApplicationException(ex); From 436b27e8fc6e7f4dae865c92736dba83a7f29cfa Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sat, 24 Jan 2015 11:08:16 +0100 Subject: [PATCH 022/171] introducing HookBranchProvider to get informations about changed branches during a hook, see issue #668 --- .../repository/api/HookBranchProvider.java | 61 ++++++ .../sonia/scm/repository/api/HookContext.java | 45 ++++- .../sonia/scm/repository/api/HookFeature.java | 21 +- .../repository/spi/HookContextProvider.java | 12 ++ .../repository/api/GitHookBranchProvider.java | 119 ++++++++++++ .../spi/GitHookContextProvider.java | 25 ++- .../repository/api/HgHookBranchProvider.java | 182 ++++++++++++++++++ .../repository/spi/HgHookContextProvider.java | 27 ++- .../spi/javahg/AbstractChangesetCommand.java | 4 +- 9 files changed, 480 insertions(+), 16 deletions(-) create mode 100644 scm-core/src/main/java/sonia/scm/repository/api/HookBranchProvider.java create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/api/GitHookBranchProvider.java create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/api/HgHookBranchProvider.java diff --git a/scm-core/src/main/java/sonia/scm/repository/api/HookBranchProvider.java b/scm-core/src/main/java/sonia/scm/repository/api/HookBranchProvider.java new file mode 100644 index 0000000000..3ad52b995c --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/repository/api/HookBranchProvider.java @@ -0,0 +1,61 @@ +/** + * 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; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.List; + +/** + * The HookBranchProvider returns informations about branch changes during the + * current hook. + * + * @author Sebastian Sdorra + * @since 1.45 + */ +public interface HookBranchProvider +{ + + /** + * Returns the list of created or modified branch names. + * + * @return list of created or modified branches + */ + public List getCreatedOrModified(); + + /** + * Returns the list deleted or closed branch names. + * + * @return list of deleted or closed branches + */ + public List getDeletedOrClosed(); +} diff --git a/scm-core/src/main/java/sonia/scm/repository/api/HookContext.java b/scm-core/src/main/java/sonia/scm/repository/api/HookContext.java index b7e3e9c5e6..8e68c9af28 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/HookContext.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/HookContext.java @@ -43,8 +43,9 @@ import sonia.scm.repository.spi.HookContextProvider; /** * The context for all repository hooks. With the {@link HookContext} class it - * is able to send messages back to the client and retrieve {@link Changeset}s - * which are added during this push/commit. + * is able to send messages back to the client, retrieve {@link Changeset}s + * which are added during this push/commit and gives informations about changed + * branches. * * @author Sebastian Sdorra * @since 1.33 @@ -78,12 +79,37 @@ public final class HookContext //~--- get methods ---------------------------------------------------------- + /** + * Returns a {@link HookBranchProvider} which is able to return informations + * about changed branches during the current hook. + * + * @return {@link HookBranchProvider} + * + * @throws HookFeatureIsNotSupportedException if the feature is not supported + * by the underlying provider + * + * @since 1.45 + */ + public HookBranchProvider getBranchProvider() + { + if (logger.isDebugEnabled()) + { + logger.debug("create branch provider for repository {}", + repository.getName()); + } + + return provider.getBranchProvider(); + } + /** * Returns a {@link HookChangesetBuilder} which is able to return all * {@link Changeset}'s during this push/commit. * * * @return {@link HookChangesetBuilder} + * + * @throws HookFeatureIsNotSupportedException if the feature is not supported + * by the underlying provider */ public HookChangesetBuilder getChangesetProvider() { @@ -111,6 +137,9 @@ public final class HookContext * * @return {@link HookMessageProvider} which is able to send message back to * the scm client + * + * @throws HookFeatureIsNotSupportedException if the feature is not supported + * by the underlying provider */ public HookMessageProvider getMessageProvider() { @@ -138,12 +167,12 @@ public final class HookContext //~--- fields --------------------------------------------------------------- - /** Field description */ - private PreProcessorUtil preProcessorUtil; + /** pre processor util */ + private final PreProcessorUtil preProcessorUtil; - /** Field description */ - private HookContextProvider provider; + /** hook context provider */ + private final HookContextProvider provider; - /** Field description */ - private Repository repository; + /** repository */ + private final Repository repository; } diff --git a/scm-core/src/main/java/sonia/scm/repository/api/HookFeature.java b/scm-core/src/main/java/sonia/scm/repository/api/HookFeature.java index 082f315a35..9594bbc168 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/HookFeature.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/HookFeature.java @@ -37,4 +37,23 @@ package sonia.scm.repository.api; * @author Sebastian Sdorra * @since 1.33 */ -public enum HookFeature { MESSAGE_PROVIDER, CHANGESET_PROVIDER; } +public enum HookFeature +{ + + /** + * Hook message provider + */ + MESSAGE_PROVIDER, + + /** + * Hook changeset provider + */ + CHANGESET_PROVIDER, + + /** + * Hook branch provider + * + * @since 1.45 + */ + BRANCH_PROVIDER; +} diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/HookContextProvider.java b/scm-core/src/main/java/sonia/scm/repository/spi/HookContextProvider.java index 0bc91679b7..0f1d039006 100644 --- a/scm-core/src/main/java/sonia/scm/repository/spi/HookContextProvider.java +++ b/scm-core/src/main/java/sonia/scm/repository/spi/HookContextProvider.java @@ -33,6 +33,7 @@ package sonia.scm.repository.spi; //~--- non-JDK imports -------------------------------------------------------- +import sonia.scm.repository.api.HookBranchProvider; import sonia.scm.repository.api.HookException; import sonia.scm.repository.api.HookFeature; import sonia.scm.repository.api.HookFeatureIsNotSupportedException; @@ -88,6 +89,17 @@ public abstract class HookContextProvider */ public abstract Set getSupportedFeatures(); + /** + * Method description + * + * + * @return + */ + public HookBranchProvider getBranchProvider() + { + throw new HookFeatureIsNotSupportedException(HookFeature.BRANCH_PROVIDER); + } + /** * Method description * diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/api/GitHookBranchProvider.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/api/GitHookBranchProvider.java new file mode 100644 index 0000000000..ecc373e2b8 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/api/GitHookBranchProvider.java @@ -0,0 +1,119 @@ +/** + * 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.collect.ImmutableList; +import com.google.common.collect.ImmutableList.Builder; + +import org.eclipse.jgit.transport.ReceiveCommand; +import org.eclipse.jgit.transport.ReceiveCommand.Type; + +import sonia.scm.repository.GitUtil; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.List; + +/** + * + * @author Sebastian Sdorra + */ +public class GitHookBranchProvider implements HookBranchProvider +{ + + /** + * Constructs ... + * + * + * @param commands + */ + public GitHookBranchProvider(List commands) + { + Builder createdOrModifiedBuilder = ImmutableList.builder(); + Builder deletedOrClosedBuilder = ImmutableList.builder(); + + for (ReceiveCommand command : commands) + { + Type type = command.getType(); + String branch = GitUtil.getBranch(command.getRefName()); + + if ((type == Type.CREATE) || (type == Type.UPDATE) + || (type == Type.UPDATE_NONFASTFORWARD)) + { + createdOrModifiedBuilder.add(branch); + } + else if (command.getType() == Type.DELETE) + { + deletedOrClosedBuilder.add(branch); + } + } + + createdOrModified = createdOrModifiedBuilder.build(); + deletedOrClosed = deletedOrClosedBuilder.build(); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + @Override + public List getCreatedOrModified() + { + return createdOrModified; + } + + /** + * Method description + * + * + * @return + */ + @Override + public List getDeletedOrClosed() + { + return deletedOrClosed; + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private final List createdOrModified; + + /** Field description */ + private final List deletedOrClosed; +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitHookContextProvider.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitHookContextProvider.java index 824247e81f..62b88db1d3 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitHookContextProvider.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitHookContextProvider.java @@ -36,7 +36,9 @@ package sonia.scm.repository.spi; import org.eclipse.jgit.transport.ReceiveCommand; import org.eclipse.jgit.transport.ReceivePack; +import sonia.scm.repository.api.GitHookBranchProvider; import sonia.scm.repository.api.GitHookMessageProvider; +import sonia.scm.repository.api.HookBranchProvider; import sonia.scm.repository.api.HookFeature; import sonia.scm.repository.api.HookMessageProvider; @@ -55,7 +57,8 @@ public class GitHookContextProvider extends HookContextProvider /** Field description */ private static final Set SUPPORTED_FEATURES = - EnumSet.of(HookFeature.MESSAGE_PROVIDER, HookFeature.CHANGESET_PROVIDER); + EnumSet.of(HookFeature.MESSAGE_PROVIDER, HookFeature.CHANGESET_PROVIDER, + HookFeature.BRANCH_PROVIDER); //~--- constructors --------------------------------------------------------- @@ -70,6 +73,7 @@ public class GitHookContextProvider extends HookContextProvider List receiveCommands) { this.receivePack = receivePack; + this.receiveCommands = receiveCommands; this.changesetProvider = new GitHookChangesetProvider(receivePack, receiveCommands); } @@ -90,6 +94,18 @@ public class GitHookContextProvider extends HookContextProvider //~--- get methods ---------------------------------------------------------- + /** + * Method description + * + * + * @return + */ + @Override + public HookBranchProvider getBranchProvider() + { + return new GitHookBranchProvider(receiveCommands); + } + /** * Method description * @@ -117,8 +133,11 @@ public class GitHookContextProvider extends HookContextProvider //~--- fields --------------------------------------------------------------- /** Field description */ - private GitHookChangesetProvider changesetProvider; + private final GitHookChangesetProvider changesetProvider; /** Field description */ - private ReceivePack receivePack; + private final List receiveCommands; + + /** Field description */ + private final ReceivePack receivePack; } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/api/HgHookBranchProvider.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/api/HgHookBranchProvider.java new file mode 100644 index 0000000000..05f05202e7 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/api/HgHookBranchProvider.java @@ -0,0 +1,182 @@ +/** + * 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.collect.ImmutableList; +import com.google.common.collect.ImmutableList.Builder; + +import sonia.scm.repository.Changeset; +import sonia.scm.repository.spi.HgHookChangesetProvider; +import sonia.scm.repository.spi.HookChangesetRequest; +import sonia.scm.repository.spi.javahg.AbstractChangesetCommand; +import sonia.scm.util.Util; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.List; + +/** + * + * @author Sebastian Sdorra + */ +public class HgHookBranchProvider implements HookBranchProvider +{ + + /** Field description */ + private static final HookChangesetRequest REQUEST = + new HookChangesetRequest(); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param changesetProvider + */ + public HgHookBranchProvider(HgHookChangesetProvider changesetProvider) + { + this.changesetProvider = changesetProvider; + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + @Override + public List getCreatedOrModified() + { + if (createdOrModified == null) + { + collect(); + } + + return createdOrModified; + } + + /** + * Method description + * + * + * @return + */ + @Override + public List getDeletedOrClosed() + { + if (deletedOrClosed == null) + { + collect(); + } + + return deletedOrClosed; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param builder + * @param c + * + * @return + */ + private List appendBranches(Builder builder, Changeset c) + { + List branches = c.getBranches(); + + if (Util.isEmpty(branches)) + { + builder.add(AbstractChangesetCommand.BRANCH_DEFAULT); + } + else + { + builder.addAll(branches); + } + + return branches; + } + + /** + * Method description + * + * + * @return + */ + private Iterable changesets() + { + return changesetProvider.handleRequest(REQUEST).getChangesets(); + } + + /** + * Method description + * + */ + private void collect() + { + Builder createdOrModifiedBuilder = ImmutableList.builder(); + Builder deletedOrClosedBuilder = ImmutableList.builder(); + + for (Changeset c : changesets()) + { + if (c.getProperty(AbstractChangesetCommand.PROPERTY_CLOSE) != null) + { + appendBranches(deletedOrClosedBuilder, c); + } + else + { + appendBranches(createdOrModifiedBuilder, c); + } + } + + createdOrModified = createdOrModifiedBuilder.build(); + deletedOrClosed = deletedOrClosedBuilder.build(); + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private final HgHookChangesetProvider changesetProvider; + + /** Field description */ + private List createdOrModified; + + /** Field description */ + private List deletedOrClosed; +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgHookContextProvider.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgHookContextProvider.java index 40d1ab5560..4087d08751 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgHookContextProvider.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgHookContextProvider.java @@ -36,7 +36,9 @@ package sonia.scm.repository.spi; import sonia.scm.repository.HgHookManager; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.repository.RepositoryHookType; +import sonia.scm.repository.api.HgHookBranchProvider; import sonia.scm.repository.api.HgHookMessageProvider; +import sonia.scm.repository.api.HookBranchProvider; import sonia.scm.repository.api.HookFeature; import sonia.scm.repository.api.HookMessageProvider; @@ -54,7 +56,8 @@ public class HgHookContextProvider extends HookContextProvider /** Field description */ private static final Set SUPPORTED_FEATURES = - EnumSet.of(HookFeature.CHANGESET_PROVIDER, HookFeature.MESSAGE_PROVIDER); + EnumSet.of(HookFeature.CHANGESET_PROVIDER, HookFeature.MESSAGE_PROVIDER, + HookFeature.BRANCH_PROVIDER); //~--- constructors --------------------------------------------------------- @@ -78,6 +81,23 @@ public class HgHookContextProvider extends HookContextProvider //~--- get methods ---------------------------------------------------------- + /** + * Method description + * + * + * @return + */ + @Override + public HookBranchProvider getBranchProvider() + { + if (hookBranchProvider == null) + { + hookBranchProvider = new HgHookBranchProvider(hookChangesetProvider); + } + + return hookBranchProvider; + } + /** * Method description * @@ -134,9 +154,12 @@ public class HgHookContextProvider extends HookContextProvider //~--- fields --------------------------------------------------------------- + /** Field description */ + private final HgHookChangesetProvider hookChangesetProvider; + /** Field description */ private HgHookMessageProvider hgMessageProvider; /** Field description */ - private HgHookChangesetProvider hookChangesetProvider; + private HgHookBranchProvider hookBranchProvider; } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/AbstractChangesetCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/AbstractChangesetCommand.java index b7aa64b801..827f86ded1 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/AbstractChangesetCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/AbstractChangesetCommand.java @@ -64,7 +64,7 @@ public abstract class AbstractChangesetCommand extends AbstractCommand { /** Field description */ - private static final String BRANCH_DEFAULT = "default"; + public static final String BRANCH_DEFAULT = "default"; /** * Character sequence that indicate the begin and end of the @@ -89,7 +89,7 @@ public abstract class AbstractChangesetCommand extends AbstractCommand "0000000000000000000000000000000000000000"; /** changeset property for closed branch */ - private static final String PROPERTY_CLOSE = "hg.close"; + public static final String PROPERTY_CLOSE = "hg.close"; /** changeset property for parent1 revision */ private static final String PROPERTY_PARENT1_REVISION = "hg.p1.rev"; From 11f366e68f4fae667d4ed0256a4cfd47bb850373 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sat, 24 Jan 2015 12:54:17 +0100 Subject: [PATCH 023/171] update mockito to version 1.10.19 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e4194b42ff..7cc8af8f47 100644 --- a/pom.xml +++ b/pom.xml @@ -421,7 +421,7 @@ - 1.10.17 + 1.10.19 1.3 4.12 From 8458439682fa98bc7984a19f4ee5c070e5e28c11 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 10 Feb 2015 20:39:39 +0100 Subject: [PATCH 024/171] close branch issue-627 From 919f5843d453feb3ae057bdc73de27ad8a9c23e5 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 10 Feb 2015 21:05:38 +0100 Subject: [PATCH 025/171] javadoc --- .../main/java/sonia/scm/web/UserAgent.java | 79 ++++++++----------- .../java/sonia/scm/web/UserAgentParser.java | 32 ++++---- .../java/sonia/scm/web/UserAgentProvider.java | 10 ++- 3 files changed, 56 insertions(+), 65 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/web/UserAgent.java b/scm-core/src/main/java/sonia/scm/web/UserAgent.java index efb2c1b429..7efd1bd1c2 100644 --- a/scm-core/src/main/java/sonia/scm/web/UserAgent.java +++ b/scm-core/src/main/java/sonia/scm/web/UserAgent.java @@ -45,6 +45,8 @@ import static com.google.common.base.Preconditions.*; import java.nio.charset.Charset; /** + * The software agent that is acting on behalf of a user. The user agent + * represents a browser or one of the repository client (svn, git or hg). * * @author Sebastian Sdorra * @since 1.45 @@ -53,7 +55,7 @@ public final class UserAgent { /** - * Constructs ... + * Constructs a new user agent * * * @param name @@ -71,12 +73,12 @@ public final class UserAgent //~--- methods -------------------------------------------------------------- /** - * Method description + * Returns the {@link Builder} for the UserAgent. * * - * @param name + * @param name name of the UserAgent * - * @return + * @return builder for UserAgent */ public static Builder builder(String name) { @@ -84,12 +86,7 @@ public final class UserAgent } /** - * Method description - * - * - * @param obj - * - * @return + * {@inheritDoc} */ @Override public boolean equals(Object obj) @@ -112,10 +109,7 @@ public final class UserAgent } /** - * Method description - * - * - * @return + * {@inheritDoc} */ @Override public int hashCode() @@ -124,10 +118,7 @@ public final class UserAgent } /** - * Method description - * - * - * @return + * {@inheritDoc} */ @Override public String toString() @@ -144,10 +135,10 @@ public final class UserAgent //~--- get methods ---------------------------------------------------------- /** - * Method description + * Returns the {@link Charset}, which is used to decode the basic + * authentication header. * - * - * @return + * @return {@link Charset} for basic authentication */ public Charset getBasicAuthenticationCharset() { @@ -155,10 +146,10 @@ public final class UserAgent } /** - * Method description + * Returns the name of UserAgent. * * - * @return + * @return name of UserAgent */ public String getName() { @@ -166,10 +157,10 @@ public final class UserAgent } /** - * Method description + * Returns {@code true} if UserAgent is a browser. * * - * @return + * @return {@code true} if UserAgent is a browser */ public boolean isBrowser() { @@ -179,20 +170,16 @@ public final class UserAgent //~--- inner classes -------------------------------------------------------- /** - * Class description - * - * - * @version Enter version here..., 14/10/15 - * @author Enter your name here... + * Builder class for {@link UserAgent}. */ public static class Builder { /** - * Constructs ... + * Constructs a new UserAgent builder. * * - * @param name + * @param name name of the UserAgent */ public Builder(String name) { @@ -202,12 +189,12 @@ public final class UserAgent //~--- methods ------------------------------------------------------------ /** - * Method description + * Sets {@link Charset} which is used to decode the basic authentication. * * - * @param basicAuthenticationCharset + * @param basicAuthenticationCharset charset for basic authentication * - * @return + * @return {@code this} */ public Builder basicAuthenticationCharset( Charset basicAuthenticationCharset) @@ -219,12 +206,12 @@ public final class UserAgent } /** - * Method description + * Set to {@code true} if the {@link UserAgent} is a browser. * * - * @param browser + * @param browser {@code true} for a browser * - * @return + * @return {@code this} */ public Builder browser(boolean browser) { @@ -234,10 +221,10 @@ public final class UserAgent } /** - * Method description + * Builds the {@link UserAgent}. * * - * @return + * @return new {@link UserAgent} */ public UserAgent build() { @@ -246,25 +233,25 @@ public final class UserAgent //~--- fields ------------------------------------------------------------- - /** Field description */ + /** name of UserAgent */ private final String name; - /** Field description */ + /** indicator for browsers */ private boolean browser = true; - /** Field description */ + /** basic authentication charset */ private Charset basicAuthenticationCharset = Charsets.ISO_8859_1; } //~--- fields --------------------------------------------------------------- - /** Field description */ + /** basic authentication charset */ private final Charset basicAuthenticationCharset; - /** Field description */ + /** indicator for browsers */ private final boolean browser; - /** Field description */ + /** name of UserAgent */ private final String name; } diff --git a/scm-core/src/main/java/sonia/scm/web/UserAgentParser.java b/scm-core/src/main/java/sonia/scm/web/UserAgentParser.java index d483abd0eb..3324521509 100644 --- a/scm-core/src/main/java/sonia/scm/web/UserAgentParser.java +++ b/scm-core/src/main/java/sonia/scm/web/UserAgentParser.java @@ -55,6 +55,8 @@ import java.util.Set; import javax.servlet.http.HttpServletRequest; /** + * Parser for User-Agent header. The UserAgentParser parses the User-Agent + * header and returns a {@link UserAgent} object. * * @author Sebastian Sdorra * @since 1.45 @@ -63,26 +65,26 @@ import javax.servlet.http.HttpServletRequest; public final class UserAgentParser { - /** Field description */ + /** name of the cache */ @VisibleForTesting static final String CACHE_NAME = "sonia.scm.user-agent"; - /** Field description */ + /** unknown UserAgent */ @VisibleForTesting static final UserAgent UNKNOWN = UserAgent.builder("UNKNOWN").build(); - /** Field description */ + /** logger */ private static final Logger logger = LoggerFactory.getLogger(UserAgentParser.class); //~--- constructors --------------------------------------------------------- /** - * Constructs ... + * Constructs a new UserAgentParser. * * - * @param providers - * @param cacheManager + * @param providers set of providers + * @param cacheManager cache manager */ @Inject public UserAgentParser(Set providers, @@ -96,12 +98,12 @@ public final class UserAgentParser //~--- methods -------------------------------------------------------------- /** - * Method description + * Extracts the User-Agent header and returns an {@link UserAgent} object. * * - * @param request + * @param request http request * - * @return + * @return {@link UserAgent} object */ public UserAgent parse(HttpServletRequest request) { @@ -109,12 +111,12 @@ public final class UserAgentParser } /** - * Method description + * Parses the User-Agent header and returns a {@link UserAgent} object. * * - * @param userAgent + * @param userAgent User-Agent header * - * @return + * @return {@link UserAgent} object */ public UserAgent parse(String userAgent) { @@ -138,7 +140,7 @@ public final class UserAgentParser ua = UNKNOWN; } - // cache.put(uas, ua); + cache.put(uas, ua); } logger.trace("return user-agent {} for {}", ua, userAgent); @@ -148,9 +150,9 @@ public final class UserAgentParser //~--- fields --------------------------------------------------------------- - /** Field description */ + /** cache for parsed UserAgents */ private final Cache cache; - /** Field description */ + /** set of providers */ private final Set providers; } diff --git a/scm-core/src/main/java/sonia/scm/web/UserAgentProvider.java b/scm-core/src/main/java/sonia/scm/web/UserAgentProvider.java index b3e68859d4..2eb58a8a36 100644 --- a/scm-core/src/main/java/sonia/scm/web/UserAgentProvider.java +++ b/scm-core/src/main/java/sonia/scm/web/UserAgentProvider.java @@ -36,8 +36,10 @@ package sonia.scm.web; import sonia.scm.plugin.ExtensionPoint; /** + * Provider to parse User-Agent header and returns an {@link UserAgent} object. + * The {@link UserAgentProvider} is used by the {@link UserAgentParser}. * - * @author Sebastian Sdorra + * @author Sebastian Sdorra * @since 1.45 */ @ExtensionPoint(multi = true) @@ -45,12 +47,12 @@ public interface UserAgentProvider { /** - * Method description + * Parses the User-Agent header and returns a {@link UserAgent} object. * * - * @param userAgentString + * @param userAgentString User-Agent header * - * @return + * @return {@link UserAgent} object */ public UserAgent parseUserAgent(String userAgentString); } From e5bfca2205dd295423f75336c85874a48cc0ddb8 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Mon, 16 Feb 2015 15:34:49 +0100 Subject: [PATCH 026/171] improve remove repository confirmation dialog --- scm-webapp/src/main/webapp/resources/js/i18n/de.js | 3 ++- .../resources/js/repository/sonia.repository.panel.js | 10 ++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/scm-webapp/src/main/webapp/resources/js/i18n/de.js b/scm-webapp/src/main/webapp/resources/js/i18n/de.js index 91a76c7685..60597a2b89 100644 --- a/scm-webapp/src/main/webapp/resources/js/i18n/de.js +++ b/scm-webapp/src/main/webapp/resources/js/i18n/de.js @@ -221,7 +221,8 @@ if (Sonia.repository.Panel){ emptyText: 'Es wurde kein Repository selektiert', removeTitleText: 'Repository entfernen', - removeMsgText: 'Repository entfernen "{0}"?', + removeMsgText: 'Diese Aktion wird das Repository "{0}" permanent von der \n\ + Festplatte löschen?
Repository entfernen?', errorTitleText: 'Fehler', errorMsgText: 'Repository entfernen fehlgeschlagen' }); diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.panel.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.panel.js index 43f4890dce..17a301509a 100644 --- a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.panel.js +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.panel.js @@ -48,7 +48,8 @@ Sonia.repository.Panel = Ext.extend(Sonia.rest.Panel, { displayArchivedRepositoriesText: 'Archive', removeTitleText: 'Remove Repository', - removeMsgText: 'Remove Repository "{0}"?', + removeMsgText: 'This action will permanently remove the data of the repository \n\ + "{0}" from your disk.
Do you want to proceed?', errorTitleText: 'Error', errorMsgText: 'Repository deletion failed', @@ -255,12 +256,12 @@ Sonia.repository.Panel = Ext.extend(Sonia.rest.Panel, { return repository; }, - executeRemoteCall: function(title, message, method, url, data, failureCallback){ + executeRemoteCall: function(title, message, method, url, data, failureCallback, icon){ Ext.MessageBox.show({ title: title, msg: message, buttons: Ext.MessageBox.OKCANCEL, - icon: Ext.MessageBox.QUESTION, + icon: icon ? icon : Ext.MessageBox.QUESTION, fn: function(result){ if ( result === 'ok' ){ @@ -339,7 +340,8 @@ Sonia.repository.Panel = Ext.extend(Sonia.rest.Panel, { this.errorTitleText, this.errorMsgText ); - } + }, + Ext.MessageBox.WARNING ); } }, From 30733f96b8199edde2df06f6d41b5ce468f9bbf6 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Mon, 16 Feb 2015 15:38:31 +0100 Subject: [PATCH 027/171] indent --- .../src/main/java/sonia/scm/web/GitRepositoryViewer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitRepositoryViewer.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitRepositoryViewer.java index 969e00888b..3cb4ec52f1 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitRepositoryViewer.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitRepositoryViewer.java @@ -158,7 +158,7 @@ public class GitRepositoryViewer try { - response.setCharacterEncoding("UTF-8"); + response.setCharacterEncoding("UTF-8"); writer = response.getWriter(); template.execute(writer, env); } From 387cb50cc74f2dd10176ff9615bf7df7b7da509d Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Mon, 16 Feb 2015 15:44:58 +0100 Subject: [PATCH 028/171] update slf4j to version 1.7.10 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7cc8af8f47..554b10d722 100644 --- a/pom.xml +++ b/pom.xml @@ -426,7 +426,7 @@ 4.12 - 1.7.9 + 1.7.10 1.1.2 2.5 3.0 From 5a7588fd2f1563dfc3d54bb6d2cccb1e4f401be2 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Mon, 16 Feb 2015 15:49:33 +0100 Subject: [PATCH 029/171] update jersey to version 1.19 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 554b10d722..88903675a0 100644 --- a/pom.xml +++ b/pom.xml @@ -430,7 +430,7 @@ 1.1.2 2.5 3.0 - 1.18.3 + 1.19 2.6.6 2.3.20 7.6.16.v20140903 From 05228bd49252eb3a4d7edf1e0c6a23cf658e3b31 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Mon, 16 Feb 2015 16:03:07 +0100 Subject: [PATCH 030/171] remove outdated asm dependency --- scm-webapp/pom.xml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index 6bdb76b94a..06b6b73e1c 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -183,13 +183,7 @@ commons-codec 1.9
- - - asm - asm - 3.3.1 - - + com.google.guava guava From 4c171732a9e0ed70d5a3d633949ca7697d20cc15 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 17 Feb 2015 09:44:23 +0100 Subject: [PATCH 031/171] update apiviz to 1.3.2 --- pom.xml | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/pom.xml b/pom.xml index 88903675a0..abc8ba9673 100644 --- a/pom.xml +++ b/pom.xml @@ -233,7 +233,7 @@ maven-source-plugin 2.2.1 - + org.apache.maven.plugins maven-clean-plugin @@ -275,7 +275,7 @@ maven-eclipse-plugin 2.6 - + @@ -356,16 +356,6 @@ - - JDK7 - - 1.7 - - - -jdk7 - - - APIviz @@ -386,8 +376,8 @@ org.jboss.apiviz.APIviz org.jboss.apiviz - apiviz${jdk.classifier} - 1.3.1.GA + apiviz + 1.3.2.GA -sourceclasspath ${project.build.outputDirectory} From 69b691f0107b33cbb4fc70e5e3141de42f0a84c9 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 17 Feb 2015 10:01:29 +0100 Subject: [PATCH 032/171] [maven-release-plugin] prepare release 1.45 --- maven/pom.xml | 4 ++-- maven/scm-maven-plugin/pom.xml | 4 ++-- maven/scm-plugin-archetype/pom.xml | 4 ++-- pom.xml | 4 ++-- scm-clients/pom.xml | 6 +++--- scm-clients/scm-cli-client/pom.xml | 6 +++--- scm-clients/scm-client-api/pom.xml | 4 ++-- scm-clients/scm-client-impl/pom.xml | 8 ++++---- scm-core/pom.xml | 4 ++-- scm-dao-orientdb/pom.xml | 8 ++++---- scm-dao-xml/pom.xml | 8 ++++---- scm-plugin-backend/pom.xml | 6 +++--- scm-plugins/pom.xml | 8 ++++---- scm-plugins/scm-git-plugin/pom.xml | 6 +++--- scm-plugins/scm-hg-plugin/pom.xml | 6 +++--- scm-plugins/scm-svn-plugin/pom.xml | 6 +++--- scm-samples/pom.xml | 4 ++-- scm-samples/scm-sample-auth/pom.xml | 6 +++--- scm-samples/scm-sample-hello/pom.xml | 6 +++--- scm-server/pom.xml | 4 ++-- scm-test/pom.xml | 6 +++--- scm-webapp/pom.xml | 24 ++++++++++++------------ support/pom.xml | 4 ++-- support/scm-support-btrace/pom.xml | 6 +++--- 24 files changed, 76 insertions(+), 76 deletions(-) diff --git a/maven/pom.xml b/maven/pom.xml index 6daff8cea8..79d3c08936 100644 --- a/maven/pom.xml +++ b/maven/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.45-SNAPSHOT + 1.45 sonia.scm.maven scm-maven-plugins pom - 1.45-SNAPSHOT + 1.45 scm-maven-plugins diff --git a/maven/scm-maven-plugin/pom.xml b/maven/scm-maven-plugin/pom.xml index 318aea3aca..7661a39b49 100644 --- a/maven/scm-maven-plugin/pom.xml +++ b/maven/scm-maven-plugin/pom.xml @@ -6,12 +6,12 @@ scm-maven-plugins sonia.scm.maven - 1.45-SNAPSHOT + 1.45 sonia.scm.maven scm-maven-plugin - 1.45-SNAPSHOT + 1.45 maven-plugin scm-maven-plugin diff --git a/maven/scm-plugin-archetype/pom.xml b/maven/scm-plugin-archetype/pom.xml index bf73b724c1..40a1988333 100644 --- a/maven/scm-plugin-archetype/pom.xml +++ b/maven/scm-plugin-archetype/pom.xml @@ -6,12 +6,12 @@ scm-maven-plugins sonia.scm.maven - 1.45-SNAPSHOT + 1.45 sonia.scm.maven scm-plugin-archetype - 1.45-SNAPSHOT + 1.45 scm-plugin-archetype diff --git a/pom.xml b/pom.xml index abc8ba9673..c11c94c6c5 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ sonia.scm scm pom - 1.45-SNAPSHOT + 1.45 The easiest way to share your Git, Mercurial and Subversion repositories over http. @@ -36,7 +36,7 @@ scm:hg:http://bitbucket.org/sdorra/scm-manager scm:hg:https://bitbucket.org/sdorra/scm-manager http://bitbucket.org/sdorra/scm-manager - HEAD + 1.45 diff --git a/scm-clients/pom.xml b/scm-clients/pom.xml index c47500616b..28c8b6d4dd 100644 --- a/scm-clients/pom.xml +++ b/scm-clients/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.45-SNAPSHOT + 1.45 sonia.scm.clients scm-clients pom - 1.45-SNAPSHOT + 1.45 scm-clients @@ -32,7 +32,7 @@ scm-core sonia.scm jar - 1.45-SNAPSHOT + 1.45 shiro-core diff --git a/scm-clients/scm-cli-client/pom.xml b/scm-clients/scm-cli-client/pom.xml index bd20c8285d..4fa4e68aad 100644 --- a/scm-clients/scm-cli-client/pom.xml +++ b/scm-clients/scm-cli-client/pom.xml @@ -6,12 +6,12 @@ scm-clients sonia.scm.clients - 1.45-SNAPSHOT + 1.45 sonia.scm.clients scm-cli-client - 1.45-SNAPSHOT + 1.45 scm-cli-client @@ -34,7 +34,7 @@ sonia.scm.clients scm-client-impl - 1.45-SNAPSHOT + 1.45 diff --git a/scm-clients/scm-client-api/pom.xml b/scm-clients/scm-client-api/pom.xml index 2c474990c1..612caa6afd 100644 --- a/scm-clients/scm-client-api/pom.xml +++ b/scm-clients/scm-client-api/pom.xml @@ -6,13 +6,13 @@ sonia.scm.clients scm-clients - 1.45-SNAPSHOT + 1.45 sonia.scm.clients scm-client-api jar - 1.45-SNAPSHOT + 1.45 scm-client-api diff --git a/scm-clients/scm-client-impl/pom.xml b/scm-clients/scm-client-impl/pom.xml index 8c1c773507..3c75ee1ae5 100644 --- a/scm-clients/scm-client-impl/pom.xml +++ b/scm-clients/scm-client-impl/pom.xml @@ -6,13 +6,13 @@ sonia.scm.clients scm-clients - 1.45-SNAPSHOT + 1.45 sonia.scm.clients scm-client-impl jar - 1.45-SNAPSHOT + 1.45 scm-client-impl @@ -36,7 +36,7 @@ sonia.scm.clients scm-client-api - 1.45-SNAPSHOT + 1.45 @@ -70,7 +70,7 @@ sonia.scm scm-test - 1.45-SNAPSHOT + 1.45 test diff --git a/scm-core/pom.xml b/scm-core/pom.xml index 30149cef7e..78c9bcca30 100644 --- a/scm-core/pom.xml +++ b/scm-core/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.45-SNAPSHOT + 1.45 sonia.scm scm-core - 1.45-SNAPSHOT + 1.45 scm-core diff --git a/scm-dao-orientdb/pom.xml b/scm-dao-orientdb/pom.xml index ed8f74b0d6..d7e46c91d8 100644 --- a/scm-dao-orientdb/pom.xml +++ b/scm-dao-orientdb/pom.xml @@ -6,12 +6,12 @@ sonia.scm scm - 1.45-SNAPSHOT + 1.45 sonia.scm scm-dao-orientdb - 1.45-SNAPSHOT + 1.45 scm-dao-orientdb @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.45-SNAPSHOT + 1.45 @@ -52,7 +52,7 @@ sonia.scm scm-test - 1.45-SNAPSHOT + 1.45 test diff --git a/scm-dao-xml/pom.xml b/scm-dao-xml/pom.xml index 67ce49d61e..c5669f15b2 100644 --- a/scm-dao-xml/pom.xml +++ b/scm-dao-xml/pom.xml @@ -6,12 +6,12 @@ sonia.scm scm - 1.45-SNAPSHOT + 1.45 sonia.scm scm-dao-xml - 1.45-SNAPSHOT + 1.45 scm-dao-xml @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.45-SNAPSHOT + 1.45 @@ -34,7 +34,7 @@ sonia.scm scm-test - 1.45-SNAPSHOT + 1.45 test diff --git a/scm-plugin-backend/pom.xml b/scm-plugin-backend/pom.xml index d6ca0a3d5b..f2245ad69c 100644 --- a/scm-plugin-backend/pom.xml +++ b/scm-plugin-backend/pom.xml @@ -6,13 +6,13 @@ scm sonia.scm - 1.45-SNAPSHOT + 1.45 sonia.scm scm-plugin-backend war - 1.45-SNAPSHOT + 1.45 ${project.artifactId} @@ -62,7 +62,7 @@ sonia.scm scm-core - 1.45-SNAPSHOT + 1.45 diff --git a/scm-plugins/pom.xml b/scm-plugins/pom.xml index d2eb4e04e0..f977f7475f 100644 --- a/scm-plugins/pom.xml +++ b/scm-plugins/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.45-SNAPSHOT + 1.45 sonia.scm.plugins scm-plugins pom - 1.45-SNAPSHOT + 1.45 scm-plugins @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.45-SNAPSHOT + 1.45 @@ -59,7 +59,7 @@ sonia.scm.maven scm-maven-plugin - 1.45-SNAPSHOT + 1.45 process-resources diff --git a/scm-plugins/scm-git-plugin/pom.xml b/scm-plugins/scm-git-plugin/pom.xml index bbacdb4cb9..353e8a5e0e 100644 --- a/scm-plugins/scm-git-plugin/pom.xml +++ b/scm-plugins/scm-git-plugin/pom.xml @@ -6,12 +6,12 @@ scm-plugins sonia.scm.plugins - 1.45-SNAPSHOT + 1.45 sonia.scm.plugins scm-git-plugin - 1.45-SNAPSHOT + 1.45 scm-git-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Git @@ -48,7 +48,7 @@ sonia.scm scm-test - 1.45-SNAPSHOT + 1.45 test diff --git a/scm-plugins/scm-hg-plugin/pom.xml b/scm-plugins/scm-hg-plugin/pom.xml index e37513a6a5..1ebfb1b7e2 100644 --- a/scm-plugins/scm-hg-plugin/pom.xml +++ b/scm-plugins/scm-hg-plugin/pom.xml @@ -6,12 +6,12 @@ sonia.scm.plugins scm-plugins - 1.45-SNAPSHOT + 1.45 sonia.scm.plugins scm-hg-plugin - 1.45-SNAPSHOT + 1.45 scm-hg-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Mercurial @@ -42,7 +42,7 @@ sonia.scm scm-test - 1.45-SNAPSHOT + 1.45 test diff --git a/scm-plugins/scm-svn-plugin/pom.xml b/scm-plugins/scm-svn-plugin/pom.xml index ca48107018..6f3cc20496 100644 --- a/scm-plugins/scm-svn-plugin/pom.xml +++ b/scm-plugins/scm-svn-plugin/pom.xml @@ -6,12 +6,12 @@ scm-plugins sonia.scm.plugins - 1.45-SNAPSHOT + 1.45 sonia.scm.plugins scm-svn-plugin - 1.45-SNAPSHOT + 1.45 scm-svn-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Subversion @@ -48,7 +48,7 @@ sonia.scm scm-test - 1.45-SNAPSHOT + 1.45 test diff --git a/scm-samples/pom.xml b/scm-samples/pom.xml index 46867de76b..b2918b71b4 100644 --- a/scm-samples/pom.xml +++ b/scm-samples/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.45-SNAPSHOT + 1.45 sonia.scm.samples scm-samples pom - 1.45-SNAPSHOT + 1.45 scm-samples diff --git a/scm-samples/scm-sample-auth/pom.xml b/scm-samples/scm-sample-auth/pom.xml index 0ed3c5275b..6e46703106 100644 --- a/scm-samples/scm-sample-auth/pom.xml +++ b/scm-samples/scm-sample-auth/pom.xml @@ -6,12 +6,12 @@ scm-samples sonia.scm.samples - 1.45-SNAPSHOT + 1.45 sonia.scm.sample scm-sample-auth - 1.45-SNAPSHOT + 1.45 scm-sample-auth Sample Authentication Plugin https://bitbucket.org/sdorra/scm-manager @@ -28,7 +28,7 @@ sonia.scm scm-core - 1.45-SNAPSHOT + 1.45 diff --git a/scm-samples/scm-sample-hello/pom.xml b/scm-samples/scm-sample-hello/pom.xml index c3ff8dc498..fcd34bbeb9 100644 --- a/scm-samples/scm-sample-hello/pom.xml +++ b/scm-samples/scm-sample-hello/pom.xml @@ -6,12 +6,12 @@ scm-samples sonia.scm.samples - 1.45-SNAPSHOT + 1.45 sonia.scm.sample scm-sample-hello - 1.45-SNAPSHOT + 1.45 scm-sample-hello A simple hello world plugin https://bitbucket.org/sdorra/scm-manager @@ -28,7 +28,7 @@ sonia.scm scm-core - 1.45-SNAPSHOT + 1.45 diff --git a/scm-server/pom.xml b/scm-server/pom.xml index b0939374cd..0f16d3ef0f 100644 --- a/scm-server/pom.xml +++ b/scm-server/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.45-SNAPSHOT + 1.45 sonia.scm scm-server - 1.45-SNAPSHOT + 1.45 scm-server jar diff --git a/scm-test/pom.xml b/scm-test/pom.xml index 872902dbd1..9d5562f6f5 100644 --- a/scm-test/pom.xml +++ b/scm-test/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.45-SNAPSHOT + 1.45 sonia.scm scm-test - 1.45-SNAPSHOT + 1.45 scm-test @@ -25,7 +25,7 @@ sonia.scm scm-core - 1.45-SNAPSHOT + 1.45 diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index 06b6b73e1c..ebadfc4b49 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.45-SNAPSHOT + 1.45 sonia.scm scm-webapp war - 1.45-SNAPSHOT + 1.45 scm-webapp @@ -38,31 +38,31 @@ sonia.scm scm-core - 1.45-SNAPSHOT + 1.45 sonia.scm scm-dao-xml - 1.45-SNAPSHOT + 1.45 sonia.scm.plugins scm-hg-plugin - 1.45-SNAPSHOT + 1.45 sonia.scm.plugins scm-svn-plugin - 1.45-SNAPSHOT + 1.45 sonia.scm.plugins scm-git-plugin - 1.45-SNAPSHOT + 1.45 @@ -274,7 +274,7 @@ sonia.scm scm-test - 1.45-SNAPSHOT + 1.45 test @@ -287,7 +287,7 @@ sonia.scm.plugins scm-git-plugin - 1.45-SNAPSHOT + 1.45 tests test @@ -295,7 +295,7 @@ sonia.scm.plugins scm-hg-plugin - 1.45-SNAPSHOT + 1.45 tests test @@ -303,7 +303,7 @@ sonia.scm.plugins scm-svn-plugin - 1.45-SNAPSHOT + 1.45 tests test @@ -529,7 +529,7 @@ sonia.scm scm-dao-orientdb - 1.45-SNAPSHOT + 1.45 diff --git a/support/pom.xml b/support/pom.xml index 82e84ed222..ba8e46028c 100644 --- a/support/pom.xml +++ b/support/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.45-SNAPSHOT + 1.45 sonia.scm.support scm-support pom - 1.45-SNAPSHOT + 1.45 scm-support diff --git a/support/scm-support-btrace/pom.xml b/support/scm-support-btrace/pom.xml index 9c3ad54f85..d785b3804b 100644 --- a/support/scm-support-btrace/pom.xml +++ b/support/scm-support-btrace/pom.xml @@ -4,12 +4,12 @@ sonia.scm.support scm-support - 1.45-SNAPSHOT + 1.45 sonia.scm scm-support-btrace - 1.45-SNAPSHOT + 1.45 jar scm-support-btrace @@ -18,7 +18,7 @@ sonia.scm scm-core - 1.45-SNAPSHOT + 1.45 From 5ceef70bd663f641d2d6cca6506ec60592dd2714 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 17 Feb 2015 10:01:29 +0100 Subject: [PATCH 033/171] [maven-release-plugin] copy for tag 1.45 From 451490e95a0dc6a10aa1c75291c3196aed76712d Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 17 Feb 2015 10:01:30 +0100 Subject: [PATCH 034/171] [maven-release-plugin] prepare for next development iteration --- maven/pom.xml | 4 ++-- maven/scm-maven-plugin/pom.xml | 4 ++-- maven/scm-plugin-archetype/pom.xml | 4 ++-- pom.xml | 4 ++-- scm-clients/pom.xml | 6 +++--- scm-clients/scm-cli-client/pom.xml | 6 +++--- scm-clients/scm-client-api/pom.xml | 4 ++-- scm-clients/scm-client-impl/pom.xml | 8 ++++---- scm-core/pom.xml | 4 ++-- scm-dao-orientdb/pom.xml | 8 ++++---- scm-dao-xml/pom.xml | 8 ++++---- scm-plugin-backend/pom.xml | 6 +++--- scm-plugins/pom.xml | 8 ++++---- scm-plugins/scm-git-plugin/pom.xml | 6 +++--- scm-plugins/scm-hg-plugin/pom.xml | 6 +++--- scm-plugins/scm-svn-plugin/pom.xml | 6 +++--- scm-samples/pom.xml | 4 ++-- scm-samples/scm-sample-auth/pom.xml | 6 +++--- scm-samples/scm-sample-hello/pom.xml | 6 +++--- scm-server/pom.xml | 4 ++-- scm-test/pom.xml | 6 +++--- scm-webapp/pom.xml | 24 ++++++++++++------------ support/pom.xml | 4 ++-- support/scm-support-btrace/pom.xml | 6 +++--- 24 files changed, 76 insertions(+), 76 deletions(-) diff --git a/maven/pom.xml b/maven/pom.xml index 79d3c08936..ad005a69fd 100644 --- a/maven/pom.xml +++ b/maven/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.45 + 1.46-SNAPSHOT sonia.scm.maven scm-maven-plugins pom - 1.45 + 1.46-SNAPSHOT scm-maven-plugins diff --git a/maven/scm-maven-plugin/pom.xml b/maven/scm-maven-plugin/pom.xml index 7661a39b49..f746988fc2 100644 --- a/maven/scm-maven-plugin/pom.xml +++ b/maven/scm-maven-plugin/pom.xml @@ -6,12 +6,12 @@ scm-maven-plugins sonia.scm.maven - 1.45 + 1.46-SNAPSHOT sonia.scm.maven scm-maven-plugin - 1.45 + 1.46-SNAPSHOT maven-plugin scm-maven-plugin diff --git a/maven/scm-plugin-archetype/pom.xml b/maven/scm-plugin-archetype/pom.xml index 40a1988333..60e63aaafb 100644 --- a/maven/scm-plugin-archetype/pom.xml +++ b/maven/scm-plugin-archetype/pom.xml @@ -6,12 +6,12 @@ scm-maven-plugins sonia.scm.maven - 1.45 + 1.46-SNAPSHOT sonia.scm.maven scm-plugin-archetype - 1.45 + 1.46-SNAPSHOT scm-plugin-archetype diff --git a/pom.xml b/pom.xml index c11c94c6c5..faab186cc5 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ sonia.scm scm pom - 1.45 + 1.46-SNAPSHOT The easiest way to share your Git, Mercurial and Subversion repositories over http. @@ -36,7 +36,7 @@ scm:hg:http://bitbucket.org/sdorra/scm-manager scm:hg:https://bitbucket.org/sdorra/scm-manager http://bitbucket.org/sdorra/scm-manager - 1.45 + HEAD diff --git a/scm-clients/pom.xml b/scm-clients/pom.xml index 28c8b6d4dd..6af3e38593 100644 --- a/scm-clients/pom.xml +++ b/scm-clients/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.45 + 1.46-SNAPSHOT sonia.scm.clients scm-clients pom - 1.45 + 1.46-SNAPSHOT scm-clients @@ -32,7 +32,7 @@ scm-core sonia.scm jar - 1.45 + 1.46-SNAPSHOT shiro-core diff --git a/scm-clients/scm-cli-client/pom.xml b/scm-clients/scm-cli-client/pom.xml index 4fa4e68aad..803e4e77d1 100644 --- a/scm-clients/scm-cli-client/pom.xml +++ b/scm-clients/scm-cli-client/pom.xml @@ -6,12 +6,12 @@ scm-clients sonia.scm.clients - 1.45 + 1.46-SNAPSHOT sonia.scm.clients scm-cli-client - 1.45 + 1.46-SNAPSHOT scm-cli-client @@ -34,7 +34,7 @@ sonia.scm.clients scm-client-impl - 1.45 + 1.46-SNAPSHOT diff --git a/scm-clients/scm-client-api/pom.xml b/scm-clients/scm-client-api/pom.xml index 612caa6afd..a629295853 100644 --- a/scm-clients/scm-client-api/pom.xml +++ b/scm-clients/scm-client-api/pom.xml @@ -6,13 +6,13 @@ sonia.scm.clients scm-clients - 1.45 + 1.46-SNAPSHOT sonia.scm.clients scm-client-api jar - 1.45 + 1.46-SNAPSHOT scm-client-api diff --git a/scm-clients/scm-client-impl/pom.xml b/scm-clients/scm-client-impl/pom.xml index 3c75ee1ae5..5c2d7d84ef 100644 --- a/scm-clients/scm-client-impl/pom.xml +++ b/scm-clients/scm-client-impl/pom.xml @@ -6,13 +6,13 @@ sonia.scm.clients scm-clients - 1.45 + 1.46-SNAPSHOT sonia.scm.clients scm-client-impl jar - 1.45 + 1.46-SNAPSHOT scm-client-impl @@ -36,7 +36,7 @@ sonia.scm.clients scm-client-api - 1.45 + 1.46-SNAPSHOT @@ -70,7 +70,7 @@ sonia.scm scm-test - 1.45 + 1.46-SNAPSHOT test diff --git a/scm-core/pom.xml b/scm-core/pom.xml index 78c9bcca30..fe25a55793 100644 --- a/scm-core/pom.xml +++ b/scm-core/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.45 + 1.46-SNAPSHOT sonia.scm scm-core - 1.45 + 1.46-SNAPSHOT scm-core diff --git a/scm-dao-orientdb/pom.xml b/scm-dao-orientdb/pom.xml index d7e46c91d8..5fe695d149 100644 --- a/scm-dao-orientdb/pom.xml +++ b/scm-dao-orientdb/pom.xml @@ -6,12 +6,12 @@ sonia.scm scm - 1.45 + 1.46-SNAPSHOT sonia.scm scm-dao-orientdb - 1.45 + 1.46-SNAPSHOT scm-dao-orientdb @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.45 + 1.46-SNAPSHOT @@ -52,7 +52,7 @@ sonia.scm scm-test - 1.45 + 1.46-SNAPSHOT test diff --git a/scm-dao-xml/pom.xml b/scm-dao-xml/pom.xml index c5669f15b2..e4d6a3f619 100644 --- a/scm-dao-xml/pom.xml +++ b/scm-dao-xml/pom.xml @@ -6,12 +6,12 @@ sonia.scm scm - 1.45 + 1.46-SNAPSHOT sonia.scm scm-dao-xml - 1.45 + 1.46-SNAPSHOT scm-dao-xml @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.45 + 1.46-SNAPSHOT @@ -34,7 +34,7 @@ sonia.scm scm-test - 1.45 + 1.46-SNAPSHOT test diff --git a/scm-plugin-backend/pom.xml b/scm-plugin-backend/pom.xml index f2245ad69c..a8d4bf36c1 100644 --- a/scm-plugin-backend/pom.xml +++ b/scm-plugin-backend/pom.xml @@ -6,13 +6,13 @@ scm sonia.scm - 1.45 + 1.46-SNAPSHOT sonia.scm scm-plugin-backend war - 1.45 + 1.46-SNAPSHOT ${project.artifactId} @@ -62,7 +62,7 @@ sonia.scm scm-core - 1.45 + 1.46-SNAPSHOT diff --git a/scm-plugins/pom.xml b/scm-plugins/pom.xml index f977f7475f..6ca6d79276 100644 --- a/scm-plugins/pom.xml +++ b/scm-plugins/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.45 + 1.46-SNAPSHOT sonia.scm.plugins scm-plugins pom - 1.45 + 1.46-SNAPSHOT scm-plugins @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.45 + 1.46-SNAPSHOT @@ -59,7 +59,7 @@ sonia.scm.maven scm-maven-plugin - 1.45 + 1.46-SNAPSHOT process-resources diff --git a/scm-plugins/scm-git-plugin/pom.xml b/scm-plugins/scm-git-plugin/pom.xml index 353e8a5e0e..74258ba06d 100644 --- a/scm-plugins/scm-git-plugin/pom.xml +++ b/scm-plugins/scm-git-plugin/pom.xml @@ -6,12 +6,12 @@ scm-plugins sonia.scm.plugins - 1.45 + 1.46-SNAPSHOT sonia.scm.plugins scm-git-plugin - 1.45 + 1.46-SNAPSHOT scm-git-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Git @@ -48,7 +48,7 @@ sonia.scm scm-test - 1.45 + 1.46-SNAPSHOT test diff --git a/scm-plugins/scm-hg-plugin/pom.xml b/scm-plugins/scm-hg-plugin/pom.xml index 1ebfb1b7e2..2d03707d2f 100644 --- a/scm-plugins/scm-hg-plugin/pom.xml +++ b/scm-plugins/scm-hg-plugin/pom.xml @@ -6,12 +6,12 @@ sonia.scm.plugins scm-plugins - 1.45 + 1.46-SNAPSHOT sonia.scm.plugins scm-hg-plugin - 1.45 + 1.46-SNAPSHOT scm-hg-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Mercurial @@ -42,7 +42,7 @@ sonia.scm scm-test - 1.45 + 1.46-SNAPSHOT test diff --git a/scm-plugins/scm-svn-plugin/pom.xml b/scm-plugins/scm-svn-plugin/pom.xml index 6f3cc20496..3b6f13a6ef 100644 --- a/scm-plugins/scm-svn-plugin/pom.xml +++ b/scm-plugins/scm-svn-plugin/pom.xml @@ -6,12 +6,12 @@ scm-plugins sonia.scm.plugins - 1.45 + 1.46-SNAPSHOT sonia.scm.plugins scm-svn-plugin - 1.45 + 1.46-SNAPSHOT scm-svn-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Subversion @@ -48,7 +48,7 @@ sonia.scm scm-test - 1.45 + 1.46-SNAPSHOT test diff --git a/scm-samples/pom.xml b/scm-samples/pom.xml index b2918b71b4..44f7d3568f 100644 --- a/scm-samples/pom.xml +++ b/scm-samples/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.45 + 1.46-SNAPSHOT sonia.scm.samples scm-samples pom - 1.45 + 1.46-SNAPSHOT scm-samples diff --git a/scm-samples/scm-sample-auth/pom.xml b/scm-samples/scm-sample-auth/pom.xml index 6e46703106..5d60a88954 100644 --- a/scm-samples/scm-sample-auth/pom.xml +++ b/scm-samples/scm-sample-auth/pom.xml @@ -6,12 +6,12 @@ scm-samples sonia.scm.samples - 1.45 + 1.46-SNAPSHOT sonia.scm.sample scm-sample-auth - 1.45 + 1.46-SNAPSHOT scm-sample-auth Sample Authentication Plugin https://bitbucket.org/sdorra/scm-manager @@ -28,7 +28,7 @@ sonia.scm scm-core - 1.45 + 1.46-SNAPSHOT diff --git a/scm-samples/scm-sample-hello/pom.xml b/scm-samples/scm-sample-hello/pom.xml index fcd34bbeb9..d219796157 100644 --- a/scm-samples/scm-sample-hello/pom.xml +++ b/scm-samples/scm-sample-hello/pom.xml @@ -6,12 +6,12 @@ scm-samples sonia.scm.samples - 1.45 + 1.46-SNAPSHOT sonia.scm.sample scm-sample-hello - 1.45 + 1.46-SNAPSHOT scm-sample-hello A simple hello world plugin https://bitbucket.org/sdorra/scm-manager @@ -28,7 +28,7 @@ sonia.scm scm-core - 1.45 + 1.46-SNAPSHOT diff --git a/scm-server/pom.xml b/scm-server/pom.xml index 0f16d3ef0f..016136c927 100644 --- a/scm-server/pom.xml +++ b/scm-server/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.45 + 1.46-SNAPSHOT sonia.scm scm-server - 1.45 + 1.46-SNAPSHOT scm-server jar diff --git a/scm-test/pom.xml b/scm-test/pom.xml index 9d5562f6f5..cf040006de 100644 --- a/scm-test/pom.xml +++ b/scm-test/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.45 + 1.46-SNAPSHOT sonia.scm scm-test - 1.45 + 1.46-SNAPSHOT scm-test @@ -25,7 +25,7 @@ sonia.scm scm-core - 1.45 + 1.46-SNAPSHOT diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index ebadfc4b49..41802c6ba4 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.45 + 1.46-SNAPSHOT sonia.scm scm-webapp war - 1.45 + 1.46-SNAPSHOT scm-webapp @@ -38,31 +38,31 @@ sonia.scm scm-core - 1.45 + 1.46-SNAPSHOT sonia.scm scm-dao-xml - 1.45 + 1.46-SNAPSHOT sonia.scm.plugins scm-hg-plugin - 1.45 + 1.46-SNAPSHOT sonia.scm.plugins scm-svn-plugin - 1.45 + 1.46-SNAPSHOT sonia.scm.plugins scm-git-plugin - 1.45 + 1.46-SNAPSHOT @@ -274,7 +274,7 @@ sonia.scm scm-test - 1.45 + 1.46-SNAPSHOT test @@ -287,7 +287,7 @@ sonia.scm.plugins scm-git-plugin - 1.45 + 1.46-SNAPSHOT tests test @@ -295,7 +295,7 @@ sonia.scm.plugins scm-hg-plugin - 1.45 + 1.46-SNAPSHOT tests test @@ -303,7 +303,7 @@ sonia.scm.plugins scm-svn-plugin - 1.45 + 1.46-SNAPSHOT tests test @@ -529,7 +529,7 @@ sonia.scm scm-dao-orientdb - 1.45 + 1.46-SNAPSHOT diff --git a/support/pom.xml b/support/pom.xml index ba8e46028c..dc889d4a48 100644 --- a/support/pom.xml +++ b/support/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.45 + 1.46-SNAPSHOT sonia.scm.support scm-support pom - 1.45 + 1.46-SNAPSHOT scm-support diff --git a/support/scm-support-btrace/pom.xml b/support/scm-support-btrace/pom.xml index d785b3804b..05aa1c6735 100644 --- a/support/scm-support-btrace/pom.xml +++ b/support/scm-support-btrace/pom.xml @@ -4,12 +4,12 @@ sonia.scm.support scm-support - 1.45 + 1.46-SNAPSHOT sonia.scm scm-support-btrace - 1.45 + 1.46-SNAPSHOT jar scm-support-btrace @@ -18,7 +18,7 @@ sonia.scm scm-core - 1.45 + 1.46-SNAPSHOT From 11c8c1994f0d64ac45c8a33de8d876a74dfa48a4 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Mon, 13 Apr 2015 13:42:39 +0200 Subject: [PATCH 035/171] set content-length header on post requests in order to fix issue #701 --- .../main/java/sonia/scm/util/HttpUtil.java | 6 ++ .../java/sonia/scm/net/URLHttpClient.java | 92 ++++++++++++++----- 2 files changed, 74 insertions(+), 24 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/util/HttpUtil.java b/scm-core/src/main/java/sonia/scm/util/HttpUtil.java index abe7fdd089..4b40214051 100644 --- a/scm-core/src/main/java/sonia/scm/util/HttpUtil.java +++ b/scm-core/src/main/java/sonia/scm/util/HttpUtil.java @@ -78,6 +78,12 @@ public final class HttpUtil * @since 1.43 */ public static final String HEADER_LOCATION = "Location"; + + /** + * content-length header + * @since 1.46 + */ + public static final String HEADER_CONTENT_LENGTH = "Content-Length"; /** * header for identifying the scm-manager client diff --git a/scm-webapp/src/main/java/sonia/scm/net/URLHttpClient.java b/scm-webapp/src/main/java/sonia/scm/net/URLHttpClient.java index 66c1fb9914..b4e5666952 100644 --- a/scm-webapp/src/main/java/sonia/scm/net/URLHttpClient.java +++ b/scm-webapp/src/main/java/sonia/scm/net/URLHttpClient.java @@ -48,8 +48,10 @@ import sonia.scm.util.Util; //~--- JDK imports ------------------------------------------------------------ import com.sun.jersey.core.util.Base64; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.io.OutputStreamWriter; import java.net.HttpURLConnection; @@ -67,6 +69,7 @@ import java.util.Map; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; +import sonia.scm.util.HttpUtil; /** * @@ -152,6 +155,9 @@ public class URLHttpClient implements HttpClient url); connection.setRequestMethod(METHOD_POST); + // send empty content-length + // see issue #701 http://goo.gl/oyTdrA + setContentLength(connection, 0); return new URLHttpResponse(connection); } @@ -347,41 +353,79 @@ public class URLHttpClient implements HttpClient { if (Util.isNotEmpty(parameters)) { + // use a ByteArrayOutputStream in order to get the final content-length + // see issue #701 http://goo.gl/oyTdrA connection.setDoOutput(true); OutputStreamWriter writer = null; + ByteArrayOutputStream baos = null; try { - writer = new OutputStreamWriter(connection.getOutputStream()); + baos = new ByteArrayOutputStream(); + writer = new OutputStreamWriter(baos); - Iterator>> it = - parameters.entrySet().iterator(); - - while (it.hasNext()) - { - Map.Entry> p = it.next(); - List values = p.getValue(); - - if (Util.isNotEmpty(values)) - { - String key = encode(p.getKey()); - - for (String value : values) - { - writer.append(key).append("=").append(encode(value)); - } - - if (it.hasNext()) - { - writer.write("&"); - } - } - } + appendPostParameters(writer, parameters); } finally { IOUtil.close(writer); + IOUtil.close(baos); + } + + if ( baos != null ){ + byte[] data = baos.toByteArray(); + appendBody(connection, data); + } + } + else + { + setContentLength(connection, 0); + } + } + + private void appendBody(HttpURLConnection connection, byte[] body) throws IOException + { + int length = body.length; + logger.trace("write {} bytes to output", length); + setContentLength(connection, length); + connection.setFixedLengthStreamingMode(length); + OutputStream os = null; + try { + os = connection.getOutputStream(); + os.write(body); + } finally { + IOUtil.close(os); + } + } + + private void setContentLength(HttpURLConnection connection, int length) + { + connection.setRequestProperty(HttpUtil.HEADER_CONTENT_LENGTH, String.valueOf(length)); + } + + private void appendPostParameters(OutputStreamWriter writer, Map> parameters) throws IOException + { + Iterator>> it = parameters.entrySet().iterator(); + + while (it.hasNext()) + { + Map.Entry> p = it.next(); + List values = p.getValue(); + + if (Util.isNotEmpty(values)) + { + String key = encode(p.getKey()); + + for (String value : values) + { + writer.append(key).append("=").append(encode(value)); + } + + if (it.hasNext()) + { + writer.write("&"); + } } } } From 518bf3fdab42d5387aa6209a0a50a986ffff4303 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Mon, 13 Apr 2015 13:45:29 +0200 Subject: [PATCH 036/171] fix missing separator char for post values with same name --- scm-webapp/src/main/java/sonia/scm/net/URLHttpClient.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scm-webapp/src/main/java/sonia/scm/net/URLHttpClient.java b/scm-webapp/src/main/java/sonia/scm/net/URLHttpClient.java index b4e5666952..5be3496784 100644 --- a/scm-webapp/src/main/java/sonia/scm/net/URLHttpClient.java +++ b/scm-webapp/src/main/java/sonia/scm/net/URLHttpClient.java @@ -417,9 +417,15 @@ public class URLHttpClient implements HttpClient { String key = encode(p.getKey()); - for (String value : values) + Iterator valueIt = values.iterator(); + + while(valueIt.hasNext()) { + String value = valueIt.next(); writer.append(key).append("=").append(encode(value)); + if ( valueIt.hasNext() ){ + writer.write("&"); + } } if (it.hasNext()) From 71291bd7ea87723e674bbb13a31b7b5a7b8e1079 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sat, 18 Apr 2015 22:11:34 +0200 Subject: [PATCH 037/171] fix wrong uft-8 filenames on raw download, see issue #697 --- .../main/java/sonia/scm/util/HttpUtil.java | 31 +++++++++++++++---- .../rest/resources/RepositoryResource.java | 2 +- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/util/HttpUtil.java b/scm-core/src/main/java/sonia/scm/util/HttpUtil.java index 4b40214051..68d669d914 100644 --- a/scm-core/src/main/java/sonia/scm/util/HttpUtil.java +++ b/scm-core/src/main/java/sonia/scm/util/HttpUtil.java @@ -73,18 +73,18 @@ 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"; - /** * content-length header * @since 1.46 */ public static final String HEADER_CONTENT_LENGTH = "Content-Length"; + /** + * location header + * @since 1.43 + */ + public static final String HEADER_LOCATION = "Location"; + /** * header for identifying the scm-manager client * @since 1.19 @@ -296,6 +296,25 @@ public final class HttpUtil } } + /** + * Creates the value for the content-disposition attachment header. The method + * creates the filename as specified in rfc6266. + * + * @param name attachment name + * @see rfc6266 section 5 + * @return value of content-disposition header + * @since 1.46 + */ + public static String createContentDispositionAttachmentHeader(String name) + { + StringBuilder buffer = new StringBuilder("attachment; "); + + buffer.append("filename=\"").append(name).append("\"; "); + buffer.append("filename*=utf-8''").append(encode(name)); + + return buffer.toString(); + } + /** * Method description * diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java index a6a494d5ca..afba957adb 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java @@ -1141,7 +1141,7 @@ public class RepositoryResource */ private String getContentDispositionName(String name) { - return "attachment; filename=\"".concat(name).concat("\""); + return HttpUtil.createContentDispositionAttachmentHeader(name); } /** From 4407c7ce9ec70a1e912e6c43bbebe861faf5cd65 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 30 Apr 2015 07:17:52 +0200 Subject: [PATCH 038/171] start implementation of a new advanced http client, in order to fix issue #709 --- .../sonia/scm/net/ahc/AdvancedHttpClient.java | 189 ++++++++ .../scm/net/ahc/AdvancedHttpRequest.java | 72 ++++ .../net/ahc/AdvancedHttpRequestWithBody.java | 216 ++++++++++ .../scm/net/ahc/AdvancedHttpResponse.java | 199 +++++++++ .../sonia/scm/net/ahc/BaseHttpRequest.java | 408 ++++++++++++++++++ .../sonia/scm/net/ahc/ByteSourceContent.java | 97 +++++ .../main/java/sonia/scm/net/ahc/Content.java | 69 +++ .../java/sonia/scm/net/ahc/FileContent.java | 109 +++++ .../sonia/scm/net/ahc/FormContentBuilder.java | 139 ++++++ .../java/sonia/scm/net/ahc/HttpMethod.java | 64 +++ .../java/sonia/scm/net/ahc/RawContent.java | 109 +++++ .../java/sonia/scm/net/ahc/StringContent.java | 58 +++ .../java/sonia/scm/net/ahc/package-info.java | 36 ++ .../scm/net/ahc/AdvancedHttpClientTest.java | 104 +++++ .../scm/net/ahc/AdvancedHttpRequestTest.java | 57 +++ .../ahc/AdvancedHttpRequestWithBodyTest.java | 127 ++++++ .../scm/net/ahc/AdvancedHttpResponseTest.java | 91 ++++ .../scm/net/ahc/BaseHttpRequestTest.java | 142 ++++++ .../scm/net/ahc/ByteSourceContentTest.java | 74 ++++ .../sonia/scm/net/ahc/FileContentTest.java | 116 +++++ .../scm/net/ahc/FormContentBuilderTest.java | 95 ++++ .../sonia/scm/net/ahc/RawContentTest.java | 72 ++++ .../sonia/scm/net/ahc/StringContentTest.java | 59 +++ .../net/ahc/DefaultAdvancedHttpClient.java | 319 ++++++++++++++ .../net/ahc/DefaultAdvancedHttpResponse.java | 194 +++++++++ 25 files changed, 3215 insertions(+) create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpClient.java create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpRequest.java create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBody.java create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpResponse.java create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/BaseHttpRequest.java create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/ByteSourceContent.java create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/Content.java create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/FileContent.java create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/FormContentBuilder.java create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/HttpMethod.java create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/RawContent.java create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/StringContent.java create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/package-info.java create mode 100644 scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpClientTest.java create mode 100644 scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpRequestTest.java create mode 100644 scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBodyTest.java create mode 100644 scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpResponseTest.java create mode 100644 scm-core/src/test/java/sonia/scm/net/ahc/BaseHttpRequestTest.java create mode 100644 scm-core/src/test/java/sonia/scm/net/ahc/ByteSourceContentTest.java create mode 100644 scm-core/src/test/java/sonia/scm/net/ahc/FileContentTest.java create mode 100644 scm-core/src/test/java/sonia/scm/net/ahc/FormContentBuilderTest.java create mode 100644 scm-core/src/test/java/sonia/scm/net/ahc/RawContentTest.java create mode 100644 scm-core/src/test/java/sonia/scm/net/ahc/StringContentTest.java create mode 100644 scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpClient.java create mode 100644 scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponse.java diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpClient.java b/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpClient.java new file mode 100644 index 0000000000..943d1a0352 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpClient.java @@ -0,0 +1,189 @@ +/** + * 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.net.ahc; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.IOException; + +/** + * Advanced client for http operations. The {@link AdvancedHttpClient} replaces + * the much more simpler implementation {@link sonia.scm.net.HttpClient}. The + * {@link AdvancedHttpClient} offers a fluid interface for handling most common + * http operations. The {@link AdvancedHttpClient} can be injected by the + * default injection mechanism of SCM-Manager. + *

 

+ * Http GET example: + * + *

+ * AdvancedHttpResponse response = client.get("https://www.scm-manager.org")
+ *                                       .decodeGZip(true)
+ *                                       .request();
+ * 
+ * System.out.println(response.contentAsString());
+ * 
+ * + *

 

+ * Http POST example: + * + *

+ * AdvancedHttpResponse response = client.post("https://www.scm-manager.org")
+ *                                       .formContent()
+ *                                       .field("firstname", "Tricia")
+ *                                       .field("lastname", "McMillan")
+ *                                       .build()
+ *                                       .request();
+ * 
+ * if (response.isSuccessful()){
+ *   System.out.println("success");
+ * }
+ * 
+ * + * @author Sebastian Sdorra + * @since 1.46 + * + * @apiviz.landmark + */ +public abstract class AdvancedHttpClient +{ + + /** + * Executes the given request and returns the http response. Implementation + * have to check, if the instance if from type + * {@link AdvancedHttpRequestWithBody} in order to handle request contents. + * + * + * @param request request to execute + * + * @return http response + * + * @throws IOException + */ + protected abstract AdvancedHttpResponse request(BaseHttpRequest request) + throws IOException; + + /** + * Returns a builder for a DELETE request. + * + * + * @param url request url + * + * @return request builder + */ + public AdvancedHttpRequestWithBody delete(String url) + { + return new AdvancedHttpRequestWithBody(this, HttpMethod.DELETE, url); + } + + /** + * Returns a builder for a HEAD request. + * + * + * @param url request url + * + * @return request builder + */ + public AdvancedHttpRequest head(String url) + { + return new AdvancedHttpRequest(this, HttpMethod.HEAD, url); + } + + /** + * Returns a request builder with a custom method. Note: not + * every method is supported by the underlying implementation of the http + * client. + * + * + * @param method http method + * @param url request url + * + * @return request builder + */ + public AdvancedHttpRequestWithBody method(String method, String url) + { + return new AdvancedHttpRequestWithBody(this, method, url); + } + + /** + * Returns a builder for a OPTIONS request. + * + * + * @param url request url + * + * @return request builder + */ + public AdvancedHttpRequestWithBody options(String url) + { + return new AdvancedHttpRequestWithBody(this, HttpMethod.OPTIONS, url); + } + + /** + * Returns a builder for a POST request. + * + * + * @param url request url + * + * @return request builder + */ + public AdvancedHttpRequestWithBody post(String url) + { + return new AdvancedHttpRequestWithBody(this, HttpMethod.POST, url); + } + + /** + * Returns a builder for a PUT request. + * + * + * @param url request url + * + * @return request builder + */ + public AdvancedHttpRequestWithBody put(String url) + { + return new AdvancedHttpRequestWithBody(this, HttpMethod.PUT, url); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Returns a builder for a GET request. + * + * + * @param url request url + * + * @return request builder + */ + public AdvancedHttpRequest get(String url) + { + return new AdvancedHttpRequest(this, HttpMethod.GET, url); + } +} diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpRequest.java b/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpRequest.java new file mode 100644 index 0000000000..b8940628b4 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpRequest.java @@ -0,0 +1,72 @@ +/** + * 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.net.ahc; + +//~--- JDK imports ------------------------------------------------------------ + +/** + * An http request without {@link Content} for example a GET or HEAD request. + * + * @author Sebastian Sdorra + * @since 1.46 + */ +public class AdvancedHttpRequest extends BaseHttpRequest +{ + + /** + * Constructs a new {@link AdvancedHttpRequest} + * + * + * @param client + * @param method + * @param url + */ + AdvancedHttpRequest(AdvancedHttpClient client, String method, + String url) + { + super(client, method, url); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Returns {@code this}. + * + * + * @return {@code this} + */ + @Override + protected AdvancedHttpRequest self() + { + return this; + } +} diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBody.java b/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBody.java new file mode 100644 index 0000000000..1acad0efb0 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBody.java @@ -0,0 +1,216 @@ +/** + * 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.net.ahc; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Charsets; +import com.google.common.io.ByteSource; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.File; + +import java.nio.charset.Charset; + +/** + * Http request with body. + * + * @author Sebastian Sdorra + * @since 1.46 + */ +public class AdvancedHttpRequestWithBody + extends BaseHttpRequest +{ + + /** + * Constructs a new {@link AdvancedHttpRequestWithBody}. + * + * + * @param client http client + * @param method http method + * @param url url + */ + AdvancedHttpRequestWithBody(AdvancedHttpClient client, String method, + String url) + { + super(client, method, url); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Sets the content length for the request. + * + * + * @param length content length + * + * @return {@code this} + */ + public AdvancedHttpRequestWithBody contentLength(long length) + { + return header("Content-Length", String.valueOf(length)); + } + + /** + * Sets the content type for the request. + * + * + * @param contentType content type + * + * @return {@code this} + */ + public AdvancedHttpRequestWithBody contentType(String contentType) + { + return header("Content-Type", contentType); + } + + /** + * Sets the content of the file as request content. + * + * + * @param file file + * + * @return {@code this} + */ + public AdvancedHttpRequestWithBody fileContent(File file) + { + this.content = new FileContent(file); + + return this; + } + + /** + * Returns a {@link FormContentBuilder}. The builder can be used to add form + * parameters as content for the request. Note: you have to + * call {@link FormContentBuilder#build()} in order to apply the form content + * to the request. + * + * @return form content builder + */ + public FormContentBuilder formContent() + { + return new FormContentBuilder(this); + } + + /** + * Sets the raw data as request content. + * + * + * @param data raw data + * + * @return {@code this} + */ + public AdvancedHttpRequestWithBody rawContent(byte[] data) + { + this.content = new RawContent(data); + + return this; + } + + /** + * Sets the raw data as request content. + * + * + * @param source byte source + * + * @return {@code this} + */ + public AdvancedHttpRequestWithBody rawContent(ByteSource source) + { + this.content = new ByteSourceContent(source); + + return this; + } + + /** + * Sets the string as request content. + * + * + * @param content string content + * + * @return {@code this} + */ + public AdvancedHttpRequestWithBody stringContent(String content) + { + return stringContent(content, Charsets.UTF_8); + } + + /** + * Sets the string as request content. + * + * + * @param content string content + * @param charset charset of content + * + * @return {@code this} + */ + public AdvancedHttpRequestWithBody stringContent(String content, + Charset charset) + { + this.content = new StringContent(content, charset); + + return this; + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Returns the content or the request. + * + * + * @return request content + */ + public Content getContent() + { + return content; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Returns {@code this}. + * + * + * @return {@code this} + */ + @Override + protected AdvancedHttpRequestWithBody self() + { + return this; + } + + //~--- fields --------------------------------------------------------------- + + /** request content */ + private Content content; +} diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpResponse.java b/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpResponse.java new file mode 100644 index 0000000000..6148b3b355 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpResponse.java @@ -0,0 +1,199 @@ +/** + * 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.net.ahc; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Charsets; +import com.google.common.collect.Iterables; +import com.google.common.collect.Multimap; +import com.google.common.io.ByteSource; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; + +/** + * Http response. The response of a {@link AdvancedHttpRequest} or + * {@link AdvancedHttpRequestWithBody}. + * + * @author Sebastian Sdorra + * @since 1.46 + */ +public abstract class AdvancedHttpResponse +{ + + //~--- methods -------------------------------------------------------------- + + /** + * Returns the content of the response as byte array. + * + * + * @return content as byte array + * + * @throws IOException + */ + public byte[] content() throws IOException + { + ByteSource content = contentAsByteSource(); + byte[] data = null; + + if (content != null) + { + data = content.read(); + } + + return data; + } + + /** + * Returns a reader for the content of the response. + * + * + * @return read of response content + * + * @throws IOException + */ + public BufferedReader contentAsReader() throws IOException + { + ByteSource content = contentAsByteSource(); + BufferedReader reader = null; + if (content != null) + { + reader = content.asCharSource(Charsets.UTF_8).openBufferedStream(); + } + + return reader; + } + + /** + * Returns response content as stream. + * + * + * @return response content as stram + * + * @throws IOException + */ + public InputStream contentAsStream() throws IOException + { + ByteSource content = contentAsByteSource(); + InputStream stream = null; + if (content != null) + { + stream = content.openBufferedStream(); + } + + return stream; + } + + /** + * Returns the response content as byte source. + * + * + * @return response content as byte source + * @throws IOException + */ + public abstract ByteSource contentAsByteSource() throws IOException; + + /** + * Returns the response content as string. + * + * + * @return response content + * + * @throws IOException + */ + public String contentAsString() throws IOException + { + ByteSource content = contentAsByteSource(); + String value = null; + if (content != null) + { + value = content.asCharSource(Charsets.UTF_8).read(); + } + + return value; + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Returns the first header value for the given header name or {@code null}. + * + * + * @param name header name + * + * @return header value or {@code null} + */ + public String getFirstHeader(String name) + { + return Iterables.getFirst(getHeaders().get(name), null); + } + + /** + * Returns the response headers. + * + * + * @return response headers + */ + public abstract Multimap getHeaders(); + + /** + * Returns {@code true} if the response was successful. A response is + * successful, if the status code is greater than 199 and lower than 400. + * + * @return {@code true} if the response was successful + */ + public boolean isSuccessful() + { + int status = getStatus(); + return status > 199 && status < 400; + } + + /** + * Returns the status code of the response. + * + * + * @return status code + */ + public abstract int getStatus(); + + /** + * Returns the status text of the response. + * + * + * @return status text + */ + public abstract String getStatusText(); +} diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/BaseHttpRequest.java b/scm-core/src/main/java/sonia/scm/net/ahc/BaseHttpRequest.java new file mode 100644 index 0000000000..f48e819ddc --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/net/ahc/BaseHttpRequest.java @@ -0,0 +1,408 @@ +/** + * 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.net.ahc; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Charsets; +import com.google.common.base.Strings; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; + +import org.apache.shiro.codec.Base64; + +import sonia.scm.util.HttpUtil; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.IOException; + +/** + * Base class for http requests. + * + * @author Sebastian Sdorra + * @param request implementation + * + * @since 1.46 + */ +public abstract class BaseHttpRequest +{ + + /** + * Constructs a new {@link BaseHttpRequest}. + * + * + * @param client http client + * @param method http method + * @param url url + */ + public BaseHttpRequest(AdvancedHttpClient client, String method, String url) + { + this.client = client; + this.method = method; + this.url = url; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Executes the request and returns the http response. + * + * + * @return http response + * + * @throws IOException + */ + public AdvancedHttpResponse request() throws IOException + { + return client.request(this); + } + + /** + * Implementing classes should return {@code this}. + * + * + * @return request instance + */ + protected abstract T self(); + + /** + * Enabled http basic authentication. + * + * + * @param username username for http basic authentication + * @param password password for http basic authentication + * + * @return http request instance + */ + public T basicAuth(String username, String password) + { + String auth = Strings.nullToEmpty(username).concat(":").concat( + Strings.nullToEmpty(password)); + + auth = Base64.encodeToString(auth.getBytes(Charsets.ISO_8859_1)); + headers.put("Authorization", "Basic ".concat(auth)); + + return self(); + } + + /** + * Enable or disabled gzip decoding. The default value is false. + * + * + * @param decodeGZip true to enable gzip decoding + * + * @return request instance + */ + public T decodeGZip(boolean decodeGZip) + { + this.decodeGZip = decodeGZip; + + return self(); + } + + /** + * Enable or disable certificate validation of ssl certificates. The default + * value is false. + * + * + * @param disableCertificateValidation true to disable certificate validation + * + * @return request instance + */ + public T disableCertificateValidation(boolean disableCertificateValidation) + { + this.disableCertificateValidation = disableCertificateValidation; + + return self(); + } + + /** + * Enable or disable the validation of ssl hostnames. The default value is + * false. + * + * + * @param disableHostnameValidation true to disable ssl hostname validation + * + * @return request instance + */ + public T disableHostnameValidation(boolean disableHostnameValidation) + { + this.disableHostnameValidation = disableHostnameValidation; + + return self(); + } + + /** + * Add http headers to request. + * + * + * @param name header name + * @param values header values + * + * @return request instance + */ + public T header(String name, Object... values) + { + for (Object v : values) + { + headers.put(name, toString(v)); + } + + return self(); + } + + /** + * Add http headers to request. + * + * + * @param name header name + * @param values header values + * + * @return request instance + */ + public T headers(String name, Iterable values) + { + for (Object v : values) + { + headers.put(name, toString(v)); + } + + return self(); + } + + /** + * Ignore proxy settings. The default value is false. + * + * + * @param ignoreProxySettings true to ignore proxy settings. + * + * @return request instance + */ + public T ignoreProxySettings(boolean ignoreProxySettings) + { + this.ignoreProxySettings = ignoreProxySettings; + + return self(); + } + + /** + * Appends a query parameter to the request. + * + * + * @param name name of query parameter + * @param values query parameter values + * + * @return request instance + */ + public T queryString(String name, Object... values) + { + for (Object v : values) + { + appendQueryString(name, v); + } + + return self(); + } + + /** + * Appends a query parameter to the request. + * + * + * @param name name of query parameter + * @param values query parameter values + * + * @return request instance + */ + public T queryStrings(String name, Iterable values) + { + for (Object v : values) + { + appendQueryString(name, v); + } + + return self(); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Return a map with http headers used for the request. + * + * + * @return map with http headers + */ + public Multimap getHeaders() + { + return headers; + } + + /** + * Returns the http method for the request. + * + * + * @return http method of request + */ + public String getMethod() + { + return method; + } + + /** + * Returns the url for the request. + * + * + * @return url of the request + */ + public String getUrl() + { + return url; + } + + /** + * Returns true if the request decodes gzip compression. + * + * + * @return true if the request decodes gzip compression + */ + public boolean isDecodeGZip() + { + return decodeGZip; + } + + /** + * Returns true if the verification of ssl certificates is disabled. + * + * + * @return true if certificate verification is disabled + */ + public boolean isDisableCertificateValidation() + { + return disableCertificateValidation; + } + + /** + * Returns true if the ssl hostname validation is disabled. + * + * + * @return true if the ssl hostname validation is disabled + */ + public boolean isDisableHostnameValidation() + { + return disableHostnameValidation; + } + + /** + * Returns true if the proxy settings are ignored. + * + * + * @return true if the proxy settings are ignored + */ + public boolean isIgnoreProxySettings() + { + return ignoreProxySettings; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Returns the value url encoded. + * + * + * @param value value to encode + * + * @return encoded value + */ + protected String encoded(Object value) + { + return HttpUtil.encode(Strings.nullToEmpty(toString(value))); + } + + /** + * Returns string representation of the given object or {@code null}, if the + * object is {@code null}. + * + * + * @param object object + * + * @return string representation or {@code null} + */ + protected String toString(Object object) + { + return (object != null) + ? object.toString() + : null; + } + + private void appendQueryString(String name, Object value) + { + StringBuilder buffer = new StringBuilder(); + + if (url.contains("?")) + { + buffer.append("&"); + } + else + { + buffer.append("?"); + } + + buffer.append(encoded(name)).append("=").append(encoded(value)); + url = url.concat(buffer.toString()); + } + + //~--- fields --------------------------------------------------------------- + + /** http client */ + protected final AdvancedHttpClient client; + + /** http header */ + private final Multimap headers = HashMultimap.create(); + + /** http method */ + private final String method; + + /** ignore proxy settings */ + private boolean ignoreProxySettings = false; + + /** disable ssl hostname validation */ + private boolean disableHostnameValidation = false; + + /** disable ssl certificate validation */ + private boolean disableCertificateValidation = false; + + /** decode gzip */ + private boolean decodeGZip = false; + + /** url of request */ + private String url; +} diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/ByteSourceContent.java b/scm-core/src/main/java/sonia/scm/net/ahc/ByteSourceContent.java new file mode 100644 index 0000000000..f4182c0975 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/net/ahc/ByteSourceContent.java @@ -0,0 +1,97 @@ +/** + * 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.net.ahc; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.io.ByteSource; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.IOException; +import java.io.OutputStream; + +/** + * {@link ByteSource} content for {@link AdvancedHttpRequestWithBody}. + * + * @author Sebastian Sdorra + * @since 1.46 + */ +public class ByteSourceContent implements Content +{ + + /** + * Constructs a new {@link ByteSourceContent}. + * + * + * @param byteSource byte source + */ + public ByteSourceContent(ByteSource byteSource) + { + this.byteSource = byteSource; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Sets the content length for the request. + * + * + * @param request http request + * + * @throws IOException + */ + @Override + public void prepare(AdvancedHttpRequestWithBody request) throws IOException + { + request.contentLength(byteSource.size()); + } + + /** + * Copies the content of the byte source to the output stream. + * + * + * @param output output stream + * + * @throws IOException + */ + @Override + public void process(OutputStream output) throws IOException + { + byteSource.copyTo(output); + } + + //~--- fields --------------------------------------------------------------- + + /** byte source */ + private final ByteSource byteSource; +} diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/Content.java b/scm-core/src/main/java/sonia/scm/net/ahc/Content.java new file mode 100644 index 0000000000..0f73e7b0d6 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/net/ahc/Content.java @@ -0,0 +1,69 @@ +/** + * 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.net.ahc; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Content of a {@link AdvancedHttpRequestWithBody}. + * + * @author Sebastian Sdorra + * @since 1.46 + */ +public interface Content +{ + + /** + * Prepares the {@link AdvancedHttpRequestWithBody} for the request content. + * Implementations can set the content type, content length or custom headers + * for the request. + * + * + * @param request request + * + * @throws IOException + */ + public void prepare(AdvancedHttpRequestWithBody request) throws IOException; + + /** + * Copies the content to the output stream. + * + * + * @param output output stream + * + * @throws IOException + */ + public void process(OutputStream output) throws IOException; +} diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/FileContent.java b/scm-core/src/main/java/sonia/scm/net/ahc/FileContent.java new file mode 100644 index 0000000000..51f1af6c21 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/net/ahc/FileContent.java @@ -0,0 +1,109 @@ +/** + * 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.net.ahc; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.io.ByteStreams; +import com.google.common.io.Closeables; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Sets the content of the file to the request. + * + * @author Sebastian Sdorra + * @since 1.46 + */ +public class FileContent implements Content +{ + + /** + * Constructs a new {@link FileContent}. + * + * + * @param file file + */ + public FileContent(File file) + { + this.file = file; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Sets the content length of the file as request header. + * + * + * @param request request + */ + @Override + public void prepare(AdvancedHttpRequestWithBody request) + { + request.contentLength(file.length()); + } + + /** + * Copies the content of the file to the output stream. + * + * + * @param output output stream + * + * @throws IOException + */ + @Override + public void process(OutputStream output) throws IOException + { + InputStream stream = null; + + try + { + stream = new FileInputStream(file); + ByteStreams.copy(stream, output); + } + finally + { + Closeables.close(stream, true); + } + } + + //~--- fields --------------------------------------------------------------- + + /** file */ + private final File file; +} diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/FormContentBuilder.java b/scm-core/src/main/java/sonia/scm/net/ahc/FormContentBuilder.java new file mode 100644 index 0000000000..d25c12ce94 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/net/ahc/FormContentBuilder.java @@ -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.net.ahc; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Strings; + +import sonia.scm.util.HttpUtil; + +/** + * The form builder is able to add form parameters to a request. + * + * @author Sebastian Sdorra + * @since 1.46 + */ +public class FormContentBuilder +{ + + /** + * Constructs a new {@link FormContentBuilder}. + * + * + * @param request request + */ + public FormContentBuilder(AdvancedHttpRequestWithBody request) + { + this.request = request; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Build the formular content and append it to the request. + * + * + * @return request instance + */ + public AdvancedHttpRequestWithBody build() + { + request.contentType("application/x-www-form-urlencoded"); + request.stringContent(builder.toString()); + + return request; + } + + /** + * Adds a formular parameter. + * + * + * @param name parameter name + * @param values parameter values + * + * @return {@code this} + */ + public FormContentBuilder fields(String name, Iterable values) + { + for (Object v : values) + { + append(name, v); + } + + return this; + } + + /** + * Adds a formular parameter. + * + * + * @param name parameter name + * @param values parameter values + * + * @return {@code this} + */ + public FormContentBuilder field(String name, Object... values) + { + for (Object v : values) + { + append(name, v); + } + + return this; + } + + private void append(String name, Object value) + { + if (!Strings.isNullOrEmpty(name)) + { + if (builder.length() > 0) + { + builder.append("&"); + } + + builder.append(HttpUtil.encode(name)).append("="); + + if (value != null) + { + builder.append(HttpUtil.encode(value.toString())); + } + } + } + + //~--- fields --------------------------------------------------------------- + + /** content builder */ + private final StringBuilder builder = new StringBuilder(); + + /** request */ + private final AdvancedHttpRequestWithBody request; +} diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/HttpMethod.java b/scm-core/src/main/java/sonia/scm/net/ahc/HttpMethod.java new file mode 100644 index 0000000000..41a23dcccb --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/net/ahc/HttpMethod.java @@ -0,0 +1,64 @@ +/** + * 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.net.ahc; + +/** + * Http methods. + * + * @author Sebastian Sdorra + * @since 1.46 + */ +public final class HttpMethod +{ + + /** http delete method */ + public static final String DELETE = "DELETE"; + + /** http get method */ + public static final String GET = "GET"; + + /** http head method */ + public static final String HEAD = "HEAD"; + + /** http options method */ + public static final String OPTIONS = "OPTIONS"; + + /** http post method */ + public static final String POST = "POST"; + + /** http put method */ + public static final String PUT = "PUT"; + + //~--- constructors --------------------------------------------------------- + + private HttpMethod() {} +} diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/RawContent.java b/scm-core/src/main/java/sonia/scm/net/ahc/RawContent.java new file mode 100644 index 0000000000..0b5e99bc49 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/net/ahc/RawContent.java @@ -0,0 +1,109 @@ +/** + * 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.net.ahc; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.annotations.VisibleForTesting; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Byte array content for {@link AdvancedHttpRequestWithBody}. + * + * @author Sebastian Sdorra + * @since 1.46 + */ +public class RawContent implements Content +{ + + /** + * Constructs a new {@link RawContent}. + * + * + * @param data data + */ + public RawContent(byte[] data) + { + this.data = data; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Sets the length of the byte array as http header. + * + * + * @param request request + */ + @Override + public void prepare(AdvancedHttpRequestWithBody request) + { + request.contentLength(data.length); + } + + /** + * Writes the byte array to the output stream. + * + * + * @param output output stream + * + * @throws IOException + */ + @Override + public void process(OutputStream output) throws IOException + { + output.write(data); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Returns content data. + * + * + * @return content data + */ + @VisibleForTesting + byte[] getData() + { + return data; + } + + //~--- fields --------------------------------------------------------------- + + /** byte array */ + private final byte[] data; +} diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/StringContent.java b/scm-core/src/main/java/sonia/scm/net/ahc/StringContent.java new file mode 100644 index 0000000000..07473447a7 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/net/ahc/StringContent.java @@ -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.net.ahc; + +//~--- JDK imports ------------------------------------------------------------ + +import java.nio.charset.Charset; + +/** + * String content for {@link AdvancedHttpRequestWithBody}. + * + * @author Sebastian Sdorra + * @since 1.46 + */ +public class StringContent extends RawContent +{ + + /** + * Constructs a new {@link StringContent}. + * + * + * @param content content + * @param charset charset + */ + public StringContent(String content, Charset charset) + { + super(content.getBytes(charset)); + } +} diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/package-info.java b/scm-core/src/main/java/sonia/scm/net/ahc/package-info.java new file mode 100644 index 0000000000..d11a603b5a --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/net/ahc/package-info.java @@ -0,0 +1,36 @@ +/** + * 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 + * + */ + + +/** + * Advanced http client. + */ +package sonia.scm.net.ahc; diff --git a/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpClientTest.java b/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpClientTest.java new file mode 100644 index 0000000000..18b291d8ad --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpClientTest.java @@ -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.net.ahc; + +import java.io.IOException; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * + * @author Sebastian Sdorra + */ +public class AdvancedHttpClientTest { + + private final AdvancedHttpClient client = new AdvancedHttpClient() + { + + @Override + protected AdvancedHttpResponse request( + BaseHttpRequest request) throws IOException + { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + }; + + private static final String URL = "https://www.scm-manager.org"; + + @Test + public void testGet() + { + AdvancedHttpRequest request = client.get(URL); + assertEquals(URL, request.getUrl()); + assertEquals(HttpMethod.GET, request.getMethod()); + } + + @Test + public void testDelete() + { + AdvancedHttpRequestWithBody request = client.delete(URL); + assertEquals(URL, request.getUrl()); + assertEquals(HttpMethod.DELETE, request.getMethod()); + } + + @Test + public void testPut() + { + AdvancedHttpRequestWithBody request = client.put(URL); + assertEquals(URL, request.getUrl()); + assertEquals(HttpMethod.PUT, request.getMethod()); + } + + @Test + public void testPost() + { + AdvancedHttpRequestWithBody request = client.post(URL); + assertEquals(URL, request.getUrl()); + assertEquals(HttpMethod.POST, request.getMethod()); + } + + @Test + public void testOptions() + { + AdvancedHttpRequestWithBody request = client.options(URL); + assertEquals(URL, request.getUrl()); + assertEquals(HttpMethod.OPTIONS, request.getMethod()); + } + + @Test + public void testHead() + { + AdvancedHttpRequest request = client.head(URL); + assertEquals(URL, request.getUrl()); + assertEquals(HttpMethod.HEAD, request.getMethod()); + } +} \ No newline at end of file diff --git a/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpRequestTest.java b/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpRequestTest.java new file mode 100644 index 0000000000..5b2d675e97 --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpRequestTest.java @@ -0,0 +1,57 @@ +/** + * 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.net.ahc; + +import org.junit.Test; +import static org.junit.Assert.*; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +/** + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +public class AdvancedHttpRequestTest { + + @Mock + private AdvancedHttpClient ahc; + + @Test + public void testSelf() + { + AdvancedHttpRequest ahr = new AdvancedHttpRequest(ahc, HttpMethod.GET, "https://www.scm-manager.org"); + assertEquals(AdvancedHttpRequest.class, ahr.self().getClass()); + } + +} \ No newline at end of file diff --git a/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBodyTest.java b/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBodyTest.java new file mode 100644 index 0000000000..8e656133e7 --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBodyTest.java @@ -0,0 +1,127 @@ +/** + * 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.net.ahc; + +import com.google.common.base.Charsets; +import com.google.common.io.ByteSource; +import java.io.File; +import java.io.IOException; +import org.junit.Test; +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; +import org.junit.Before; +import org.junit.Rule; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +/** + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +public class AdvancedHttpRequestWithBodyTest { + + @Mock + private AdvancedHttpClient ahc; + + private AdvancedHttpRequestWithBody request; + + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder(); + + @Before + public void before(){ + request = new AdvancedHttpRequestWithBody(ahc, HttpMethod.PUT, "https://www.scm-manager.org"); + } + + @Test + public void testContentLength() + { + request.contentLength(12l); + assertEquals("12", request.getHeaders().get("Content-Length").iterator().next()); + } + + @Test + public void testContentType(){ + request.contentType("text/plain"); + assertEquals("text/plain", request.getHeaders().get("Content-Type").iterator().next()); + } + + @Test + public void testFileContent() throws IOException{ + File file = tempFolder.newFile(); + request.fileContent(file); + assertThat(request.getContent(), instanceOf(FileContent.class)); + } + + @Test + public void testRawContent() throws IOException { + request.rawContent("test".getBytes(Charsets.UTF_8)); + assertThat(request.getContent(), instanceOf(RawContent.class)); + } + + @Test + public void testRawContentWithByteSource() throws IOException { + ByteSource bs = ByteSource.wrap("test".getBytes(Charsets.UTF_8)); + request.rawContent(bs); + assertThat(request.getContent(), instanceOf(ByteSourceContent.class)); + } + + @Test + public void testFormContent(){ + FormContentBuilder builder = request.formContent(); + assertNotNull(builder); + builder.build(); + assertThat(request.getContent(), instanceOf(StringContent.class)); + } + + @Test + public void testStringContent(){ + request.stringContent("test"); + assertThat(request.getContent(), instanceOf(StringContent.class)); + } + + @Test + public void testStringContentWithCharset(){ + request.stringContent("test", Charsets.UTF_8); + assertThat(request.getContent(), instanceOf(StringContent.class)); + } + + @Test + public void testSelf() + { + assertEquals(AdvancedHttpRequestWithBody.class, request.self().getClass()); + } + +} \ No newline at end of file diff --git a/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpResponseTest.java b/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpResponseTest.java new file mode 100644 index 0000000000..9b3ccaa348 --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpResponseTest.java @@ -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.net.ahc; + +import com.google.common.base.Charsets; +import com.google.common.io.ByteSource; +import com.google.common.io.ByteStreams; +import com.google.common.io.CharStreams; +import java.io.IOException; +import org.junit.Test; +import static org.junit.Assert.*; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.Mock; +import static org.mockito.Mockito.*; +import org.mockito.runners.MockitoJUnitRunner; + +/** + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +public class AdvancedHttpResponseTest { + + @Mock(answer = Answers.CALLS_REAL_METHODS) + private AdvancedHttpResponse response; + + @Test + public void testContent() throws IOException + { + ByteSource bs = ByteSource.wrap("test123".getBytes(Charsets.UTF_8)); + when(response.contentAsByteSource()).thenReturn(bs); + byte[] data = response.content(); + assertEquals("test123", new String(data, Charsets.UTF_8)); + } + + @Test + public void testContentAsString() throws IOException + { + ByteSource bs = ByteSource.wrap("123test".getBytes(Charsets.UTF_8)); + when(response.contentAsByteSource()).thenReturn(bs); + assertEquals("123test", response.contentAsString()); + } + + @Test + public void testContentAsReader() throws IOException + { + ByteSource bs = ByteSource.wrap("abc123".getBytes(Charsets.UTF_8)); + when(response.contentAsByteSource()).thenReturn(bs); + assertEquals("abc123", CharStreams.toString(response.contentAsReader())); + } + + @Test + public void testContentAsStream() throws IOException + { + ByteSource bs = ByteSource.wrap("cde456".getBytes(Charsets.UTF_8)); + when(response.contentAsByteSource()).thenReturn(bs); + byte[] data = ByteStreams.toByteArray(response.contentAsStream()); + assertEquals("cde456", new String(data, Charsets.UTF_8)); + } + +} \ No newline at end of file diff --git a/scm-core/src/test/java/sonia/scm/net/ahc/BaseHttpRequestTest.java b/scm-core/src/test/java/sonia/scm/net/ahc/BaseHttpRequestTest.java new file mode 100644 index 0000000000..7adab5aa84 --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/net/ahc/BaseHttpRequestTest.java @@ -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.net.ahc; + +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import java.io.IOException; +import java.util.Collection; +import org.junit.Test; +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import static org.mockito.Mockito.*; +import org.mockito.runners.MockitoJUnitRunner; + +/** + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +public class BaseHttpRequestTest { + + @Mock + private AdvancedHttpClient ahc; + + private BaseHttpRequest request; + + @Before + public void before(){ + request = new AdvancedHttpRequest(ahc, HttpMethod.GET, "https://www.scm-manager.org"); + } + + @Test + public void testBasicAuth() + { + request.basicAuth("tricia", "mcmillian123"); + Multimap headers = request.getHeaders(); + assertEquals("Basic dHJpY2lhOm1jbWlsbGlhbjEyMw==", headers.get("Authorization").iterator().next()); + } + + @Test + public void testQueryString(){ + request.queryString("a", "b"); + assertEquals("https://www.scm-manager.org?a=b", request.getUrl()); + } + + @Test + public void testQueryStringMultiple(){ + request.queryString("a", "b"); + request.queryString("c", "d", "e"); + assertEquals("https://www.scm-manager.org?a=b&c=d&c=e", request.getUrl()); + } + + @Test + public void testQueryStringEncoded(){ + request.queryString("a", "äüö"); + assertEquals("https://www.scm-manager.org?a=%C3%A4%C3%BC%C3%B6", request.getUrl()); + } + + @Test + public void testQueryStrings(){ + Iterable i1 = Lists.newArrayList("b"); + Iterable i2 = Lists.newArrayList("d", "e"); + request.queryStrings("a", i1); + request.queryStrings("c", i2); + assertEquals("https://www.scm-manager.org?a=b&c=d&c=e", request.getUrl()); + } + + @Test + public void testQuerqStringNullValue(){ + request.queryString("a", null, "b"); + assertEquals("https://www.scm-manager.org?a=&a=b", request.getUrl()); + } + + @Test + public void testHeader(){ + request.header("a", "b"); + assertEquals("b", request.getHeaders().get("a").iterator().next()); + } + + @Test + public void testHeaderMultiple(){ + request.header("a", "b", "c", "d"); + Collection values = request.getHeaders().get("a"); + assertThat(values, containsInAnyOrder("b", "c", "d")); + } + + @Test + public void testRequest() throws IOException{ + request.request(); + verify(ahc).request(request); + } + + @Test + public void testBuilderMethods(){ + Iterable i1 = Lists.newArrayList("b"); + assertThat(request.decodeGZip(true), instanceOf(AdvancedHttpRequest.class)); + assertTrue(request.isDecodeGZip()); + assertThat(request.disableCertificateValidation(true), instanceOf(AdvancedHttpRequest.class)); + assertTrue(request.isDisableCertificateValidation()); + assertThat(request.disableHostnameValidation(true), instanceOf(AdvancedHttpRequest.class)); + assertTrue(request.isDisableHostnameValidation()); + assertThat(request.ignoreProxySettings(true), instanceOf(AdvancedHttpRequest.class)); + assertTrue(request.isIgnoreProxySettings()); + assertThat(request.header("a", "b"), instanceOf(AdvancedHttpRequest.class)); + assertThat(request.headers("a", i1), instanceOf(AdvancedHttpRequest.class)); + assertThat(request.queryString("a", "b"), instanceOf(AdvancedHttpRequest.class)); + assertThat(request.queryStrings("a", i1), instanceOf(AdvancedHttpRequest.class)); + } + +} \ No newline at end of file diff --git a/scm-core/src/test/java/sonia/scm/net/ahc/ByteSourceContentTest.java b/scm-core/src/test/java/sonia/scm/net/ahc/ByteSourceContentTest.java new file mode 100644 index 0000000000..24487f6582 --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/net/ahc/ByteSourceContentTest.java @@ -0,0 +1,74 @@ +/** + * 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.net.ahc; + +import com.google.common.base.Charsets; +import com.google.common.io.ByteSource; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import org.junit.Test; +import static org.junit.Assert.*; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import static org.mockito.Mockito.*; +import org.mockito.runners.MockitoJUnitRunner; + +/** + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +public class ByteSourceContentTest { + + @Mock + private AdvancedHttpRequestWithBody request; + + @Test + public void testPrepareRequest() throws IOException + { + ByteSource source = ByteSource.wrap("abc".getBytes(Charsets.UTF_8)); + ByteSourceContent content = new ByteSourceContent(source); + content.prepare(request); + verify(request).contentLength(3l); + } + + @Test + public void testProcess() throws IOException{ + ByteSource source = ByteSource.wrap("abc".getBytes(Charsets.UTF_8)); + ByteSourceContent content = new ByteSourceContent(source); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + content.process(baos); + assertEquals("abc", baos.toString("UTF-8")); + } + +} \ No newline at end of file diff --git a/scm-core/src/test/java/sonia/scm/net/ahc/FileContentTest.java b/scm-core/src/test/java/sonia/scm/net/ahc/FileContentTest.java new file mode 100644 index 0000000000..95b381b880 --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/net/ahc/FileContentTest.java @@ -0,0 +1,116 @@ +/** + * 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.net.ahc; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Charsets; +import com.google.common.io.Files; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; + +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import static org.junit.Assert.*; + +import static org.mockito.Mockito.*; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; + +/** + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +public class FileContentTest +{ + + /** + * Method description + * + * + * @throws IOException + */ + @Test + public void testPrepareRequest() throws IOException + { + FileContent content = create("abc"); + + content.prepare(request); + verify(request).contentLength(3l); + } + + /** + * Method description + * + * + * @throws IOException + */ + @Test + public void testProcess() throws IOException + { + FileContent content = create("abc"); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + content.process(baos); + assertEquals("abc", baos.toString("UTF-8")); + } + + private FileContent create(String value) throws IOException + { + File file = temp.newFile(); + + Files.write("abc", file, Charsets.UTF_8); + + return new FileContent(file); + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + /** Field description */ + @Mock + private AdvancedHttpRequestWithBody request; +} diff --git a/scm-core/src/test/java/sonia/scm/net/ahc/FormContentBuilderTest.java b/scm-core/src/test/java/sonia/scm/net/ahc/FormContentBuilderTest.java new file mode 100644 index 0000000000..56cc6d7379 --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/net/ahc/FormContentBuilderTest.java @@ -0,0 +1,95 @@ +/** + * 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.net.ahc; + +import com.google.common.collect.Lists; +import org.junit.Test; +import static org.junit.Assert.*; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import static org.mockito.Mockito.*; +import org.mockito.runners.MockitoJUnitRunner; + +/** + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +public class FormContentBuilderTest { + + @Mock + private AdvancedHttpRequestWithBody request; + + @InjectMocks + private FormContentBuilder builder; + + @Test + public void testFieldEncoding() + { + builder.field("a", "ü", "ä", "ö").build(); + assertContent("a=%C3%BC&a=%C3%A4&a=%C3%B6"); + } + + @Test + public void testBuild() + { + builder.field("a", "b").build(); + assertContent("a=b"); + verify(request).contentType("application/x-www-form-urlencoded"); + } + + @Test + public void testFieldWithArray() + { + builder.field("a", "b").field("c", "d", "e").build(); + assertContent("a=b&c=d&c=e"); + } + + @Test + public void testFieldWithIterable() + { + Iterable i1 = Lists.newArrayList("b"); + builder.fields("a", i1) + .fields("c", Lists.newArrayList("d", "e")) + .build(); + assertContent("a=b&c=d&c=e"); + } + + private void assertContent(String content){ + ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + verify(request).stringContent(captor.capture()); + assertEquals(content, captor.getValue()); + } + +} \ No newline at end of file diff --git a/scm-core/src/test/java/sonia/scm/net/ahc/RawContentTest.java b/scm-core/src/test/java/sonia/scm/net/ahc/RawContentTest.java new file mode 100644 index 0000000000..f4ee8b7031 --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/net/ahc/RawContentTest.java @@ -0,0 +1,72 @@ +/** + * 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.net.ahc; + +import com.google.common.base.Charsets; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import org.junit.Test; +import static org.junit.Assert.*; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import static org.mockito.Mockito.*; +import org.mockito.runners.MockitoJUnitRunner; + +/** + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +public class RawContentTest { + + + @Mock + private AdvancedHttpRequestWithBody request; + + @Test + public void testPrepareRequest() + { + RawContent raw = new RawContent("abc".getBytes(Charsets.UTF_8)); + raw.prepare(request); + verify(request).contentLength(3); + } + + @Test + public void testProcess() throws IOException + { + RawContent raw = new RawContent("abc".getBytes(Charsets.UTF_8)); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + raw.process(baos); + assertEquals("abc", baos.toString("UTF-8")); + } + +} \ No newline at end of file diff --git a/scm-core/src/test/java/sonia/scm/net/ahc/StringContentTest.java b/scm-core/src/test/java/sonia/scm/net/ahc/StringContentTest.java new file mode 100644 index 0000000000..bd191438ca --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/net/ahc/StringContentTest.java @@ -0,0 +1,59 @@ +/** + * 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.net.ahc; + +import com.google.common.base.Charsets; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * + * @author Sebastian Sdorra + */ +public class StringContentTest { + + + @Test + public void testStringContent() + { + StringContent sc = new StringContent("abc", Charsets.UTF_8); + assertEquals("abc", new String(sc.getData())); + } + + @Test + public void testStringContentWithCharset() + { + StringContent sc = new StringContent("üäö", Charsets.ISO_8859_1); + assertEquals("üäö", new String(sc.getData(), Charsets.ISO_8859_1)); + } + +} \ No newline at end of file diff --git a/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpClient.java b/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpClient.java new file mode 100644 index 0000000000..1aadef143c --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpClient.java @@ -0,0 +1,319 @@ +/** + * 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.net.ahc; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Strings; +import com.google.common.collect.Multimap; +import com.google.common.io.Closeables; +import com.google.inject.Inject; + +import org.apache.shiro.codec.Base64; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sonia.scm.config.ScmConfiguration; +import sonia.scm.net.Proxies; +import sonia.scm.net.TrustAllHostnameVerifier; +import sonia.scm.net.TrustAllTrustManager; +import sonia.scm.util.HttpUtil; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.IOException; +import java.io.OutputStream; + +import java.net.HttpURLConnection; +import java.net.InetSocketAddress; +import java.net.ProtocolException; +import java.net.Proxy; +import java.net.SocketAddress; +import java.net.URL; + +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; + +/** + * + * @author Sebastian Sdorra + * @since 1.46 + */ +public class DefaultAdvancedHttpClient extends AdvancedHttpClient +{ + + /** Field description */ + public static final String CREDENTIAL_SEPARATOR = ":"; + + /** Field description */ + public static final String HEADER_PROXY_AUTHORIZATION = "Proxy-Authorization"; + + /** Field description */ + public static final String PREFIX_BASIC_AUTHENTICATION = "Basic "; + + /** Field description */ + public static final int TIMEOUT_CONNECTION = 30000; + + /** Field description */ + public static final int TIMEOUT_RAED = 1200000; + + /** + * the logger for DefaultAdvancedHttpClient + */ + private static final Logger logger = + LoggerFactory.getLogger(DefaultAdvancedHttpClient.class); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param configuration + */ + @Inject + public DefaultAdvancedHttpClient(ScmConfiguration configuration) + { + this.configuration = configuration; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param request + * + * @return + * + * @throws IOException + */ + @Override + protected AdvancedHttpResponse request(BaseHttpRequest request) + throws IOException + { + HttpURLConnection connection = openConnection(request, + new URL(request.getUrl())); + + applyBaseSettings(request, connection); + + if (connection instanceof HttpsURLConnection) + { + applySSLSettings(request, (HttpsURLConnection) connection); + } + + Content content = null; + + if (request instanceof AdvancedHttpRequestWithBody) + { + AdvancedHttpRequestWithBody ahrwb = (AdvancedHttpRequestWithBody) request; + + content = ahrwb.getContent(); + + if (content != null) + { + content.prepare(ahrwb); + } + else + { + request.header(HttpUtil.HEADER_CONTENT_LENGTH, "0"); + } + } + else + { + request.header(HttpUtil.HEADER_CONTENT_LENGTH, "0"); + } + + applyHeaders(request, connection); + + if (content != null) + { + applyContent(connection, content); + } + + return new DefaultAdvancedHttpResponse(connection, + connection.getResponseCode(), connection.getResponseMessage()); + } + + private void appendProxyAuthentication(HttpURLConnection connection) + { + String username = configuration.getProxyUser(); + String password = configuration.getProxyPassword(); + + if (!Strings.isNullOrEmpty(username) ||!Strings.isNullOrEmpty(password)) + { + logger.debug("append proxy authentication header for user {}", username); + + String auth = username.concat(CREDENTIAL_SEPARATOR).concat(password); + + auth = Base64.encodeToString(auth.getBytes()); + connection.addRequestProperty(HEADER_PROXY_AUTHORIZATION, + PREFIX_BASIC_AUTHENTICATION.concat(auth)); + } + } + + private void applyBaseSettings(BaseHttpRequest request, + HttpURLConnection connection) + throws ProtocolException + { + connection.setRequestMethod(request.getMethod()); + connection.setReadTimeout(TIMEOUT_RAED); + connection.setConnectTimeout(TIMEOUT_CONNECTION); + } + + private void applyContent(HttpURLConnection connection, Content content) + throws IOException + { + connection.setDoOutput(true); + + OutputStream output = null; + + try + { + output = connection.getOutputStream(); + content.process(output); + } + finally + { + Closeables.close(output, true); + } + } + + private void applyHeaders(BaseHttpRequest request, + HttpURLConnection connection) + { + Multimap headers = request.getHeaders(); + + for (String key : headers.keySet()) + { + for (String value : headers.get(key)) + { + connection.setRequestProperty(key, value); + } + } + } + + private void applySSLSettings(BaseHttpRequest request, + HttpsURLConnection connection) + { + if (request.isDisableCertificateValidation()) + { + logger.trace("disable certificate validation"); + + try + { + TrustManager[] trustAllCerts = new TrustManager[] { + new TrustAllTrustManager() }; + SSLContext sc = SSLContext.getInstance("SSL"); + + sc.init(null, trustAllCerts, new java.security.SecureRandom()); + connection.setSSLSocketFactory(sc.getSocketFactory()); + } + catch (KeyManagementException ex) + { + logger.error("could not disable certificate validation", ex); + } + catch (NoSuchAlgorithmException ex) + { + logger.error("could not disable certificate validation", ex); + } + } + + if (request.isDisableHostnameValidation()) + { + logger.trace("disable hostname validation"); + connection.setHostnameVerifier(new TrustAllHostnameVerifier()); + } + } + + private HttpURLConnection openConnection(BaseHttpRequest request, URL url) + throws IOException + { + HttpURLConnection connection; + + if (isProxyEnabled(request)) + { + connection = openProxyConnection(request, url); + appendProxyAuthentication(connection); + } + else + { + if (request.isIgnoreProxySettings()) + { + logger.trace("ignore proxy settings"); + } + + logger.debug("fetch {}", url.toExternalForm()); + + connection = (HttpURLConnection) url.openConnection(); + } + + return connection; + } + + private HttpURLConnection openProxyConnection(BaseHttpRequest request, + URL url) + throws IOException + { + if (logger.isDebugEnabled()) + { + logger.debug("fetch '{}' using proxy {}:{}", url.toExternalForm(), + configuration.getProxyServer(), configuration.getProxyPort()); + } + + SocketAddress address = + new InetSocketAddress(configuration.getProxyServer(), + configuration.getProxyPort()); + + return (HttpURLConnection) url.openConnection(new Proxy(Proxy.Type.HTTP, + address)); + } + + //~--- get methods ---------------------------------------------------------- + + private boolean isProxyEnabled(BaseHttpRequest request) + { + return !request.isIgnoreProxySettings() + && Proxies.isEnabled(configuration, request.getUrl()); + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private final ScmConfiguration configuration; +} diff --git a/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponse.java b/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponse.java new file mode 100644 index 0000000000..a679995a44 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponse.java @@ -0,0 +1,194 @@ +/** + * 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.net.ahc; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import com.google.common.io.ByteSource; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.IOException; +import java.io.InputStream; + +import java.net.HttpURLConnection; + +import java.util.List; +import java.util.Map.Entry; + +/** + * + * @author Sebastian Sdorra + * @since 1.46 + */ +public class DefaultAdvancedHttpResponse extends AdvancedHttpResponse +{ + + /** + * Constructs ... + * + * + * @param connection + * @param status + * @param statusText + */ + DefaultAdvancedHttpResponse(HttpURLConnection connection, int status, + String statusText) + { + this.connection = connection; + this.status = status; + this.statusText = statusText; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @return + * + * @throws IOException + */ + @Override + public ByteSource contentAsByteSource() throws IOException + { + return new URLConnectionByteSource(connection); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + @Override + public Multimap getHeaders() + { + if (headers == null) + { + headers = HashMultimap.create(); + + for (Entry> e : + connection.getHeaderFields().entrySet()) + { + headers.putAll(e.getKey(), e.getValue()); + } + } + + return headers; + } + + /** + * Method description + * + * + * @return + */ + @Override + public int getStatus() + { + return status; + } + + /** + * Method description + * + * + * @return + */ + @Override + public String getStatusText() + { + return statusText; + } + + //~--- inner classes -------------------------------------------------------- + + /** + * {@link ByteSource} implementation of a http connection. + */ + private static class URLConnectionByteSource extends ByteSource + { + + /** + * Constructs a new {@link URLConnectionByteSource}. + * + * + * @param connection http connection + */ + private URLConnectionByteSource(HttpURLConnection connection) + { + this.connection = connection; + } + + //~--- methods ------------------------------------------------------------ + + /** + * Opens the http connection. + * + * + * @return http connection + * + * @throws IOException + */ + @Override + public InputStream openStream() throws IOException + { + return connection.getInputStream(); + } + + //~--- fields ------------------------------------------------------------- + + /** http connection */ + private final HttpURLConnection connection; + } + + + //~--- fields --------------------------------------------------------------- + + /** http connection */ + private final HttpURLConnection connection; + + /** Field description */ + private final int status; + + /** Field description */ + private final String statusText; + + /** http headers */ + private Multimap headers; +} From 3c6306059f35b009a3ddb97a2b6f0d14cb13dc78 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Fri, 1 May 2015 12:36:49 +0200 Subject: [PATCH 039/171] more unit tests for ahc --- .../sonia/scm/net/ahc/BaseHttpRequest.java | 3 +- .../scm/net/ahc/AdvancedHttpResponseTest.java | 39 ++ .../scm/net/ahc/BaseHttpRequestTest.java | 2 +- .../net/ahc/DefaultAdvancedHttpClient.java | 83 ++++- .../net/ahc/DefaultAdvancedHttpResponse.java | 38 +- .../ahc/DefaultAdvancedHttpClientTest.java | 352 ++++++++++++++++++ .../ahc/DefaultAdvancedHttpResponseTest.java | 121 ++++++ 7 files changed, 591 insertions(+), 47 deletions(-) create mode 100644 scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpClientTest.java create mode 100644 scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/BaseHttpRequest.java b/scm-core/src/main/java/sonia/scm/net/ahc/BaseHttpRequest.java index f48e819ddc..c80ae9b1c4 100644 --- a/scm-core/src/main/java/sonia/scm/net/ahc/BaseHttpRequest.java +++ b/scm-core/src/main/java/sonia/scm/net/ahc/BaseHttpRequest.java @@ -36,6 +36,7 @@ package sonia.scm.net.ahc; import com.google.common.base.Charsets; import com.google.common.base.Strings; import com.google.common.collect.HashMultimap; +import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Multimap; import org.apache.shiro.codec.Base64; @@ -386,7 +387,7 @@ public abstract class BaseHttpRequest protected final AdvancedHttpClient client; /** http header */ - private final Multimap headers = HashMultimap.create(); + private final Multimap headers = LinkedHashMultimap.create(); /** http method */ private final String method; diff --git a/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpResponseTest.java b/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpResponseTest.java index 9b3ccaa348..277bda856d 100644 --- a/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpResponseTest.java +++ b/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpResponseTest.java @@ -32,6 +32,8 @@ package sonia.scm.net.ahc; import com.google.common.base.Charsets; +import com.google.common.collect.LinkedHashMultimap; +import com.google.common.collect.Multimap; import com.google.common.io.ByteSource; import com.google.common.io.ByteStreams; import com.google.common.io.CharStreams; @@ -87,5 +89,42 @@ public class AdvancedHttpResponseTest { byte[] data = ByteStreams.toByteArray(response.contentAsStream()); assertEquals("cde456", new String(data, Charsets.UTF_8)); } + + @Test + public void testGetFirstHeader() throws IOException + { + Multimap mm = LinkedHashMultimap.create(); + mm.put("Test", "One"); + mm.put("Test-2", "One"); + mm.put("Test-2", "Two"); + when(response.getHeaders()).thenReturn(mm); + assertEquals("One", response.getFirstHeader("Test")); + assertEquals("One", response.getFirstHeader("Test-2")); + assertNull(response.getFirstHeader("Test-3")); + } + + @Test + public void testIsSuccessful() throws IOException + { + // successful + when(response.getStatus()).thenReturn(200); + assertTrue(response.isSuccessful()); + when(response.getStatus()).thenReturn(201); + assertTrue(response.isSuccessful()); + when(response.getStatus()).thenReturn(204); + assertTrue(response.isSuccessful()); + when(response.getStatus()).thenReturn(301); + assertTrue(response.isSuccessful()); + + // not successful + when(response.getStatus()).thenReturn(400); + assertFalse(response.isSuccessful()); + when(response.getStatus()).thenReturn(404); + assertFalse(response.isSuccessful()); + when(response.getStatus()).thenReturn(500); + assertFalse(response.isSuccessful()); + when(response.getStatus()).thenReturn(199); + assertFalse(response.isSuccessful()); + } } \ No newline at end of file diff --git a/scm-core/src/test/java/sonia/scm/net/ahc/BaseHttpRequestTest.java b/scm-core/src/test/java/sonia/scm/net/ahc/BaseHttpRequestTest.java index 7adab5aa84..9b3ce995f5 100644 --- a/scm-core/src/test/java/sonia/scm/net/ahc/BaseHttpRequestTest.java +++ b/scm-core/src/test/java/sonia/scm/net/ahc/BaseHttpRequestTest.java @@ -113,7 +113,7 @@ public class BaseHttpRequestTest { public void testHeaderMultiple(){ request.header("a", "b", "c", "d"); Collection values = request.getHeaders().get("a"); - assertThat(values, containsInAnyOrder("b", "c", "d")); + assertThat(values, contains("b", "c", "d")); } @Test diff --git a/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpClient.java b/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpClient.java index 1aadef143c..6b80d6bdae 100644 --- a/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpClient.java +++ b/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpClient.java @@ -33,6 +33,7 @@ package sonia.scm.net.ahc; //~--- non-JDK imports -------------------------------------------------------- +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; import com.google.common.collect.Multimap; import com.google.common.io.Closeables; @@ -69,6 +70,8 @@ import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; /** + * Default implementation of the {@link AdvancedHttpClient}. The default + * implementation uses {@link HttpURLConnection}. * * @author Sebastian Sdorra * @since 1.46 @@ -76,20 +79,23 @@ import javax.net.ssl.TrustManager; public class DefaultAdvancedHttpClient extends AdvancedHttpClient { - /** Field description */ - public static final String CREDENTIAL_SEPARATOR = ":"; + /** credential separator */ + private static final String CREDENTIAL_SEPARATOR = ":"; - /** Field description */ - public static final String HEADER_PROXY_AUTHORIZATION = "Proxy-Authorization"; + /** proxy authorization header */ + @VisibleForTesting + static final String HEADER_PROXY_AUTHORIZATION = "Proxy-Authorization"; - /** Field description */ - public static final String PREFIX_BASIC_AUTHENTICATION = "Basic "; + /** basic authentication prefix */ + private static final String PREFIX_BASIC_AUTHENTICATION = "Basic "; - /** Field description */ - public static final int TIMEOUT_CONNECTION = 30000; + /** connection timeout */ + @VisibleForTesting + static final int TIMEOUT_CONNECTION = 30000; - /** Field description */ - public static final int TIMEOUT_RAED = 1200000; + /** read timeout */ + @VisibleForTesting + static final int TIMEOUT_RAED = 1200000; /** * the logger for DefaultAdvancedHttpClient @@ -100,10 +106,10 @@ public class DefaultAdvancedHttpClient extends AdvancedHttpClient //~--- constructors --------------------------------------------------------- /** - * Constructs ... + * Constructs a new {@link DefaultAdvancedHttpClient}. * * - * @param configuration + * @param configuration scm-manager main configuration */ @Inject public DefaultAdvancedHttpClient(ScmConfiguration configuration) @@ -114,12 +120,50 @@ public class DefaultAdvancedHttpClient extends AdvancedHttpClient //~--- methods -------------------------------------------------------------- /** - * Method description + * Creates a new {@link HttpURLConnection} from the given {@link URL}. The + * method is visible for testing. * * - * @param request + * @param url url * - * @return + * @return new {@link HttpURLConnection} + * + * @throws IOException + */ + @VisibleForTesting + protected HttpURLConnection createConnection(URL url) throws IOException + { + return (HttpURLConnection) url.openConnection(); + } + + /** + * Creates a new proxy {@link HttpURLConnection} from the given {@link URL} + * and {@link SocketAddress}. The method is visible for testing. + * + * + * @param url url + * @param address proxy socket address + * + * @return new proxy {@link HttpURLConnection} + * + * @throws IOException + */ + @VisibleForTesting + protected HttpURLConnection createProxyConnecton(URL url, + SocketAddress address) + throws IOException + { + return (HttpURLConnection) url.openConnection(new Proxy(Proxy.Type.HTTP, + address)); + } + + /** + * Executes the given request and returns the server response. + * + * + * @param request http request + * + * @return server response * * @throws IOException */ @@ -223,7 +267,7 @@ public class DefaultAdvancedHttpClient extends AdvancedHttpClient { for (String value : headers.get(key)) { - connection.setRequestProperty(key, value); + connection.addRequestProperty(key, value); } } } @@ -280,7 +324,7 @@ public class DefaultAdvancedHttpClient extends AdvancedHttpClient logger.debug("fetch {}", url.toExternalForm()); - connection = (HttpURLConnection) url.openConnection(); + connection = createConnection(url); } return connection; @@ -300,8 +344,7 @@ public class DefaultAdvancedHttpClient extends AdvancedHttpClient new InetSocketAddress(configuration.getProxyServer(), configuration.getProxyPort()); - return (HttpURLConnection) url.openConnection(new Proxy(Proxy.Type.HTTP, - address)); + return createProxyConnecton(url, address); } //~--- get methods ---------------------------------------------------------- @@ -314,6 +357,6 @@ public class DefaultAdvancedHttpClient extends AdvancedHttpClient //~--- fields --------------------------------------------------------------- - /** Field description */ + /** scm-manager main configuration */ private final ScmConfiguration configuration; } diff --git a/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponse.java b/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponse.java index a679995a44..d5eb7d1727 100644 --- a/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponse.java +++ b/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponse.java @@ -34,6 +34,7 @@ package sonia.scm.net.ahc; //~--- non-JDK imports -------------------------------------------------------- import com.google.common.collect.HashMultimap; +import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Multimap; import com.google.common.io.ByteSource; @@ -48,6 +49,7 @@ import java.util.List; import java.util.Map.Entry; /** + * Http server response object of {@link DefaultAdvancedHttpClient}. * * @author Sebastian Sdorra * @since 1.46 @@ -56,12 +58,12 @@ public class DefaultAdvancedHttpResponse extends AdvancedHttpResponse { /** - * Constructs ... + * Constructs a new {@link DefaultAdvancedHttpResponse}. * * - * @param connection - * @param status - * @param statusText + * @param connection http connection + * @param status response status code + * @param statusText response status text */ DefaultAdvancedHttpResponse(HttpURLConnection connection, int status, String statusText) @@ -74,12 +76,7 @@ public class DefaultAdvancedHttpResponse extends AdvancedHttpResponse //~--- methods -------------------------------------------------------------- /** - * Method description - * - * - * @return - * - * @throws IOException + * {@inheritDoc} */ @Override public ByteSource contentAsByteSource() throws IOException @@ -90,17 +87,14 @@ public class DefaultAdvancedHttpResponse extends AdvancedHttpResponse //~--- get methods ---------------------------------------------------------- /** - * Method description - * - * - * @return + * {@inheritDoc} */ @Override public Multimap getHeaders() { if (headers == null) { - headers = HashMultimap.create(); + headers = LinkedHashMultimap.create(); for (Entry> e : connection.getHeaderFields().entrySet()) @@ -113,10 +107,7 @@ public class DefaultAdvancedHttpResponse extends AdvancedHttpResponse } /** - * Method description - * - * - * @return + * {@inheritDoc} */ @Override public int getStatus() @@ -125,10 +116,7 @@ public class DefaultAdvancedHttpResponse extends AdvancedHttpResponse } /** - * Method description - * - * - * @return + * {@inheritDoc} */ @Override public String getStatusText() @@ -183,10 +171,10 @@ public class DefaultAdvancedHttpResponse extends AdvancedHttpResponse /** http connection */ private final HttpURLConnection connection; - /** Field description */ + /** server response status */ private final int status; - /** Field description */ + /** server response text */ private final String statusText; /** http headers */ diff --git a/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpClientTest.java b/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpClientTest.java new file mode 100644 index 0000000000..451c07b5cc --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpClientTest.java @@ -0,0 +1,352 @@ +/** + * 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.net.ahc; + +//~--- non-JDK imports -------------------------------------------------------- + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import sonia.scm.config.ScmConfiguration; +import sonia.scm.net.TrustAllHostnameVerifier; +import sonia.scm.util.HttpUtil; + +import static org.junit.Assert.*; + +import static org.mockito.Mockito.*; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import java.net.HttpURLConnection; +import java.net.SocketAddress; +import java.net.URL; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLSocketFactory; + +/** + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +public class DefaultAdvancedHttpClientTest +{ + + /** + * Method description + * + * + * @throws IOException + */ + @Test + public void testApplyBaseSettings() throws IOException + { + new AdvancedHttpRequest(client, HttpMethod.GET, + "https://www.scm-manager.org").request(); + verify(connection).setRequestMethod(HttpMethod.GET); + verify(connection).setReadTimeout(DefaultAdvancedHttpClient.TIMEOUT_RAED); + verify(connection).setConnectTimeout( + DefaultAdvancedHttpClient.TIMEOUT_CONNECTION); + verify(connection).addRequestProperty(HttpUtil.HEADER_CONTENT_LENGTH, "0"); + } + + /** + * Method description + * + * + * @throws IOException + */ + @Test + public void testApplyContent() throws IOException + { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + when(connection.getOutputStream()).thenReturn(baos); + + AdvancedHttpRequestWithBody request = + new AdvancedHttpRequestWithBody(client, HttpMethod.PUT, + "https://www.scm-manager.org"); + + request.stringContent("test").request(); + verify(connection).setDoOutput(true); + verify(connection).addRequestProperty(HttpUtil.HEADER_CONTENT_LENGTH, "4"); + assertEquals("test", baos.toString("UTF-8")); + } + + /** + * Method description + * + * + * @throws IOException + */ + @Test + public void testApplyHeaders() throws IOException + { + AdvancedHttpRequest request = new AdvancedHttpRequest(client, + HttpMethod.POST, + "http://www.scm-manager.org"); + + request.header("Header-One", "One").header("Header-Two", "Two").request(); + verify(connection).setRequestMethod(HttpMethod.POST); + verify(connection).addRequestProperty("Header-One", "One"); + verify(connection).addRequestProperty("Header-Two", "Two"); + } + + /** + * Method description + * + * + * @throws IOException + */ + @Test + public void testApplyMultipleHeaders() throws IOException + { + AdvancedHttpRequest request = new AdvancedHttpRequest(client, + HttpMethod.POST, + "http://www.scm-manager.org"); + + request.header("Header-One", "One").header("Header-One", "Two").request(); + verify(connection).setRequestMethod(HttpMethod.POST); + verify(connection).addRequestProperty("Header-One", "One"); + verify(connection).addRequestProperty("Header-One", "Two"); + } + + /** + * Method description + * + * + * @throws IOException + */ + @Test + public void testBodyRequestWithoutContent() throws IOException + { + AdvancedHttpRequestWithBody request = + new AdvancedHttpRequestWithBody(client, HttpMethod.PUT, + "https://www.scm-manager.org"); + + request.request(); + verify(connection).addRequestProperty(HttpUtil.HEADER_CONTENT_LENGTH, "0"); + } + + /** + * Method description + * + * + * @throws IOException + */ + @Test + public void testDisableCertificateValidation() throws IOException + { + AdvancedHttpRequest request = new AdvancedHttpRequest(client, + HttpMethod.GET, + "https://www.scm-manager.org"); + + request.disableCertificateValidation(true).request(); + verify(connection).setSSLSocketFactory(any(SSLSocketFactory.class)); + } + + /** + * Method description + * + * + * @throws IOException + */ + @Test + public void testDisableHostnameValidation() throws IOException + { + AdvancedHttpRequest request = new AdvancedHttpRequest(client, + HttpMethod.GET, + "https://www.scm-manager.org"); + + request.disableHostnameValidation(true).request(); + verify(connection).setHostnameVerifier(any(TrustAllHostnameVerifier.class)); + } + + /** + * Method description + * + * + * @throws IOException + */ + @Test + public void testIgnoreProxy() throws IOException + { + configuration.setProxyServer("proxy.scm-manager.org"); + configuration.setProxyPort(8090); + configuration.setEnableProxy(true); + new AdvancedHttpRequest(client, HttpMethod.GET, + "https://www.scm-manager.org").ignoreProxySettings(true).request(); + assertFalse(client.proxyConnection); + } + + /** + * Method description + * + * + * @throws IOException + */ + @Test + public void testProxyConnection() throws IOException + { + configuration.setProxyServer("proxy.scm-manager.org"); + configuration.setProxyPort(8090); + configuration.setEnableProxy(true); + new AdvancedHttpRequest(client, HttpMethod.GET, + "https://www.scm-manager.org").request(); + assertTrue(client.proxyConnection); + } + + /** + * Method description + * + * + * @throws IOException + */ + @Test + public void testProxyWithAuthentication() throws IOException + { + configuration.setProxyServer("proxy.scm-manager.org"); + configuration.setProxyPort(8090); + configuration.setProxyUser("tricia"); + configuration.setProxyPassword("tricias secret"); + configuration.setEnableProxy(true); + new AdvancedHttpRequest(client, HttpMethod.GET, + "https://www.scm-manager.org").request(); + assertTrue(client.proxyConnection); + verify(connection).addRequestProperty( + DefaultAdvancedHttpClient.HEADER_PROXY_AUTHORIZATION, + "Basic dHJpY2lhOnRyaWNpYXMgc2VjcmV0"); + } + + //~--- set methods ---------------------------------------------------------- + + /** + * Method description + * + */ + @Before + public void setUp() + { + configuration = new ScmConfiguration(); + client = new TestingAdvacedHttpClient(configuration); + } + + //~--- inner classes -------------------------------------------------------- + + /** + * Class description + * + * + * @version Enter version here..., 15/05/01 + * @author Enter your name here... + */ + public class TestingAdvacedHttpClient extends DefaultAdvancedHttpClient + { + + /** + * Constructs ... + * + * + * @param configuration + */ + public TestingAdvacedHttpClient(ScmConfiguration configuration) + { + super(configuration); + } + + //~--- methods ------------------------------------------------------------ + + /** + * Method description + * + * + * @param url + * + * @return + * + * @throws IOException + */ + @Override + protected HttpURLConnection createConnection(URL url) throws IOException + { + return connection; + } + + /** + * Method description + * + * + * @param url + * @param address + * + * @return + * + * @throws IOException + */ + @Override + protected HttpURLConnection createProxyConnecton(URL url, + SocketAddress address) + throws IOException + { + proxyConnection = true; + + return connection; + } + + //~--- fields ------------------------------------------------------------- + + /** Field description */ + private boolean proxyConnection = false; + } + + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private TestingAdvacedHttpClient client; + + /** Field description */ + private ScmConfiguration configuration; + + /** Field description */ + @Mock + private HttpsURLConnection connection; +} diff --git a/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java b/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java new file mode 100644 index 0000000000..470f1d14ae --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java @@ -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.net.ahc; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Charsets; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.io.ByteSource; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import static org.hamcrest.Matchers.*; + +import static org.junit.Assert.*; + +import static org.mockito.Mockito.*; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.ByteArrayInputStream; +import java.io.IOException; + +import java.net.HttpURLConnection; + +import java.util.LinkedHashMap; +import java.util.List; + +/** + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +public class DefaultAdvancedHttpResponseTest +{ + + /** + * Method description + * + * + * @throws IOException + */ + @Test + public void testContentAsByteSource() throws IOException + { + ByteArrayInputStream bais = + new ByteArrayInputStream("test".getBytes(Charsets.UTF_8)); + + when(connection.getInputStream()).thenReturn(bais); + + AdvancedHttpResponse response = new DefaultAdvancedHttpResponse(connection, + 200, "OK"); + ByteSource content = response.contentAsByteSource(); + + assertEquals("test", content.asCharSource(Charsets.UTF_8).read()); + } + + /** + * Method description + * + */ + @Test + public void testGetHeaders() + { + LinkedHashMap> map = Maps.newLinkedHashMap(); + List test = Lists.newArrayList("One", "Two"); + + map.put("Test", test); + when(connection.getHeaderFields()).thenReturn(map); + + AdvancedHttpResponse response = new DefaultAdvancedHttpResponse(connection, + 200, "OK"); + Multimap headers = response.getHeaders(); + + assertThat(headers.get("Test"), contains("One", "Two")); + assertTrue(headers.get("Test-2").isEmpty()); + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + @Mock + private HttpURLConnection connection; +} From 722d2616a898f1699db0dc6b282b299e2d8cd225 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Fri, 1 May 2015 14:21:15 +0200 Subject: [PATCH 040/171] return error stream, if input stream could not be returned --- .../net/ahc/DefaultAdvancedHttpResponse.java | 36 ++++++++++++++++--- .../ahc/DefaultAdvancedHttpResponseTest.java | 22 ++++++++++++ 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponse.java b/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponse.java index d5eb7d1727..c42e36deb5 100644 --- a/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponse.java +++ b/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponse.java @@ -33,11 +33,13 @@ package sonia.scm.net.ahc; //~--- non-JDK imports -------------------------------------------------------- -import com.google.common.collect.HashMultimap; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Multimap; import com.google.common.io.ByteSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + //~--- JDK imports ------------------------------------------------------------ import java.io.IOException; @@ -132,6 +134,14 @@ public class DefaultAdvancedHttpResponse extends AdvancedHttpResponse private static class URLConnectionByteSource extends ByteSource { + /** + * the logger for URLConnectionByteSource + */ + private static final Logger logger = + LoggerFactory.getLogger(URLConnectionByteSource.class); + + //~--- constructors ------------------------------------------------------- + /** * Constructs a new {@link URLConnectionByteSource}. * @@ -146,17 +156,35 @@ public class DefaultAdvancedHttpResponse extends AdvancedHttpResponse //~--- methods ------------------------------------------------------------ /** - * Opens the http connection. + * Opens the input stream of http connection, if an error occurs during + * opening the method will return the error stream instead. * * - * @return http connection + * @return input or error stream of http connection * * @throws IOException */ @Override public InputStream openStream() throws IOException { - return connection.getInputStream(); + InputStream stream; + + try + { + stream = connection.getInputStream(); + } + catch (IOException ex) + { + if (logger.isDebugEnabled()) + { + logger.debug( + "could not open input stream, open error stream instead", ex); + } + + stream = connection.getErrorStream(); + } + + return stream; } //~--- fields ------------------------------------------------------------- diff --git a/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java b/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java index 470f1d14ae..063b8b6f09 100644 --- a/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java +++ b/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java @@ -92,6 +92,28 @@ public class DefaultAdvancedHttpResponseTest assertEquals("test", content.asCharSource(Charsets.UTF_8).read()); } + /** + * Method description + * + * + * @throws IOException + */ + @Test + public void testContentAsByteSourceWithFailedRequest() throws IOException + { + ByteArrayInputStream bais = + new ByteArrayInputStream("test".getBytes(Charsets.UTF_8)); + + when(connection.getInputStream()).thenThrow(IOException.class); + when(connection.getErrorStream()).thenReturn(bais); + + AdvancedHttpResponse response = new DefaultAdvancedHttpResponse(connection, + 404, "NOT FOUND"); + ByteSource content = response.contentAsByteSource(); + + assertEquals("test", content.asCharSource(Charsets.UTF_8).read()); + } + /** * Method description * From 31b1fd3bf5d91860a5ce1af2bc2fa49afcfb3401 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 3 May 2015 10:06:39 +0200 Subject: [PATCH 041/171] fix typo --- .../java/sonia/scm/repository/DefaultRepositoryManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java b/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java index 318b122c80..da5af847de 100644 --- a/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java +++ b/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java @@ -468,7 +468,7 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager } else { - throw new ScmSecurityException("not enaugh permissions"); + throw new ScmSecurityException("not enough permissions"); } } From 1f4524bb208375c62ac8129eba84f6eb54cbe5b5 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 3 May 2015 15:51:21 +0200 Subject: [PATCH 042/171] added json and xml support to ahc --- .../sonia/scm/net/ahc/AdvancedHttpClient.java | 31 ++- .../net/ahc/AdvancedHttpRequestWithBody.java | 55 ++++++ .../scm/net/ahc/AdvancedHttpResponse.java | 187 ++++++++++++++---- .../sonia/scm/net/ahc/ContentTransformer.java | 83 ++++++++ .../net/ahc/ContentTransformerException.java | 72 +++++++ .../ContentTransformerNotFoundException.java | 60 ++++++ .../java/sonia/scm/net/ahc/ContentType.java | 52 +++++ .../scm/net/ahc/AdvancedHttpClientTest.java | 26 +-- .../ahc/AdvancedHttpRequestWithBodyTest.java | 42 ++++ .../scm/net/ahc/AdvancedHttpResponseTest.java | 84 ++++++++ .../main/java/sonia/scm/ScmServletModule.java | 14 +- .../net/ahc/DefaultAdvancedHttpClient.java | 56 +++++- .../net/ahc/DefaultAdvancedHttpResponse.java | 22 ++- .../scm/net/ahc/JsonContentTransformer.java | 149 ++++++++++++++ .../scm/net/ahc/XmlContentTransformer.java | 134 +++++++++++++ .../ahc/DefaultAdvancedHttpClientTest.java | 31 ++- .../ahc/DefaultAdvancedHttpResponseTest.java | 20 +- .../net/ahc/JsonContentTransformerTest.java | 93 +++++++++ .../net/ahc/XmlContentTransformerTest.java | 92 +++++++++ 19 files changed, 1222 insertions(+), 81 deletions(-) create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/ContentTransformer.java create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/ContentTransformerException.java create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/ContentTransformerNotFoundException.java create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/ContentType.java create mode 100644 scm-webapp/src/main/java/sonia/scm/net/ahc/JsonContentTransformer.java create mode 100644 scm-webapp/src/main/java/sonia/scm/net/ahc/XmlContentTransformer.java create mode 100644 scm-webapp/src/test/java/sonia/scm/net/ahc/JsonContentTransformerTest.java create mode 100644 scm-webapp/src/test/java/sonia/scm/net/ahc/XmlContentTransformerTest.java diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpClient.java b/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpClient.java index 943d1a0352..aa660f21ca 100644 --- a/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpClient.java +++ b/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpClient.java @@ -39,7 +39,7 @@ import java.io.IOException; * Advanced client for http operations. The {@link AdvancedHttpClient} replaces * the much more simpler implementation {@link sonia.scm.net.HttpClient}. The * {@link AdvancedHttpClient} offers a fluid interface for handling most common - * http operations. The {@link AdvancedHttpClient} can be injected by the + * http operations. The {@link AdvancedHttpClient} can be injected by the * default injection mechanism of SCM-Manager. *

 

* Http GET example: @@ -48,13 +48,13 @@ import java.io.IOException; * AdvancedHttpResponse response = client.get("https://www.scm-manager.org") * .decodeGZip(true) * .request(); - * + * * System.out.println(response.contentAsString()); * - * + * *

 

* Http POST example: - * + * *

  * AdvancedHttpResponse response = client.post("https://www.scm-manager.org")
  *                                       .formContent()
@@ -62,7 +62,7 @@ import java.io.IOException;
  *                                       .field("lastname", "McMillan")
  *                                       .build()
  *                                       .request();
- * 
+ *
  * if (response.isSuccessful()){
  *   System.out.println("success");
  * }
@@ -70,15 +70,28 @@ import java.io.IOException;
  *
  * @author Sebastian Sdorra
  * @since 1.46
- * 
+ *
  * @apiviz.landmark
  */
 public abstract class AdvancedHttpClient
 {
 
+  /**
+   * Creates a {@link ContentTransformer} for the given Content-Type.
+   *
+   * @param type object type
+   * @param contentType content-type
+   * @throws ContentTransformerNotFoundException if no
+   *   {@link ContentTransformer} could be found for the content-type
+   *
+   * @return {@link ContentTransformer}
+   */
+  protected abstract ContentTransformer createTransformer(Class type,
+    String contentType);
+
   /**
    * Executes the given request and returns the http response. Implementation
-   * have to check, if the instance if from type 
+   * have to check, if the instance if from type
    * {@link AdvancedHttpRequestWithBody} in order to handle request contents.
    *
    *
@@ -118,8 +131,8 @@ public abstract class AdvancedHttpClient
   }
 
   /**
-   * Returns a request builder with a custom method. Note: not 
-   * every method is supported by the underlying implementation of the http 
+   * Returns a request builder with a custom method. Note: not
+   * every method is supported by the underlying implementation of the http
    * client.
    *
    *
diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBody.java b/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBody.java
index 1acad0efb0..dd5ec6f425 100644
--- a/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBody.java
+++ b/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBody.java
@@ -122,6 +122,21 @@ public class AdvancedHttpRequestWithBody
     return new FormContentBuilder(this);
   }
 
+  /**
+   * Transforms the given object to a xml string and set this string as request
+   * content.
+   *
+   * @param object object to transform
+   * @throws ContentTransformerNotFoundException if no
+   *   {@link ContentTransformer} could be found for the json content-type
+   *
+   * @return {@code this}
+   */
+  public AdvancedHttpRequestWithBody jsonContent(Object object)
+  {
+    return transformedContent(ContentType.JSON, object);
+  }
+
   /**
    * Sets the raw data as request content.
    *
@@ -182,6 +197,46 @@ public class AdvancedHttpRequestWithBody
     return this;
   }
 
+  /**
+   * Transforms the given object to a string and set this string as request
+   * content. The content-type is used to pick the right
+   * {@link ContentTransformer}. The method will throw an exception if no
+   * {@link ContentTransformer} for the content-type could be found.
+   *
+   * @param contentType content-type
+   * @param object object to transform
+   * @throws ContentTransformerNotFoundException if no
+   *   {@link ContentTransformer} could be found for the given content-type
+   *
+   * @return {@code this}
+   */
+  public AdvancedHttpRequestWithBody transformedContent(String contentType,
+    Object object)
+  {
+    ContentTransformer transformer =
+      client.createTransformer(object.getClass(), contentType);
+    ByteSource value = transformer.marshall(object);
+
+    contentType(contentType);
+
+    return rawContent(value);
+  }
+
+  /**
+   * Transforms the given object to a xml string and set this string as request
+   * content.
+   *
+   * @param object object to transform
+   * @throws ContentTransformerNotFoundException if no
+   *   {@link ContentTransformer} could be found for the xml content-type
+   *
+   * @return {@code this}
+   */
+  public AdvancedHttpRequestWithBody xmlContent(Object object)
+  {
+    return transformedContent(ContentType.XML, object);
+  }
+
   //~--- get methods ----------------------------------------------------------
 
   /**
diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpResponse.java b/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpResponse.java
index 6148b3b355..1cb087710b 100644
--- a/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpResponse.java
+++ b/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpResponse.java
@@ -34,6 +34,7 @@ package sonia.scm.net.ahc;
 //~--- non-JDK imports --------------------------------------------------------
 
 import com.google.common.base.Charsets;
+import com.google.common.base.Strings;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Multimap;
 import com.google.common.io.ByteSource;
@@ -45,7 +46,7 @@ import java.io.IOException;
 import java.io.InputStream;
 
 /**
- * Http response. The response of a {@link AdvancedHttpRequest} or 
+ * Http response. The response of a {@link AdvancedHttpRequest} or
  * {@link AdvancedHttpRequestWithBody}.
  *
  * @author Sebastian Sdorra
@@ -54,8 +55,56 @@ import java.io.InputStream;
 public abstract class AdvancedHttpResponse
 {
 
+  /**
+   * Returns the response content as byte source.
+   *
+   *
+   * @return response content as byte source
+   * @throws IOException
+   */
+  public abstract ByteSource contentAsByteSource() throws IOException;
+
+  //~--- get methods ----------------------------------------------------------
+
+  /**
+   * Returns the response headers.
+   *
+   *
+   * @return response headers
+   */
+  public abstract Multimap getHeaders();
+
+  /**
+   * Returns the status code of the response.
+   *
+   *
+   * @return status code
+   */
+  public abstract int getStatus();
+
+  /**
+   * Returns the status text of the response.
+   *
+   *
+   * @return status text
+   */
+  public abstract String getStatusText();
+
   //~--- methods --------------------------------------------------------------
 
+  /**
+   * Creates a {@link ContentTransformer} for the given Content-Type.
+   *
+   * @param type object type
+   * @param contentType content-type
+   * @throws ContentTransformerNotFoundException if no
+   *   {@link ContentTransformer} could be found for the content-type
+   *
+   * @return {@link ContentTransformer}
+   */
+  protected abstract ContentTransformer createTransformer(Class type,
+    String contentType);
+
   /**
    * Returns the content of the response as byte array.
    *
@@ -89,6 +138,7 @@ public abstract class AdvancedHttpResponse
   {
     ByteSource content = contentAsByteSource();
     BufferedReader reader = null;
+
     if (content != null)
     {
       reader = content.asCharSource(Charsets.UTF_8).openBufferedStream();
@@ -109,6 +159,7 @@ public abstract class AdvancedHttpResponse
   {
     ByteSource content = contentAsByteSource();
     InputStream stream = null;
+
     if (content != null)
     {
       stream = content.openBufferedStream();
@@ -116,15 +167,6 @@ public abstract class AdvancedHttpResponse
 
     return stream;
   }
-  
-  /**
-   * Returns the response content as byte source.
-   * 
-   * 
-   * @return response content as byte source
-   * @throws IOException 
-   */
-  public abstract ByteSource contentAsByteSource() throws IOException;
 
   /**
    * Returns the response content as string.
@@ -138,6 +180,7 @@ public abstract class AdvancedHttpResponse
   {
     ByteSource content = contentAsByteSource();
     String value = null;
+
     if (content != null)
     {
       value = content.asCharSource(Charsets.UTF_8).read();
@@ -146,6 +189,99 @@ public abstract class AdvancedHttpResponse
     return value;
   }
 
+  /**
+   * Transforms the response content from json to the given type.
+   *
+   * @param  object type
+   * @param type object type
+   *
+   * @throws ContentTransformerNotFoundException if no
+   *   {@link ContentTransformer} could be found for the json content-type
+   *
+   * @return transformed object
+   *
+   * @throws IOException
+   */
+  public  T contentFromJson(Class type) throws IOException
+  {
+    return contentTransformed(type, ContentType.JSON);
+  }
+
+  /**
+   * Transforms the response content from xml to the given type.
+   *
+   * @param  object type
+   * @param type object type
+   *
+   * @throws ContentTransformerNotFoundException if no
+   *   {@link ContentTransformer} could be found for the xml content-type
+   *
+   * @return transformed object
+   *
+   * @throws IOException
+   */
+  public  T contentFromXml(Class type) throws IOException
+  {
+    return contentTransformed(type, ContentType.XML);
+  }
+
+  /**
+   * Transforms the response content to the given type. The method uses the
+   * content-type header to pick the right {@link ContentTransformer}.
+   *
+   * @param  object type
+   * @param type object type
+   *
+   * @throws ContentTransformerNotFoundException if no
+   *   {@link ContentTransformer} could be found for the content-type
+   *
+   * @return transformed object
+   *
+   * @throws IOException
+   */
+  public  T contentTransformed(Class type) throws IOException
+  {
+    String contentType = getFirstHeader("Content-Type");
+
+    if (Strings.isNullOrEmpty(contentType))
+    {
+      throw new ContentTransformerException(
+        "response does not return a Content-Type header");
+    }
+
+    return contentTransformed(type, contentType);
+  }
+
+  /**
+   * Transforms the response content from xml to the given type.
+   *
+   * @param  object type
+   * @param type object type
+   * @param contentType type to pick {@link ContentTransformer}
+   *
+   * @throws ContentTransformerNotFoundException if no
+   *   {@link ContentTransformer} could be found for the content-type
+   *
+   * @return transformed object
+   *
+   * @throws IOException
+   */
+  public  T contentTransformed(Class type, String contentType)
+    throws IOException
+  {
+    T object = null;
+    ByteSource source = contentAsByteSource();
+
+    if (source != null)
+    {
+      ContentTransformer transformer = createTransformer(type, contentType);
+
+      object = transformer.unmarshall(type, contentAsByteSource());
+    }
+
+    return object;
+  }
+
   //~--- get methods ----------------------------------------------------------
 
   /**
@@ -162,38 +298,15 @@ public abstract class AdvancedHttpResponse
   }
 
   /**
-   * Returns the response headers.
-   *
-   *
-   * @return response headers
-   */
-  public abstract Multimap getHeaders();
-
-  /**
-   * Returns {@code true} if the response was successful. A response is 
+   * Returns {@code true} if the response was successful. A response is
    * successful, if the status code is greater than 199 and lower than 400.
-   * 
+   *
    * @return {@code true} if the response was successful
    */
   public boolean isSuccessful()
   {
     int status = getStatus();
-    return status > 199 && status < 400;
-  }
-  
-  /**
-   * Returns the status code of the response.
-   *
-   *
-   * @return status code
-   */
-  public abstract int getStatus();
 
-  /**
-   * Returns the status text of the response.
-   *
-   *
-   * @return status text
-   */
-  public abstract String getStatusText();
+    return (status > 199) && (status < 400);
+  }
 }
diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/ContentTransformer.java b/scm-core/src/main/java/sonia/scm/net/ahc/ContentTransformer.java
new file mode 100644
index 0000000000..0902e2ff79
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/net/ahc/ContentTransformer.java
@@ -0,0 +1,83 @@
+/**
+ * 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.net.ahc;
+
+import com.google.common.io.ByteSource;
+import sonia.scm.plugin.ExtensionPoint;
+
+/**
+ * Transforms {@link ByteSource} content to an object and vice versa. This class
+ * is an extension point, this means that plugins can define their own
+ * {@link ContentTransformer} implementations by implementing the interface and
+ * annotate the implementation with the {@link sonia.scm.plugin.ext.Extension} 
+ * annotation.
+ *
+ * @author Sebastian Sdorra
+ * @since 1.46
+ */
+@ExtensionPoint
+public interface ContentTransformer
+{
+  
+  /**
+   * Returns {@code true} if the transformer is responsible for the given 
+   * object and content type.
+   * 
+   * @param type object type
+   * @param contentType content type
+   * 
+   * @return {@code true} if the transformer is responsible
+   */
+  public boolean isResponsible(Class type, String contentType);
+
+  /**
+   * Marshalls the given object into a {@link ByteSource}.
+   *
+   *
+   * @param object object to marshall
+   *
+   * @return string content
+   */
+  public ByteSource marshall(Object object);
+
+  /**
+   * Unmarshall the {@link ByteSource} content to an object of the given type.
+   *
+   *
+   * @param type type of result object
+   * @param content content
+   * @param  type of result object
+   *
+   * @return
+   */
+  public  T unmarshall(Class type, ByteSource content);
+}
diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/ContentTransformerException.java b/scm-core/src/main/java/sonia/scm/net/ahc/ContentTransformerException.java
new file mode 100644
index 0000000000..d4e2299206
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/net/ahc/ContentTransformerException.java
@@ -0,0 +1,72 @@
+/**
+ * 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.net.ahc;
+
+/**
+ * A {@link ContentTransformerException} is thrown if an error occurs during
+ * content transformation by an {@link ContentTransformer}.
+ *
+ * @author Sebastian Sdorra
+ *
+ * @since 1.46
+ */
+public class ContentTransformerException extends RuntimeException
+{
+
+  /** Field description */
+  private static final long serialVersionUID = 367333504151208563L;
+
+  //~--- constructors ---------------------------------------------------------
+
+  /**
+   * Constructs a new {@link ContentTransformerException}.
+   *
+   *
+   * @param message exception message
+   */
+  public ContentTransformerException(String message)
+  {
+    super(message);
+  }
+
+  /**
+   * Constructs a new {@link ContentTransformerException}.
+   *
+   *
+   * @param message exception message
+   * @param cause exception cause
+   */
+  public ContentTransformerException(String message, Throwable cause)
+  {
+    super(message, cause);
+  }
+}
diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/ContentTransformerNotFoundException.java b/scm-core/src/main/java/sonia/scm/net/ahc/ContentTransformerNotFoundException.java
new file mode 100644
index 0000000000..a8e85cdaf2
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/net/ahc/ContentTransformerNotFoundException.java
@@ -0,0 +1,60 @@
+/**
+ * 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.net.ahc;
+
+/**
+ * The ContentTransformerNotFoundException is thrown, if no
+ * {@link ContentTransformer} could be found for the given type.
+ *
+ * @author Sebastian Sdorra
+ * @since 1.46
+ */
+public class ContentTransformerNotFoundException
+  extends ContentTransformerException
+{
+
+  /** Field description */
+  private static final long serialVersionUID = 6374525163951460938L;
+
+  //~--- constructors ---------------------------------------------------------
+
+  /**
+   * Constructs a new ContentTransformerNotFoundException.
+   *
+   *
+   * @param message exception message
+   */
+  public ContentTransformerNotFoundException(String message)
+  {
+    super(message);
+  }
+}
diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/ContentType.java b/scm-core/src/main/java/sonia/scm/net/ahc/ContentType.java
new file mode 100644
index 0000000000..8e3b299f96
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/net/ahc/ContentType.java
@@ -0,0 +1,52 @@
+/**
+ * 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.net.ahc;
+
+/**
+ * Content-Types.
+ *
+ * @author Sebastian Sdorra
+ * @since 1.46
+ */
+public final class ContentType
+{
+
+  /** json content type */
+  public static final String JSON = "application/json";
+
+  /** xml content type */
+  public static final String XML = "application/xml";
+
+  //~--- constructors ---------------------------------------------------------
+
+  private ContentType() {}
+}
diff --git a/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpClientTest.java b/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpClientTest.java
index 18b291d8ad..59549c24dd 100644
--- a/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpClientTest.java
+++ b/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpClientTest.java
@@ -31,26 +31,22 @@
 
 package sonia.scm.net.ahc;
 
-import java.io.IOException;
 import org.junit.Test;
 import static org.junit.Assert.*;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
 
 /**
  *
  * @author Sebastian Sdorra
  */
+@RunWith(MockitoJUnitRunner.class)
 public class AdvancedHttpClientTest {
 
-  private final AdvancedHttpClient client = new  AdvancedHttpClient()
-  {
-
-    @Override
-    protected AdvancedHttpResponse request(
-      BaseHttpRequest request) throws IOException
-    {
-      throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
-    }
-  };
+  @Mock(answer = Answers.CALLS_REAL_METHODS)
+  private AdvancedHttpClient client;
 
   private static final String URL = "https://www.scm-manager.org";
   
@@ -101,4 +97,12 @@ public class AdvancedHttpClientTest {
     assertEquals(URL, request.getUrl());
     assertEquals(HttpMethod.HEAD, request.getMethod());
   }
+  
+  @Test
+  public void testMethod()
+  {
+    AdvancedHttpRequestWithBody request = client.method("PROPFIND", URL);
+    assertEquals(URL, request.getUrl());
+    assertEquals("PROPFIND", request.getMethod());
+  }
 }
\ No newline at end of file
diff --git a/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBodyTest.java b/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBodyTest.java
index 8e656133e7..5a7c55a46d 100644
--- a/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBodyTest.java
+++ b/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBodyTest.java
@@ -33,11 +33,14 @@ package sonia.scm.net.ahc;
 
 import com.google.common.base.Charsets;
 import com.google.common.io.ByteSource;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
+import java.io.UnsupportedEncodingException;
 import org.junit.Test;
 import static org.junit.Assert.*;
 import static org.hamcrest.Matchers.*;
+import static org.mockito.Mockito.*;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.rules.TemporaryFolder;
@@ -55,6 +58,9 @@ public class AdvancedHttpRequestWithBodyTest {
   @Mock
   private AdvancedHttpClient ahc;
   
+  @Mock
+  private ContentTransformer transformer;
+  
   private AdvancedHttpRequestWithBody request;
   
   @Rule
@@ -118,6 +124,42 @@ public class AdvancedHttpRequestWithBodyTest {
     assertThat(request.getContent(), instanceOf(StringContent.class));
   }
   
+  @Test
+  public void testXmlContent() throws IOException{
+    when(ahc.createTransformer(String.class, ContentType.XML)).thenReturn(transformer);
+    when(transformer.marshall("")).thenReturn(ByteSource.wrap("".getBytes(Charsets.UTF_8)));
+    Content content = request.xmlContent("").getContent();
+    assertThat(content, instanceOf(ByteSourceContent.class));
+    ByteSourceContent bsc = (ByteSourceContent) content;
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    bsc.process(baos);
+    assertEquals("", baos.toString("UTF-8"));
+  }
+  
+  @Test
+  public void testJsonContent() throws IOException{
+    when(ahc.createTransformer(String.class, ContentType.JSON)).thenReturn(transformer);
+    when(transformer.marshall("{}")).thenReturn(ByteSource.wrap("{'root': {}}".getBytes(Charsets.UTF_8)));
+    Content content = request.jsonContent("{}").getContent();
+    assertThat(content, instanceOf(ByteSourceContent.class));
+    ByteSourceContent bsc = (ByteSourceContent) content;
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    bsc.process(baos);
+    assertEquals("{'root': {}}", baos.toString("UTF-8"));
+  }
+  
+ @Test
+  public void testTransformedContent() throws IOException{
+    when(ahc.createTransformer(String.class, "text/plain")).thenReturn(transformer);
+    when(transformer.marshall("hello")).thenReturn(ByteSource.wrap("hello world".getBytes(Charsets.UTF_8)));
+    Content content = request.transformedContent("text/plain", "hello").getContent();
+    assertThat(content, instanceOf(ByteSourceContent.class));
+    ByteSourceContent bsc = (ByteSourceContent) content;
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    bsc.process(baos);
+    assertEquals("hello world", baos.toString("UTF-8"));
+  }
+  
   @Test
   public void testSelf()
   {
diff --git a/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpResponseTest.java b/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpResponseTest.java
index 277bda856d..6fc149f36c 100644
--- a/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpResponseTest.java
+++ b/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpResponseTest.java
@@ -55,6 +55,9 @@ public class AdvancedHttpResponseTest {
 
   @Mock(answer = Answers.CALLS_REAL_METHODS)
   private AdvancedHttpResponse response;
+  
+  @Mock
+  private ContentTransformer transformer;
 
   @Test
   public void testContent() throws IOException
@@ -65,6 +68,12 @@ public class AdvancedHttpResponseTest {
     assertEquals("test123", new String(data, Charsets.UTF_8));
   }
   
+  @Test
+  public void testContentWithoutByteSource() throws IOException
+  {
+    assertNull(response.content());
+  }
+  
   @Test
   public void testContentAsString() throws IOException
   {
@@ -73,6 +82,12 @@ public class AdvancedHttpResponseTest {
     assertEquals("123test", response.contentAsString());
   }
   
+  @Test
+  public void testContentAsStingWithoutByteSource() throws IOException 
+  {
+    assertNull(response.contentAsString());
+  }
+  
   @Test
   public void testContentAsReader() throws IOException
   {
@@ -81,6 +96,12 @@ public class AdvancedHttpResponseTest {
     assertEquals("abc123", CharStreams.toString(response.contentAsReader()));
   }
   
+  @Test
+  public void testContentAsReaderWithoutByteSource() throws IOException
+  {
+    assertNull(response.contentAsReader());
+  }
+  
   @Test
   public void testContentAsStream() throws IOException
   {
@@ -90,6 +111,69 @@ public class AdvancedHttpResponseTest {
     assertEquals("cde456", new String(data, Charsets.UTF_8));
   }
   
+  @Test
+  public void testContentAsStreamWithoutByteSource() throws IOException
+  {
+    assertNull(response.contentAsStream());
+  }
+  
+  @Test
+  public void testContentFromJson() throws IOException{
+    ByteSource bs = ByteSource.wrap("{}".getBytes(Charsets.UTF_8));
+    when(response.contentAsByteSource()).thenReturn(bs);
+    when(response.createTransformer(String.class, ContentType.JSON)).thenReturn(transformer);
+    when(transformer.unmarshall(String.class, bs)).thenReturn("{root: null}");
+    String c = response.contentFromJson(String.class);
+    assertEquals("{root: null}", c);
+  }
+  
+  @Test
+  public void testContentFromXml() throws IOException{
+    ByteSource bs = ByteSource.wrap("".getBytes(Charsets.UTF_8));
+    when(response.contentAsByteSource()).thenReturn(bs);
+    when(response.createTransformer(String.class, ContentType.XML)).thenReturn(transformer);
+    when(transformer.unmarshall(String.class, bs)).thenReturn("");
+    String c = response.contentFromXml(String.class);
+    assertEquals("", c);
+  }
+  
+  @Test(expected = ContentTransformerException.class)
+  public void testContentTransformedWithoutHeader() throws IOException{
+    Multimap map = LinkedHashMultimap.create();
+    when(response.getHeaders()).thenReturn(map);
+    response.contentTransformed(String.class);
+  }
+  
+  @Test
+  public void testContentTransformedFromHeader() throws IOException{
+    Multimap map = LinkedHashMultimap.create();
+    map.put("Content-Type", "text/plain");
+    when(response.getHeaders()).thenReturn(map);
+    when(response.createTransformer(String.class, "text/plain")).thenReturn(
+      transformer);
+    ByteSource bs = ByteSource.wrap("hello".getBytes(Charsets.UTF_8));
+    when(response.contentAsByteSource()).thenReturn(bs);
+    when(transformer.unmarshall(String.class, bs)).thenReturn("hello world");
+    String v = response.contentTransformed(String.class);
+    assertEquals("hello world", v);
+  }
+  
+  @Test
+  public void testContentTransformed() throws IOException{
+    when(response.createTransformer(String.class, "text/plain")).thenReturn(
+      transformer);
+    ByteSource bs = ByteSource.wrap("hello".getBytes(Charsets.UTF_8));
+    when(response.contentAsByteSource()).thenReturn(bs);
+    when(transformer.unmarshall(String.class, bs)).thenReturn("hello world");
+    String v = response.contentTransformed(String.class, "text/plain");
+    assertEquals("hello world", v);
+  }
+  
+  @Test
+  public void testContentTransformedWithoutByteSource() throws IOException{
+    assertNull(response.contentTransformed(String.class, "text/plain"));
+  }
+  
   @Test
   public void testGetFirstHeader() throws IOException
   {
diff --git a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java
index 100f171cd1..9087755f93 100644
--- a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java
+++ b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java
@@ -157,6 +157,11 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
+import sonia.scm.net.ahc.AdvancedHttpClient;
+import sonia.scm.net.ahc.ContentTransformer;
+import sonia.scm.net.ahc.DefaultAdvancedHttpClient;
+import sonia.scm.net.ahc.JsonContentTransformer;
+import sonia.scm.net.ahc.XmlContentTransformer;
 import sonia.scm.web.UserAgentParser;
 
 /**
@@ -219,7 +224,7 @@ public class ScmServletModule extends ServletModule
     PATTERN_STYLESHEET, "*.json", "*.xml", "*.txt" };
 
   /** Field description */
-  private static Logger logger =
+  private static final Logger logger =
     LoggerFactory.getLogger(ScmServletModule.class);
 
   //~--- constructors ---------------------------------------------------------
@@ -315,6 +320,13 @@ public class ScmServletModule extends ServletModule
 
     // bind httpclient
     bind(HttpClient.class, URLHttpClient.class);
+    
+    // bind ahc
+    Multibinder transformers =
+      Multibinder.newSetBinder(binder(), ContentTransformer.class);
+    transformers.addBinding().to(XmlContentTransformer.class);
+    transformers.addBinding().to(JsonContentTransformer.class);
+    bind(AdvancedHttpClient.class).to(DefaultAdvancedHttpClient.class);
 
     // bind resourcemanager
     if (context.getStage() == Stage.DEVELOPMENT)
diff --git a/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpClient.java b/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpClient.java
index 6b80d6bdae..2c19afb44d 100644
--- a/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpClient.java
+++ b/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpClient.java
@@ -65,12 +65,14 @@ import java.net.URL;
 import java.security.KeyManagementException;
 import java.security.NoSuchAlgorithmException;
 
+import java.util.Set;
+
 import javax.net.ssl.HttpsURLConnection;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.TrustManager;
 
 /**
- * Default implementation of the {@link AdvancedHttpClient}. The default 
+ * Default implementation of the {@link AdvancedHttpClient}. The default
  * implementation uses {@link HttpURLConnection}.
  *
  * @author Sebastian Sdorra
@@ -79,16 +81,10 @@ import javax.net.ssl.TrustManager;
 public class DefaultAdvancedHttpClient extends AdvancedHttpClient
 {
 
-  /** credential separator */
-  private static final String CREDENTIAL_SEPARATOR = ":";
-
   /** proxy authorization header */
   @VisibleForTesting
   static final String HEADER_PROXY_AUTHORIZATION = "Proxy-Authorization";
 
-  /** basic authentication prefix */
-  private static final String PREFIX_BASIC_AUTHENTICATION = "Basic ";
-
   /** connection timeout */
   @VisibleForTesting
   static final int TIMEOUT_CONNECTION = 30000;
@@ -97,6 +93,12 @@ public class DefaultAdvancedHttpClient extends AdvancedHttpClient
   @VisibleForTesting
   static final int TIMEOUT_RAED = 1200000;
 
+  /** credential separator */
+  private static final String CREDENTIAL_SEPARATOR = ":";
+
+  /** basic authentication prefix */
+  private static final String PREFIX_BASIC_AUTHENTICATION = "Basic ";
+
   /**
    * the logger for DefaultAdvancedHttpClient
    */
@@ -110,17 +112,20 @@ public class DefaultAdvancedHttpClient extends AdvancedHttpClient
    *
    *
    * @param configuration scm-manager main configuration
+   * @param contentTransformers content transformer
    */
   @Inject
-  public DefaultAdvancedHttpClient(ScmConfiguration configuration)
+  public DefaultAdvancedHttpClient(ScmConfiguration configuration,
+    Set contentTransformers)
   {
     this.configuration = configuration;
+    this.contentTransformers = contentTransformers;
   }
 
   //~--- methods --------------------------------------------------------------
 
   /**
-   * Creates a new {@link HttpURLConnection} from the given {@link URL}. The 
+   * Creates a new {@link HttpURLConnection} from the given {@link URL}. The
    * method is visible for testing.
    *
    *
@@ -157,6 +162,34 @@ public class DefaultAdvancedHttpClient extends AdvancedHttpClient
       address));
   }
 
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected ContentTransformer createTransformer(Class type, String contentType)
+  {
+    ContentTransformer responsible = null;
+
+    for (ContentTransformer transformer : contentTransformers)
+    {
+      if (transformer.isResponsible(type, contentType))
+      {
+        responsible = transformer;
+
+        break;
+      }
+    }
+
+    if (responsible == null)
+    {
+      throw new ContentTransformerNotFoundException(
+        "could not find content transformer for content type ".concat(
+          contentType));
+    }
+
+    return responsible;
+  }
+
   /**
    * Executes the given request and returns the server response.
    *
@@ -210,7 +243,7 @@ public class DefaultAdvancedHttpClient extends AdvancedHttpClient
       applyContent(connection, content);
     }
 
-    return new DefaultAdvancedHttpResponse(connection,
+    return new DefaultAdvancedHttpResponse(this, connection,
       connection.getResponseCode(), connection.getResponseMessage());
   }
 
@@ -359,4 +392,7 @@ public class DefaultAdvancedHttpClient extends AdvancedHttpClient
 
   /** scm-manager main configuration */
   private final ScmConfiguration configuration;
+
+  /** set of content transformers */
+  private final Set contentTransformers;
 }
diff --git a/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponse.java b/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponse.java
index c42e36deb5..21179ce1b6 100644
--- a/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponse.java
+++ b/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponse.java
@@ -62,14 +62,15 @@ public class DefaultAdvancedHttpResponse extends AdvancedHttpResponse
   /**
    * Constructs a new {@link DefaultAdvancedHttpResponse}.
    *
-   *
+   * @param client ahc
    * @param connection http connection
    * @param status response status code
    * @param statusText response status text
    */
-  DefaultAdvancedHttpResponse(HttpURLConnection connection, int status,
-    String statusText)
+  DefaultAdvancedHttpResponse(DefaultAdvancedHttpClient client,
+    HttpURLConnection connection, int status, String statusText)
   {
+    this.client = client;
     this.connection = connection;
     this.status = status;
     this.statusText = statusText;
@@ -126,6 +127,18 @@ public class DefaultAdvancedHttpResponse extends AdvancedHttpResponse
     return statusText;
   }
 
+  //~--- methods --------------------------------------------------------------
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected ContentTransformer createTransformer(Class type,
+    String contentType)
+  {
+    return client.createTransformer(type, contentType);
+  }
+
   //~--- inner classes --------------------------------------------------------
 
   /**
@@ -196,6 +209,9 @@ public class DefaultAdvancedHttpResponse extends AdvancedHttpResponse
 
   //~--- fields ---------------------------------------------------------------
 
+  /** Field description */
+  private final DefaultAdvancedHttpClient client;
+
   /** http connection */
   private final HttpURLConnection connection;
 
diff --git a/scm-webapp/src/main/java/sonia/scm/net/ahc/JsonContentTransformer.java b/scm-webapp/src/main/java/sonia/scm/net/ahc/JsonContentTransformer.java
new file mode 100644
index 0000000000..29d45f00c5
--- /dev/null
+++ b/scm-webapp/src/main/java/sonia/scm/net/ahc/JsonContentTransformer.java
@@ -0,0 +1,149 @@
+/**
+ * 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.net.ahc;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.google.common.io.ByteSource;
+
+import org.codehaus.jackson.map.ObjectMapper;
+
+import sonia.scm.plugin.ext.Extension;
+import sonia.scm.util.IOUtil;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.ws.rs.core.MediaType;
+import org.codehaus.jackson.map.AnnotationIntrospector;
+import org.codehaus.jackson.map.introspect.JacksonAnnotationIntrospector;
+import org.codehaus.jackson.xc.JaxbAnnotationIntrospector;
+
+/**
+ * {@link ContentTransformer} for json. The {@link JsonContentTransformer} uses
+ * jacksons {@link ObjectMapper} to marshalling/unmarshalling.
+ *
+ * @author Sebastian Sdorra
+ * @since 1.46
+ */
+@Extension
+public class JsonContentTransformer implements ContentTransformer
+{
+
+  public JsonContentTransformer()
+  {
+    this.mapper = new ObjectMapper();
+    AnnotationIntrospector jackson = new JacksonAnnotationIntrospector();
+    AnnotationIntrospector jaxb = new JaxbAnnotationIntrospector();
+    AnnotationIntrospector pair = new AnnotationIntrospector.Pair(jackson, jaxb);
+    this.mapper.setAnnotationIntrospector(pair);
+  }
+
+  
+  
+  
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public ByteSource marshall(Object object)
+  {
+    ByteSource source = null;
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+    try
+    {
+      mapper.writeValue(baos, object);
+      source = ByteSource.wrap(baos.toByteArray());
+    }
+    catch (IOException ex)
+    {
+      throw new ContentTransformerException("could not marshall object", ex);
+    }
+
+    return source;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public  T unmarshall(Class type, ByteSource content)
+  {
+    T object = null;
+    InputStream stream = null;
+
+    try
+    {
+      stream = content.openBufferedStream();
+      object = mapper.readValue(stream, type);
+    }
+    catch (IOException ex)
+    {
+      throw new ContentTransformerException("could not unmarshall content", ex);
+    }
+    finally
+    {
+      IOUtil.close(stream);
+    }
+
+    return object;
+  }
+
+  //~--- get methods ----------------------------------------------------------
+
+  /**
+   * Returns {@code true}, if the content type is compatible with
+   * application/json.
+   *
+   *
+   * @param type object type
+   * @param contentType content type
+   *
+   * @return {@code true}, if the content type is compatible with
+   *   application/json
+   */
+  @Override
+  public boolean isResponsible(Class type, String contentType)
+  {
+    return MediaType.valueOf(contentType).isCompatible(
+      MediaType.APPLICATION_JSON_TYPE);
+  }
+
+  //~--- fields ---------------------------------------------------------------
+
+  /** object mapper */
+  private final ObjectMapper mapper;
+}
diff --git a/scm-webapp/src/main/java/sonia/scm/net/ahc/XmlContentTransformer.java b/scm-webapp/src/main/java/sonia/scm/net/ahc/XmlContentTransformer.java
new file mode 100644
index 0000000000..1fd1983d23
--- /dev/null
+++ b/scm-webapp/src/main/java/sonia/scm/net/ahc/XmlContentTransformer.java
@@ -0,0 +1,134 @@
+/**
+ * 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.net.ahc;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.google.common.io.ByteSource;
+
+import sonia.scm.plugin.ext.Extension;
+import sonia.scm.util.IOUtil;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.ws.rs.core.MediaType;
+
+import javax.xml.bind.DataBindingException;
+import javax.xml.bind.JAXB;
+
+/**
+ * {@link ContentTransformer} for xml. The {@link XmlContentTransformer} uses 
+ * jaxb to marshalling/unmarshalling.
+ *
+ * @author Sebastian Sdorra
+ * @since 1.46
+ */
+@Extension
+public class XmlContentTransformer implements ContentTransformer
+{
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public ByteSource marshall(Object object)
+  {
+    ByteSource source = null;
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+    try
+    {
+      JAXB.marshal(object, baos);
+      source = ByteSource.wrap(baos.toByteArray());
+    }
+    catch (DataBindingException ex)
+    {
+      throw new ContentTransformerException("could not marshall object", ex);
+    }
+
+    return source;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public  T unmarshall(Class type, ByteSource content)
+  {
+    T object = null;
+    InputStream stream = null;
+
+    try
+    {
+      stream = content.openBufferedStream();
+      object = JAXB.unmarshal(stream, type);
+    }
+    catch (IOException ex)
+    {
+      throw new ContentTransformerException("could not unmarshall content", ex);
+    }
+    catch (DataBindingException ex)
+    {
+      throw new ContentTransformerException("could not unmarshall content", ex);
+    }
+    finally
+    {
+      IOUtil.close(stream);
+    }
+
+    return object;
+  }
+
+  //~--- get methods ----------------------------------------------------------
+
+  /**
+   * Returns {@code true}, if the content type is compatible with 
+   * application/xml.
+   *
+   *
+   * @param type object type
+   * @param contentType content type
+   *
+   * @return {@code true}, if the content type is compatible with 
+   *   application/xml
+   */
+  @Override
+  public boolean isResponsible(Class type, String contentType)
+  {
+    return MediaType.valueOf(contentType).isCompatible(
+      MediaType.APPLICATION_XML_TYPE);
+  }
+}
diff --git a/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpClientTest.java b/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpClientTest.java
index 451c07b5cc..cd26af9d4b 100644
--- a/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpClientTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpClientTest.java
@@ -59,6 +59,9 @@ import java.net.HttpURLConnection;
 import java.net.SocketAddress;
 import java.net.URL;
 
+import java.util.HashSet;
+import java.util.Set;
+
 import javax.net.ssl.HttpsURLConnection;
 import javax.net.ssl.SSLSocketFactory;
 
@@ -87,6 +90,20 @@ public class DefaultAdvancedHttpClientTest
       DefaultAdvancedHttpClient.TIMEOUT_CONNECTION);
     verify(connection).addRequestProperty(HttpUtil.HEADER_CONTENT_LENGTH, "0");
   }
+  
+  @Test(expected = ContentTransformerNotFoundException.class)
+  public void testContentTransformerNotFound(){
+    client.createTransformer(String.class, "text/plain");
+  }
+  
+  @Test
+  public void testContentTransformer(){
+    ContentTransformer transformer = mock(ContentTransformer.class);
+    when(transformer.isResponsible(String.class, "text/plain")).thenReturn(Boolean.TRUE);
+    transformers.add(transformer);
+    ContentTransformer t = client.createTransformer(String.class, "text/plain");
+    assertSame(transformer, t);
+  }
 
   /**
    * Method description
@@ -266,7 +283,8 @@ public class DefaultAdvancedHttpClientTest
   public void setUp()
   {
     configuration = new ScmConfiguration();
-    client = new TestingAdvacedHttpClient(configuration);
+    transformers = new HashSet();
+    client = new TestingAdvacedHttpClient(configuration, transformers);
   }
 
   //~--- inner classes --------------------------------------------------------
@@ -276,7 +294,7 @@ public class DefaultAdvancedHttpClientTest
    *
    *
    * @version        Enter version here..., 15/05/01
-   * @author         Enter your name here...    
+   * @author         Enter your name here...
    */
   public class TestingAdvacedHttpClient extends DefaultAdvancedHttpClient
   {
@@ -286,10 +304,12 @@ public class DefaultAdvancedHttpClientTest
      *
      *
      * @param configuration
+     * @param transformers
      */
-    public TestingAdvacedHttpClient(ScmConfiguration configuration)
+    public TestingAdvacedHttpClient(ScmConfiguration configuration,
+      Set transformers)
     {
-      super(configuration);
+      super(configuration, transformers);
     }
 
     //~--- methods ------------------------------------------------------------
@@ -349,4 +369,7 @@ public class DefaultAdvancedHttpClientTest
   /** Field description */
   @Mock
   private HttpsURLConnection connection;
+
+  /** Field description */
+  private Set transformers;
 }
diff --git a/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java b/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java
index 063b8b6f09..f19c89eb5f 100644
--- a/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java
@@ -47,6 +47,8 @@ import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
 
+import sonia.scm.config.ScmConfiguration;
+
 import static org.hamcrest.Matchers.*;
 
 import static org.junit.Assert.*;
@@ -60,6 +62,7 @@ import java.io.IOException;
 
 import java.net.HttpURLConnection;
 
+import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 
@@ -85,8 +88,8 @@ public class DefaultAdvancedHttpResponseTest
 
     when(connection.getInputStream()).thenReturn(bais);
 
-    AdvancedHttpResponse response = new DefaultAdvancedHttpResponse(connection,
-                                      200, "OK");
+    AdvancedHttpResponse response = new DefaultAdvancedHttpResponse(client,
+                                      connection, 200, "OK");
     ByteSource content = response.contentAsByteSource();
 
     assertEquals("test", content.asCharSource(Charsets.UTF_8).read());
@@ -107,8 +110,8 @@ public class DefaultAdvancedHttpResponseTest
     when(connection.getInputStream()).thenThrow(IOException.class);
     when(connection.getErrorStream()).thenReturn(bais);
 
-    AdvancedHttpResponse response = new DefaultAdvancedHttpResponse(connection,
-                                      404, "NOT FOUND");
+    AdvancedHttpResponse response = new DefaultAdvancedHttpResponse(client,
+                                      connection, 404, "NOT FOUND");
     ByteSource content = response.contentAsByteSource();
 
     assertEquals("test", content.asCharSource(Charsets.UTF_8).read());
@@ -127,8 +130,8 @@ public class DefaultAdvancedHttpResponseTest
     map.put("Test", test);
     when(connection.getHeaderFields()).thenReturn(map);
 
-    AdvancedHttpResponse response = new DefaultAdvancedHttpResponse(connection,
-                                      200, "OK");
+    AdvancedHttpResponse response = new DefaultAdvancedHttpResponse(client,
+                                      connection, 200, "OK");
     Multimap headers = response.getHeaders();
 
     assertThat(headers.get("Test"), contains("One", "Two"));
@@ -137,6 +140,11 @@ public class DefaultAdvancedHttpResponseTest
 
   //~--- fields ---------------------------------------------------------------
 
+  /** Field description */
+  private final DefaultAdvancedHttpClient client =
+    new DefaultAdvancedHttpClient(new ScmConfiguration(),
+      new HashSet());
+
   /** Field description */
   @Mock
   private HttpURLConnection connection;
diff --git a/scm-webapp/src/test/java/sonia/scm/net/ahc/JsonContentTransformerTest.java b/scm-webapp/src/test/java/sonia/scm/net/ahc/JsonContentTransformerTest.java
new file mode 100644
index 0000000000..ff50427470
--- /dev/null
+++ b/scm-webapp/src/test/java/sonia/scm/net/ahc/JsonContentTransformerTest.java
@@ -0,0 +1,93 @@
+/**
+ * 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.net.ahc;
+
+import com.google.common.io.ByteSource;
+import java.io.IOException;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+import org.junit.Test;
+import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.*;
+import static org.mockito.Mockito.*;
+
+/**
+ *
+ * @author Sebastian Sdorra
+ */
+public class JsonContentTransformerTest {
+
+
+  private final JsonContentTransformer transformer = new JsonContentTransformer();
+
+  @Test
+  public void testIsResponsible()
+  {
+    assertTrue(transformer.isResponsible(String.class, "application/json"));
+    assertTrue(transformer.isResponsible(String.class, "application/json;charset=UTF-8"));
+    assertFalse(transformer.isResponsible(String.class, "text/plain"));
+  }
+  
+  @Test
+  public void testMarshallAndUnmarshall() throws IOException{
+    ByteSource bs = transformer.marshall(new TestObject("test"));
+    TestObject to = transformer.unmarshall(TestObject.class, bs);
+    assertEquals("test", to.value);
+  }
+  
+  @Test(expected = ContentTransformerException.class)
+  public void testUnmarshallIOException() throws IOException{
+    ByteSource bs = mock(ByteSource.class);
+    when(bs.openBufferedStream()).thenThrow(IOException.class);
+    transformer.unmarshall(String.class, bs);
+  }
+  
+  private static class TestObject2 {}
+  
+  @XmlRootElement(name = "test")
+  @XmlAccessorType(XmlAccessType.FIELD)
+  private static class TestObject {
+  
+    private String value;
+
+    public TestObject()
+    {
+    }
+    
+    public TestObject(String value)
+    {
+      this.value = value;
+    }
+    
+  }
+}
\ No newline at end of file
diff --git a/scm-webapp/src/test/java/sonia/scm/net/ahc/XmlContentTransformerTest.java b/scm-webapp/src/test/java/sonia/scm/net/ahc/XmlContentTransformerTest.java
new file mode 100644
index 0000000000..ce87752b4f
--- /dev/null
+++ b/scm-webapp/src/test/java/sonia/scm/net/ahc/XmlContentTransformerTest.java
@@ -0,0 +1,92 @@
+/**
+ * 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.net.ahc;
+
+import com.google.common.io.ByteSource;
+import java.io.IOException;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+import org.junit.Test;
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+/**
+ *
+ * @author Sebastian Sdorra
+ */
+public class XmlContentTransformerTest {
+
+  private final XmlContentTransformer transformer = new XmlContentTransformer();
+
+  @Test
+  public void testIsResponsible()
+  {
+    assertTrue(transformer.isResponsible(String.class, "application/xml"));
+    assertTrue(transformer.isResponsible(String.class, "application/xml;charset=UTF-8"));
+    assertFalse(transformer.isResponsible(String.class, "text/plain"));
+  }
+  
+  @Test
+  public void testMarshallAndUnmarshall() throws IOException{
+    ByteSource bs = transformer.marshall(new TestObject("test"));
+    TestObject to = transformer.unmarshall(TestObject.class, bs);
+    assertEquals("test", to.value);
+  }
+  
+  @Test(expected = ContentTransformerException.class)
+  public void testUnmarshallIOException() throws IOException{
+    ByteSource bs = mock(ByteSource.class);
+    when(bs.openBufferedStream()).thenThrow(IOException.class);
+    transformer.unmarshall(String.class, bs);
+  }
+  
+  private static class TestObject2 {}
+  
+  @XmlRootElement(name = "test")
+  @XmlAccessorType(XmlAccessType.FIELD)
+  private static class TestObject {
+  
+    private String value;
+
+    public TestObject()
+    {
+    }
+    
+    public TestObject(String value)
+    {
+      this.value = value;
+    }
+    
+  }
+
+}
\ No newline at end of file

From a7d33529fd93d9e3a7671927d4c2e878cac4fb4a Mon Sep 17 00:00:00 2001
From: Sebastian Sdorra 
Date: Sun, 3 May 2015 17:06:35 +0200
Subject: [PATCH 043/171] mark old http-client api as deprecated

---
 scm-core/src/main/java/sonia/scm/net/HttpClient.java          | 3 +++
 scm-core/src/main/java/sonia/scm/net/HttpRequest.java         | 4 ++++
 scm-core/src/main/java/sonia/scm/net/HttpResponse.java        | 4 ++++
 .../java/sonia/scm/net/ahc/JsonContentTransformerTest.java    | 1 -
 4 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/scm-core/src/main/java/sonia/scm/net/HttpClient.java b/scm-core/src/main/java/sonia/scm/net/HttpClient.java
index b9b891f557..6af89bcb71 100644
--- a/scm-core/src/main/java/sonia/scm/net/HttpClient.java
+++ b/scm-core/src/main/java/sonia/scm/net/HttpClient.java
@@ -48,7 +48,10 @@ import java.util.Map;
  * @apiviz.landmark
  * @apiviz.uses sonia.scm.net.HttpRequest
  * @apiviz.uses sonia.scm.net.HttpResponse
+ * 
+ * @deprecated use {@link sonia.scm.net.ahc.AdvancedHttpClient} instead.
  */
+@Deprecated
 public interface HttpClient
 {
 
diff --git a/scm-core/src/main/java/sonia/scm/net/HttpRequest.java b/scm-core/src/main/java/sonia/scm/net/HttpRequest.java
index f3ccf9719c..3f171a3fa7 100644
--- a/scm-core/src/main/java/sonia/scm/net/HttpRequest.java
+++ b/scm-core/src/main/java/sonia/scm/net/HttpRequest.java
@@ -51,7 +51,11 @@ import java.util.Map;
  *
  * @author Sebastian Sdorra
  * @since 1.9
+ * 
+ * @deprecated use {@link sonia.scm.net.ahc.AdvancedHttpRequest} or 
+ *   {@link sonia.scm.net.ahc.AdvancedHttpRequestWithBody} instead.
  */
+@Deprecated
 public class HttpRequest
 {
 
diff --git a/scm-core/src/main/java/sonia/scm/net/HttpResponse.java b/scm-core/src/main/java/sonia/scm/net/HttpResponse.java
index 83cdf9ee61..ef297c2809 100644
--- a/scm-core/src/main/java/sonia/scm/net/HttpResponse.java
+++ b/scm-core/src/main/java/sonia/scm/net/HttpResponse.java
@@ -41,12 +41,16 @@ import java.io.InputStream;
 
 import java.util.List;
 import java.util.Map;
+import sonia.scm.net.ahc.AdvancedHttpResponse;
 
 /**
  * Response of a {@link HttpRequest} execute by the {@link HttpClient}.
  *
  * @author Sebastian Sdorra
+ * 
+ * @deprecated use {@link AdvancedHttpResponse} instead
  */
+@Deprecated
 public interface HttpResponse extends Closeable
 {
 
diff --git a/scm-webapp/src/test/java/sonia/scm/net/ahc/JsonContentTransformerTest.java b/scm-webapp/src/test/java/sonia/scm/net/ahc/JsonContentTransformerTest.java
index ff50427470..1828c5ccf2 100644
--- a/scm-webapp/src/test/java/sonia/scm/net/ahc/JsonContentTransformerTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/net/ahc/JsonContentTransformerTest.java
@@ -38,7 +38,6 @@ import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlRootElement;
 import org.junit.Test;
 import static org.junit.Assert.*;
-import static org.hamcrest.Matchers.*;
 import static org.mockito.Mockito.*;
 
 /**

From cb68170c5dfcb9a3e8ef21fd5a088614648c9fc9 Mon Sep 17 00:00:00 2001
From: Sebastian Sdorra 
Date: Sun, 17 May 2015 12:28:23 +0200
Subject: [PATCH 044/171] close branch issue-697


From 656085c698afdd64826670d5d0cd25392b75f7ed Mon Sep 17 00:00:00 2001
From: Sebastian Sdorra 
Date: Sun, 17 May 2015 14:04:58 +0200
Subject: [PATCH 045/171] use ahc for internal http operations

---
 .../api/rest/resources/HgConfigResource.java  | 12 ++---
 .../scm/installer/AbstractHgInstaller.java    |  4 +-
 .../java/sonia/scm/installer/HgInstaller.java |  6 +--
 .../scm/installer/HgPackageInstaller.java     | 12 ++---
 .../sonia/scm/installer/HgPackageReader.java  | 34 +++++---------
 .../sonia/scm/repository/HgHookManager.java   | 36 ++++++---------
 .../java/sonia/scm/net/URLHttpClient.java     |  2 +
 .../scm/plugin/DefaultPluginManager.java      | 46 +++----------------
 8 files changed, 52 insertions(+), 100 deletions(-)

diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/rest/resources/HgConfigResource.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/rest/resources/HgConfigResource.java
index 7435024e19..3ef39c163f 100644
--- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/rest/resources/HgConfigResource.java
+++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/rest/resources/HgConfigResource.java
@@ -43,7 +43,7 @@ import sonia.scm.installer.HgInstallerFactory;
 import sonia.scm.installer.HgPackage;
 import sonia.scm.installer.HgPackageReader;
 import sonia.scm.installer.HgPackages;
-import sonia.scm.net.HttpClient;
+import sonia.scm.net.ahc.AdvancedHttpClient;
 import sonia.scm.repository.HgConfig;
 import sonia.scm.repository.HgRepositoryHandler;
 
@@ -89,8 +89,8 @@ public class HgConfigResource
    * @param pkgReader
    */
   @Inject
-  public HgConfigResource(HttpClient client, HgRepositoryHandler handler,
-                          HgPackageReader pkgReader)
+  public HgConfigResource(AdvancedHttpClient client,
+    HgRepositoryHandler handler, HgPackageReader pkgReader)
   {
     this.client = client;
     this.handler = handler;
@@ -158,7 +158,7 @@ public class HgConfigResource
     if (pkg != null)
     {
       if (HgInstallerFactory.createInstaller().installPackage(client, handler,
-              SCMContext.getContext().getBaseDirectory(), pkg))
+        SCMContext.getContext().getBaseDirectory(), pkg))
       {
         response = Response.noContent().build();
       }
@@ -262,7 +262,7 @@ public class HgConfigResource
   @POST
   @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
   public Response setConfig(@Context UriInfo uriInfo, HgConfig config)
-          throws IOException
+    throws IOException
   {
     handler.setConfig(config);
     handler.storeConfig();
@@ -338,7 +338,7 @@ public class HgConfigResource
   //~--- fields ---------------------------------------------------------------
 
   /** Field description */
-  private HttpClient client;
+  private AdvancedHttpClient client;
 
   /** Field description */
   private HgRepositoryHandler handler;
diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/installer/AbstractHgInstaller.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/installer/AbstractHgInstaller.java
index 637825a407..bd5dfd7095 100644
--- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/installer/AbstractHgInstaller.java
+++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/installer/AbstractHgInstaller.java
@@ -35,7 +35,6 @@ package sonia.scm.installer;
 
 //~--- non-JDK imports --------------------------------------------------------
 
-import sonia.scm.net.HttpClient;
 import sonia.scm.repository.HgConfig;
 import sonia.scm.repository.HgRepositoryHandler;
 import sonia.scm.util.IOUtil;
@@ -44,6 +43,7 @@ import sonia.scm.util.IOUtil;
 
 import java.io.File;
 import java.io.IOException;
+import sonia.scm.net.ahc.AdvancedHttpClient;
 
 /**
  *
@@ -93,7 +93,7 @@ public abstract class AbstractHgInstaller implements HgInstaller
    * @return
    */
   @Override
-  public boolean installPackage(HttpClient client, HgRepositoryHandler handler,
+  public boolean installPackage(AdvancedHttpClient client, HgRepositoryHandler handler,
                                 File baseDirectory, HgPackage pkg)
   {
     return new HgPackageInstaller(client, handler, baseDirectory,
diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/installer/HgInstaller.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/installer/HgInstaller.java
index 219ce5204e..52f18c1824 100644
--- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/installer/HgInstaller.java
+++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/installer/HgInstaller.java
@@ -35,7 +35,7 @@ package sonia.scm.installer;
 
 //~--- non-JDK imports --------------------------------------------------------
 
-import sonia.scm.net.HttpClient;
+import sonia.scm.net.ahc.AdvancedHttpClient;
 import sonia.scm.repository.HgConfig;
 import sonia.scm.repository.HgRepositoryHandler;
 
@@ -78,8 +78,8 @@ public interface HgInstaller
    *
    * @return
    */
-  public boolean installPackage(HttpClient client, HgRepositoryHandler handler,
-                                File baseDirectory, HgPackage pkg);
+  public boolean installPackage(AdvancedHttpClient client,
+    HgRepositoryHandler handler, File baseDirectory, HgPackage pkg);
 
   /**
    * Method description
diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/installer/HgPackageInstaller.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/installer/HgPackageInstaller.java
index 91bb6fce0c..7406505b5a 100644
--- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/installer/HgPackageInstaller.java
+++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/installer/HgPackageInstaller.java
@@ -40,10 +40,10 @@ import org.slf4j.LoggerFactory;
 
 import sonia.scm.SCMContext;
 import sonia.scm.io.ZipUnArchiver;
-import sonia.scm.net.HttpClient;
-import sonia.scm.repository.HgWindowsPackageFix;
+import sonia.scm.net.ahc.AdvancedHttpClient;
 import sonia.scm.repository.HgConfig;
 import sonia.scm.repository.HgRepositoryHandler;
+import sonia.scm.repository.HgWindowsPackageFix;
 import sonia.scm.util.IOUtil;
 
 //~--- JDK imports ------------------------------------------------------------
@@ -80,8 +80,8 @@ public class HgPackageInstaller implements Runnable
    * @param baseDirectory
    * @param pkg
    */
-  public HgPackageInstaller(HttpClient client, HgRepositoryHandler handler,
-    File baseDirectory, HgPackage pkg)
+  public HgPackageInstaller(AdvancedHttpClient client,
+    HgRepositoryHandler handler, File baseDirectory, HgPackage pkg)
   {
     this.client = client;
     this.handler = handler;
@@ -155,7 +155,7 @@ public class HgPackageInstaller implements Runnable
       }
 
       // TODO error handling
-      input = client.get(pkg.getUrl()).getContent();
+      input = client.get(pkg.getUrl()).request().contentAsStream();
       output = new FileOutputStream(file);
       IOUtil.copy(input, output);
     }
@@ -265,7 +265,7 @@ public class HgPackageInstaller implements Runnable
   private File baseDirectory;
 
   /** Field description */
-  private HttpClient client;
+  private AdvancedHttpClient client;
 
   /** Field description */
   private HgRepositoryHandler handler;
diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/installer/HgPackageReader.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/installer/HgPackageReader.java
index b4ee9cc2c9..a90adff3cc 100644
--- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/installer/HgPackageReader.java
+++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/installer/HgPackageReader.java
@@ -36,7 +36,6 @@ package sonia.scm.installer;
 //~--- non-JDK imports --------------------------------------------------------
 
 import com.google.inject.Inject;
-import com.google.inject.Provider;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -44,22 +43,17 @@ import org.slf4j.LoggerFactory;
 import sonia.scm.PlatformType;
 import sonia.scm.cache.Cache;
 import sonia.scm.cache.CacheManager;
-import sonia.scm.net.HttpClient;
-import sonia.scm.net.HttpResponse;
-import sonia.scm.util.IOUtil;
+import sonia.scm.net.ahc.AdvancedHttpClient;
 import sonia.scm.util.SystemUtil;
 import sonia.scm.util.Util;
 
 //~--- JDK imports ------------------------------------------------------------
 
 import java.io.IOException;
-import java.io.InputStream;
 
 import java.util.ArrayList;
 import java.util.List;
 
-import javax.xml.bind.JAXB;
-
 /**
  *
  * @author Sebastian Sdorra
@@ -85,14 +79,14 @@ public class HgPackageReader
    *
    *
    * @param cacheManager
-   * @param httpClientProvider
+   * @param httpClient
    */
   @Inject
   public HgPackageReader(CacheManager cacheManager,
-                         Provider httpClientProvider)
+    AdvancedHttpClient httpClient)
   {
     cache = cacheManager.getCache(String.class, HgPackages.class, CACHENAME);
-    this.httpClientProvider = httpClientProvider;
+    this.httpClient = httpClient;
   }
 
   //~--- get methods ----------------------------------------------------------
@@ -167,7 +161,7 @@ public class HgPackageReader
           if (logger.isDebugEnabled())
           {
             logger.debug("reject package {}, because of wrong platform {}",
-                         pkg.getId(), pkg.getPlatform());
+              pkg.getId(), pkg.getPlatform());
           }
 
           add = false;
@@ -181,7 +175,7 @@ public class HgPackageReader
           if (logger.isDebugEnabled())
           {
             logger.debug("reject package {}, because of wrong arch {}",
-                         pkg.getId(), pkg.getArch());
+              pkg.getId(), pkg.getArch());
           }
 
           add = false;
@@ -218,23 +212,19 @@ public class HgPackageReader
     }
 
     HgPackages packages = null;
-    InputStream input = null;
 
     try
     {
-      HttpResponse response = httpClientProvider.get().get(PACKAGEURL);
-
-      input = response.getContent();
-      packages = JAXB.unmarshal(input, HgPackages.class);
+      //J-
+      packages = httpClient.get(PACKAGEURL)
+                           .request()
+                           .contentFromXml(HgPackages.class);
+      //J+
     }
     catch (IOException ex)
     {
       logger.error("could not read HgPackages from ".concat(PACKAGEURL), ex);
     }
-    finally
-    {
-      IOUtil.close(input);
-    }
 
     if (packages == null)
     {
@@ -251,5 +241,5 @@ public class HgPackageReader
   private Cache cache;
 
   /** Field description */
-  private Provider httpClientProvider;
+  private AdvancedHttpClient httpClient;
 }
diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgHookManager.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgHookManager.java
index 6107e33d05..03c07fd011 100644
--- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgHookManager.java
+++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgHookManager.java
@@ -47,9 +47,6 @@ import org.slf4j.LoggerFactory;
 
 import sonia.scm.ConfigChangedListener;
 import sonia.scm.config.ScmConfiguration;
-import sonia.scm.net.HttpClient;
-import sonia.scm.net.HttpRequest;
-import sonia.scm.net.HttpResponse;
 import sonia.scm.util.HttpUtil;
 import sonia.scm.util.Util;
 
@@ -60,6 +57,7 @@ import java.io.IOException;
 import java.util.UUID;
 
 import javax.servlet.http.HttpServletRequest;
+import sonia.scm.net.ahc.AdvancedHttpClient;
 
 /**
  *
@@ -86,17 +84,17 @@ public class HgHookManager implements ConfigChangedListener
    *
    * @param configuration
    * @param httpServletRequestProvider
-   * @param httpClientProvider
+   * @param httpClient
    */
   @Inject
   public HgHookManager(ScmConfiguration configuration,
     Provider httpServletRequestProvider,
-    Provider httpClientProvider)
+    AdvancedHttpClient httpClient)
   {
     this.configuration = configuration;
     this.configuration.addListener(this);
     this.httpServletRequestProvider = httpServletRequestProvider;
-    this.httpClientProvider = httpClientProvider;
+    this.httpClient = httpClient;
   }
 
   //~--- methods --------------------------------------------------------------
@@ -359,20 +357,16 @@ public class HgHookManager implements ConfigChangedListener
     {
       url = url.concat("?ping=true");
 
-      if (logger.isTraceEnabled())
-      {
-        logger.trace("check hook url {}", url);
-      }
-
-      HttpRequest request = new HttpRequest(url);
-
-      request.setDisableCertificateValidation(true);
-      request.setDisableHostnameValidation(true);
-      request.setIgnoreProxySettings(true);
-
-      HttpResponse response = httpClientProvider.get().get(request);
-
-      result = response.getStatusCode() == 204;
+      logger.trace("check hook url {}", url);
+      //J-
+      int sc = httpClient.get(url)
+                         .disableHostnameValidation(true)
+                         .disableCertificateValidation(true)
+                         .ignoreProxySettings(true)
+                         .request()
+                         .getStatus();
+      //J+
+      result = sc == 204;
     }
     catch (IOException ex)
     {
@@ -397,7 +391,7 @@ public class HgHookManager implements ConfigChangedListener
   private volatile String hookUrl;
 
   /** Field description */
-  private Provider httpClientProvider;
+  private AdvancedHttpClient httpClient;
 
   /** Field description */
   private Provider httpServletRequestProvider;
diff --git a/scm-webapp/src/main/java/sonia/scm/net/URLHttpClient.java b/scm-webapp/src/main/java/sonia/scm/net/URLHttpClient.java
index 5be3496784..83b0fd23d5 100644
--- a/scm-webapp/src/main/java/sonia/scm/net/URLHttpClient.java
+++ b/scm-webapp/src/main/java/sonia/scm/net/URLHttpClient.java
@@ -74,7 +74,9 @@ import sonia.scm.util.HttpUtil;
 /**
  *
  * @author Sebastian Sdorra
+ * @deprecated use {@link sonia.scm.net.ahc.AdvancedHttpClient}
  */
+@Deprecated
 public class URLHttpClient implements HttpClient
 {
 
diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java
index b2250a3c21..7ef8f69c4b 100644
--- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java
+++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java
@@ -40,21 +40,18 @@ import com.google.common.collect.ImmutableSet.Builder;
 import com.google.common.collect.Sets;
 import com.google.common.io.Files;
 import com.google.inject.Inject;
-import com.google.inject.Provider;
 import com.google.inject.Singleton;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import sonia.scm.ConfigChangedListener;
-import sonia.scm.ConfigurationException;
 import sonia.scm.SCMContext;
 import sonia.scm.SCMContextProvider;
 import sonia.scm.cache.Cache;
 import sonia.scm.cache.CacheManager;
 import sonia.scm.config.ScmConfiguration;
 import sonia.scm.io.ZipUnArchiver;
-import sonia.scm.net.HttpClient;
 import sonia.scm.util.AssertUtil;
 import sonia.scm.util.IOUtil;
 import sonia.scm.util.SecurityUtil;
@@ -79,9 +76,7 @@ import java.util.Map;
 import java.util.Set;
 
 import javax.xml.bind.JAXB;
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.JAXBException;
-import javax.xml.bind.Unmarshaller;
+import sonia.scm.net.ahc.AdvancedHttpClient;
 
 /**
  *
@@ -122,18 +117,18 @@ public class DefaultPluginManager
    * @param configuration
    * @param pluginLoader
    * @param cacheManager
-   * @param clientProvider
+   * @param httpClient
    */
   @Inject
   public DefaultPluginManager(SCMContextProvider context,
     ScmConfiguration configuration, PluginLoader pluginLoader,
-    CacheManager cacheManager, Provider clientProvider)
+    CacheManager cacheManager, AdvancedHttpClient httpClient)
   {
     this.context = context;
     this.configuration = configuration;
     this.cache = cacheManager.getCache(String.class, PluginCenter.class,
       CACHE_NAME);
-    this.clientProvider = clientProvider;
+    this.httpClient = httpClient;
     installedPlugins = new HashMap();
 
     for (Plugin plugin : pluginLoader.getInstalledPlugins())
@@ -146,16 +141,6 @@ public class DefaultPluginManager
       }
     }
 
-    try
-    {
-      unmarshaller =
-        JAXBContext.newInstance(PluginCenter.class).createUnmarshaller();
-    }
-    catch (JAXBException ex)
-    {
-      throw new ConfigurationException(ex);
-    }
-
     File file = findAdvancedConfiguration();
 
     if (file.exists())
@@ -654,21 +639,9 @@ public class DefaultPluginManager
 
         if (Util.isNotEmpty(pluginUrl))
         {
-          InputStream input = null;
-
           try
           {
-            input = clientProvider.get().get(pluginUrl).getContent();
-
-            /*
-             *  TODO: add gzip support
-             *
-             * if (gzip)
-             * {
-             * input = new GZIPInputStream(input);
-             * }
-             */
-            center = (PluginCenter) unmarshaller.unmarshal(input);
+            center = httpClient.get(pluginUrl).request().contentFromXml(PluginCenter.class);
             preparePlugins(center);
             cache.put(PluginCenter.class.getName(), center);
 
@@ -690,10 +663,6 @@ public class DefaultPluginManager
           {
             logger.error("could not load plugins from plugin center", ex);
           }
-          finally
-          {
-            IOUtil.close(input);
-          }
         }
 
         if (center == null)
@@ -794,7 +763,7 @@ public class DefaultPluginManager
   private Cache cache;
 
   /** Field description */
-  private Provider clientProvider;
+  private AdvancedHttpClient httpClient;
 
   /** Field description */
   private ScmConfiguration configuration;
@@ -807,7 +776,4 @@ public class DefaultPluginManager
 
   /** Field description */
   private AetherPluginHandler pluginHandler;
-
-  /** Field description */
-  private Unmarshaller unmarshaller;
 }

From 96b946a55cb5a2b288df34b1967b6540fa635904 Mon Sep 17 00:00:00 2001
From: Sebastian Sdorra 
Date: Sun, 17 May 2015 14:05:55 +0200
Subject: [PATCH 046/171] close branch issue-709


From bdb202dd8cd59c5d2f24e5750924a5b807ced30a Mon Sep 17 00:00:00 2001
From: Sebastian Sdorra 
Date: Thu, 11 Jun 2015 22:10:32 +0200
Subject: [PATCH 047/171] track time for processing mercurial request on debug
 logging

---
 .../src/main/java/sonia/scm/web/HgCGIServlet.java              | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java
index 410d20847a..4ba165c630 100644
--- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java
+++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java
@@ -35,6 +35,7 @@ package sonia.scm.web;
 
 //~--- non-JDK imports --------------------------------------------------------
 
+import com.google.common.base.Stopwatch;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
@@ -205,7 +206,9 @@ public class HgCGIServlet extends HttpServlet
   {
     if (requestListenerUtil.callListeners(request, response, repository))
     {
+      Stopwatch sw = Stopwatch.createStarted();
       process(request, response, repository);
+      logger.debug("mercurial request finished in {}", sw.stop());
     }
     else if (logger.isDebugEnabled())
     {

From d0032b09d86a1f77b3739b1592f65dffc25f0d6c Mon Sep 17 00:00:00 2001
From: Sebastian Sdorra 
Date: Thu, 11 Jun 2015 22:11:51 +0200
Subject: [PATCH 048/171] use cached thread pool to process mercurial process
 error streams

---
 .../sonia/scm/web/cgi/DefaultCGIExecutor.java | 18 +++++++---
 .../web/cgi/DefaultCGIExecutorFactory.java    | 33 ++++++++++++++++---
 2 files changed, 42 insertions(+), 9 deletions(-)

diff --git a/scm-webapp/src/main/java/sonia/scm/web/cgi/DefaultCGIExecutor.java b/scm-webapp/src/main/java/sonia/scm/web/cgi/DefaultCGIExecutor.java
index 95d6ce2575..3eaa684080 100644
--- a/scm-webapp/src/main/java/sonia/scm/web/cgi/DefaultCGIExecutor.java
+++ b/scm-webapp/src/main/java/sonia/scm/web/cgi/DefaultCGIExecutor.java
@@ -58,6 +58,7 @@ import java.io.OutputStream;
 
 import java.util.Enumeration;
 import java.util.Map;
+import java.util.concurrent.ExecutorService;
 
 import javax.servlet.ServletContext;
 import javax.servlet.ServletInputStream;
@@ -94,15 +95,17 @@ public class DefaultCGIExecutor extends AbstractCGIExecutor
    * Constructs ...
    *
    *
+   * @param executor to handle error stream processing
    * @param configuration
    * @param context
    * @param request
    * @param response
    */
-  public DefaultCGIExecutor(ScmConfiguration configuration,
-    ServletContext context, HttpServletRequest request,
-    HttpServletResponse response)
+  public DefaultCGIExecutor(ExecutorService executor,
+    ScmConfiguration configuration, ServletContext context,
+    HttpServletRequest request, HttpServletResponse response)
   {
+    this.executor = executor;
     this.configuration = configuration;
     this.context = context;
     this.request = request;
@@ -507,7 +510,7 @@ public class DefaultCGIExecutor extends AbstractCGIExecutor
    */
   private void processErrorStreamAsync(final Process process)
   {
-    new Thread(new Runnable()
+    executor.execute(new Runnable()
     {
       @Override
       public void run()
@@ -528,7 +531,7 @@ public class DefaultCGIExecutor extends AbstractCGIExecutor
           IOUtil.close(errorStream);
         }
       }
-    }).start();
+    });
   }
 
   /**
@@ -539,6 +542,8 @@ public class DefaultCGIExecutor extends AbstractCGIExecutor
    */
   private void processServletInput(Process process)
   {
+    logger.trace("process servlet input");
+
     OutputStream processOS = null;
     ServletInputStream servletIS = null;
 
@@ -637,6 +642,9 @@ public class DefaultCGIExecutor extends AbstractCGIExecutor
 
   //~--- fields ---------------------------------------------------------------
 
+  /** executor to handle error stream processing */
+  private final ExecutorService executor;
+
   /** Field description */
   private ScmConfiguration configuration;
 
diff --git a/scm-webapp/src/main/java/sonia/scm/web/cgi/DefaultCGIExecutorFactory.java b/scm-webapp/src/main/java/sonia/scm/web/cgi/DefaultCGIExecutorFactory.java
index eaa89224e2..8da159f3c0 100644
--- a/scm-webapp/src/main/java/sonia/scm/web/cgi/DefaultCGIExecutorFactory.java
+++ b/scm-webapp/src/main/java/sonia/scm/web/cgi/DefaultCGIExecutorFactory.java
@@ -35,10 +35,15 @@ package sonia.scm.web.cgi;
 
 //~--- non-JDK imports --------------------------------------------------------
 
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
 import sonia.scm.config.ScmConfiguration;
 
 //~--- JDK imports ------------------------------------------------------------
 
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
 import javax.servlet.ServletContext;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -50,6 +55,21 @@ import javax.servlet.http.HttpServletResponse;
 public class DefaultCGIExecutorFactory implements CGIExecutorFactory
 {
 
+  /**
+   * Constructs ...
+   *
+   */
+  public DefaultCGIExecutorFactory()
+  {
+    //J-
+    this.executor = Executors.newCachedThreadPool(
+      new ThreadFactoryBuilder().setNameFormat("cgi-pool-%d").build()
+    );
+    //J+
+  }
+
+  //~--- methods --------------------------------------------------------------
+
   /**
    * Method description
    *
@@ -63,10 +83,15 @@ public class DefaultCGIExecutorFactory implements CGIExecutorFactory
    */
   @Override
   public CGIExecutor createExecutor(ScmConfiguration configuration,
-                                    ServletContext context,
-                                    HttpServletRequest request,
-                                    HttpServletResponse response)
+    ServletContext context, HttpServletRequest request,
+    HttpServletResponse response)
   {
-    return new DefaultCGIExecutor(configuration, context, request, response);
+    return new DefaultCGIExecutor(executor, configuration, context, request,
+      response);
   }
+
+  //~--- fields ---------------------------------------------------------------
+
+  /** Field description */
+  private final ExecutorService executor;
 }

From ee0278b893a95fa1c39d43a5e7a6593ccc3f7cc5 Mon Sep 17 00:00:00 2001
From: Sebastian Sdorra 
Date: Sat, 13 Jun 2015 18:11:22 +0200
Subject: [PATCH 049/171] do not show error message for syntax highliting on
 txt and cs files, see issue #731

---
 .../resources/js/panel/sonia.panel.syntaxhighlighterpanel.js | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/scm-webapp/src/main/webapp/resources/js/panel/sonia.panel.syntaxhighlighterpanel.js b/scm-webapp/src/main/webapp/resources/js/panel/sonia.panel.syntaxhighlighterpanel.js
index 8dcc40b176..686df63cbb 100644
--- a/scm-webapp/src/main/webapp/resources/js/panel/sonia.panel.syntaxhighlighterpanel.js
+++ b/scm-webapp/src/main/webapp/resources/js/panel/sonia.panel.syntaxhighlighterpanel.js
@@ -49,6 +49,7 @@ Sonia.panel.SyntaxHighlighterPanel = Ext.extend(Ext.Panel, {
     fileName: 'shBrushColdFusion.js'
   },{
     name: 'C#',
+    brush: 'csharp',
     aliases: ['cs', 'c-sharp', 'csharp'],
     fileName: 'shBrushCSharp.js'
   },{
@@ -97,6 +98,7 @@ Sonia.panel.SyntaxHighlighterPanel = Ext.extend(Ext.Panel, {
     fileName: 'shBrushPhp.js'
   },{
     name: 'Plain Text',
+    brush: 'plain',
     aliases: ['plain', 'text', 'txt', 'log'],
     fileName: 'shBrushPlain.js'
   },{
@@ -160,6 +162,9 @@ Sonia.panel.SyntaxHighlighterPanel = Ext.extend(Ext.Panel, {
         for ( var j=0;j
Date: Sat, 13 Jun 2015 19:56:16 +0200
Subject: [PATCH 050/171] expose latest changeset id of branch

---
 .../java/sonia/scm/repository/Branch.java     | 35 +++++++++++++++++--
 .../repository/spi/GitBranchesCommand.java    |  2 +-
 .../scm/repository/spi/HgBranchesCommand.java | 13 ++++++-
 3 files changed, 46 insertions(+), 4 deletions(-)

diff --git a/scm-core/src/main/java/sonia/scm/repository/Branch.java b/scm-core/src/main/java/sonia/scm/repository/Branch.java
index 9c16259d91..ac8a064c71 100644
--- a/scm-core/src/main/java/sonia/scm/repository/Branch.java
+++ b/scm-core/src/main/java/sonia/scm/repository/Branch.java
@@ -73,12 +73,28 @@ public final class Branch implements Serializable
    *
    *
    * @param name name of the branch
+   *
+   * @deprecated use {@link Branch#Branch(String, String)} instead
    */
+  @Deprecated
   public Branch(String name)
   {
     this.name = name;
   }
 
+  /**
+   * Constructs a new branch.
+   *
+   *
+   * @param name name of the branch
+   * @param revision latest revision of the branch
+   */
+  public Branch(String name, String revision)
+  {
+    this.name = name;
+    this.revision = revision;
+  }
+
   //~--- methods --------------------------------------------------------------
 
   /**
@@ -104,7 +120,8 @@ public final class Branch implements Serializable
 
     final Branch other = (Branch) obj;
 
-    return Objects.equal(name, other.name);
+    return Objects.equal(name, other.name)
+      && Objects.equal(revision, other.revision);
   }
 
   /**
@@ -116,7 +133,7 @@ public final class Branch implements Serializable
   @Override
   public int hashCode()
   {
-    return Objects.hashCode(name);
+    return Objects.hashCode(name, revision);
   }
 
   /**
@@ -131,6 +148,7 @@ public final class Branch implements Serializable
     //J-
     return Objects.toStringHelper(this)
                   .add("name", name)
+                  .add("revision", revision)
                   .toString();
     //J+
   }
@@ -148,8 +166,21 @@ public final class Branch implements Serializable
     return name;
   }
 
+  /**
+   * Returns the latest revision of the branch.
+   *
+   * @return latest revision of branch
+   */
+  public String getRevision()
+  {
+    return revision;
+  }
+
   //~--- fields ---------------------------------------------------------------
 
   /** name of the branch */
   private String name;
+
+  /** Field description */
+  private String revision;
 }
diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBranchesCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBranchesCommand.java
index 6ffa1f28de..df32c2b5eb 100644
--- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBranchesCommand.java
+++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBranchesCommand.java
@@ -105,7 +105,7 @@ public class GitBranchesCommand extends AbstractGitCommand
 
           if (branchName != null)
           {
-            branch = new Branch(branchName);
+            branch = new Branch(branchName, GitUtil.getId(ref.getObjectId()));
           }
 
           return branch;
diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgBranchesCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgBranchesCommand.java
index d32560baac..64d544c5ed 100644
--- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgBranchesCommand.java
+++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgBranchesCommand.java
@@ -30,10 +30,13 @@
  */
 
 
+
 package sonia.scm.repository.spi;
 
 //~--- non-JDK imports --------------------------------------------------------
 
+import com.aragost.javahg.Changeset;
+
 import com.google.common.base.Function;
 import com.google.common.collect.Lists;
 
@@ -92,7 +95,15 @@ public class HgBranchesCommand extends AbstractCommand
       @Override
       public Branch apply(com.aragost.javahg.commands.Branch hgBranch)
       {
-        return new Branch(hgBranch.getName());
+        String node = null;
+        Changeset changeset = hgBranch.getBranchTip();
+
+        if (changeset != null)
+        {
+          node = changeset.getNode();
+        }
+
+        return new Branch(hgBranch.getName(), node);
       }
     });
 

From 3e343386647976292cb84a3bfa4aad311b3c5d1b Mon Sep 17 00:00:00 2001
From: Sebastian Sdorra 
Date: Sat, 13 Jun 2015 20:00:35 +0200
Subject: [PATCH 051/171] added to branch switcher to repository browser, see
 issue #355

---
 .../sonia.repository.repositorybrowser.js     | 48 +++++++++++++++++--
 1 file changed, 44 insertions(+), 4 deletions(-)

diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.repositorybrowser.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.repositorybrowser.js
index 25d40bbc9e..74771944a9 100644
--- a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.repositorybrowser.js
+++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.repositorybrowser.js
@@ -154,6 +154,40 @@ Sonia.repository.RepositoryBrowser = Ext.extend(Ext.grid.GridPanel, {
     var items = [this.repository.name];
     
     var type = Sonia.repository.getTypeByName( this.repository.type );
+    var branches = false;
+    if (type && type.supportedCommands && type.supportedCommands.indexOf('BRANCHES') >= 0){
+      
+      branches = true;
+      
+      var branchStore = new Sonia.rest.JsonStore({
+        proxy: new Ext.data.HttpProxy({
+          url: restUrl + 'repositories/' + this.repository.id + '/branches.json',
+          method: 'GET',
+          disableCaching: false
+        }),
+        root: 'branch',
+        idProperty: 'name',
+        fields: [ 'name', 'revision' ]
+      });
+
+      items.push('->','Branches:', ' ',{
+        xtype: 'combo',
+        valueField: 'revision',
+        displayField: 'name',
+        typeAhead: false,
+        editable: false,
+        triggerAction: 'all',
+        store: branchStore,
+        listeners: {
+          select: {
+            fn: this.selectRev,
+            scope: this
+          }
+        }
+      });
+      
+    }
+   
     if ( type && type.supportedCommands && type.supportedCommands.indexOf('TAGS') >= 0){
     
       var tagStore = new Sonia.rest.JsonStore({
@@ -167,7 +201,13 @@ Sonia.repository.RepositoryBrowser = Ext.extend(Ext.grid.GridPanel, {
         fields: [ 'name', 'revision' ]
       });
 
-      items.push('->','Tags:', ' ',{
+      if (branches){
+        items.push(' ');
+      } else {
+        items.push('->');
+      }
+
+      items.push('Tags:', ' ',{
         xtype: 'combo',
         valueField: 'revision',
         displayField: 'name',
@@ -177,7 +217,7 @@ Sonia.repository.RepositoryBrowser = Ext.extend(Ext.grid.GridPanel, {
         store: tagStore,
         listeners: {
           select: {
-            fn: this.selectTag,
+            fn: this.selectRev,
             scope: this
           }
         }
@@ -193,10 +233,10 @@ Sonia.repository.RepositoryBrowser = Ext.extend(Ext.grid.GridPanel, {
     return tbar;
   },
   
-  selectTag: function(combo, rec){
+  selectRev: function(combo, rec){
     var tag = rec.get('name');
     if (debug){
-      console.debug('select tag ' + tag);
+      console.debug('select rev ' + tag);
     }
     this.revision = rec.get('revision');
     this.getStore().load({

From 9475971a2a861781edc8b42121d13ec45074935d Mon Sep 17 00:00:00 2001
From: Sebastian Sdorra 
Date: Sat, 13 Jun 2015 20:02:44 +0200
Subject: [PATCH 052/171] fix rendering of bottom toolbar in repository
 browser, if path is null

---
 .../js/repository/sonia.repository.repositorybrowser.js    | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.repositorybrowser.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.repositorybrowser.js
index 74771944a9..0241b2edbd 100644
--- a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.repositorybrowser.js
+++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.repositorybrowser.js
@@ -414,7 +414,12 @@ Sonia.repository.RepositoryBrowser = Ext.extend(Ext.grid.GridPanel, {
     var bbar = this.getBottomToolbar();
     bbar.removeAll();
     
-    var parts = path.split('/');
+    var parts;
+    if (path){
+      parts = path.split('/');
+    } else {
+      parts = [];
+    }
     var currentPath = '';
     var items = [this.createFolderButton(currentPath, '')];
           

From 69d95de054809e0a59d5cef5deec6d8c363cf296 Mon Sep 17 00:00:00 2001
From: Sebastian Sdorra 
Date: Sat, 13 Jun 2015 20:43:49 +0200
Subject: [PATCH 053/171] link modification to files on commit panel, see issue
 #356

---
 .../src/main/webapp/resources/css/style.css   |  2 +-
 .../sonia.repository.commitpanel.js           | 58 +++++++++++++++++--
 2 files changed, 53 insertions(+), 7 deletions(-)

diff --git a/scm-webapp/src/main/webapp/resources/css/style.css b/scm-webapp/src/main/webapp/resources/css/style.css
index 355e8afcbe..deb968f35e 100644
--- a/scm-webapp/src/main/webapp/resources/css/style.css
+++ b/scm-webapp/src/main/webapp/resources/css/style.css
@@ -153,7 +153,7 @@ a.scm-link:hover {
 }
 
 .scm-commit {
-  margin-bottom: 50px;
+  margin-bottom: 65px;
 }
 
 .scm-commit h1 {
diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.commitpanel.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.commitpanel.js
index b711a477d3..52761771ad 100644
--- a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.commitpanel.js
+++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.commitpanel.js
@@ -73,10 +73,16 @@ Sonia.repository.CommitPanel = Ext.extend(Ext.Panel, {
                    ',
   
   templateModifications: '
    \n\ -
  • {.}
  • \n\ -
  • {.}
  • \n\ -
  • {.}
  • \n\ -
', + \n\ +
  • {.}
  • \n\ +
    \n\ + \n\ +
  • {.}
  • \n\ +
    \n\ + \n\ +
  • {.}
  • \n\ +
    \n\ + ', // header panel commitPanel: null, @@ -84,7 +90,15 @@ Sonia.repository.CommitPanel = Ext.extend(Ext.Panel, { initComponent: function(){ this.commitPanel = new Ext.Panel({ - tpl: new Ext.XTemplate(this.templateCommit + this.templateModifications) + tpl: new Ext.XTemplate(this.templateCommit + this.templateModifications), + listeners: { + render: { + fn: function(panel){ + panel.body.on('click', this.onClick, this); + }, + scope: this + } + } }); this.diffPanel = new Sonia.panel.SyntaxHighlighterPanel({ @@ -132,7 +146,39 @@ Sonia.repository.CommitPanel = Ext.extend(Ext.Panel, { ); } }); - } + }, + + onClick: function(e){ + var el = e.getTarget('a.scm-link'); + if (el){ + var path = el.rel; + if (path){ + this.openFile(path); + } + } + }, + + openFile: function(path){ + if ( debug ){ + console.debug( 'open file: ' + path ); + } + + var id = Sonia.repository.createContentId( + this.repository, + path, + this.revision + ); + + main.addTab({ + id: id, + path: path, + revision: this.revision, + repository: this.repository, + xtype: 'contentPanel', + closable: true, + autoScroll: true + }); + }, }); From c8631511623d95551aa4295d88ef625c5d4cee0a Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sat, 13 Jun 2015 21:33:22 +0200 Subject: [PATCH 054/171] added util method to open files --- .../sonia.repository.commitpanel.js | 22 ++---------------- .../js/repository/sonia.repository.js | 23 +++++++++++++++++++ .../sonia.repository.repositorybrowser.js | 20 +--------------- 3 files changed, 26 insertions(+), 39 deletions(-) diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.commitpanel.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.commitpanel.js index 52761771ad..7b72c4fca3 100644 --- a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.commitpanel.js +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.commitpanel.js @@ -159,26 +159,8 @@ Sonia.repository.CommitPanel = Ext.extend(Ext.Panel, { }, openFile: function(path){ - if ( debug ){ - console.debug( 'open file: ' + path ); - } - - var id = Sonia.repository.createContentId( - this.repository, - path, - this.revision - ); - - main.addTab({ - id: id, - path: path, - revision: this.revision, - repository: this.repository, - xtype: 'contentPanel', - closable: true, - autoScroll: true - }); - }, + Sonia.repository.openFile(this.repository, path, this.revision); + } }); diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.js index 3d291994d4..237b0df38f 100644 --- a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.js +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.js @@ -189,4 +189,27 @@ Sonia.repository.get = function(id, callback){ } }); } +}; + +/** open file */ +Sonia.repository.openFile = function(repository, path, revision){ + if ( debug ){ + console.debug( 'open file: ' + path ); + } + + var id = Sonia.repository.createContentId( + repository, + path, + revision + ); + + main.addTab({ + id: id, + path: path, + revision: revision, + repository: repository, + xtype: 'contentPanel', + closable: true, + autoScroll: true + }); }; \ No newline at end of file diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.repositorybrowser.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.repositorybrowser.js index 0241b2edbd..12464868da 100644 --- a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.repositorybrowser.js +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.repositorybrowser.js @@ -339,25 +339,7 @@ Sonia.repository.RepositoryBrowser = Ext.extend(Ext.grid.GridPanel, { }, openFile: function(path){ - if ( debug ){ - console.debug( 'open file: ' + path ); - } - - var id = Sonia.repository.createContentId( - this.repository, - path, - this.revision - ); - - main.addTab({ - id: id, - path: path, - revision: this.revision, - repository: this.repository, - xtype: 'contentPanel', - closable: true, - autoScroll: true - }); + Sonia.repository.openFile(this.repository, path, this.revision); }, changeDirectory: function(path){ From 407033940c728de32174058cd8884ccdb64f206f Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sat, 13 Jun 2015 21:58:08 +0200 Subject: [PATCH 055/171] update slf4j to version 1.7.12 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index faab186cc5..57e36351b6 100644 --- a/pom.xml +++ b/pom.xml @@ -416,7 +416,7 @@ 4.12 - 1.7.10 + 1.7.12 1.1.2 2.5 3.0 From 54616e381253caf4e330d2dc4ca33bde8d035c44 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sat, 13 Jun 2015 21:58:40 +0200 Subject: [PATCH 056/171] update logback to version 1.1.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 57e36351b6..0180419611 100644 --- a/pom.xml +++ b/pom.xml @@ -417,7 +417,7 @@ 1.7.12 - 1.1.2 + 1.1.3 2.5 3.0 1.19 From 20f858d16a8a0a6940fe54671f3cb68f5a37b483 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 14 Jun 2015 12:02:08 +0200 Subject: [PATCH 057/171] update jgit to version 3.7.1.201504261725-r-scm1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0180419611..7a56cfcf1e 100644 --- a/pom.xml +++ b/pom.xml @@ -429,7 +429,7 @@ 1.2.3 - 3.5.3.201412180710-r-scm1 + 3.7.1.201504261725-r-scm1 1.8.5-scm2 From 976b888e3af297c72b0ea65265c7bd9c19a8b412 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 14 Jun 2015 12:21:42 +0200 Subject: [PATCH 058/171] update enunciate to version 1.30.1 --- scm-webapp/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index 41802c6ba4..73b8c8d5f4 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -511,7 +511,7 @@ target/scm-it default 2.28.0 - 1.28 + 1.30.1 1.13.1 1.0 3.0.5 From 41df67df8b06847810538b5bb7b5054506b7d52f Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 14 Jun 2015 18:27:29 +0200 Subject: [PATCH 059/171] clear tag/branch selection, if the other one was selected --- .../sonia.repository.repositorybrowser.js | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.repositorybrowser.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.repositorybrowser.js index 12464868da..7ec444222c 100644 --- a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.repositorybrowser.js +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.repositorybrowser.js @@ -171,6 +171,7 @@ Sonia.repository.RepositoryBrowser = Ext.extend(Ext.grid.GridPanel, { }); items.push('->','Branches:', ' ',{ + id: 'branchComboBox', xtype: 'combo', valueField: 'revision', displayField: 'name', @@ -180,7 +181,7 @@ Sonia.repository.RepositoryBrowser = Ext.extend(Ext.grid.GridPanel, { store: branchStore, listeners: { select: { - fn: this.selectRev, + fn: this.selectBranch, scope: this } } @@ -208,6 +209,7 @@ Sonia.repository.RepositoryBrowser = Ext.extend(Ext.grid.GridPanel, { } items.push('Tags:', ' ',{ + id: 'tagComboBox', xtype: 'combo', valueField: 'revision', displayField: 'name', @@ -217,7 +219,7 @@ Sonia.repository.RepositoryBrowser = Ext.extend(Ext.grid.GridPanel, { store: tagStore, listeners: { select: { - fn: this.selectRev, + fn: this.selectTag, scope: this } } @@ -233,7 +235,15 @@ Sonia.repository.RepositoryBrowser = Ext.extend(Ext.grid.GridPanel, { return tbar; }, - selectRev: function(combo, rec){ + selectBranch: function(combo, rec){ + this.selectRev(rec, Ext.getCmp('tagComboBox')); + }, + + selectTag: function(combo, rec){ + this.selectRev(rec, Ext.getCmp('branchComboBox')); + }, + + selectRev: function(rec, comboToClear){ var tag = rec.get('name'); if (debug){ console.debug('select rev ' + tag); @@ -248,6 +258,11 @@ Sonia.repository.RepositoryBrowser = Ext.extend(Ext.grid.GridPanel, { this.reRenderBottomBar(this.path); this.updateHistory(); + + // clear other combobox + if (comboToClear){ + comboToClear.clearValue(); + } }, loadStore: function(store, records, extra){ From 6c96a57cf84f1f50b965039e127f17ff01d88454 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 14 Jun 2015 18:59:38 +0200 Subject: [PATCH 060/171] fix a bug in git submodule detection --- .../scm/repository/spi/GitBrowseCommand.java | 143 +++++++----------- 1 file changed, 57 insertions(+), 86 deletions(-) diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBrowseCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBrowseCommand.java index f6e0c9a778..a2962f43c4 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBrowseCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBrowseCommand.java @@ -45,6 +45,7 @@ import org.eclipse.jgit.lib.ObjectLoader; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevTree; import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.submodule.SubmoduleWalk; import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.treewalk.filter.AndTreeFilter; import org.eclipse.jgit.treewalk.filter.PathFilter; @@ -168,70 +169,6 @@ public class GitBrowseCommand extends AbstractGitCommand /** * Method description * - * - * @param files - * @param repo - * @param revId - * @param path - * - * @throws IOException - * @throws RepositoryException - */ - private void appendSubModules(List files, - org.eclipse.jgit.lib.Repository repo, ObjectId revId, String path) - throws IOException, RepositoryException - { - path = Util.nonNull(path); - - Map subRepositories = subrepositoryCache.get(revId); - - if (subRepositories == null) - { - subRepositories = getSubRepositories(repo, revId); - subrepositoryCache.put(revId, subRepositories); - } - - if (subRepositories != null) - { - for (Entry e : subRepositories.entrySet()) - { - String p = e.getKey(); - - if (p.startsWith(path)) - { - p = p.substring(path.length()); - - if (p.startsWith("/")) - { - p = p.substring(1); - } - - if (p.endsWith("/")) - { - p = p.substring(0, p.length() - 1); - } - - if (!p.contains("/")) - { - FileObject fo = new FileObject(); - - fo.setDirectory(true); - fo.setPath(path); - fo.setName(p); - fo.setSubRepository(e.getValue()); - files.add(fo); - } - } - } - } - } - - /** - * Method description - * - * - * - * * @param repo * @param request * @param revId @@ -243,9 +180,9 @@ public class GitBrowseCommand extends AbstractGitCommand */ private FileObject createFileObject(org.eclipse.jgit.lib.Repository repo, BrowseCommandRequest request, ObjectId revId, TreeWalk treeWalk) - throws IOException + throws IOException, RepositoryException { - FileObject file = null; + FileObject file; try { @@ -256,26 +193,43 @@ public class GitBrowseCommand extends AbstractGitCommand file.setName(treeWalk.getNameString()); file.setPath(path); - ObjectLoader loader = repo.open(treeWalk.getObjectId(0)); + SubRepository sub = null; - file.setDirectory(loader.getType() == Constants.OBJ_TREE); - file.setLength(loader.getSize()); - - // don't show message and date for directories to improve performance - if (!file.isDirectory() &&!request.isDisableLastCommit()) + if (!request.isDisableSubRepositoryDetection()) { - logger.trace("fetch last commit for {} at {}", path, revId.getName()); + sub = getSubRepository(repo, revId, path); + } - RevCommit commit = getLatestCommit(repo, revId, path); + if (sub != null) + { + logger.trace("{} seems to be a sub repository", path); + file.setDirectory(true); + file.setSubRepository(sub); + } + else + { + ObjectLoader loader = repo.open(treeWalk.getObjectId(0)); - if (commit != null) + file.setDirectory(loader.getType() == Constants.OBJ_TREE); + file.setLength(loader.getSize()); + + // don't show message and date for directories to improve performance + if (!file.isDirectory() &&!request.isDisableLastCommit()) { - file.setLastModified(GitUtil.getCommitTime(commit)); - file.setDescription(commit.getShortMessage()); - } - else if (logger.isWarnEnabled()) - { - logger.warn("could not find latest commit for {} on {}", path, revId); + logger.trace("fetch last commit for {} at {}", path, revId.getName()); + + RevCommit commit = getLatestCommit(repo, revId, path); + + if (commit != null) + { + file.setLastModified(GitUtil.getCommitTime(commit)); + file.setDescription(commit.getShortMessage()); + } + else if (logger.isWarnEnabled()) + { + logger.warn("could not find latest commit for {} on {}", path, + revId); + } } } } @@ -385,11 +339,6 @@ public class GitBrowseCommand extends AbstractGitCommand String path = request.getPath(); - if (!request.isDisableSubRepositoryDetection()) - { - appendSubModules(files, repo, revId, path); - } - if (Util.isEmpty(path)) { while (treeWalk.next()) @@ -491,6 +440,28 @@ public class GitBrowseCommand extends AbstractGitCommand return subRepositories; } + private SubRepository getSubRepository(org.eclipse.jgit.lib.Repository repo, + ObjectId revId, String path) + throws IOException, RepositoryException + { + Map subRepositories = subrepositoryCache.get(revId); + + if (subRepositories == null) + { + subRepositories = getSubRepositories(repo, revId); + subrepositoryCache.put(revId, subRepositories); + } + + SubRepository sub = null; + + if (subRepositories != null) + { + sub = subRepositories.get(path); + } + + return sub; + } + //~--- fields --------------------------------------------------------------- /** Field description */ From e9f4d26196a2bf639c80c67be1c33acbcb5a88b7 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 14 Jun 2015 19:51:50 +0200 Subject: [PATCH 061/171] update nativepkg-maven-plugin to version 1.1.2 --- scm-server/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scm-server/pom.xml b/scm-server/pom.xml index 016136c927..d94f8d664a 100644 --- a/scm-server/pom.xml +++ b/scm-server/pom.xml @@ -178,7 +178,7 @@ com.github.sdorra nativepkg-maven-plugin - 1.1.0 + 1.1.2 From b24df8d9f1f71d951d3d3907e4955c3fb54652b5 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 14 Jun 2015 19:53:33 +0200 Subject: [PATCH 062/171] [maven-release-plugin] prepare release 1.46 --- maven/pom.xml | 4 ++-- maven/scm-maven-plugin/pom.xml | 4 ++-- maven/scm-plugin-archetype/pom.xml | 4 ++-- pom.xml | 4 ++-- scm-clients/pom.xml | 6 +++--- scm-clients/scm-cli-client/pom.xml | 6 +++--- scm-clients/scm-client-api/pom.xml | 4 ++-- scm-clients/scm-client-impl/pom.xml | 8 ++++---- scm-core/pom.xml | 4 ++-- scm-dao-orientdb/pom.xml | 8 ++++---- scm-dao-xml/pom.xml | 8 ++++---- scm-plugin-backend/pom.xml | 6 +++--- scm-plugins/pom.xml | 8 ++++---- scm-plugins/scm-git-plugin/pom.xml | 6 +++--- scm-plugins/scm-hg-plugin/pom.xml | 6 +++--- scm-plugins/scm-svn-plugin/pom.xml | 6 +++--- scm-samples/pom.xml | 4 ++-- scm-samples/scm-sample-auth/pom.xml | 6 +++--- scm-samples/scm-sample-hello/pom.xml | 6 +++--- scm-server/pom.xml | 4 ++-- scm-test/pom.xml | 6 +++--- scm-webapp/pom.xml | 24 ++++++++++++------------ support/pom.xml | 4 ++-- support/scm-support-btrace/pom.xml | 6 +++--- 24 files changed, 76 insertions(+), 76 deletions(-) diff --git a/maven/pom.xml b/maven/pom.xml index ad005a69fd..b330106ddf 100644 --- a/maven/pom.xml +++ b/maven/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.46-SNAPSHOT + 1.46 sonia.scm.maven scm-maven-plugins pom - 1.46-SNAPSHOT + 1.46 scm-maven-plugins diff --git a/maven/scm-maven-plugin/pom.xml b/maven/scm-maven-plugin/pom.xml index f746988fc2..6678a8c0d5 100644 --- a/maven/scm-maven-plugin/pom.xml +++ b/maven/scm-maven-plugin/pom.xml @@ -6,12 +6,12 @@ scm-maven-plugins sonia.scm.maven - 1.46-SNAPSHOT + 1.46 sonia.scm.maven scm-maven-plugin - 1.46-SNAPSHOT + 1.46 maven-plugin scm-maven-plugin diff --git a/maven/scm-plugin-archetype/pom.xml b/maven/scm-plugin-archetype/pom.xml index 60e63aaafb..95f3d8c775 100644 --- a/maven/scm-plugin-archetype/pom.xml +++ b/maven/scm-plugin-archetype/pom.xml @@ -6,12 +6,12 @@ scm-maven-plugins sonia.scm.maven - 1.46-SNAPSHOT + 1.46 sonia.scm.maven scm-plugin-archetype - 1.46-SNAPSHOT + 1.46 scm-plugin-archetype diff --git a/pom.xml b/pom.xml index 7a56cfcf1e..5adac7da53 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ sonia.scm scm pom - 1.46-SNAPSHOT + 1.46 The easiest way to share your Git, Mercurial and Subversion repositories over http. @@ -36,7 +36,7 @@ scm:hg:http://bitbucket.org/sdorra/scm-manager scm:hg:https://bitbucket.org/sdorra/scm-manager http://bitbucket.org/sdorra/scm-manager - HEAD + 1.46 diff --git a/scm-clients/pom.xml b/scm-clients/pom.xml index 6af3e38593..34e9546cdb 100644 --- a/scm-clients/pom.xml +++ b/scm-clients/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.46-SNAPSHOT + 1.46 sonia.scm.clients scm-clients pom - 1.46-SNAPSHOT + 1.46 scm-clients @@ -32,7 +32,7 @@ scm-core sonia.scm jar - 1.46-SNAPSHOT + 1.46 shiro-core diff --git a/scm-clients/scm-cli-client/pom.xml b/scm-clients/scm-cli-client/pom.xml index 803e4e77d1..6ad5af7ac8 100644 --- a/scm-clients/scm-cli-client/pom.xml +++ b/scm-clients/scm-cli-client/pom.xml @@ -6,12 +6,12 @@ scm-clients sonia.scm.clients - 1.46-SNAPSHOT + 1.46 sonia.scm.clients scm-cli-client - 1.46-SNAPSHOT + 1.46 scm-cli-client @@ -34,7 +34,7 @@ sonia.scm.clients scm-client-impl - 1.46-SNAPSHOT + 1.46 diff --git a/scm-clients/scm-client-api/pom.xml b/scm-clients/scm-client-api/pom.xml index a629295853..983e8e58ab 100644 --- a/scm-clients/scm-client-api/pom.xml +++ b/scm-clients/scm-client-api/pom.xml @@ -6,13 +6,13 @@ sonia.scm.clients scm-clients - 1.46-SNAPSHOT + 1.46 sonia.scm.clients scm-client-api jar - 1.46-SNAPSHOT + 1.46 scm-client-api diff --git a/scm-clients/scm-client-impl/pom.xml b/scm-clients/scm-client-impl/pom.xml index 5c2d7d84ef..95a231f78a 100644 --- a/scm-clients/scm-client-impl/pom.xml +++ b/scm-clients/scm-client-impl/pom.xml @@ -6,13 +6,13 @@ sonia.scm.clients scm-clients - 1.46-SNAPSHOT + 1.46 sonia.scm.clients scm-client-impl jar - 1.46-SNAPSHOT + 1.46 scm-client-impl @@ -36,7 +36,7 @@ sonia.scm.clients scm-client-api - 1.46-SNAPSHOT + 1.46 @@ -70,7 +70,7 @@ sonia.scm scm-test - 1.46-SNAPSHOT + 1.46 test diff --git a/scm-core/pom.xml b/scm-core/pom.xml index fe25a55793..b62cc70ec6 100644 --- a/scm-core/pom.xml +++ b/scm-core/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.46-SNAPSHOT + 1.46 sonia.scm scm-core - 1.46-SNAPSHOT + 1.46 scm-core diff --git a/scm-dao-orientdb/pom.xml b/scm-dao-orientdb/pom.xml index 5fe695d149..c2c677bf73 100644 --- a/scm-dao-orientdb/pom.xml +++ b/scm-dao-orientdb/pom.xml @@ -6,12 +6,12 @@ sonia.scm scm - 1.46-SNAPSHOT + 1.46 sonia.scm scm-dao-orientdb - 1.46-SNAPSHOT + 1.46 scm-dao-orientdb @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.46-SNAPSHOT + 1.46 @@ -52,7 +52,7 @@ sonia.scm scm-test - 1.46-SNAPSHOT + 1.46 test diff --git a/scm-dao-xml/pom.xml b/scm-dao-xml/pom.xml index e4d6a3f619..1268dec4dd 100644 --- a/scm-dao-xml/pom.xml +++ b/scm-dao-xml/pom.xml @@ -6,12 +6,12 @@ sonia.scm scm - 1.46-SNAPSHOT + 1.46 sonia.scm scm-dao-xml - 1.46-SNAPSHOT + 1.46 scm-dao-xml @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.46-SNAPSHOT + 1.46 @@ -34,7 +34,7 @@ sonia.scm scm-test - 1.46-SNAPSHOT + 1.46 test diff --git a/scm-plugin-backend/pom.xml b/scm-plugin-backend/pom.xml index a8d4bf36c1..ab25d2357a 100644 --- a/scm-plugin-backend/pom.xml +++ b/scm-plugin-backend/pom.xml @@ -6,13 +6,13 @@ scm sonia.scm - 1.46-SNAPSHOT + 1.46 sonia.scm scm-plugin-backend war - 1.46-SNAPSHOT + 1.46 ${project.artifactId} @@ -62,7 +62,7 @@ sonia.scm scm-core - 1.46-SNAPSHOT + 1.46 diff --git a/scm-plugins/pom.xml b/scm-plugins/pom.xml index 6ca6d79276..e7a63128d5 100644 --- a/scm-plugins/pom.xml +++ b/scm-plugins/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.46-SNAPSHOT + 1.46 sonia.scm.plugins scm-plugins pom - 1.46-SNAPSHOT + 1.46 scm-plugins @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.46-SNAPSHOT + 1.46 @@ -59,7 +59,7 @@ sonia.scm.maven scm-maven-plugin - 1.46-SNAPSHOT + 1.46 process-resources diff --git a/scm-plugins/scm-git-plugin/pom.xml b/scm-plugins/scm-git-plugin/pom.xml index 74258ba06d..f5ba7cb6c5 100644 --- a/scm-plugins/scm-git-plugin/pom.xml +++ b/scm-plugins/scm-git-plugin/pom.xml @@ -6,12 +6,12 @@ scm-plugins sonia.scm.plugins - 1.46-SNAPSHOT + 1.46 sonia.scm.plugins scm-git-plugin - 1.46-SNAPSHOT + 1.46 scm-git-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Git @@ -48,7 +48,7 @@ sonia.scm scm-test - 1.46-SNAPSHOT + 1.46 test diff --git a/scm-plugins/scm-hg-plugin/pom.xml b/scm-plugins/scm-hg-plugin/pom.xml index 2d03707d2f..61054a7216 100644 --- a/scm-plugins/scm-hg-plugin/pom.xml +++ b/scm-plugins/scm-hg-plugin/pom.xml @@ -6,12 +6,12 @@ sonia.scm.plugins scm-plugins - 1.46-SNAPSHOT + 1.46 sonia.scm.plugins scm-hg-plugin - 1.46-SNAPSHOT + 1.46 scm-hg-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Mercurial @@ -42,7 +42,7 @@ sonia.scm scm-test - 1.46-SNAPSHOT + 1.46 test diff --git a/scm-plugins/scm-svn-plugin/pom.xml b/scm-plugins/scm-svn-plugin/pom.xml index 3b6f13a6ef..4bfb15a9ac 100644 --- a/scm-plugins/scm-svn-plugin/pom.xml +++ b/scm-plugins/scm-svn-plugin/pom.xml @@ -6,12 +6,12 @@ scm-plugins sonia.scm.plugins - 1.46-SNAPSHOT + 1.46 sonia.scm.plugins scm-svn-plugin - 1.46-SNAPSHOT + 1.46 scm-svn-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Subversion @@ -48,7 +48,7 @@ sonia.scm scm-test - 1.46-SNAPSHOT + 1.46 test diff --git a/scm-samples/pom.xml b/scm-samples/pom.xml index 44f7d3568f..e522d4a498 100644 --- a/scm-samples/pom.xml +++ b/scm-samples/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.46-SNAPSHOT + 1.46 sonia.scm.samples scm-samples pom - 1.46-SNAPSHOT + 1.46 scm-samples diff --git a/scm-samples/scm-sample-auth/pom.xml b/scm-samples/scm-sample-auth/pom.xml index 5d60a88954..956eed4468 100644 --- a/scm-samples/scm-sample-auth/pom.xml +++ b/scm-samples/scm-sample-auth/pom.xml @@ -6,12 +6,12 @@ scm-samples sonia.scm.samples - 1.46-SNAPSHOT + 1.46 sonia.scm.sample scm-sample-auth - 1.46-SNAPSHOT + 1.46 scm-sample-auth Sample Authentication Plugin https://bitbucket.org/sdorra/scm-manager @@ -28,7 +28,7 @@ sonia.scm scm-core - 1.46-SNAPSHOT + 1.46 diff --git a/scm-samples/scm-sample-hello/pom.xml b/scm-samples/scm-sample-hello/pom.xml index d219796157..dc9d45baa0 100644 --- a/scm-samples/scm-sample-hello/pom.xml +++ b/scm-samples/scm-sample-hello/pom.xml @@ -6,12 +6,12 @@ scm-samples sonia.scm.samples - 1.46-SNAPSHOT + 1.46 sonia.scm.sample scm-sample-hello - 1.46-SNAPSHOT + 1.46 scm-sample-hello A simple hello world plugin https://bitbucket.org/sdorra/scm-manager @@ -28,7 +28,7 @@ sonia.scm scm-core - 1.46-SNAPSHOT + 1.46 diff --git a/scm-server/pom.xml b/scm-server/pom.xml index d94f8d664a..797e3d7d8a 100644 --- a/scm-server/pom.xml +++ b/scm-server/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.46-SNAPSHOT + 1.46 sonia.scm scm-server - 1.46-SNAPSHOT + 1.46 scm-server jar diff --git a/scm-test/pom.xml b/scm-test/pom.xml index cf040006de..bb6e6c544e 100644 --- a/scm-test/pom.xml +++ b/scm-test/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.46-SNAPSHOT + 1.46 sonia.scm scm-test - 1.46-SNAPSHOT + 1.46 scm-test @@ -25,7 +25,7 @@ sonia.scm scm-core - 1.46-SNAPSHOT + 1.46 diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index 73b8c8d5f4..0080e9c9c1 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.46-SNAPSHOT + 1.46 sonia.scm scm-webapp war - 1.46-SNAPSHOT + 1.46 scm-webapp @@ -38,31 +38,31 @@ sonia.scm scm-core - 1.46-SNAPSHOT + 1.46 sonia.scm scm-dao-xml - 1.46-SNAPSHOT + 1.46 sonia.scm.plugins scm-hg-plugin - 1.46-SNAPSHOT + 1.46 sonia.scm.plugins scm-svn-plugin - 1.46-SNAPSHOT + 1.46 sonia.scm.plugins scm-git-plugin - 1.46-SNAPSHOT + 1.46 @@ -274,7 +274,7 @@ sonia.scm scm-test - 1.46-SNAPSHOT + 1.46 test @@ -287,7 +287,7 @@ sonia.scm.plugins scm-git-plugin - 1.46-SNAPSHOT + 1.46 tests test @@ -295,7 +295,7 @@ sonia.scm.plugins scm-hg-plugin - 1.46-SNAPSHOT + 1.46 tests test @@ -303,7 +303,7 @@ sonia.scm.plugins scm-svn-plugin - 1.46-SNAPSHOT + 1.46 tests test @@ -529,7 +529,7 @@ sonia.scm scm-dao-orientdb - 1.46-SNAPSHOT + 1.46 diff --git a/support/pom.xml b/support/pom.xml index dc889d4a48..d4f354565d 100644 --- a/support/pom.xml +++ b/support/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.46-SNAPSHOT + 1.46 sonia.scm.support scm-support pom - 1.46-SNAPSHOT + 1.46 scm-support diff --git a/support/scm-support-btrace/pom.xml b/support/scm-support-btrace/pom.xml index 05aa1c6735..0021fb102f 100644 --- a/support/scm-support-btrace/pom.xml +++ b/support/scm-support-btrace/pom.xml @@ -4,12 +4,12 @@ sonia.scm.support scm-support - 1.46-SNAPSHOT + 1.46 sonia.scm scm-support-btrace - 1.46-SNAPSHOT + 1.46 jar scm-support-btrace @@ -18,7 +18,7 @@ sonia.scm scm-core - 1.46-SNAPSHOT + 1.46 From c7caf6f201f6cf96d3049a6f6781141a863aa934 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 14 Jun 2015 19:53:33 +0200 Subject: [PATCH 063/171] [maven-release-plugin] copy for tag 1.46 From 9ba2fa8975135b42e812dc2980fd25042eeafa42 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 14 Jun 2015 19:53:33 +0200 Subject: [PATCH 064/171] [maven-release-plugin] prepare for next development iteration --- maven/pom.xml | 4 ++-- maven/scm-maven-plugin/pom.xml | 4 ++-- maven/scm-plugin-archetype/pom.xml | 4 ++-- pom.xml | 4 ++-- scm-clients/pom.xml | 6 +++--- scm-clients/scm-cli-client/pom.xml | 6 +++--- scm-clients/scm-client-api/pom.xml | 4 ++-- scm-clients/scm-client-impl/pom.xml | 8 ++++---- scm-core/pom.xml | 4 ++-- scm-dao-orientdb/pom.xml | 8 ++++---- scm-dao-xml/pom.xml | 8 ++++---- scm-plugin-backend/pom.xml | 6 +++--- scm-plugins/pom.xml | 8 ++++---- scm-plugins/scm-git-plugin/pom.xml | 6 +++--- scm-plugins/scm-hg-plugin/pom.xml | 6 +++--- scm-plugins/scm-svn-plugin/pom.xml | 6 +++--- scm-samples/pom.xml | 4 ++-- scm-samples/scm-sample-auth/pom.xml | 6 +++--- scm-samples/scm-sample-hello/pom.xml | 6 +++--- scm-server/pom.xml | 4 ++-- scm-test/pom.xml | 6 +++--- scm-webapp/pom.xml | 24 ++++++++++++------------ support/pom.xml | 4 ++-- support/scm-support-btrace/pom.xml | 6 +++--- 24 files changed, 76 insertions(+), 76 deletions(-) diff --git a/maven/pom.xml b/maven/pom.xml index b330106ddf..c2f1fa1e2c 100644 --- a/maven/pom.xml +++ b/maven/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.46 + 1.47-SNAPSHOT sonia.scm.maven scm-maven-plugins pom - 1.46 + 1.47-SNAPSHOT scm-maven-plugins diff --git a/maven/scm-maven-plugin/pom.xml b/maven/scm-maven-plugin/pom.xml index 6678a8c0d5..9c4bb678b3 100644 --- a/maven/scm-maven-plugin/pom.xml +++ b/maven/scm-maven-plugin/pom.xml @@ -6,12 +6,12 @@ scm-maven-plugins sonia.scm.maven - 1.46 + 1.47-SNAPSHOT sonia.scm.maven scm-maven-plugin - 1.46 + 1.47-SNAPSHOT maven-plugin scm-maven-plugin diff --git a/maven/scm-plugin-archetype/pom.xml b/maven/scm-plugin-archetype/pom.xml index 95f3d8c775..e30464a745 100644 --- a/maven/scm-plugin-archetype/pom.xml +++ b/maven/scm-plugin-archetype/pom.xml @@ -6,12 +6,12 @@ scm-maven-plugins sonia.scm.maven - 1.46 + 1.47-SNAPSHOT sonia.scm.maven scm-plugin-archetype - 1.46 + 1.47-SNAPSHOT scm-plugin-archetype diff --git a/pom.xml b/pom.xml index 5adac7da53..93fb88c813 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ sonia.scm scm pom - 1.46 + 1.47-SNAPSHOT The easiest way to share your Git, Mercurial and Subversion repositories over http. @@ -36,7 +36,7 @@ scm:hg:http://bitbucket.org/sdorra/scm-manager scm:hg:https://bitbucket.org/sdorra/scm-manager http://bitbucket.org/sdorra/scm-manager - 1.46 + HEAD diff --git a/scm-clients/pom.xml b/scm-clients/pom.xml index 34e9546cdb..6ebe7b4ff8 100644 --- a/scm-clients/pom.xml +++ b/scm-clients/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.46 + 1.47-SNAPSHOT sonia.scm.clients scm-clients pom - 1.46 + 1.47-SNAPSHOT scm-clients @@ -32,7 +32,7 @@ scm-core sonia.scm jar - 1.46 + 1.47-SNAPSHOT shiro-core diff --git a/scm-clients/scm-cli-client/pom.xml b/scm-clients/scm-cli-client/pom.xml index 6ad5af7ac8..6779d55ed6 100644 --- a/scm-clients/scm-cli-client/pom.xml +++ b/scm-clients/scm-cli-client/pom.xml @@ -6,12 +6,12 @@ scm-clients sonia.scm.clients - 1.46 + 1.47-SNAPSHOT sonia.scm.clients scm-cli-client - 1.46 + 1.47-SNAPSHOT scm-cli-client @@ -34,7 +34,7 @@ sonia.scm.clients scm-client-impl - 1.46 + 1.47-SNAPSHOT diff --git a/scm-clients/scm-client-api/pom.xml b/scm-clients/scm-client-api/pom.xml index 983e8e58ab..4014a3f623 100644 --- a/scm-clients/scm-client-api/pom.xml +++ b/scm-clients/scm-client-api/pom.xml @@ -6,13 +6,13 @@ sonia.scm.clients scm-clients - 1.46 + 1.47-SNAPSHOT sonia.scm.clients scm-client-api jar - 1.46 + 1.47-SNAPSHOT scm-client-api diff --git a/scm-clients/scm-client-impl/pom.xml b/scm-clients/scm-client-impl/pom.xml index 95a231f78a..b3abff916e 100644 --- a/scm-clients/scm-client-impl/pom.xml +++ b/scm-clients/scm-client-impl/pom.xml @@ -6,13 +6,13 @@ sonia.scm.clients scm-clients - 1.46 + 1.47-SNAPSHOT sonia.scm.clients scm-client-impl jar - 1.46 + 1.47-SNAPSHOT scm-client-impl @@ -36,7 +36,7 @@ sonia.scm.clients scm-client-api - 1.46 + 1.47-SNAPSHOT @@ -70,7 +70,7 @@ sonia.scm scm-test - 1.46 + 1.47-SNAPSHOT test diff --git a/scm-core/pom.xml b/scm-core/pom.xml index b62cc70ec6..7e2157d218 100644 --- a/scm-core/pom.xml +++ b/scm-core/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.46 + 1.47-SNAPSHOT sonia.scm scm-core - 1.46 + 1.47-SNAPSHOT scm-core diff --git a/scm-dao-orientdb/pom.xml b/scm-dao-orientdb/pom.xml index c2c677bf73..89c7dfc76c 100644 --- a/scm-dao-orientdb/pom.xml +++ b/scm-dao-orientdb/pom.xml @@ -6,12 +6,12 @@ sonia.scm scm - 1.46 + 1.47-SNAPSHOT sonia.scm scm-dao-orientdb - 1.46 + 1.47-SNAPSHOT scm-dao-orientdb @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.46 + 1.47-SNAPSHOT @@ -52,7 +52,7 @@ sonia.scm scm-test - 1.46 + 1.47-SNAPSHOT test diff --git a/scm-dao-xml/pom.xml b/scm-dao-xml/pom.xml index 1268dec4dd..9a294bc97f 100644 --- a/scm-dao-xml/pom.xml +++ b/scm-dao-xml/pom.xml @@ -6,12 +6,12 @@ sonia.scm scm - 1.46 + 1.47-SNAPSHOT sonia.scm scm-dao-xml - 1.46 + 1.47-SNAPSHOT scm-dao-xml @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.46 + 1.47-SNAPSHOT @@ -34,7 +34,7 @@ sonia.scm scm-test - 1.46 + 1.47-SNAPSHOT test diff --git a/scm-plugin-backend/pom.xml b/scm-plugin-backend/pom.xml index ab25d2357a..735e6e9d73 100644 --- a/scm-plugin-backend/pom.xml +++ b/scm-plugin-backend/pom.xml @@ -6,13 +6,13 @@ scm sonia.scm - 1.46 + 1.47-SNAPSHOT sonia.scm scm-plugin-backend war - 1.46 + 1.47-SNAPSHOT ${project.artifactId} @@ -62,7 +62,7 @@ sonia.scm scm-core - 1.46 + 1.47-SNAPSHOT diff --git a/scm-plugins/pom.xml b/scm-plugins/pom.xml index e7a63128d5..297eac7f9c 100644 --- a/scm-plugins/pom.xml +++ b/scm-plugins/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.46 + 1.47-SNAPSHOT sonia.scm.plugins scm-plugins pom - 1.46 + 1.47-SNAPSHOT scm-plugins @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.46 + 1.47-SNAPSHOT @@ -59,7 +59,7 @@ sonia.scm.maven scm-maven-plugin - 1.46 + 1.47-SNAPSHOT process-resources diff --git a/scm-plugins/scm-git-plugin/pom.xml b/scm-plugins/scm-git-plugin/pom.xml index f5ba7cb6c5..20412bc9a1 100644 --- a/scm-plugins/scm-git-plugin/pom.xml +++ b/scm-plugins/scm-git-plugin/pom.xml @@ -6,12 +6,12 @@ scm-plugins sonia.scm.plugins - 1.46 + 1.47-SNAPSHOT sonia.scm.plugins scm-git-plugin - 1.46 + 1.47-SNAPSHOT scm-git-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Git @@ -48,7 +48,7 @@ sonia.scm scm-test - 1.46 + 1.47-SNAPSHOT test diff --git a/scm-plugins/scm-hg-plugin/pom.xml b/scm-plugins/scm-hg-plugin/pom.xml index 61054a7216..8fb45bc664 100644 --- a/scm-plugins/scm-hg-plugin/pom.xml +++ b/scm-plugins/scm-hg-plugin/pom.xml @@ -6,12 +6,12 @@ sonia.scm.plugins scm-plugins - 1.46 + 1.47-SNAPSHOT sonia.scm.plugins scm-hg-plugin - 1.46 + 1.47-SNAPSHOT scm-hg-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Mercurial @@ -42,7 +42,7 @@ sonia.scm scm-test - 1.46 + 1.47-SNAPSHOT test diff --git a/scm-plugins/scm-svn-plugin/pom.xml b/scm-plugins/scm-svn-plugin/pom.xml index 4bfb15a9ac..e8eb31d80e 100644 --- a/scm-plugins/scm-svn-plugin/pom.xml +++ b/scm-plugins/scm-svn-plugin/pom.xml @@ -6,12 +6,12 @@ scm-plugins sonia.scm.plugins - 1.46 + 1.47-SNAPSHOT sonia.scm.plugins scm-svn-plugin - 1.46 + 1.47-SNAPSHOT scm-svn-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Subversion @@ -48,7 +48,7 @@ sonia.scm scm-test - 1.46 + 1.47-SNAPSHOT test diff --git a/scm-samples/pom.xml b/scm-samples/pom.xml index e522d4a498..0452dc7348 100644 --- a/scm-samples/pom.xml +++ b/scm-samples/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.46 + 1.47-SNAPSHOT sonia.scm.samples scm-samples pom - 1.46 + 1.47-SNAPSHOT scm-samples diff --git a/scm-samples/scm-sample-auth/pom.xml b/scm-samples/scm-sample-auth/pom.xml index 956eed4468..a08a8faa92 100644 --- a/scm-samples/scm-sample-auth/pom.xml +++ b/scm-samples/scm-sample-auth/pom.xml @@ -6,12 +6,12 @@ scm-samples sonia.scm.samples - 1.46 + 1.47-SNAPSHOT sonia.scm.sample scm-sample-auth - 1.46 + 1.47-SNAPSHOT scm-sample-auth Sample Authentication Plugin https://bitbucket.org/sdorra/scm-manager @@ -28,7 +28,7 @@ sonia.scm scm-core - 1.46 + 1.47-SNAPSHOT diff --git a/scm-samples/scm-sample-hello/pom.xml b/scm-samples/scm-sample-hello/pom.xml index dc9d45baa0..b2e3310e7b 100644 --- a/scm-samples/scm-sample-hello/pom.xml +++ b/scm-samples/scm-sample-hello/pom.xml @@ -6,12 +6,12 @@ scm-samples sonia.scm.samples - 1.46 + 1.47-SNAPSHOT sonia.scm.sample scm-sample-hello - 1.46 + 1.47-SNAPSHOT scm-sample-hello A simple hello world plugin https://bitbucket.org/sdorra/scm-manager @@ -28,7 +28,7 @@ sonia.scm scm-core - 1.46 + 1.47-SNAPSHOT diff --git a/scm-server/pom.xml b/scm-server/pom.xml index 797e3d7d8a..2c860afce5 100644 --- a/scm-server/pom.xml +++ b/scm-server/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.46 + 1.47-SNAPSHOT sonia.scm scm-server - 1.46 + 1.47-SNAPSHOT scm-server jar diff --git a/scm-test/pom.xml b/scm-test/pom.xml index bb6e6c544e..cba62852db 100644 --- a/scm-test/pom.xml +++ b/scm-test/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.46 + 1.47-SNAPSHOT sonia.scm scm-test - 1.46 + 1.47-SNAPSHOT scm-test @@ -25,7 +25,7 @@ sonia.scm scm-core - 1.46 + 1.47-SNAPSHOT diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index 0080e9c9c1..4f2e59e4ad 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.46 + 1.47-SNAPSHOT sonia.scm scm-webapp war - 1.46 + 1.47-SNAPSHOT scm-webapp @@ -38,31 +38,31 @@ sonia.scm scm-core - 1.46 + 1.47-SNAPSHOT sonia.scm scm-dao-xml - 1.46 + 1.47-SNAPSHOT sonia.scm.plugins scm-hg-plugin - 1.46 + 1.47-SNAPSHOT sonia.scm.plugins scm-svn-plugin - 1.46 + 1.47-SNAPSHOT sonia.scm.plugins scm-git-plugin - 1.46 + 1.47-SNAPSHOT @@ -274,7 +274,7 @@ sonia.scm scm-test - 1.46 + 1.47-SNAPSHOT test @@ -287,7 +287,7 @@ sonia.scm.plugins scm-git-plugin - 1.46 + 1.47-SNAPSHOT tests test @@ -295,7 +295,7 @@ sonia.scm.plugins scm-hg-plugin - 1.46 + 1.47-SNAPSHOT tests test @@ -303,7 +303,7 @@ sonia.scm.plugins scm-svn-plugin - 1.46 + 1.47-SNAPSHOT tests test @@ -529,7 +529,7 @@ sonia.scm scm-dao-orientdb - 1.46 + 1.47-SNAPSHOT diff --git a/support/pom.xml b/support/pom.xml index d4f354565d..ae6bb2cd11 100644 --- a/support/pom.xml +++ b/support/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.46 + 1.47-SNAPSHOT sonia.scm.support scm-support pom - 1.46 + 1.47-SNAPSHOT scm-support diff --git a/support/scm-support-btrace/pom.xml b/support/scm-support-btrace/pom.xml index 0021fb102f..1f26a49fee 100644 --- a/support/scm-support-btrace/pom.xml +++ b/support/scm-support-btrace/pom.xml @@ -4,12 +4,12 @@ sonia.scm.support scm-support - 1.46 + 1.47-SNAPSHOT sonia.scm scm-support-btrace - 1.46 + 1.47-SNAPSHOT jar scm-support-btrace @@ -18,7 +18,7 @@ sonia.scm scm-core - 1.46 + 1.47-SNAPSHOT From bba9109d05415d5ba7d1b351509310a8441ceaa5 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 9 Jul 2015 21:18:23 +0200 Subject: [PATCH 065/171] getCompleteUrl of HttpUtil should now respect forwarding headers, see issue #748 --- .../main/java/sonia/scm/util/HttpUtil.java | 141 +++++++++++++++++- .../java/sonia/scm/util/HttpUtilTest.java | 130 ++++++++++++++++ 2 files changed, 267 insertions(+), 4 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/util/HttpUtil.java b/scm-core/src/main/java/sonia/scm/util/HttpUtil.java index 68d669d914..aca27eb917 100644 --- a/scm-core/src/main/java/sonia/scm/util/HttpUtil.java +++ b/scm-core/src/main/java/sonia/scm/util/HttpUtil.java @@ -35,7 +35,9 @@ package sonia.scm.util; //~--- non-JDK imports -------------------------------------------------------- +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.CharMatcher; +import com.google.common.base.Objects; import com.google.common.base.Strings; import org.slf4j.Logger; @@ -97,6 +99,24 @@ public final class HttpUtil /** authentication header */ public static final String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate"; + /** + * The original host requested by the client in the Host HTTP request header. + * @since 1.47 + */ + public static final String HEADER_X_FORWARDED_HOST = "X-Forwarded-Host"; + + /** + * The original port requested by the client. + * @since 1.47 + */ + public static final String HEADER_X_FORWARDED_PORT = "X-Forwarded-Port"; + + /** + * The original protocol (http or https) requested by the client. + * @since 1.47 + */ + public static final String HEADER_X_FORWARDED_PROTO = "X-Forwarded-Proto"; + /** * Default http port * @since 1.5 @@ -579,21 +599,31 @@ public final class HttpUtil //~--- get methods ---------------------------------------------------------- /** - * Returns an absolute url with context path. + * Returns an absolute url with context path. The method creates the url from + * forwarding request headers, if they are available. * * * @param request http client request * @param pathSegments * * @return absolute url with context path + * + * @see Issue 748 * @since 1.16 */ public static String getCompleteUrl(HttpServletRequest request, String... pathSegments) { - String baseUrl = - request.getRequestURL().toString().replace(request.getRequestURI(), - Util.EMPTY_STRING).concat(request.getContextPath()); + String baseUrl; + + if (isForwarded(request)) + { + baseUrl = createForwardedBaseUrl(request); + } + else + { + baseUrl = createBaseUrl(request); + } if (Util.isNotEmpty(pathSegments)) { @@ -623,6 +653,24 @@ public final class HttpUtil return append(configuration.getBaseUrl(), path); } + /** + * Method description + * + * + * @param request + * @param header + * @param defaultValue + * + * @return + */ + public static String getHeader(HttpServletRequest request, String header, + String defaultValue) + { + String value = request.getHeader(header); + + return Objects.firstNonNull(value, defaultValue); + } + /** * Returns the port of the url parameter. * @@ -777,6 +825,21 @@ public final class HttpUtil return "chunked".equals(request.getHeader("Transfer-Encoding")); } + /** + * Returns {@code true} if the request is forwarded by a reverse proxy. The + * method uses the X-Forwarded-Host header to identify a forwarded request. + * + * @param request servlet request + * + * @return {@code true} if the request is forwarded + * + * @since 1.47 + */ + public static boolean isForwarded(HttpServletRequest request) + { + return !Strings.isNullOrEmpty(request.getHeader(HEADER_X_FORWARDED_HOST)); + } + /** * Returns true if the http request is send by the scm-manager web interface. * @@ -791,4 +854,74 @@ public final class HttpUtil return SCM_CLIENT_WUI.equalsIgnoreCase( request.getHeader(HEADER_SCM_CLIENT)); } + + //~--- methods -------------------------------------------------------------- + + /** + * Creates base url for request url. + * + * @param request http servlet request + * + * @return base url from request + * + * @since 1.47 + */ + @VisibleForTesting + static String createBaseUrl(HttpServletRequest request) + { + return request.getRequestURL().toString().replace(request.getRequestURI(), + Util.EMPTY_STRING).concat(request.getContextPath()); + } + + /** + * Creates base url from forwarding request headers. + * + * @param request http servlet request + * + * @return base url from forward headers + * + * @since 1.47 + */ + @VisibleForTesting + static String createForwardedBaseUrl(HttpServletRequest request) + { + String proto = getHeader(request, HEADER_X_FORWARDED_PROTO, + request.getScheme()); + String host; + String fhost = getHeader(request, HEADER_X_FORWARDED_HOST, + request.getScheme()); + String port = request.getHeader(HEADER_X_FORWARDED_PORT); + int s = fhost.indexOf(SEPARATOR_PORT); + + if (s > 0) + { + host = fhost.substring(0, s); + + if (Strings.isNullOrEmpty(port)) + { + port = fhost.substring(s + 1); + } + } + else + { + host = fhost; + } + + StringBuilder buffer = new StringBuilder(proto); + + buffer.append(SEPARATOR_SCHEME).append(host).append(SEPARATOR_PORT); + + if (Strings.isNullOrEmpty(port)) + { + buffer.append(String.valueOf(request.getServerPort())); + } + else + { + buffer.append(port); + } + + buffer.append(request.getContextPath()); + + return buffer.toString(); + } } diff --git a/scm-core/src/test/java/sonia/scm/util/HttpUtilTest.java b/scm-core/src/test/java/sonia/scm/util/HttpUtilTest.java index 585bc0e005..b85221f8c8 100644 --- a/scm-core/src/test/java/sonia/scm/util/HttpUtilTest.java +++ b/scm-core/src/test/java/sonia/scm/util/HttpUtilTest.java @@ -168,6 +168,136 @@ public class HttpUtilTest HttpUtil.checkForCRLFInjection("abcka"); } + /** + * Method description + * + */ + @Test + public void testCreateBaseUrl() + { + HttpServletRequest request = mock(HttpServletRequest.class); + String url = "https://www.scm-manager.org/test/as/db"; + + when(request.getRequestURL()).thenReturn(new StringBuffer(url)); + when(request.getRequestURI()).thenReturn("/test/as/db"); + when(request.getContextPath()).thenReturn("/scm"); + assertEquals("https://www.scm-manager.org/scm", + HttpUtil.createBaseUrl(request)); + } + + /** + * Method description + * + */ + @Test + public void testCreateForwardedUrl() + { + HttpServletRequest request = mock(HttpServletRequest.class); + + when(request.getHeader(HttpUtil.HEADER_X_FORWARDED_HOST)).thenReturn( + "www.scm-manager.org"); + when(request.getHeader(HttpUtil.HEADER_X_FORWARDED_PROTO)).thenReturn( + "https"); + when(request.getHeader(HttpUtil.HEADER_X_FORWARDED_PORT)).thenReturn("443"); + when(request.getContextPath()).thenReturn("/scm"); + assertEquals("https://www.scm-manager.org:443/scm", + HttpUtil.createForwardedBaseUrl(request)); + } + + /** + * Method description + * + */ + @Test + public void testCreateForwardedUrlWithPortAndProtoFromRequest() + { + HttpServletRequest request = mock(HttpServletRequest.class); + + when(request.getHeader(HttpUtil.HEADER_X_FORWARDED_HOST)).thenReturn( + "www.scm-manager.org"); + when(request.getScheme()).thenReturn("https"); + when(request.getServerPort()).thenReturn(443); + when(request.getContextPath()).thenReturn("/scm"); + assertEquals("https://www.scm-manager.org:443/scm", + HttpUtil.createForwardedBaseUrl(request)); + } + + /** + * Method description + * + */ + @Test + public void testCreateForwardedUrlWithPortInHost() + { + HttpServletRequest request = mock(HttpServletRequest.class); + + when(request.getHeader(HttpUtil.HEADER_X_FORWARDED_HOST)).thenReturn( + "www.scm-manager.org:443"); + when(request.getHeader(HttpUtil.HEADER_X_FORWARDED_PROTO)).thenReturn( + "https"); + when(request.getContextPath()).thenReturn("/scm"); + assertEquals("https://www.scm-manager.org:443/scm", + HttpUtil.createForwardedBaseUrl(request)); + } + + /** + * Method description + * + */ + @Test + public void testCreateForwardedUrlWithPortInHostAndPortHeader() + { + HttpServletRequest request = mock(HttpServletRequest.class); + + when(request.getHeader(HttpUtil.HEADER_X_FORWARDED_HOST)).thenReturn( + "www.scm-manager.org:80"); + when(request.getHeader(HttpUtil.HEADER_X_FORWARDED_PROTO)).thenReturn( + "https"); + when(request.getHeader(HttpUtil.HEADER_X_FORWARDED_PORT)).thenReturn("443"); + when(request.getContextPath()).thenReturn("/scm"); + assertEquals("https://www.scm-manager.org:443/scm", + HttpUtil.createForwardedBaseUrl(request)); + } + + /** + * Method description + * + */ + @Test + public void testGetCompleteUrl() + { + HttpServletRequest request = mock(HttpServletRequest.class); + String url = "https://www.scm-manager.org/test/as/db"; + + when(request.getRequestURL()).thenReturn(new StringBuffer(url)); + when(request.getRequestURI()).thenReturn("/test/as/db"); + when(request.getScheme()).thenReturn("https"); + when(request.getServerPort()).thenReturn(443); + when(request.getContextPath()).thenReturn("/scm"); + assertEquals("https://www.scm-manager.org/scm", + HttpUtil.getCompleteUrl(request)); + when(request.getHeader(HttpUtil.HEADER_X_FORWARDED_HOST)).thenReturn( + "scm.scm-manager.org"); + assertEquals("https://scm.scm-manager.org:443/scm", + HttpUtil.getCompleteUrl(request)); + } + + /** + * Method description + * + */ + @Test + public void testIsForwarded() + { + HttpServletRequest request = mock(HttpServletRequest.class); + + assertFalse(HttpUtil.isForwarded(request)); + when(request.getHeader(HttpUtil.HEADER_X_FORWARDED_HOST)).thenReturn(""); + assertFalse(HttpUtil.isForwarded(request)); + when(request.getHeader(HttpUtil.HEADER_X_FORWARDED_HOST)).thenReturn("ser"); + assertTrue(HttpUtil.isForwarded(request)); + } + /** * Method description * From cfa9663f8f44612071d3c05f8d78f837add244d4 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 9 Jul 2015 21:29:29 +0200 Subject: [PATCH 066/171] close branch issue-748 From e949d5ae8792972b7d3c699d7e1603b02b3e72a2 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 21 Oct 2015 21:00:04 +0200 Subject: [PATCH 067/171] JsonContentTransformer should not fail on unknown json properties --- .../scm/net/ahc/JsonContentTransformer.java | 28 ++-- .../net/ahc/JsonContentTransformerTest.java | 130 ++++++++++++++---- 2 files changed, 121 insertions(+), 37 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/net/ahc/JsonContentTransformer.java b/scm-webapp/src/main/java/sonia/scm/net/ahc/JsonContentTransformer.java index 29d45f00c5..5e61ed6965 100644 --- a/scm-webapp/src/main/java/sonia/scm/net/ahc/JsonContentTransformer.java +++ b/scm-webapp/src/main/java/sonia/scm/net/ahc/JsonContentTransformer.java @@ -35,7 +35,11 @@ package sonia.scm.net.ahc; import com.google.common.io.ByteSource; +import org.codehaus.jackson.map.AnnotationIntrospector; +import org.codehaus.jackson.map.DeserializationConfig; import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.map.introspect.JacksonAnnotationIntrospector; +import org.codehaus.jackson.xc.JaxbAnnotationIntrospector; import sonia.scm.plugin.ext.Extension; import sonia.scm.util.IOUtil; @@ -47,9 +51,6 @@ import java.io.IOException; import java.io.InputStream; import javax.ws.rs.core.MediaType; -import org.codehaus.jackson.map.AnnotationIntrospector; -import org.codehaus.jackson.map.introspect.JacksonAnnotationIntrospector; -import org.codehaus.jackson.xc.JaxbAnnotationIntrospector; /** * {@link ContentTransformer} for json. The {@link JsonContentTransformer} uses @@ -62,18 +63,26 @@ import org.codehaus.jackson.xc.JaxbAnnotationIntrospector; public class JsonContentTransformer implements ContentTransformer { + /** + * Constructs a new {@link JsonContentTransformer}. + * + */ public JsonContentTransformer() { this.mapper = new ObjectMapper(); + + // allow jackson and jaxb annotations AnnotationIntrospector jackson = new JacksonAnnotationIntrospector(); AnnotationIntrospector jaxb = new JaxbAnnotationIntrospector(); - AnnotationIntrospector pair = new AnnotationIntrospector.Pair(jackson, jaxb); - this.mapper.setAnnotationIntrospector(pair); + + this.mapper.setAnnotationIntrospector(new AnnotationIntrospector.Pair(jackson, jaxb)); + + // do not fail on unknown json properties + this.mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false); } - - - + //~--- methods -------------------------------------------------------------- + /** * {@inheritDoc} */ @@ -138,8 +147,7 @@ public class JsonContentTransformer implements ContentTransformer @Override public boolean isResponsible(Class type, String contentType) { - return MediaType.valueOf(contentType).isCompatible( - MediaType.APPLICATION_JSON_TYPE); + return MediaType.valueOf(contentType).isCompatible(MediaType.APPLICATION_JSON_TYPE); } //~--- fields --------------------------------------------------------------- diff --git a/scm-webapp/src/test/java/sonia/scm/net/ahc/JsonContentTransformerTest.java b/scm-webapp/src/test/java/sonia/scm/net/ahc/JsonContentTransformerTest.java index 1828c5ccf2..1a11390993 100644 --- a/scm-webapp/src/test/java/sonia/scm/net/ahc/JsonContentTransformerTest.java +++ b/scm-webapp/src/test/java/sonia/scm/net/ahc/JsonContentTransformerTest.java @@ -1,10 +1,10 @@ /** * 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, @@ -13,7 +13,7 @@ * 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 @@ -24,31 +24,57 @@ * 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.net.ahc; +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Charsets; import com.google.common.io.ByteSource; +import com.google.common.io.CharSource; + +import org.junit.Test; + +import static org.junit.Assert.*; + +import static org.mockito.Mockito.*; + +//~--- JDK imports ------------------------------------------------------------ + import java.io.IOException; + import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlRootElement; -import org.junit.Test; -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; /** * * @author Sebastian Sdorra */ -public class JsonContentTransformerTest { +public class JsonContentTransformerTest +{ + /** + * Method description + * + */ + @Test + public void testDoNotFailOnUnknownProperties() { + ByteSource bs = ByteSource.wrap("{\"value\": \"test\", \"other\": \"test2\"}".getBytes(Charsets.UTF_8)); + TestObject obj = transformer.unmarshall(TestObject.class, bs); + assertEquals("test", obj.value); + } - private final JsonContentTransformer transformer = new JsonContentTransformer(); - + /** + * Method description + * + */ @Test public void testIsResponsible() { @@ -56,37 +82,87 @@ public class JsonContentTransformerTest { assertTrue(transformer.isResponsible(String.class, "application/json;charset=UTF-8")); assertFalse(transformer.isResponsible(String.class, "text/plain")); } - + + /** + * Method description + * + * + * @throws IOException + */ @Test - public void testMarshallAndUnmarshall() throws IOException{ + public void testMarshallAndUnmarshall() throws IOException + { ByteSource bs = transformer.marshall(new TestObject("test")); TestObject to = transformer.unmarshall(TestObject.class, bs); + assertEquals("test", to.value); } - + + /** + * Method description + * + * + * @throws IOException + */ @Test(expected = ContentTransformerException.class) - public void testUnmarshallIOException() throws IOException{ + public void testUnmarshallIOException() throws IOException + { ByteSource bs = mock(ByteSource.class); + when(bs.openBufferedStream()).thenThrow(IOException.class); transformer.unmarshall(String.class, bs); } - - private static class TestObject2 {} - + + //~--- inner classes -------------------------------------------------------- + + /** + * Class description + * + * + * @version Enter version here..., 15/10/21 + * @author Enter your name here... + */ @XmlRootElement(name = "test") @XmlAccessorType(XmlAccessType.FIELD) - private static class TestObject { - - private String value; + private static class TestObject + { - public TestObject() - { - } - + /** + * Constructs ... + * + */ + public TestObject() {} + + /** + * Constructs ... + * + * + * @param value + */ public TestObject(String value) { this.value = value; } - + + //~--- fields ------------------------------------------------------------- + + /** Field description */ + private String value; } -} \ No newline at end of file + + + /** + * Class description + * + * + * @version Enter version here..., 15/10/21 + * @author Enter your name here... + */ + private static class TestObject2 {} + + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private final JsonContentTransformer transformer = new JsonContentTransformer(); +} From 7c35a60f2878cf5af42d848227c0a415213c17ee Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 27 Oct 2015 22:13:21 +0100 Subject: [PATCH 068/171] fix wrong file permissions as mentioned in #766 --- scm-server/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scm-server/pom.xml b/scm-server/pom.xml index 2c860afce5..e05adebb75 100644 --- a/scm-server/pom.xml +++ b/scm-server/pom.xml @@ -244,13 +244,13 @@ /etc/default/scm-server ${project.basedir}/src/main/nativepkg/default - 0744 + 0644 true /etc/init.d/scm-server ${project.basedir}/src/main/nativepkg/init-script - 0744 + 0755 From 721d1c8adb5dec9be5d4ea39779613cb402eac9c Mon Sep 17 00:00:00 2001 From: Coby Pritchard Date: Thu, 3 Dec 2015 20:10:08 +0000 Subject: [PATCH 069/171] Add Jetty host interface variable. Allows to run a specific interface or locahost instead of just all interfaces. --- scm-server/src/main/nativepkg/default | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scm-server/src/main/nativepkg/default b/scm-server/src/main/nativepkg/default index f2675e1673..5e04061e0c 100644 --- a/scm-server/src/main/nativepkg/default +++ b/scm-server/src/main/nativepkg/default @@ -29,6 +29,9 @@ # http://bitbucket.org/sdorra/scm-manager # +# scm-server host interface +HOST=0.0.0.0 + # scm-server port PORT=8080 @@ -51,4 +54,4 @@ LOGDIR=/var/log/scm # EXTRA_JVM_ARGUMENTS="$EXTRA_JVM_ARGUMENTS -Xms1g -Xmx1g" # pass extra jvm arguments -EXTRA_JVM_ARGUMENTS="$EXTRA_JVM_ARGUMENTS -Djetty.port=$PORT" +EXTRA_JVM_ARGUMENTS="$EXTRA_JVM_ARGUMENTS -Djetty.host=$HOST -Djetty.port=$PORT" From cb1eb5e915ba7df95d44aa399e11b25893d2752d Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 13 Dec 2015 14:21:09 +0100 Subject: [PATCH 070/171] update commons-daemon-native to version 1.0.15.1 to fix scm-server start on macos --- scm-server/pom.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scm-server/pom.xml b/scm-server/pom.xml index e05adebb75..317bd9ae38 100644 --- a/scm-server/pom.xml +++ b/scm-server/pom.xml @@ -75,7 +75,7 @@ scm-server - ${commons.daemon.version} + ${commons.daemon.native.version} sonia.scm.server.ScmServerDaemon commons-daemon @@ -178,7 +178,7 @@ com.github.sdorra nativepkg-maven-plugin - 1.1.2 + 1.1.3 @@ -308,6 +308,7 @@ 1.0.15 + 1.0.15.1 ${project.build.directory}/appassembler/commons-daemon/scm-server From 9834d4788f23851637f52aaab2702c1811f0d52e Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 13 Dec 2015 14:44:52 +0100 Subject: [PATCH 071/171] update jetty to version 7.6.18.v20150929 --- pom.xml | 3 ++- scm-clients/scm-cli-client/pom.xml | 2 +- scm-clients/scm-client-impl/pom.xml | 2 +- scm-webapp/pom.xml | 6 +++--- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 93fb88c813..668b13cb81 100644 --- a/pom.xml +++ b/pom.xml @@ -423,7 +423,8 @@ 1.19 2.6.6 2.3.20 - 7.6.16.v20140903 + 7.6.18.v20150929 + 7.6.16.v20140903 1.2.3 diff --git a/scm-clients/scm-cli-client/pom.xml b/scm-clients/scm-cli-client/pom.xml index 6779d55ed6..d1bd41276b 100644 --- a/scm-clients/scm-cli-client/pom.xml +++ b/scm-clients/scm-cli-client/pom.xml @@ -168,7 +168,7 @@ org.mortbay.jetty jetty-maven-plugin - ${jetty.version} + ${jetty.maven.version} 8085 STOP diff --git a/scm-clients/scm-client-impl/pom.xml b/scm-clients/scm-client-impl/pom.xml index b3abff916e..4ea0544ffc 100644 --- a/scm-clients/scm-client-impl/pom.xml +++ b/scm-clients/scm-client-impl/pom.xml @@ -176,7 +176,7 @@ org.mortbay.jetty jetty-maven-plugin - ${jetty.version} + ${jetty.maven.version} 8085 STOP diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index 4f2e59e4ad..0e98df13a0 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -466,7 +466,7 @@ org.mortbay.jetty jetty-maven-plugin - ${jetty.version} + ${jetty.maven.version} 8005 STOP @@ -617,7 +617,7 @@ org.mortbay.jetty jetty-maven-plugin - ${jetty.version} + ${jetty.maven.version} 8085 STOP @@ -697,7 +697,7 @@ org.mortbay.jetty jetty-maven-plugin - ${jetty.version} + ${jetty.maven.version} 8086 STOP From d6ba9c696642a3c75fb9c097a4d1b1f36c8a1cd9 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 13 Dec 2015 14:52:12 +0100 Subject: [PATCH 072/171] update slf4j to version 1.7.13 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 668b13cb81..a1d5851d4d 100644 --- a/pom.xml +++ b/pom.xml @@ -416,7 +416,7 @@ 4.12 - 1.7.12 + 1.7.13 1.1.3 2.5 3.0 From fec6cb79f7db1eb9da3ca5cb67d8f7551a99fece Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 13 Dec 2015 14:55:16 +0100 Subject: [PATCH 073/171] update shiro to version 1.2.4 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a1d5851d4d..1741dd8855 100644 --- a/pom.xml +++ b/pom.xml @@ -427,7 +427,7 @@ 7.6.16.v20140903 - 1.2.3 + 1.2.4 3.7.1.201504261725-r-scm1 From 488d4e33239becd0368c78173fb62dd71ae918ec Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sat, 23 Jan 2016 22:02:25 +0100 Subject: [PATCH 074/171] implemented xsrf protection, see issue #793 --- .../main/java/sonia/scm/ScmServletModule.java | 4 + .../resources/AuthenticationResource.java | 6 + .../java/sonia/scm/security/XsrfCookies.java | 87 +++++++++ .../scm/security/XsrfProtectionFilter.java | 112 +++++++++++ .../main/webapp/resources/js/sonia.global.js | 13 ++ .../sonia/scm/security/XsrfCookiesTest.java | 108 +++++++++++ .../security/XsrfProtectionFilterTest.java | 181 ++++++++++++++++++ 7 files changed, 511 insertions(+) create mode 100644 scm-webapp/src/main/java/sonia/scm/security/XsrfCookies.java create mode 100644 scm-webapp/src/main/java/sonia/scm/security/XsrfProtectionFilter.java create mode 100644 scm-webapp/src/test/java/sonia/scm/security/XsrfCookiesTest.java create mode 100644 scm-webapp/src/test/java/sonia/scm/security/XsrfProtectionFilterTest.java diff --git a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java index 9087755f93..acc58fed15 100644 --- a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java +++ b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java @@ -162,6 +162,7 @@ import sonia.scm.net.ahc.ContentTransformer; import sonia.scm.net.ahc.DefaultAdvancedHttpClient; import sonia.scm.net.ahc.JsonContentTransformer; import sonia.scm.net.ahc.XmlContentTransformer; +import sonia.scm.security.XsrfProtectionFilter; import sonia.scm.web.UserAgentParser; /** @@ -365,6 +366,9 @@ public class ScmServletModule extends ServletModule filter(PATTERN_ALL).through(LoggingFilter.class); } + // protect api agains xsrf attacks + filter(PATTERN_RESTAPI).through(XsrfProtectionFilter.class); + /* * filter(PATTERN_PAGE, * PATTERN_STATIC_RESOURCES).through(StaticResourceFilter.class); diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AuthenticationResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AuthenticationResource.java index b7f02187cb..36d296e1c5 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AuthenticationResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AuthenticationResource.java @@ -79,9 +79,11 @@ import sonia.scm.util.HttpUtil; import java.util.Collection; import java.util.Collections; import java.util.List; +import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; import javax.ws.rs.DefaultValue; import javax.ws.rs.FormParam; @@ -96,6 +98,7 @@ import javax.ws.rs.core.Response; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlRootElement; +import sonia.scm.security.XsrfCookies; /** * @@ -261,6 +264,9 @@ public class AuthenticationResource public Response logout(@Context HttpServletRequest request, @Context HttpServletResponse response) { + // remove xsrf token + XsrfCookies.remove(request, response); + Subject subject = SecurityUtils.getSubject(); subject.logout(); diff --git a/scm-webapp/src/main/java/sonia/scm/security/XsrfCookies.java b/scm-webapp/src/main/java/sonia/scm/security/XsrfCookies.java new file mode 100644 index 0000000000..de3c26995a --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/security/XsrfCookies.java @@ -0,0 +1,87 @@ +/** + * 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.security; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * Util methods to handle XsrfCookies. + * + * @author Sebastian Sdorra + * @version 1.47 + */ +public final class XsrfCookies +{ + + private XsrfCookies() + { + } + + /** + * Creates a new xsrf protection cookie and add it to the response. + * + * @param request http servlet request + * @param response http servlet response + * @param token xsrf token + */ + public static void create(HttpServletRequest request, HttpServletResponse response, String token){ + applyCookie(request, response, new Cookie(XsrfProtectionFilter.KEY, token)); + + } + + /** + * Removes the current xsrf protection cookie from response. + * + * @param request http servlet request + * @param response http servlet response + */ + public static void remove(HttpServletRequest request, HttpServletResponse response) + { + Cookie[] cookies = request.getCookies(); + if ( cookies != null ){ + for ( Cookie c : cookies ){ + if ( XsrfProtectionFilter.KEY.equals(c.getName()) ){ + c.setMaxAge(0); + c.setValue(null); + applyCookie(request, response, c); + } + } + } + } + + private static void applyCookie(HttpServletRequest request, HttpServletResponse response, Cookie cookie){ + cookie.setPath(request.getContextPath()); + response.addCookie(cookie); + } + +} diff --git a/scm-webapp/src/main/java/sonia/scm/security/XsrfProtectionFilter.java b/scm-webapp/src/main/java/sonia/scm/security/XsrfProtectionFilter.java new file mode 100644 index 0000000000..7e7391190c --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/security/XsrfProtectionFilter.java @@ -0,0 +1,112 @@ +/** + * 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.security; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Strings; +import java.io.IOException; +import java.util.UUID; +import javax.inject.Singleton; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.util.HttpUtil; +import sonia.scm.web.filter.HttpFilter; + +/** + * Xsrf protection http filter. The filter will issue an cookie with an xsrf protection token on the first ajax request + * of the scm web interface and marks the http session as xsrf protected. On every other request within a protected + * session, the web interface has to send the token from the cookie as http header on every request. If the filter + * receives an request to a protected session, without proper xsrf header the filter will abort the request and send an + * http error code back to the client. If the filter receives an request to a non protected session, from a non web + * interface client the filter will call the chain. + * + * TODO for scm-manager 2 we have to store the csrf token as part of the jwt token instead of session. + * + * @see https://bitbucket.org/sdorra/scm-manager/issues/793/json-hijacking-vulnerability-cwe-116-cwe + * @author Sebastian Sdorra + * @version 1.47 + */ +@Singleton +public final class XsrfProtectionFilter extends HttpFilter +{ + + /** + * the logger for XsrfProtectionFilter + */ + private static final Logger logger = LoggerFactory.getLogger(XsrfProtectionFilter.class); + + /** + * Key used for session, header and cookie. + */ + static final String KEY = "X-XSRF-Token"; + + @Override + protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws + IOException, ServletException + { + HttpSession session = request.getSession(true); + String storedToken = (String) session.getAttribute(KEY); + if ( ! Strings.isNullOrEmpty(storedToken) ){ + String headerToken = request.getHeader(KEY); + if ( storedToken.equals(headerToken) ){ + logger.trace("received valid xsrf protected request"); + chain.doFilter(request, response); + } else { + // is forbidden the correct status code? + logger.warn("received request to a xsrf protected session without proper xsrf token"); + response.sendError(HttpServletResponse.SC_FORBIDDEN); + } + } else if (HttpUtil.isWUIRequest(request)) { + logger.debug("received wui request, mark session as xsrf protected and issue a new token"); + String token = createToken(); + session.setAttribute(KEY, token); + XsrfCookies.create(request, response, token); + chain.doFilter(request, response); + } else { + // handle non webinterface clients, which does not need xsrf protection + logger.trace("received request to a non xsrf protected session"); + chain.doFilter(request, response); + } + } + + private String createToken() + { + // TODO create interface and use a better method + return UUID.randomUUID().toString(); + } + +} diff --git a/scm-webapp/src/main/webapp/resources/js/sonia.global.js b/scm-webapp/src/main/webapp/resources/js/sonia.global.js index 32dec370a9..4513a05e6e 100644 --- a/scm-webapp/src/main/webapp/resources/js/sonia.global.js +++ b/scm-webapp/src/main/webapp/resources/js/sonia.global.js @@ -37,6 +37,19 @@ Ext.Ajax.defaultHeaders = { 'X-SCM-Client': 'WUI' }; +// XSRF protection +Ext.Ajax.on('beforerequest', function(conn, options){ + var token = Ext.util.Cookies.get('X-XSRF-Token'); + console.log(token); + console.log(options); + if (token){ + if (!options.headers){ + options.headers = {}; + } + options.headers['X-XSRF-Token'] = token; + } +}); + var state = null; var admin = false; diff --git a/scm-webapp/src/test/java/sonia/scm/security/XsrfCookiesTest.java b/scm-webapp/src/test/java/sonia/scm/security/XsrfCookiesTest.java new file mode 100644 index 0000000000..64dd27bc3b --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/security/XsrfCookiesTest.java @@ -0,0 +1,108 @@ +/** + * 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.security; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.junit.Test; +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import static org.mockito.Mockito.*; +import org.mockito.runners.MockitoJUnitRunner; + +/** + * Tests for the {@link XsrfCookies} util class. + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +public class XsrfCookiesTest { + + @Mock + private HttpServletRequest request; + + @Mock + private HttpServletResponse response; + + /** + * Prepare mocks for testing. + */ + @Before + public void prepareMocks(){ + when(request.getContextPath()).thenReturn("/scm"); + } + + /** + * Tests create method. + */ + @Test + public void testCreate() + { + XsrfCookies.create(request, response, "mytoken"); + + // capture cookie + ArgumentCaptor captor = ArgumentCaptor.forClass(Cookie.class); + verify(response).addCookie(captor.capture()); + + // check for cookie + Cookie cookie = captor.getValue(); + assertEquals(XsrfProtectionFilter.KEY, cookie.getName()); + assertEquals("/scm", cookie.getPath()); + assertEquals("mytoken", cookie.getValue()); + } + + /** + * Tests remove method. + */ + @Test + public void testRemove(){ + Cookie cookie = new Cookie(XsrfProtectionFilter.KEY, "mytoken"); + cookie.setMaxAge(15); + when(request.getCookies()).thenReturn(new Cookie[]{cookie}); + XsrfCookies.remove(request, response); + + // capture cookie + ArgumentCaptor captor = ArgumentCaptor.forClass(Cookie.class); + verify(response).addCookie(captor.capture()); + + // check the captured cookie + Cookie c = captor.getValue(); + assertEquals("cookie max age should be set to 0", 0, c.getMaxAge()); + assertEquals("cookie path should be equals", cookie.getPath(), c.getPath()); + assertNull("cookie value shuld be null", c.getValue()); + } + +} \ No newline at end of file diff --git a/scm-webapp/src/test/java/sonia/scm/security/XsrfProtectionFilterTest.java b/scm-webapp/src/test/java/sonia/scm/security/XsrfProtectionFilterTest.java new file mode 100644 index 0000000000..2cb187c24d --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/security/XsrfProtectionFilterTest.java @@ -0,0 +1,181 @@ +/** + * 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.security; + +import java.io.IOException; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import org.junit.Test; +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import static org.mockito.Mockito.*; +import org.mockito.runners.MockitoJUnitRunner; +import sonia.scm.util.HttpUtil; + +/** + * Unit tests for {@link XsrfProtectionFilter}. + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +public class XsrfProtectionFilterTest { + + @Mock + private HttpServletRequest request; + + @Mock + private HttpServletResponse response; + + @Mock + private HttpSession session; + + @Mock + private FilterChain chain; + + private final XsrfProtectionFilter filter = new XsrfProtectionFilter(); + + /** + * Prepare mocks for testing. + */ + @Before + public void setUp(){ + when(request.getSession(true)).thenReturn(session); + when(request.getContextPath()).thenReturn("/scm"); + } + + /** + * Test filter method for non web interface clients. + * + * @throws IOException + * @throws ServletException + */ + @Test + public void testDoFilterFromNonWuiClient() throws IOException, ServletException + { + filter.doFilter(request, response, chain); + verify(chain).doFilter(request, response); + } + + /** + * Test filter method for first web interface request. + * + * @throws IOException + * @throws ServletException + */ + @Test + public void testDoFilterIssuesTokenOnFirstWuiRequest() throws IOException, ServletException + { + when(request.getHeader(HttpUtil.HEADER_SCM_CLIENT)).thenReturn(HttpUtil.SCM_CLIENT_WUI); + + // call the filter + filter.doFilter(request, response, chain); + + // capture cookie + ArgumentCaptor captor = ArgumentCaptor.forClass(Cookie.class); + verify(response).addCookie(captor.capture()); + + // check for cookie + Cookie cookie = captor.getValue(); + assertEquals(XsrfProtectionFilter.KEY, cookie.getName()); + assertEquals("/scm", cookie.getPath()); + assertNotNull(cookie.getValue()); + + // ensure filter chain is called + verify(chain).doFilter(request, response); + } + + /** + * Test filter method on protected session with an invalid xsrf token. + * + * @throws IOException + * @throws ServletException + */ + @Test + public void testDoFilterWithInvalidToken() throws IOException, ServletException { + when(request.getHeader(HttpUtil.HEADER_SCM_CLIENT)).thenReturn(HttpUtil.SCM_CLIENT_WUI); + when(request.getHeader(XsrfProtectionFilter.KEY)).thenReturn("invalidtoken"); + when(session.getAttribute(XsrfProtectionFilter.KEY)).thenReturn("mytoken"); + + // call the filter + filter.doFilter(request, response, chain); + + // ensure response send forbidden and the chain was never called + verify(response).sendError(HttpServletResponse.SC_FORBIDDEN); + verify(chain, never()).doFilter(request, response); + } + + /** + * Test filter method on protected session without xsrf token. + * + * @throws IOException + * @throws ServletException + */ + @Test + public void testDoFilterOnProtectedSessionWithoutToken() throws IOException, ServletException { + when(request.getHeader(HttpUtil.HEADER_SCM_CLIENT)).thenReturn(HttpUtil.SCM_CLIENT_WUI); + when(session.getAttribute(XsrfProtectionFilter.KEY)).thenReturn("mytoken"); + + // call the filter + filter.doFilter(request, response, chain); + + // ensure response send forbidden and the chain was never called + verify(response).sendError(HttpServletResponse.SC_FORBIDDEN); + verify(chain, never()).doFilter(request, response); + } + + /** + * Test filter method on protected session with valid xsrf token. + * + * @throws IOException + * @throws ServletException + */ + @Test + public void testDoFilterOnProtectedSessionWithValidToken() throws IOException, ServletException { + when(request.getHeader(HttpUtil.HEADER_SCM_CLIENT)).thenReturn(HttpUtil.SCM_CLIENT_WUI); + when(request.getHeader(XsrfProtectionFilter.KEY)).thenReturn("mytoken"); + when(session.getAttribute(XsrfProtectionFilter.KEY)).thenReturn("mytoken"); + + // call the filter + filter.doFilter(request, response, chain); + + // ensure chain was called + verify(chain).doFilter(request, response); + } + +} \ No newline at end of file From 5cdde75188de154b267f55d5ea3db4b35a6fde11 Mon Sep 17 00:00:00 2001 From: corux Date: Tue, 15 Mar 2016 21:08:33 +0000 Subject: [PATCH 075/171] case insensitive sorting the of name column --- .../webapp/resources/js/repository/sonia.repository.grid.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.grid.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.grid.js index 0003de58b0..4362ea721b 100644 --- a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.grid.js +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.grid.js @@ -82,7 +82,8 @@ Sonia.repository.Grid = Ext.extend(Sonia.rest.Grid, { name: 'group', convert: this.convertToGroup },{ - name: 'name' + name: 'name', + sortType:'asUCString' },{ name: 'type' },{ From 920b2eca39eee4531739ed72b2cb32a19bcfc2df Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 17 Mar 2016 09:42:34 +0100 Subject: [PATCH 076/171] added option for plugins to change ssl context --- .../main/java/sonia/scm/ScmServletModule.java | 5 ++ .../sonia/scm/net/SSLContextProvider.java | 89 +++++++++++++++++++ .../net/ahc/DefaultAdvancedHttpClient.java | 13 ++- .../ahc/DefaultAdvancedHttpClientTest.java | 3 +- .../ahc/DefaultAdvancedHttpResponseTest.java | 3 +- .../net/ahc/JsonContentTransformerTest.java | 1 - 6 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 scm-webapp/src/main/java/sonia/scm/net/SSLContextProvider.java diff --git a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java index 9087755f93..dded38f0e5 100644 --- a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java +++ b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java @@ -157,6 +157,8 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; +import javax.net.ssl.SSLContext; +import sonia.scm.net.SSLContextProvider; import sonia.scm.net.ahc.AdvancedHttpClient; import sonia.scm.net.ahc.ContentTransformer; import sonia.scm.net.ahc.DefaultAdvancedHttpClient; @@ -318,6 +320,9 @@ public class ScmServletModule extends ServletModule bind(ChangesetViewerUtil.class); bind(RepositoryBrowserUtil.class); + // bind sslcontext provider + bind(SSLContext.class).toProvider(SSLContextProvider.class); + // bind httpclient bind(HttpClient.class, URLHttpClient.class); diff --git a/scm-webapp/src/main/java/sonia/scm/net/SSLContextProvider.java b/scm-webapp/src/main/java/sonia/scm/net/SSLContextProvider.java new file mode 100644 index 0000000000..aadf8a2b13 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/net/SSLContextProvider.java @@ -0,0 +1,89 @@ +/** + * 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.net; + +import com.google.common.base.Throwables; +import com.google.inject.Inject; +import java.security.NoSuchAlgorithmException; +import javax.inject.Named; +import javax.inject.Provider; +import javax.net.ssl.SSLContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Provider for {@link SSLContext}. The provider will first try to retrieve the {@link SSLContext} from an "default" + * named optional provider, if this fails the provider will return the jvm default context. + * + * @author Sebastian Sdorra + * @version 1.47 + */ +public final class SSLContextProvider implements Provider +{ + + /** + * the logger for SSLContextProvider + */ + private static final Logger logger = LoggerFactory.getLogger(SSLContextProvider.class); + + @Named("default") + @Inject(optional = true) + private Provider sslContextProvider; + + @Override + public SSLContext get() + { + SSLContext context = null; + if (sslContextProvider != null) + { + context = sslContextProvider.get(); + } + + if (context == null) + { + try + { + logger.trace("could not find ssl context provider, use jvm default"); + context = SSLContext.getDefault(); + } + catch (NoSuchAlgorithmException ex) + { + throw Throwables.propagate(ex); + } + } + else + { + logger.trace("use custom ssl context from provider"); + } + return context; + } + +} diff --git a/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpClient.java b/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpClient.java index 2c19afb44d..df6c93515b 100644 --- a/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpClient.java +++ b/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpClient.java @@ -66,6 +66,7 @@ import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.util.Set; +import javax.inject.Provider; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; @@ -113,13 +114,15 @@ public class DefaultAdvancedHttpClient extends AdvancedHttpClient * * @param configuration scm-manager main configuration * @param contentTransformers content transformer + * @param sslContextProvider ssl context provider */ @Inject public DefaultAdvancedHttpClient(ScmConfiguration configuration, - Set contentTransformers) + Set contentTransformers, Provider sslContextProvider) { this.configuration = configuration; this.contentTransformers = contentTransformers; + this.sslContextProvider = sslContextProvider; } //~--- methods -------------------------------------------------------------- @@ -329,6 +332,11 @@ public class DefaultAdvancedHttpClient extends AdvancedHttpClient { logger.error("could not disable certificate validation", ex); } + } + else + { + logger.trace("set ssl socker factory from provider"); + connection.setSSLSocketFactory(sslContextProvider.get().getSocketFactory()); } if (request.isDisableHostnameValidation()) @@ -395,4 +403,7 @@ public class DefaultAdvancedHttpClient extends AdvancedHttpClient /** set of content transformers */ private final Set contentTransformers; + + /** ssl context provider */ + private final Provider sslContextProvider; } diff --git a/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpClientTest.java b/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpClientTest.java index cd26af9d4b..faac9a99e5 100644 --- a/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpClientTest.java +++ b/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpClientTest.java @@ -64,6 +64,7 @@ import java.util.Set; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSocketFactory; +import sonia.scm.net.SSLContextProvider; /** * @@ -309,7 +310,7 @@ public class DefaultAdvancedHttpClientTest public TestingAdvacedHttpClient(ScmConfiguration configuration, Set transformers) { - super(configuration, transformers); + super(configuration, transformers, new SSLContextProvider()); } //~--- methods ------------------------------------------------------------ diff --git a/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java b/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java index f19c89eb5f..0a2b4cbdb3 100644 --- a/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java +++ b/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java @@ -65,6 +65,7 @@ import java.net.HttpURLConnection; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; +import sonia.scm.net.SSLContextProvider; /** * @@ -143,7 +144,7 @@ public class DefaultAdvancedHttpResponseTest /** Field description */ private final DefaultAdvancedHttpClient client = new DefaultAdvancedHttpClient(new ScmConfiguration(), - new HashSet()); + new HashSet(), new SSLContextProvider()); /** Field description */ @Mock diff --git a/scm-webapp/src/test/java/sonia/scm/net/ahc/JsonContentTransformerTest.java b/scm-webapp/src/test/java/sonia/scm/net/ahc/JsonContentTransformerTest.java index 1a11390993..8094d1c06f 100644 --- a/scm-webapp/src/test/java/sonia/scm/net/ahc/JsonContentTransformerTest.java +++ b/scm-webapp/src/test/java/sonia/scm/net/ahc/JsonContentTransformerTest.java @@ -37,7 +37,6 @@ package sonia.scm.net.ahc; import com.google.common.base.Charsets; import com.google.common.io.ByteSource; -import com.google.common.io.CharSource; import org.junit.Test; From 652b98f53c1eee2f7bd6f66d23da8425ea7fab53 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 24 May 2016 21:12:09 +0200 Subject: [PATCH 077/171] #793 added configuration parameter to enable/disable xsrf protection. The protection is disabled by default until it is battle tested. --- .../sonia/scm/config/ScmConfiguration.java | 37 +++++++++++++++++++ .../scm/security/XsrfProtectionFilter.java | 26 ++++++++++++- .../security/XsrfProtectionFilterTest.java | 32 +++++++++++++++- 3 files changed, 93 insertions(+), 2 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java b/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java index eebefe4e1c..9fea54289e 100644 --- a/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java +++ b/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java @@ -177,6 +177,7 @@ public class ScmConfiguration this.skipFailedAuthenticators = other.skipFailedAuthenticators; this.loginAttemptLimit = other.loginAttemptLimit; this.loginAttemptLimitTimeout = other.loginAttemptLimitTimeout; + this.enabledXsrfProtection = other.enabledXsrfProtection; // deprecated fields this.servername = other.servername; @@ -424,6 +425,19 @@ public class ScmConfiguration return disableGroupingGrid; } + /** + * Returns {@code true} if the cookie xsrf protection is enabled. + * + * @see Issue 793 + * @return {@code true} if the cookie xsrf protection is enabled + * + * @since 1.47 + */ + public boolean isEnabledXsrfProtection() + { + return enabledXsrfProtection; + } + /** * Returns true if port forwarding is enabled. * @@ -800,6 +814,21 @@ public class ScmConfiguration this.sslPort = sslPort; } + /** + * Set {@code true} to enable xsrf cookie protection. + * + * @param enabledXsrfProtection {@code true} to enable xsrf protection + * @see Issue 793 + * + * @since 1.47 + */ + public void setEnabledXsrfProtection(boolean enabledXsrfProtection) + { + this.enabledXsrfProtection = enabledXsrfProtection; + } + + + //~--- fields --------------------------------------------------------------- /** Field description */ @@ -913,4 +942,12 @@ public class ScmConfiguration /** Field description */ private boolean anonymousAccessEnabled = false; + + /** + * Enables xsrf cookie protection. + * + * @since 1.47 + */ + @XmlElement(name = "xsrf-protection") + private boolean enabledXsrfProtection = false; } diff --git a/scm-webapp/src/main/java/sonia/scm/security/XsrfProtectionFilter.java b/scm-webapp/src/main/java/sonia/scm/security/XsrfProtectionFilter.java index 7e7391190c..333c71066f 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/XsrfProtectionFilter.java +++ b/scm-webapp/src/main/java/sonia/scm/security/XsrfProtectionFilter.java @@ -32,6 +32,7 @@ package sonia.scm.security; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; +import com.google.inject.Inject; import java.io.IOException; import java.util.UUID; import javax.inject.Singleton; @@ -43,6 +44,7 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sonia.scm.config.ScmConfiguration; import sonia.scm.util.HttpUtil; import sonia.scm.web.filter.HttpFilter; @@ -52,7 +54,8 @@ import sonia.scm.web.filter.HttpFilter; * session, the web interface has to send the token from the cookie as http header on every request. If the filter * receives an request to a protected session, without proper xsrf header the filter will abort the request and send an * http error code back to the client. If the filter receives an request to a non protected session, from a non web - * interface client the filter will call the chain. + * interface client the filter will call the chain. The {@link XsrfProtectionFilter} is disabled by default and can be + * enabled with {@link ScmConfiguration#setEnabledXsrfProtection(boolean)}. * * TODO for scm-manager 2 we have to store the csrf token as part of the jwt token instead of session. * @@ -73,10 +76,30 @@ public final class XsrfProtectionFilter extends HttpFilter * Key used for session, header and cookie. */ static final String KEY = "X-XSRF-Token"; + + @Inject + public XsrfProtectionFilter(ScmConfiguration configuration) + { + this.configuration = configuration; + } @Override protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException + { + if (configuration.isEnabledXsrfProtection()) + { + doXsrfProtection(request, response, chain); + } + else + { + logger.trace("xsrf protection is disabled, skipping check"); + chain.doFilter(request, response); + } + } + + private void doXsrfProtection(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws + IOException, ServletException { HttpSession session = request.getSession(true); String storedToken = (String) session.getAttribute(KEY); @@ -109,4 +132,5 @@ public final class XsrfProtectionFilter extends HttpFilter return UUID.randomUUID().toString(); } + private ScmConfiguration configuration; } diff --git a/scm-webapp/src/test/java/sonia/scm/security/XsrfProtectionFilterTest.java b/scm-webapp/src/test/java/sonia/scm/security/XsrfProtectionFilterTest.java index 2cb187c24d..0a2145de04 100644 --- a/scm-webapp/src/test/java/sonia/scm/security/XsrfProtectionFilterTest.java +++ b/scm-webapp/src/test/java/sonia/scm/security/XsrfProtectionFilterTest.java @@ -44,8 +44,10 @@ import org.junit.Before; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; +import org.mockito.Mockito; import static org.mockito.Mockito.*; import org.mockito.runners.MockitoJUnitRunner; +import sonia.scm.config.ScmConfiguration; import sonia.scm.util.HttpUtil; /** @@ -68,7 +70,9 @@ public class XsrfProtectionFilterTest { @Mock private FilterChain chain; - private final XsrfProtectionFilter filter = new XsrfProtectionFilter(); + private final ScmConfiguration configuration = new ScmConfiguration(); + + private final XsrfProtectionFilter filter = new XsrfProtectionFilter(configuration); /** * Prepare mocks for testing. @@ -77,6 +81,7 @@ public class XsrfProtectionFilterTest { public void setUp(){ when(request.getSession(true)).thenReturn(session); when(request.getContextPath()).thenReturn("/scm"); + configuration.setEnabledXsrfProtection(true); } /** @@ -91,6 +96,31 @@ public class XsrfProtectionFilterTest { filter.doFilter(request, response, chain); verify(chain).doFilter(request, response); } + + /** + * Test filter method with disabled xsrf protection. + * + * @throws IOException + * @throws ServletException + */ + @Test + public void testDoFilterWithDisabledXsrfProtection() throws IOException, ServletException + { + // disable xsrf protection + configuration.setEnabledXsrfProtection(false); + + // set webui user-agent + when(request.getHeader(HttpUtil.HEADER_SCM_CLIENT)).thenReturn(HttpUtil.SCM_CLIENT_WUI); + + // call the filter + filter.doFilter(request, response, chain); + + // verify that no xsrf other any other cookie was set + verify(response, never()).addCookie(Mockito.any(Cookie.class)); + + // ensure filter chain is called + verify(chain).doFilter(request, response); + } /** * Test filter method for first web interface request. From 9dce816fb634d7444d379468801c60eaaf602dfd Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 24 May 2016 21:25:14 +0200 Subject: [PATCH 078/171] added checkbox to enable xsrf protection --- .../resources/js/config/sonia.config.scmconfigpanel.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/scm-webapp/src/main/webapp/resources/js/config/sonia.config.scmconfigpanel.js b/scm-webapp/src/main/webapp/resources/js/config/sonia.config.scmconfigpanel.js index 29efa3fa00..a985688a3e 100644 --- a/scm-webapp/src/main/webapp/resources/js/config/sonia.config.scmconfigpanel.js +++ b/scm-webapp/src/main/webapp/resources/js/config/sonia.config.scmconfigpanel.js @@ -102,6 +102,10 @@ Sonia.config.ScmConfigPanel = Ext.extend(Sonia.config.ConfigPanel,{ forceBaseUrlHelpText: 'Redirects to the base url if the request comes from a other url', disableGroupingGridHelpText: 'Disable repository Groups. A complete page reload is required after a change of this value.', enableRepositoryArchiveHelpText: 'Enable repository archives. A complete page reload is required after a change of this value.', + + // TODO i18n + enableXsrfProtectionText: 'Enable Xsrf Protection', + enableXsrfProtectionHelpText: 'Enable Xsrf Cookie Protection. Note: This feature is still experimental.', initComponent: function(){ @@ -166,6 +170,12 @@ Sonia.config.ScmConfigPanel = Ext.extend(Sonia.config.ConfigPanel,{ name: 'skip-failed-authenticators', inputValue: 'true', helpText: this.skipFailedAuthenticatorsHelpText + },{ + xtype: 'checkbox', + fieldLabel: this.enableXsrfProtectionText, + name: 'xsrf-protection', + inputValue: 'true', + helpText: this.enableXsrfProtectionHelpText },{ xtype: 'numberfield', fieldLabel: this.loginAttemptLimitText, From 4a7fe345ecddfe3c1b81d260f8c4f8b368a7c344 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 24 May 2016 21:26:01 +0200 Subject: [PATCH 079/171] close branch issue-793 From 8475acbba025b5c121bbffdf20ce9c98139658e1 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 25 May 2016 08:41:47 +0200 Subject: [PATCH 080/171] #809 fix order of script resources in production stage --- .../resources/AbstractResourceManager.java | 7 +- .../AbstractResourceManagerTest.java | 93 +++++++++++++++++++ .../resources/DefaultResourceManagerTest.java | 74 +++++++++++++++ .../resources/ResourceManagerTestBase.java | 76 +++++++++++++++ 4 files changed, 248 insertions(+), 2 deletions(-) create mode 100644 scm-webapp/src/test/java/sonia/scm/resources/AbstractResourceManagerTest.java create mode 100644 scm-webapp/src/test/java/sonia/scm/resources/DefaultResourceManagerTest.java create mode 100644 scm-webapp/src/test/java/sonia/scm/resources/ResourceManagerTestBase.java diff --git a/scm-webapp/src/main/java/sonia/scm/resources/AbstractResourceManager.java b/scm-webapp/src/main/java/sonia/scm/resources/AbstractResourceManager.java index 4890e4b983..45cc766582 100644 --- a/scm-webapp/src/main/java/sonia/scm/resources/AbstractResourceManager.java +++ b/scm-webapp/src/main/java/sonia/scm/resources/AbstractResourceManager.java @@ -150,6 +150,9 @@ public abstract class AbstractResourceManager implements ResourceManager processPlugin(resources, plugin); } } + + // fix order of script resources, see https://goo.gl/ok03l4 + Collections.sort(resources); return resources; } @@ -291,10 +294,10 @@ public abstract class AbstractResourceManager implements ResourceManager //~--- fields ------------------------------------------------------------- /** Field description */ - private String name; + private final String name; /** Field description */ - private ResourceType type; + private final ResourceType type; } diff --git a/scm-webapp/src/test/java/sonia/scm/resources/AbstractResourceManagerTest.java b/scm-webapp/src/test/java/sonia/scm/resources/AbstractResourceManagerTest.java new file mode 100644 index 0000000000..2bb564a60a --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/resources/AbstractResourceManagerTest.java @@ -0,0 +1,93 @@ +/*** + * Copyright (c) 2015, 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. + * + * https://bitbucket.org/sdorra/scm-manager + * + */ + +package sonia.scm.resources; + +import com.google.common.collect.ImmutableSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.servlet.ServletContext; +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.hamcrest.Matchers.*; +import org.mockito.runners.MockitoJUnitRunner; +import sonia.scm.plugin.PluginLoader; + +/** + * Unit tests for {@link AbstractResourceManager}. + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +public class AbstractResourceManagerTest extends ResourceManagerTestBase +{ + + private DummyResourceManager resourceManager; + + @Before + public void setUp() + { + Set resourceHandlers = ImmutableSet.of(resourceHandler); + resourceManager = new DummyResourceManager(servletContext, pluginLoader, resourceHandlers); + } + + /** + * Test {@link AbstractResourceManager#getScriptResources()} in the correct order. + * + * @see Issue 809 + */ + @Test + public void testGetScriptResources() + { + appendScriptResources("a/b.js", "z/a.js", "a/a.js"); + List scripts = resourceManager.getScriptResources(); + assertThat(scripts, contains("a/a.js", "a/b.js", "z/a.js")); + } + + private static class DummyResourceManager extends AbstractResourceManager + { + + public DummyResourceManager(ServletContext servletContext, PluginLoader pluginLoader, Set resourceHandlers) + { + super(servletContext, pluginLoader, resourceHandlers); + } + + @Override + protected void collectResources(Map resourceMap) + { + } + + } + +} diff --git a/scm-webapp/src/test/java/sonia/scm/resources/DefaultResourceManagerTest.java b/scm-webapp/src/test/java/sonia/scm/resources/DefaultResourceManagerTest.java new file mode 100644 index 0000000000..9b6b838e13 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/resources/DefaultResourceManagerTest.java @@ -0,0 +1,74 @@ +/*** + * Copyright (c) 2015, 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. + * + * https://bitbucket.org/sdorra/scm-manager + * + */ + +package sonia.scm.resources; + +import com.google.common.collect.ImmutableSet; +import java.util.List; +import java.util.Set; +import org.junit.Test; +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; + +/** + * Unit tests for {@link DefaultResourceManager}. + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +public class DefaultResourceManagerTest extends ResourceManagerTestBase { + + private DefaultResourceManager resourceManager; + + /** + * Set up {@link DefaultResourceManager} for tests. + */ + @Before + public void setUp() + { + Set resourceHandlers = ImmutableSet.of(resourceHandler); + resourceManager = new DefaultResourceManager(servletContext, pluginLoader, resourceHandlers); + } + + /** + * Test {@link DefaultResourceManager#getResources(sonia.scm.resources.ResourceType)} method. + */ + @Test + public void testGetResources() + { + appendScriptResources("a/b.js", "z/a.js", "a/a.js"); + List resources = resourceManager.getResources(ResourceType.SCRIPT); + assertEquals(1, resources.size()); + } + +} \ No newline at end of file diff --git a/scm-webapp/src/test/java/sonia/scm/resources/ResourceManagerTestBase.java b/scm-webapp/src/test/java/sonia/scm/resources/ResourceManagerTestBase.java new file mode 100644 index 0000000000..bb73bffe8e --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/resources/ResourceManagerTestBase.java @@ -0,0 +1,76 @@ +/*** + * Copyright (c) 2015, 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. + * + * https://bitbucket.org/sdorra/scm-manager + * + */ + +package sonia.scm.resources; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Sets; +import java.util.List; +import javax.servlet.ServletContext; +import org.mockito.Mock; +import static org.mockito.Mockito.when; +import sonia.scm.plugin.Plugin; +import sonia.scm.plugin.PluginLoader; +import sonia.scm.plugin.PluginResources; + +/** + * Base class for {@link ResourceManager} tests. + * + * @author Sebastian Sdorra + */ +public abstract class ResourceManagerTestBase +{ + + @Mock + protected ServletContext servletContext; + + @Mock + protected PluginLoader pluginLoader; + + @Mock + protected ResourceHandler resourceHandler; + + + /** + * Append scripts resources to plugin loader. + * + * @param resources resource names + */ + protected void appendScriptResources(String... resources) + { + PluginResources pluginResources = new PluginResources(); + pluginResources.setScriptResources(Sets.newHashSet(resources)); + Plugin plugin = new Plugin(); + plugin.setResources(pluginResources); + List plugins = ImmutableList.of(plugin); + when(pluginLoader.getInstalledPlugins()).thenReturn(plugins); + } +} From 56891afae2d15562c5bfc2bb32028f3a90109154 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 25 May 2016 08:42:46 +0200 Subject: [PATCH 081/171] added nbproject folder to .hgignore file --- .hgignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgignore b/.hgignore index 14e7f7a85d..353d320a34 100644 --- a/.hgignore +++ b/.hgignore @@ -1,6 +1,7 @@ # netbeans temp & private files /?target/ nbactions.*\.xml +/?nbproject/ nb-configuration\.xml # MacOS X Files \.DS_Store$ From 71b742388cb24855dabf5be0cd3e4be1e0e12f49 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 25 May 2016 10:03:04 +0200 Subject: [PATCH 082/171] do not swallow the ScmSecurityException in PermissionFilter --- .../scm/web/filter/PermissionFilter.java | 32 ++++++------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/web/filter/PermissionFilter.java b/scm-core/src/main/java/sonia/scm/web/filter/PermissionFilter.java index defa83a1b7..7a3ded673d 100644 --- a/scm-core/src/main/java/sonia/scm/web/filter/PermissionFilter.java +++ b/scm-core/src/main/java/sonia/scm/web/filter/PermissionFilter.java @@ -161,33 +161,24 @@ public abstract class PermissionFilter extends HttpFilter if (hasPermission(repository, writeRequest)) { - if (logger.isTraceEnabled()) - { - logger.trace("{} access to repository {} for user {} granted", - getActionAsString(writeRequest), repository.getName(), - getUserName(subject)); - } + logger.trace("{} access to repository {} for user {} granted", + getActionAsString(writeRequest), repository.getName(), + getUserName(subject)); chain.doFilter(request, response); } else { - if (logger.isInfoEnabled()) - { - logger.info("{} access to repository {} for user {} denied", - getActionAsString(writeRequest), repository.getName(), - getUserName(subject)); - } - + logger.info("{} access to repository {} for user {} denied", + getActionAsString(writeRequest), repository.getName(), + getUserName(subject)); + sendAccessDenied(request, response, subject); } } else { - if (logger.isDebugEnabled()) - { - logger.debug("repository not found"); - } + logger.debug("repository not found"); response.sendError(HttpServletResponse.SC_NOT_FOUND); } @@ -210,12 +201,7 @@ public abstract class PermissionFilter extends HttpFilter } catch (ScmSecurityException ex) { - if (logger.isWarnEnabled()) - { - logger.warn("user {} has not enough permissions", - subject.getPrincipal()); - } - + logger.warn("user " + subject.getPrincipal() + " has not enough permissions", ex); sendAccessDenied(request, response, subject); } From e079481e51233248c3cf47264e040f700f4e10fd Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 25 May 2016 10:28:46 +0200 Subject: [PATCH 083/171] #828 focus same repository as was selected previously --- .../js/repository/sonia.repository.grid.js | 28 ++++++++++++++++++- .../js/repository/sonia.repository.js | 9 ++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.grid.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.grid.js index 4362ea721b..19f8fc311d 100644 --- a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.grid.js +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.grid.js @@ -482,8 +482,34 @@ Sonia.repository.Grid = Ext.extend(Sonia.rest.Grid, { listener.call(listener.scope, item, panels); } }); + + // get the xtype of the currently opened tab + // search for the tab with the same xtype and activate it + // see issue https://goo.gl/3RGnA3 + var activeTab = 0; + var activeXtype = this.getActiveTabXtype(); + if (activeXtype){ + for (var i=0; i Date: Wed, 25 May 2016 11:04:03 +0200 Subject: [PATCH 084/171] update slf4j to version 1.7.21 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1741dd8855..80332ef9dd 100644 --- a/pom.xml +++ b/pom.xml @@ -416,7 +416,7 @@ 4.12 - 1.7.13 + 1.7.21 1.1.3 2.5 3.0 From 655e5d53e9eff687c679030dec96581d582f0e8b Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 25 May 2016 11:07:53 +0200 Subject: [PATCH 085/171] update logback to version 1.1.7 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 80332ef9dd..cf3f5029de 100644 --- a/pom.xml +++ b/pom.xml @@ -417,7 +417,7 @@ 1.7.21 - 1.1.3 + 1.1.7 2.5 3.0 1.19 From b19f73fc490de8c3364e133043be865258f18616 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 25 May 2016 11:14:41 +0200 Subject: [PATCH 086/171] update jersey to version 1.19.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cf3f5029de..2590228c31 100644 --- a/pom.xml +++ b/pom.xml @@ -420,7 +420,7 @@ 1.1.7 2.5 3.0 - 1.19 + 1.19.1 2.6.6 2.3.20 7.6.18.v20150929 From a011bb3e8d61a13ba031aebc3a6af81088e72ad4 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 25 May 2016 11:23:41 +0200 Subject: [PATCH 087/171] update jetty to version 7.6.19.v20160209 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2590228c31..35e261328b 100644 --- a/pom.xml +++ b/pom.xml @@ -423,7 +423,7 @@ 1.19.1 2.6.6 2.3.20 - 7.6.18.v20150929 + 7.6.19.v20160209 7.6.16.v20140903 From 13bea6e502e8775c1e36b18eadd165f13573df70 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 25 May 2016 16:32:25 +0200 Subject: [PATCH 088/171] implemented small scheduler engine --- pom.xml | 1 + .../java/sonia/scm/schedule/Scheduler.java | 65 ++++++ .../main/java/sonia/scm/schedule/Task.java | 47 ++++ scm-webapp/pom.xml | 12 + .../java/sonia/scm/ScmContextListener.java | 3 + .../main/java/sonia/scm/ScmServletModule.java | 5 + .../scm/schedule/InjectionEnabledJob.java | 90 +++++++ .../sonia/scm/schedule/QuartzScheduler.java | 175 ++++++++++++++ .../java/sonia/scm/schedule/QuartzTask.java | 68 ++++++ .../scm/schedule/InjectionEnabledJobTest.java | 168 +++++++++++++ .../scm/schedule/QuartzSchedulerTest.java | 220 ++++++++++++++++++ .../sonia/scm/schedule/QuartzTaskTest.java | 89 +++++++ 12 files changed, 943 insertions(+) create mode 100644 scm-core/src/main/java/sonia/scm/schedule/Scheduler.java create mode 100644 scm-core/src/main/java/sonia/scm/schedule/Task.java create mode 100644 scm-webapp/src/main/java/sonia/scm/schedule/InjectionEnabledJob.java create mode 100644 scm-webapp/src/main/java/sonia/scm/schedule/QuartzScheduler.java create mode 100644 scm-webapp/src/main/java/sonia/scm/schedule/QuartzTask.java create mode 100644 scm-webapp/src/test/java/sonia/scm/schedule/InjectionEnabledJobTest.java create mode 100644 scm-webapp/src/test/java/sonia/scm/schedule/QuartzSchedulerTest.java create mode 100644 scm-webapp/src/test/java/sonia/scm/schedule/QuartzTaskTest.java diff --git a/pom.xml b/pom.xml index 35e261328b..2a8d1a2f9b 100644 --- a/pom.xml +++ b/pom.xml @@ -435,6 +435,7 @@ 15.0 + 2.2.3 1.6 diff --git a/scm-core/src/main/java/sonia/scm/schedule/Scheduler.java b/scm-core/src/main/java/sonia/scm/schedule/Scheduler.java new file mode 100644 index 0000000000..dfad4baeed --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/schedule/Scheduler.java @@ -0,0 +1,65 @@ +/*** + * Copyright (c) 2015, 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. + * + * https://bitbucket.org/sdorra/scm-manager + * + */ + +package sonia.scm.schedule; + +import java.io.Closeable; + +/** + * Scheduler is able to run tasks on the future in a background thread. Task can be scheduled with cron like expression. + * Note: Task are always executed in an administrative context. + * @author Sebastian Sdorra + * @since 1.47 + */ +public interface Scheduler extends Closeable { + + /** + * Schedule a new task for future execution. + * + * @param expression cron expression + * @param runnable action + * + * @return cancelable task + */ + public Task schedule(String expression, Runnable runnable); + + /** + * Schedule a new task for future execution. The method will create a new instance of the runnable for every + * execution. The runnable can use injection. + * + * @param expression cron expression + * @param runnable action class + * + * @return cancelable task + */ + public Task schedule(String expression, Class runnable); + +} diff --git a/scm-core/src/main/java/sonia/scm/schedule/Task.java b/scm-core/src/main/java/sonia/scm/schedule/Task.java new file mode 100644 index 0000000000..dc601bcb82 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/schedule/Task.java @@ -0,0 +1,47 @@ +/*** + * Copyright (c) 2015, 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. + * + * https://bitbucket.org/sdorra/scm-manager + * + */ + +package sonia.scm.schedule; + +/** + * Tasks are executed in the future and can be running more than once. A task execution can be canceled. + * + * @author Sebastian Sdorra + * @since 1.47 + */ +public interface Task { + + /** + * Cancel task execution. + */ + public void cancel(); + +} diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index 0e98df13a0..aa26a109e0 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -190,6 +190,18 @@ ${guava.version} + + org.quartz-scheduler + quartz + ${quartz.version} + + + c3p0 + c3p0 + + + + diff --git a/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java b/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java index 3845c985f7..2c2efff045 100644 --- a/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java +++ b/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java @@ -60,6 +60,7 @@ import java.util.List; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import sonia.scm.repository.HealthCheckContextListener; +import sonia.scm.schedule.Scheduler; /** * @@ -79,6 +80,8 @@ public class ScmContextListener extends GuiceServletContextListener { if ((globalInjector != null) &&!startupError) { + // close Scheduler + IOUtil.close(globalInjector.getInstance(Scheduler.class)); // close RepositoryManager IOUtil.close(globalInjector.getInstance(RepositoryManager.class)); diff --git a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java index 132bc617c3..813c296558 100644 --- a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java +++ b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java @@ -164,6 +164,8 @@ import sonia.scm.net.ahc.ContentTransformer; import sonia.scm.net.ahc.DefaultAdvancedHttpClient; import sonia.scm.net.ahc.JsonContentTransformer; import sonia.scm.net.ahc.XmlContentTransformer; +import sonia.scm.schedule.QuartzScheduler; +import sonia.scm.schedule.Scheduler; import sonia.scm.security.XsrfProtectionFilter; import sonia.scm.web.UserAgentParser; @@ -282,6 +284,9 @@ public class ScmServletModule extends ServletModule bind(PluginLoader.class).toInstance(pluginLoader); bind(PluginManager.class, DefaultPluginManager.class); + // bind scheduler + bind(Scheduler.class).to(QuartzScheduler.class); + // note CipherUtil uses an other generator bind(KeyGenerator.class).to(DefaultKeyGenerator.class); bind(CipherHandler.class).toInstance(cu.getCipherHandler()); diff --git a/scm-webapp/src/main/java/sonia/scm/schedule/InjectionEnabledJob.java b/scm-webapp/src/main/java/sonia/scm/schedule/InjectionEnabledJob.java new file mode 100644 index 0000000000..44ac5ae3ab --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/schedule/InjectionEnabledJob.java @@ -0,0 +1,90 @@ +/*** + * Copyright (c) 2015, 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. + * + * https://bitbucket.org/sdorra/scm-manager + * + */ + +package sonia.scm.schedule; + +import com.google.common.base.Preconditions; +import com.google.inject.Injector; +import com.google.inject.Provider; +import org.quartz.Job; +import org.quartz.JobDataMap; +import org.quartz.JobDetail; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.web.security.AdministrationContext; +import sonia.scm.web.security.PrivilegedAction; + +/** + * InjectionEnabledJob allows the execution of quartz jobs and enable injection on them. + * + * @author Sebastian Sdorra + * @since 1.47 + */ +public class InjectionEnabledJob implements Job { + + private static final Logger logger = LoggerFactory.getLogger(InjectionEnabledJob.class); + + @Override + public void execute(JobExecutionContext jec) throws JobExecutionException { + Preconditions.checkNotNull(jec, "execution context is null"); + + JobDetail detail = jec.getJobDetail(); + Preconditions.checkNotNull(detail, "job detail not provided"); + + JobDataMap dataMap = detail.getJobDataMap(); + Preconditions.checkNotNull(dataMap, "job detail does not contain data map"); + + Injector injector = (Injector) dataMap.get(Injector.class.getName()); + Preconditions.checkNotNull(injector, "data map does not contain injector"); + + final Provider runnableProvider = (Provider) dataMap.get(Runnable.class.getName()); + if (runnableProvider == null) { + throw new JobExecutionException("could not find runnable provider"); + } + + AdministrationContext ctx = injector.getInstance(AdministrationContext.class); + ctx.runAsAdmin(new PrivilegedAction() + { + @Override + public void run() + { + logger.trace("create runnable from provider"); + Runnable runnable = runnableProvider.get(); + logger.debug("execute injection enabled job {}", runnable.getClass()); + runnable.run(); + } + }); + } + + +} diff --git a/scm-webapp/src/main/java/sonia/scm/schedule/QuartzScheduler.java b/scm-webapp/src/main/java/sonia/scm/schedule/QuartzScheduler.java new file mode 100644 index 0000000000..5b46d438d8 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/schedule/QuartzScheduler.java @@ -0,0 +1,175 @@ +/*** + * Copyright (c) 2015, 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. + * + * https://bitbucket.org/sdorra/scm-manager + * + */ + +package sonia.scm.schedule; + +import com.google.common.base.Throwables; +import com.google.inject.Injector; +import com.google.inject.Provider; +import com.google.inject.Singleton; +import java.io.IOException; +import javax.inject.Inject; +import org.quartz.CronScheduleBuilder; +import org.quartz.JobBuilder; +import org.quartz.JobDataMap; +import org.quartz.JobDetail; +import org.quartz.SchedulerException; +import org.quartz.Trigger; +import org.quartz.TriggerBuilder; +import org.quartz.impl.StdSchedulerFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.Initable; +import sonia.scm.SCMContextProvider; + +/** + * {@link Scheduler} which uses the quartz scheduler. + * + * @author Sebastian Sdorra + * @since 1.47 + * + * @see Quartz Job Scheduler + */ +@Singleton +public class QuartzScheduler implements Scheduler, Initable { + + private static final Logger logger = LoggerFactory.getLogger(QuartzScheduler.class); + + private final Injector injector; + private final org.quartz.Scheduler scheduler; + + /** + * Creates a new quartz scheduler. + * + * @param injector injector + */ + @Inject + public QuartzScheduler(Injector injector) + { + this.injector = injector; + + // get default scheduler + try { + scheduler = StdSchedulerFactory.getDefaultScheduler(); + } catch (SchedulerException ex) { + throw Throwables.propagate(ex); + } + } + + /** + * Creates a new quartz scheduler. This constructor is only for testing. + * + * @param injector injector + * @param scheduler quartz scheduler + */ + QuartzScheduler(Injector injector, org.quartz.Scheduler scheduler) + { + this.injector = injector; + this.scheduler = scheduler; + } + + @Override + public void init(SCMContextProvider context) + { + try + { + if (!scheduler.isStarted()) + { + scheduler.start(); + } + } + catch (SchedulerException ex) + { + logger.error("can not start scheduler", ex); + } + } + + @Override + public void close() throws IOException + { + try + { + if (scheduler.isStarted()){ + scheduler.shutdown(); + } + } + catch (SchedulerException ex) + { + logger.error("can not stop scheduler", ex); + } + } + + @Override + public Task schedule(String expression, final Runnable runnable) + { + return schedule(expression, new Provider(){ + @Override + public Runnable get() + { + return runnable; + } + }); + } + + @Override + public Task schedule(String expression, Class runnable) + { + return schedule(expression, injector.getProvider(runnable)); + } + + private Task schedule(String expression, Provider provider){ + // create data map with injection provider for InjectionEnabledJob + JobDataMap map = new JobDataMap(); + map.put(Runnable.class.getName(), provider); + map.put(Injector.class.getName(), injector); + + // create job detail for InjectionEnabledJob with the provider for the annotated class + JobDetail detail = JobBuilder.newJob(InjectionEnabledJob.class) + .usingJobData(map) + .build(); + + // create a trigger with the cron expression from the annotation + Trigger trigger = TriggerBuilder.newTrigger() + .forJob(detail) + .withSchedule(CronScheduleBuilder.cronSchedule(expression)) + .build(); + + try { + scheduler.scheduleJob(detail, trigger); + } catch (SchedulerException ex) { + throw Throwables.propagate(ex); + } + + return new QuartzTask(scheduler, trigger.getJobKey()); + } + + +} diff --git a/scm-webapp/src/main/java/sonia/scm/schedule/QuartzTask.java b/scm-webapp/src/main/java/sonia/scm/schedule/QuartzTask.java new file mode 100644 index 0000000000..a45790a2e9 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/schedule/QuartzTask.java @@ -0,0 +1,68 @@ +/*** + * Copyright (c) 2015, 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. + * + * https://bitbucket.org/sdorra/scm-manager + * + */ + +package sonia.scm.schedule; + +import com.google.common.base.Throwables; +import org.quartz.JobKey; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; + +/** + * Task implementation for the {@link QuartzScheduler}. + * + * @author Sebastian Sdorra + */ +public class QuartzTask implements Task { + + private final org.quartz.Scheduler scheduler; + private final JobKey jobKey; + + QuartzTask(Scheduler scheduler, JobKey jobKey) + { + this.scheduler = scheduler; + this.jobKey = jobKey; + } + + @Override + public void cancel() + { + try + { + scheduler.deleteJob(jobKey); + } + catch (SchedulerException ex) + { + throw Throwables.propagate(ex); + } + } + +} diff --git a/scm-webapp/src/test/java/sonia/scm/schedule/InjectionEnabledJobTest.java b/scm-webapp/src/test/java/sonia/scm/schedule/InjectionEnabledJobTest.java new file mode 100644 index 0000000000..9545577c9f --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/schedule/InjectionEnabledJobTest.java @@ -0,0 +1,168 @@ +/*** + * Copyright (c) 2015, 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. + * + * https://bitbucket.org/sdorra/scm-manager + * + */ + +package sonia.scm.schedule; + +import com.google.inject.Injector; +import com.google.inject.Provider; +import org.junit.Test; +import static org.mockito.Mockito.*; +import org.junit.Rule; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; +import org.quartz.JobDataMap; +import org.quartz.JobDetail; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import sonia.scm.web.security.AdministrationContext; +import sonia.scm.web.security.PrivilegedAction; + +/** + * Unit tests for {@link InjectionEnabledJob}. + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +public class InjectionEnabledJobTest { + + @Mock + private Injector injector; + + @Mock + private JobDataMap dataMap; + + @Mock + private JobDetail detail; + + @Mock + private JobExecutionContext jec; + + @Mock + private Provider runnable; + + @Mock + private AdministrationContext context; + + @Rule + public ExpectedException expected = ExpectedException.none(); + + /** + * Tests {@link InjectionEnabledJob#execute(org.quartz.JobExecutionContext)} without context. + * + * @throws JobExecutionException + */ + @Test + public void testExecuteWithoutContext() throws JobExecutionException + { + expected.expect(NullPointerException.class); + expected.expectMessage("execution context"); + new InjectionEnabledJob().execute(null); + } + + /** + * Tests {@link InjectionEnabledJob#execute(org.quartz.JobExecutionContext)} without job detail. + * + * @throws JobExecutionException + */ + @Test + public void testExecuteWithoutJobDetail() throws JobExecutionException + { + expected.expect(NullPointerException.class); + expected.expectMessage("detail"); + new InjectionEnabledJob().execute(jec); + } + + /** + * Tests {@link InjectionEnabledJob#execute(org.quartz.JobExecutionContext)} without data map. + * + * @throws JobExecutionException + */ + @Test + public void testExecuteWithoutDataMap() throws JobExecutionException + { + when(jec.getJobDetail()).thenReturn(detail); + expected.expect(NullPointerException.class); + expected.expectMessage("data map"); + new InjectionEnabledJob().execute(jec); + } + + /** + * Tests {@link InjectionEnabledJob#execute(org.quartz.JobExecutionContext)} without injector. + * + * @throws JobExecutionException + */ + @Test + public void testExecuteWithoutInjector() throws JobExecutionException + { + when(jec.getJobDetail()).thenReturn(detail); + when(detail.getJobDataMap()).thenReturn(dataMap); + expected.expect(NullPointerException.class); + expected.expectMessage("injector"); + new InjectionEnabledJob().execute(jec); + } + + /** + * Tests {@link InjectionEnabledJob#execute(org.quartz.JobExecutionContext)} without runnable. + * + * @throws JobExecutionException + */ + @Test + public void testExecuteWithoutRunnable() throws JobExecutionException + { + when(jec.getJobDetail()).thenReturn(detail); + when(detail.getJobDataMap()).thenReturn(dataMap); + when(dataMap.get(Injector.class.getName())).thenReturn(injector); + expected.expect(JobExecutionException.class); + expected.expectMessage("runnable"); + new InjectionEnabledJob().execute(jec); + } + + /** + * Tests {@link InjectionEnabledJob#execute(org.quartz.JobExecutionContext)}. + * + * @throws JobExecutionException + */ + @Test + public void testExecute() throws JobExecutionException + { + when(jec.getJobDetail()).thenReturn(detail); + when(detail.getJobDataMap()).thenReturn(dataMap); + when(dataMap.get(Injector.class.getName())).thenReturn(injector); + when(dataMap.get(Runnable.class.getName())).thenReturn(runnable); + when(injector.getInstance(AdministrationContext.class)).thenReturn(context); + new InjectionEnabledJob().execute(jec); + verify(context).runAsAdmin(Mockito.any(PrivilegedAction.class)); + } + +} \ No newline at end of file diff --git a/scm-webapp/src/test/java/sonia/scm/schedule/QuartzSchedulerTest.java b/scm-webapp/src/test/java/sonia/scm/schedule/QuartzSchedulerTest.java new file mode 100644 index 0000000000..2f3dc86ea1 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/schedule/QuartzSchedulerTest.java @@ -0,0 +1,220 @@ +/*** + * Copyright (c) 2015, 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. + * + * https://bitbucket.org/sdorra/scm-manager + * + */ + +package sonia.scm.schedule; + +import com.google.inject.Injector; +import com.google.inject.Provider; +import java.io.IOException; +import org.junit.Test; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; +import static org.hamcrest.Matchers.*; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.quartz.CronTrigger; +import org.quartz.JobDetail; +import org.quartz.SchedulerException; +import org.quartz.Trigger; + +/** + * Unit tests for {@link QuartzScheduler}. + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +public class QuartzSchedulerTest { + + @Mock + private Injector injector; + + @Mock + private org.quartz.Scheduler quartzScheduler; + + private QuartzScheduler scheduler; + + @Before + public void setUp() + { + scheduler = new QuartzScheduler(injector, quartzScheduler); + } + + /** + * Tests {@link QuartzScheduler#schedule(java.lang.String, java.lang.Runnable)}. + * + * @throws SchedulerException + */ + @Test + public void testSchedule() throws SchedulerException + { + DummyRunnable dr = new DummyRunnable(); + Task task = scheduler.schedule("42 2 * * * ?", dr); + assertNotNull(task); + + ArgumentCaptor detailCaptor = ArgumentCaptor.forClass(JobDetail.class); + ArgumentCaptor triggerCaptor = ArgumentCaptor.forClass(Trigger.class); + verify(quartzScheduler).scheduleJob(detailCaptor.capture(), triggerCaptor.capture()); + + Trigger trigger = triggerCaptor.getValue(); + assertThat(trigger, is(instanceOf(CronTrigger.class))); + CronTrigger cron = (CronTrigger) trigger; + assertEquals("42 2 * * * ?", cron.getCronExpression()); + + JobDetail detail = detailCaptor.getValue(); + assertEquals(InjectionEnabledJob.class, detail.getJobClass()); + Provider runnable = (Provider) detail.getJobDataMap().get(Runnable.class.getName()); + assertNotNull(runnable); + assertSame(dr, runnable.get()); + assertEquals(injector, detail.getJobDataMap().get(Injector.class.getName())); + } + + /** + * Tests {@link QuartzScheduler#schedule(java.lang.String, java.lang.Class)}. + * + * @throws SchedulerException + */ + @Test + public void testScheduleWithClass() throws SchedulerException + { + scheduler.schedule("42 * * * * ?", DummyRunnable.class); + + verify(injector).getProvider(DummyRunnable.class); + + ArgumentCaptor detailCaptor = ArgumentCaptor.forClass(JobDetail.class); + ArgumentCaptor triggerCaptor = ArgumentCaptor.forClass(Trigger.class); + verify(quartzScheduler).scheduleJob(detailCaptor.capture(), triggerCaptor.capture()); + + Trigger trigger = triggerCaptor.getValue(); + assertThat(trigger, is(instanceOf(CronTrigger.class))); + CronTrigger cron = (CronTrigger) trigger; + assertEquals("42 * * * * ?", cron.getCronExpression()); + + JobDetail detail = detailCaptor.getValue(); + assertEquals(InjectionEnabledJob.class, detail.getJobClass()); + assertEquals(injector, detail.getJobDataMap().get(Injector.class.getName())); + } + + /** + * Tests {@link QuartzScheduler#init(sonia.scm.SCMContextProvider)}. + * + * @throws SchedulerException + */ + @Test + public void testInit() throws SchedulerException + { + when(quartzScheduler.isStarted()).thenReturn(Boolean.FALSE); + scheduler.init(null); + verify(quartzScheduler).start(); + } + + /** + * Tests {@link QuartzScheduler#init(sonia.scm.SCMContextProvider)} when the underlying scheduler is already started. + * + * @throws SchedulerException + */ + @Test + public void testInitAlreadyRunning() throws SchedulerException + { + when(quartzScheduler.isStarted()).thenReturn(Boolean.TRUE); + scheduler.init(null); + verify(quartzScheduler, never()).start(); + } + + /** + * Tests {@link QuartzScheduler#init(sonia.scm.SCMContextProvider)} when the underlying scheduler throws an exception. + * + * @throws SchedulerException + */ + @Test + public void testInitException() throws SchedulerException + { + when(quartzScheduler.isStarted()).thenThrow(SchedulerException.class); + scheduler.init(null); + verify(quartzScheduler, never()).start(); + } + + /** + * Tests {@link QuartzScheduler#close()}. + * + * @throws IOException + * @throws SchedulerException + */ + @Test + public void testClose() throws IOException, SchedulerException + { + when(quartzScheduler.isStarted()).thenReturn(Boolean.TRUE); + scheduler.close(); + verify(quartzScheduler).shutdown(); + } + + /** + * Tests {@link QuartzScheduler#close()} when the underlying scheduler is not running. + * + * @throws IOException + * @throws SchedulerException + */ + @Test + public void testCloseNotRunning() throws IOException, SchedulerException + { + when(quartzScheduler.isStarted()).thenReturn(Boolean.FALSE); + scheduler.close(); + verify(quartzScheduler, never()).shutdown(); + } + + /** + * Tests {@link QuartzScheduler#close()} when the underlying scheduler throws an exception. + * + * @throws IOException + * @throws SchedulerException + */ + @Test + public void testCloseException() throws IOException, SchedulerException + { + when(quartzScheduler.isStarted()).thenThrow(SchedulerException.class); + scheduler.close(); + verify(quartzScheduler, never()).shutdown(); + } + + + public static class DummyRunnable implements Runnable { + + @Override + public void run() + { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + } +} \ No newline at end of file diff --git a/scm-webapp/src/test/java/sonia/scm/schedule/QuartzTaskTest.java b/scm-webapp/src/test/java/sonia/scm/schedule/QuartzTaskTest.java new file mode 100644 index 0000000000..5ba5c19373 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/schedule/QuartzTaskTest.java @@ -0,0 +1,89 @@ +/*** + * Copyright (c) 2015, 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. + * + * https://bitbucket.org/sdorra/scm-manager + * + */ + +package sonia.scm.schedule; + +import org.junit.Test; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; +import static org.hamcrest.Matchers.*; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.quartz.JobKey; +import org.quartz.SchedulerException; + +/** + * Unit tests for {@link QuartzTask}. + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +public class QuartzTaskTest { + + @Mock + private org.quartz.Scheduler scheduler; + + private final JobKey jobKey = new JobKey("sample"); + + private QuartzTask task; + + @Before + public void setUp(){ + task = new QuartzTask(scheduler, jobKey); + } + + /** + * Tests {@link QuartzTask#cancel()}. + * + * @throws SchedulerException + */ + @Test + public void testCancel() throws SchedulerException + { + task.cancel(); + verify(scheduler).deleteJob(jobKey); + } + + /** + * Tests {@link QuartzTask#cancel()} when the scheduler throws an exception. + * @throws org.quartz.SchedulerException + */ + @Test(expected = RuntimeException.class) + public void testCancelWithException() throws SchedulerException + { + when(scheduler.deleteJob(jobKey)).thenThrow(SchedulerException.class); + task.cancel(); + } + +} \ No newline at end of file From a9697c7c83d3fd848f7fafc7dfa8a0194eccab88 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 25 May 2016 16:33:34 +0200 Subject: [PATCH 089/171] #801 added implmentation for running git gc in an configurable interval --- .../java/sonia/scm/repository/GitConfig.java | 16 +- .../java/sonia/scm/repository/GitGcTask.java | 149 ++++++++++++++++++ .../scm/repository/GitRepositoryHandler.java | 53 ++++++- .../main/resources/sonia/scm/git.config.js | 8 + .../repository/GitRepositoryHandlerTest.java | 10 +- 5 files changed, 233 insertions(+), 3 deletions(-) create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitGcTask.java diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitConfig.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitConfig.java index 9f508effe6..80fe8907ac 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitConfig.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitConfig.java @@ -35,6 +35,9 @@ package sonia.scm.repository; //~--- JDK imports ------------------------------------------------------------ +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; /** @@ -42,4 +45,15 @@ import javax.xml.bind.annotation.XmlRootElement; * @author Sebastian Sdorra */ @XmlRootElement(name = "config") -public class GitConfig extends SimpleRepositoryConfig {} +@XmlAccessorType(XmlAccessType.FIELD) +public class GitConfig extends SimpleRepositoryConfig { + + @XmlElement(name = "gc-expression") + private String gcExpression; + + public String getGcExpression() + { + return gcExpression; + } + +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitGcTask.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitGcTask.java new file mode 100644 index 0000000000..f6d1b85fdf --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitGcTask.java @@ -0,0 +1,149 @@ +/*** + * Copyright (c) 2015, 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. + * + * https://bitbucket.org/sdorra/scm-manager + * + */ + +package sonia.scm.repository; + +import com.google.common.base.Stopwatch; +import com.google.inject.Inject; +import java.io.File; +import java.io.IOException; +import java.util.Map; +import java.util.Properties; +import org.eclipse.jgit.api.GarbageCollectCommand; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Executes git gc on every git repository. Statistics of the gc process are logged to the info level. The task is + * disabled by default and must be enabled through the global git configuration. + * + * @author Sebastian Sdorra + * @since 1.47 + */ +public class GitGcTask implements Runnable { + + private static final String SP = System.getProperty("line.seperator", "\n"); + + private static final Logger logger = LoggerFactory.getLogger(GitGcTask.class); + + private final RepositoryManager repositoryManager; + private final RepositoryDirectoryHandler repositoryHandler; + + @Inject + public GitGcTask(RepositoryManager repositoryManager) + { + this.repositoryManager = repositoryManager; + this.repositoryHandler = (RepositoryDirectoryHandler) repositoryManager.getHandler(GitRepositoryHandler.TYPE_NAME); + } + + @Override + public void run() + { + for (Repository repository : repositoryManager.getAll()) + { + handle(repository); + } + } + + private void handle(Repository repository){ + if (GitRepositoryHandler.TYPE_NAME.equals(repository.getType())) + { + if (repository.isValid() && repository.isHealthy()) + { + logger.info("start git gc for repository {}", repository.getName()); + Stopwatch sw = Stopwatch.createStarted(); + gc(repository); + logger.debug("gc of repository {} has finished after {}", repository.getName(), sw.stop()); + } + else + { + logger.debug("skip non valid/healthy repository {}", repository.getName()); + } + } + else + { + logger.trace("skip non git repository {}", repository.getName()); + } + } + + private void appendProperties(StringBuilder buffer, Properties properties){ + for (Map.Entry entry : properties.entrySet()){ + buffer.append(SP).append(" - ").append(entry.getKey()).append(" = ").append(entry.getValue()); + } + } + + private String message(Repository repository, Properties statistics, String span){ + StringBuilder buffer = new StringBuilder("gc statistics for "); + buffer.append(repository.getName()).append(" ").append(span).append(" execution:"); + appendProperties(buffer, statistics); + return buffer.toString(); + } + + private void statistics(Repository repository, GarbageCollectCommand gcc) throws GitAPIException { + Properties properties = gcc.getStatistics(); + logger.info(message(repository, properties, "before")); + } + + private void execute(Repository repository, GarbageCollectCommand gcc) throws GitAPIException { + Properties properties = gcc.call(); + logger.info(message(repository, properties, "after")); + } + + private void gc(Repository repository){ + File file = repositoryHandler.getDirectory(repository); + Git git = null; + try { + git = Git.open(file); + GarbageCollectCommand gcc = git.gc(); + // print statistics before execution, because it looks like + // jgit returns the statistics after gc has finished + statistics(repository, gcc); + execute(repository, gcc); + } + catch (IOException ex) + { + logger.warn("failed to open git repository", ex); + } + catch (GitAPIException ex) + { + logger.warn("failed running git gc command", ex); + } + finally + { + if (git != null){ + git.close(); + } + } + } + +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHandler.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHandler.java index 713e589f8c..d919eacb68 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHandler.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHandler.java @@ -35,6 +35,7 @@ package sonia.scm.repository; //~--- non-JDK imports -------------------------------------------------------- +import com.google.common.base.Strings; import com.google.inject.Inject; import com.google.inject.Singleton; @@ -51,6 +52,15 @@ import sonia.scm.util.AssertUtil; import java.io.File; import java.io.IOException; +import java.util.Timer; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.SCMContextProvider; +import sonia.scm.schedule.Scheduler; +import sonia.scm.schedule.Task; /** * @@ -74,12 +84,18 @@ public class GitRepositoryHandler /** Field description */ public static final String TYPE_NAME = "git"; + + private static final Logger logger = LoggerFactory.getLogger(GitRepositoryHandler.class); /** Field description */ public static final Type TYPE = new RepositoryType(TYPE_NAME, TYPE_DISPLAYNAME, GitRepositoryServiceProvider.COMMANDS); + private static final Object LOCK = new Object(); + + private Task task; + //~--- constructors --------------------------------------------------------- /** @@ -88,15 +104,48 @@ public class GitRepositoryHandler * * @param storeFactory * @param fileSystem + * @param scheduler */ @Inject - public GitRepositoryHandler(StoreFactory storeFactory, FileSystem fileSystem) + public GitRepositoryHandler(StoreFactory storeFactory, FileSystem fileSystem, Scheduler scheduler) { super(storeFactory, fileSystem); + this.scheduler = scheduler; } //~--- get methods ---------------------------------------------------------- + @Override + public void init(SCMContextProvider context) + { + super.init(context); + scheduleGc(); + } + + @Override + public void setConfig(GitConfig config) + { + super.setConfig(config); + scheduleGc(); + } + + private void scheduleGc() + { + synchronized (LOCK){ + if ( task != null ){ + logger.debug("cancel existing git gc task"); + task.cancel(); + task = null; + } + String exp = getConfig().getGcExpression(); + if (!Strings.isNullOrEmpty(exp)) + { + logger.info("schedule git gc task with expression {}", exp); + task = scheduler.schedule(exp, GitGcTask.class); + } + } + } + /** * Method description * @@ -314,4 +363,6 @@ public class GitRepositoryHandler { return new File(directory, DIRECTORY_REFS).exists(); } + + private final Scheduler scheduler; } diff --git a/scm-plugins/scm-git-plugin/src/main/resources/sonia/scm/git.config.js b/scm-plugins/scm-git-plugin/src/main/resources/sonia/scm/git.config.js index cb0aa9f16c..510b6aa97c 100644 --- a/scm-plugins/scm-git-plugin/src/main/resources/sonia/scm/git.config.js +++ b/scm-plugins/scm-git-plugin/src/main/resources/sonia/scm/git.config.js @@ -36,10 +36,12 @@ Sonia.git.ConfigPanel = Ext.extend(Sonia.config.SimpleConfigForm, { // labels titleText: 'Git Settings', repositoryDirectoryText: 'Repository directory', + gcExpressionText: 'Git GC Cron Expression', disabledText: 'Disabled', // helpTexts repositoryDirectoryHelpText: 'Location of the Git repositories.', + gcExpressionHelpText: 'Quartz Cron Expression to run git gc in intervals.', disabledHelpText: 'Enable or disable the Git plugin.\n\ Note you have to reload the page, after changing this value.', @@ -54,6 +56,12 @@ Sonia.git.ConfigPanel = Ext.extend(Sonia.config.SimpleConfigForm, { fieldLabel: this.repositoryDirectoryText, helpText: this.repositoryDirectoryHelpText, allowBlank : false + },{ + xtype: 'textfield', + name: 'gc-expression', + fieldLabel: this.gcExpressionText, + helpText: this.gcExpressionHelpText, + allowBlank : true },{ xtype: 'checkbox', name: 'disabled', diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryHandlerTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryHandlerTest.java index eb9646ffe8..b6ee30a23a 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryHandlerTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryHandlerTest.java @@ -43,14 +43,22 @@ import static org.junit.Assert.*; //~--- JDK imports ------------------------------------------------------------ import java.io.File; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import sonia.scm.schedule.Scheduler; /** * * @author Sebastian Sdorra */ +@RunWith(MockitoJUnitRunner.class) public class GitRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase { + @Mock + private Scheduler scheduler; + /** * Method description * @@ -90,7 +98,7 @@ public class GitRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase File directory) { GitRepositoryHandler repositoryHandler = new GitRepositoryHandler(factory, - new DefaultFileSystem()); + new DefaultFileSystem(), scheduler); repositoryHandler.init(contextProvider); From 6c62716119bdca94ece810c88f999da59765831d Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 25 May 2016 21:30:52 +0200 Subject: [PATCH 090/171] removed broken maven repositories --- pom.xml | 6 ------ scm-plugins/scm-svn-plugin/pom.xml | 6 ------ scm-test/pom.xml | 6 ------ 3 files changed, 18 deletions(-) diff --git a/pom.xml b/pom.xml index 2a8d1a2f9b..bba92379e3 100644 --- a/pom.xml +++ b/pom.xml @@ -80,12 +80,6 @@ - - java.net2 - Repository hosting the javaee artifacts - http://download.java.net/maven/2 - - maven.scm-manager.org scm-manager release repository diff --git a/scm-plugins/scm-svn-plugin/pom.xml b/scm-plugins/scm-svn-plugin/pom.xml index e8eb31d80e..9174f85261 100644 --- a/scm-plugins/scm-svn-plugin/pom.xml +++ b/scm-plugins/scm-svn-plugin/pom.xml @@ -82,12 +82,6 @@ scm-manager release repository http://maven.scm-manager.org/nexus/content/groups/public - - - maven.tmatesoft.com - tmatesoft release repository - http://maven.tmatesoft.com/content/repositories/releases/ - diff --git a/scm-test/pom.xml b/scm-test/pom.xml index cba62852db..0bc2248c24 100644 --- a/scm-test/pom.xml +++ b/scm-test/pom.xml @@ -73,12 +73,6 @@ jgit release repository http://download.eclipse.org/jgit/maven - - - maven.tmatesoft.com - tmatesoft release repository - http://maven.tmatesoft.com/content/repositories/releases/ - From 0ef064c1fb19725c80281473cfa175cc84f40344 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 31 May 2016 09:39:42 +0200 Subject: [PATCH 091/171] added unit test for GitGcTask --- .../java/sonia/scm/repository/GitGcTask.java | 17 ++- .../sonia/scm/repository/GitGcTaskTest.java | 128 ++++++++++++++++++ 2 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitGcTaskTest.java diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitGcTask.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitGcTask.java index f6d1b85fdf..248eae92d1 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitGcTask.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitGcTask.java @@ -31,6 +31,7 @@ package sonia.scm.repository; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Stopwatch; import com.google.inject.Inject; import java.io.File; @@ -123,7 +124,7 @@ public class GitGcTask implements Runnable { File file = repositoryHandler.getDirectory(repository); Git git = null; try { - git = Git.open(file); + git = open(file); GarbageCollectCommand gcc = git.gc(); // print statistics before execution, because it looks like // jgit returns the statistics after gc has finished @@ -145,5 +146,19 @@ public class GitGcTask implements Runnable { } } } + + /** + * Opens the git repository. This method is only visible for testing purposes. + * + * @param file repository directory + * + * @return git for repository + * + * @throws IOException + */ + @VisibleForTesting + protected Git open(File file) throws IOException { + return Git.open(file); + } } diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitGcTaskTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitGcTaskTest.java new file mode 100644 index 0000000000..57fdd59c2f --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitGcTaskTest.java @@ -0,0 +1,128 @@ +/*** + * Copyright (c) 2015, 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. + * + * https://bitbucket.org/sdorra/scm-manager + * + */ + +package sonia.scm.repository; + +import com.google.common.collect.Lists; +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Properties; +import org.eclipse.jgit.api.GarbageCollectCommand; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.junit.Test; +import static org.mockito.Mockito.*; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +/** + * Unit tests for {@link GitGcTask}. + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +public class GitGcTaskTest +{ + + @Mock + private RepositoryManager manager; + + @Mock + private RepositoryDirectoryHandler handler; + + @Mock + private GarbageCollectCommand gcc; + + @Mock + private Git git; + + private GitGcTask task; + + /** + * Setup mocks for tests. + * + * @throws GitAPIException + */ + @Before + public void setUp() throws GitAPIException + { + when(git.gc()).thenReturn(gcc); + when(gcc.getStatistics()).thenReturn(new Properties()); + when(gcc.call()).thenReturn(new Properties()); + when(manager.getHandler(GitRepositoryHandler.TYPE_NAME)).thenReturn(handler); + task = new GitGcTask(manager){ + + @Override + protected Git open(File file) throws IOException + { + return git; + } + + }; + } + + /** + * Tests {@link GitGcTask#run()}. + * + * @throws GitAPIException + */ + @Test + public void testRun() throws GitAPIException + { + // prepare repositories for task + Repository unhealthy = mock(Repository.class); + when(unhealthy.getType()).thenReturn("git"); + when(unhealthy.isHealthy()).thenReturn(Boolean.FALSE); + + Repository invalid = mock(Repository.class); + when(unhealthy.getType()).thenReturn("git"); + when(unhealthy.isValid()).thenReturn(Boolean.FALSE); + + List repositories = Lists.newArrayList( + RepositoryTestData.create42Puzzle("git"), + RepositoryTestData.createHeartOfGold("hg"), + unhealthy, + invalid + ); + when(manager.getAll()).thenReturn(repositories); + + // run + task.run(); + + // gc command should only be called once + verify(gcc).getStatistics(); + verify(gcc).call(); + } + +} \ No newline at end of file From b970c47d32c3076d669b4414e979d391aa024616 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 31 May 2016 10:05:17 +0200 Subject: [PATCH 092/171] improve help text for git gc task --- .../src/main/resources/sonia/scm/git.config.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/scm-plugins/scm-git-plugin/src/main/resources/sonia/scm/git.config.js b/scm-plugins/scm-git-plugin/src/main/resources/sonia/scm/git.config.js index 510b6aa97c..4a5c75caae 100644 --- a/scm-plugins/scm-git-plugin/src/main/resources/sonia/scm/git.config.js +++ b/scm-plugins/scm-git-plugin/src/main/resources/sonia/scm/git.config.js @@ -41,7 +41,18 @@ Sonia.git.ConfigPanel = Ext.extend(Sonia.config.SimpleConfigForm, { // helpTexts repositoryDirectoryHelpText: 'Location of the Git repositories.', - gcExpressionHelpText: 'Quartz Cron Expression to run git gc in intervals.', + // TODO i18n + gcExpressionHelpText: '

    Use Quartz Cron Expressions (SECOND MINUTE HOUR DAYOFMONTH MONTH DAYOFWEEK) to run git gc in intervals.

    \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ +
    SECONDSeconds within the minute (0–59)
    MINUTEMinutes within the hour (0–59)
    HOURThe hour of the day (0–23)
    DAYOFMONTHThe day of the month (1–31)
    MONTHThe month (1–12)
    DAYOFWEEKThe day of the week (MON, TUE, WED, THU, FRI, SAT, SUN)
    \n\ +

    E.g.: To run the task on every sunday at two o\'clock in the morning: 0 0 2 ? * SUN

    \n\ +

    For more informations please have a look at Quartz CronTrigger

    ', disabledHelpText: 'Enable or disable the Git plugin.\n\ Note you have to reload the page, after changing this value.', From 4dfaa7cf2f595f95d4ac4f296f97154c7aaf40ba Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 31 May 2016 10:57:18 +0200 Subject: [PATCH 093/171] update enunciate to version 1.31 --- scm-webapp/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index aa26a109e0..a19c8b57d8 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -523,7 +523,7 @@ target/scm-it default 2.28.0 - 1.30.1 + 1.31 1.13.1 1.0 3.0.5 From e049de11b0f72c5a73553b1bf6785bae4643f538 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 31 May 2016 11:12:37 +0200 Subject: [PATCH 094/171] [maven-release-plugin] prepare release 1.47 --- maven/pom.xml | 4 ++-- maven/scm-maven-plugin/pom.xml | 4 ++-- maven/scm-plugin-archetype/pom.xml | 4 ++-- pom.xml | 4 ++-- scm-clients/pom.xml | 6 +++--- scm-clients/scm-cli-client/pom.xml | 6 +++--- scm-clients/scm-client-api/pom.xml | 4 ++-- scm-clients/scm-client-impl/pom.xml | 8 ++++---- scm-core/pom.xml | 4 ++-- scm-dao-orientdb/pom.xml | 8 ++++---- scm-dao-xml/pom.xml | 8 ++++---- scm-plugin-backend/pom.xml | 6 +++--- scm-plugins/pom.xml | 8 ++++---- scm-plugins/scm-git-plugin/pom.xml | 6 +++--- scm-plugins/scm-hg-plugin/pom.xml | 6 +++--- scm-plugins/scm-svn-plugin/pom.xml | 6 +++--- scm-samples/pom.xml | 4 ++-- scm-samples/scm-sample-auth/pom.xml | 6 +++--- scm-samples/scm-sample-hello/pom.xml | 6 +++--- scm-server/pom.xml | 4 ++-- scm-test/pom.xml | 6 +++--- scm-webapp/pom.xml | 24 ++++++++++++------------ support/pom.xml | 4 ++-- support/scm-support-btrace/pom.xml | 6 +++--- 24 files changed, 76 insertions(+), 76 deletions(-) diff --git a/maven/pom.xml b/maven/pom.xml index c2f1fa1e2c..b142855c2b 100644 --- a/maven/pom.xml +++ b/maven/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.47-SNAPSHOT + 1.47 sonia.scm.maven scm-maven-plugins pom - 1.47-SNAPSHOT + 1.47 scm-maven-plugins diff --git a/maven/scm-maven-plugin/pom.xml b/maven/scm-maven-plugin/pom.xml index 9c4bb678b3..278ed8b9cf 100644 --- a/maven/scm-maven-plugin/pom.xml +++ b/maven/scm-maven-plugin/pom.xml @@ -6,12 +6,12 @@ scm-maven-plugins sonia.scm.maven - 1.47-SNAPSHOT + 1.47 sonia.scm.maven scm-maven-plugin - 1.47-SNAPSHOT + 1.47 maven-plugin scm-maven-plugin diff --git a/maven/scm-plugin-archetype/pom.xml b/maven/scm-plugin-archetype/pom.xml index e30464a745..894b947d0e 100644 --- a/maven/scm-plugin-archetype/pom.xml +++ b/maven/scm-plugin-archetype/pom.xml @@ -6,12 +6,12 @@ scm-maven-plugins sonia.scm.maven - 1.47-SNAPSHOT + 1.47 sonia.scm.maven scm-plugin-archetype - 1.47-SNAPSHOT + 1.47 scm-plugin-archetype diff --git a/pom.xml b/pom.xml index bba92379e3..f16529bb70 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ sonia.scm scm pom - 1.47-SNAPSHOT + 1.47 The easiest way to share your Git, Mercurial and Subversion repositories over http. @@ -36,7 +36,7 @@ scm:hg:http://bitbucket.org/sdorra/scm-manager scm:hg:https://bitbucket.org/sdorra/scm-manager http://bitbucket.org/sdorra/scm-manager - HEAD + 1.47 diff --git a/scm-clients/pom.xml b/scm-clients/pom.xml index 6ebe7b4ff8..736ee82d1d 100644 --- a/scm-clients/pom.xml +++ b/scm-clients/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.47-SNAPSHOT + 1.47 sonia.scm.clients scm-clients pom - 1.47-SNAPSHOT + 1.47 scm-clients @@ -32,7 +32,7 @@ scm-core sonia.scm jar - 1.47-SNAPSHOT + 1.47 shiro-core diff --git a/scm-clients/scm-cli-client/pom.xml b/scm-clients/scm-cli-client/pom.xml index d1bd41276b..7f3d7a7ba5 100644 --- a/scm-clients/scm-cli-client/pom.xml +++ b/scm-clients/scm-cli-client/pom.xml @@ -6,12 +6,12 @@ scm-clients sonia.scm.clients - 1.47-SNAPSHOT + 1.47 sonia.scm.clients scm-cli-client - 1.47-SNAPSHOT + 1.47 scm-cli-client @@ -34,7 +34,7 @@ sonia.scm.clients scm-client-impl - 1.47-SNAPSHOT + 1.47 diff --git a/scm-clients/scm-client-api/pom.xml b/scm-clients/scm-client-api/pom.xml index 4014a3f623..69fc7799c9 100644 --- a/scm-clients/scm-client-api/pom.xml +++ b/scm-clients/scm-client-api/pom.xml @@ -6,13 +6,13 @@ sonia.scm.clients scm-clients - 1.47-SNAPSHOT + 1.47 sonia.scm.clients scm-client-api jar - 1.47-SNAPSHOT + 1.47 scm-client-api diff --git a/scm-clients/scm-client-impl/pom.xml b/scm-clients/scm-client-impl/pom.xml index 4ea0544ffc..46d001f154 100644 --- a/scm-clients/scm-client-impl/pom.xml +++ b/scm-clients/scm-client-impl/pom.xml @@ -6,13 +6,13 @@ sonia.scm.clients scm-clients - 1.47-SNAPSHOT + 1.47 sonia.scm.clients scm-client-impl jar - 1.47-SNAPSHOT + 1.47 scm-client-impl @@ -36,7 +36,7 @@ sonia.scm.clients scm-client-api - 1.47-SNAPSHOT + 1.47 @@ -70,7 +70,7 @@ sonia.scm scm-test - 1.47-SNAPSHOT + 1.47 test diff --git a/scm-core/pom.xml b/scm-core/pom.xml index 7e2157d218..646a5b8f8c 100644 --- a/scm-core/pom.xml +++ b/scm-core/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.47-SNAPSHOT + 1.47 sonia.scm scm-core - 1.47-SNAPSHOT + 1.47 scm-core diff --git a/scm-dao-orientdb/pom.xml b/scm-dao-orientdb/pom.xml index 89c7dfc76c..5c7452137e 100644 --- a/scm-dao-orientdb/pom.xml +++ b/scm-dao-orientdb/pom.xml @@ -6,12 +6,12 @@ sonia.scm scm - 1.47-SNAPSHOT + 1.47 sonia.scm scm-dao-orientdb - 1.47-SNAPSHOT + 1.47 scm-dao-orientdb @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.47-SNAPSHOT + 1.47 @@ -52,7 +52,7 @@ sonia.scm scm-test - 1.47-SNAPSHOT + 1.47 test diff --git a/scm-dao-xml/pom.xml b/scm-dao-xml/pom.xml index 9a294bc97f..0c5014752f 100644 --- a/scm-dao-xml/pom.xml +++ b/scm-dao-xml/pom.xml @@ -6,12 +6,12 @@ sonia.scm scm - 1.47-SNAPSHOT + 1.47 sonia.scm scm-dao-xml - 1.47-SNAPSHOT + 1.47 scm-dao-xml @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.47-SNAPSHOT + 1.47 @@ -34,7 +34,7 @@ sonia.scm scm-test - 1.47-SNAPSHOT + 1.47 test diff --git a/scm-plugin-backend/pom.xml b/scm-plugin-backend/pom.xml index 735e6e9d73..b5fe8c0e67 100644 --- a/scm-plugin-backend/pom.xml +++ b/scm-plugin-backend/pom.xml @@ -6,13 +6,13 @@ scm sonia.scm - 1.47-SNAPSHOT + 1.47 sonia.scm scm-plugin-backend war - 1.47-SNAPSHOT + 1.47 ${project.artifactId} @@ -62,7 +62,7 @@ sonia.scm scm-core - 1.47-SNAPSHOT + 1.47 diff --git a/scm-plugins/pom.xml b/scm-plugins/pom.xml index 297eac7f9c..7c396ae813 100644 --- a/scm-plugins/pom.xml +++ b/scm-plugins/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.47-SNAPSHOT + 1.47 sonia.scm.plugins scm-plugins pom - 1.47-SNAPSHOT + 1.47 scm-plugins @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.47-SNAPSHOT + 1.47 @@ -59,7 +59,7 @@ sonia.scm.maven scm-maven-plugin - 1.47-SNAPSHOT + 1.47 process-resources diff --git a/scm-plugins/scm-git-plugin/pom.xml b/scm-plugins/scm-git-plugin/pom.xml index 20412bc9a1..cd2c14d7a3 100644 --- a/scm-plugins/scm-git-plugin/pom.xml +++ b/scm-plugins/scm-git-plugin/pom.xml @@ -6,12 +6,12 @@ scm-plugins sonia.scm.plugins - 1.47-SNAPSHOT + 1.47 sonia.scm.plugins scm-git-plugin - 1.47-SNAPSHOT + 1.47 scm-git-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Git @@ -48,7 +48,7 @@ sonia.scm scm-test - 1.47-SNAPSHOT + 1.47 test diff --git a/scm-plugins/scm-hg-plugin/pom.xml b/scm-plugins/scm-hg-plugin/pom.xml index 8fb45bc664..797ff58174 100644 --- a/scm-plugins/scm-hg-plugin/pom.xml +++ b/scm-plugins/scm-hg-plugin/pom.xml @@ -6,12 +6,12 @@ sonia.scm.plugins scm-plugins - 1.47-SNAPSHOT + 1.47 sonia.scm.plugins scm-hg-plugin - 1.47-SNAPSHOT + 1.47 scm-hg-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Mercurial @@ -42,7 +42,7 @@ sonia.scm scm-test - 1.47-SNAPSHOT + 1.47 test diff --git a/scm-plugins/scm-svn-plugin/pom.xml b/scm-plugins/scm-svn-plugin/pom.xml index 9174f85261..7d430856bf 100644 --- a/scm-plugins/scm-svn-plugin/pom.xml +++ b/scm-plugins/scm-svn-plugin/pom.xml @@ -6,12 +6,12 @@ scm-plugins sonia.scm.plugins - 1.47-SNAPSHOT + 1.47 sonia.scm.plugins scm-svn-plugin - 1.47-SNAPSHOT + 1.47 scm-svn-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Subversion @@ -48,7 +48,7 @@ sonia.scm scm-test - 1.47-SNAPSHOT + 1.47 test diff --git a/scm-samples/pom.xml b/scm-samples/pom.xml index 0452dc7348..03ccf84e8f 100644 --- a/scm-samples/pom.xml +++ b/scm-samples/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.47-SNAPSHOT + 1.47 sonia.scm.samples scm-samples pom - 1.47-SNAPSHOT + 1.47 scm-samples diff --git a/scm-samples/scm-sample-auth/pom.xml b/scm-samples/scm-sample-auth/pom.xml index a08a8faa92..0951c9e5f8 100644 --- a/scm-samples/scm-sample-auth/pom.xml +++ b/scm-samples/scm-sample-auth/pom.xml @@ -6,12 +6,12 @@ scm-samples sonia.scm.samples - 1.47-SNAPSHOT + 1.47 sonia.scm.sample scm-sample-auth - 1.47-SNAPSHOT + 1.47 scm-sample-auth Sample Authentication Plugin https://bitbucket.org/sdorra/scm-manager @@ -28,7 +28,7 @@ sonia.scm scm-core - 1.47-SNAPSHOT + 1.47 diff --git a/scm-samples/scm-sample-hello/pom.xml b/scm-samples/scm-sample-hello/pom.xml index b2e3310e7b..06300ba98e 100644 --- a/scm-samples/scm-sample-hello/pom.xml +++ b/scm-samples/scm-sample-hello/pom.xml @@ -6,12 +6,12 @@ scm-samples sonia.scm.samples - 1.47-SNAPSHOT + 1.47 sonia.scm.sample scm-sample-hello - 1.47-SNAPSHOT + 1.47 scm-sample-hello A simple hello world plugin https://bitbucket.org/sdorra/scm-manager @@ -28,7 +28,7 @@ sonia.scm scm-core - 1.47-SNAPSHOT + 1.47 diff --git a/scm-server/pom.xml b/scm-server/pom.xml index 317bd9ae38..0aa2b6b5bf 100644 --- a/scm-server/pom.xml +++ b/scm-server/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.47-SNAPSHOT + 1.47 sonia.scm scm-server - 1.47-SNAPSHOT + 1.47 scm-server jar diff --git a/scm-test/pom.xml b/scm-test/pom.xml index 0bc2248c24..d61a08e4f9 100644 --- a/scm-test/pom.xml +++ b/scm-test/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.47-SNAPSHOT + 1.47 sonia.scm scm-test - 1.47-SNAPSHOT + 1.47 scm-test @@ -25,7 +25,7 @@ sonia.scm scm-core - 1.47-SNAPSHOT + 1.47 diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index a19c8b57d8..c1283c02e4 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.47-SNAPSHOT + 1.47 sonia.scm scm-webapp war - 1.47-SNAPSHOT + 1.47 scm-webapp @@ -38,31 +38,31 @@ sonia.scm scm-core - 1.47-SNAPSHOT + 1.47 sonia.scm scm-dao-xml - 1.47-SNAPSHOT + 1.47 sonia.scm.plugins scm-hg-plugin - 1.47-SNAPSHOT + 1.47 sonia.scm.plugins scm-svn-plugin - 1.47-SNAPSHOT + 1.47 sonia.scm.plugins scm-git-plugin - 1.47-SNAPSHOT + 1.47 @@ -286,7 +286,7 @@ sonia.scm scm-test - 1.47-SNAPSHOT + 1.47 test @@ -299,7 +299,7 @@ sonia.scm.plugins scm-git-plugin - 1.47-SNAPSHOT + 1.47 tests test @@ -307,7 +307,7 @@ sonia.scm.plugins scm-hg-plugin - 1.47-SNAPSHOT + 1.47 tests test @@ -315,7 +315,7 @@ sonia.scm.plugins scm-svn-plugin - 1.47-SNAPSHOT + 1.47 tests test @@ -541,7 +541,7 @@ sonia.scm scm-dao-orientdb - 1.47-SNAPSHOT + 1.47 diff --git a/support/pom.xml b/support/pom.xml index ae6bb2cd11..b4bf09d02a 100644 --- a/support/pom.xml +++ b/support/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.47-SNAPSHOT + 1.47 sonia.scm.support scm-support pom - 1.47-SNAPSHOT + 1.47 scm-support diff --git a/support/scm-support-btrace/pom.xml b/support/scm-support-btrace/pom.xml index 1f26a49fee..b7f5650017 100644 --- a/support/scm-support-btrace/pom.xml +++ b/support/scm-support-btrace/pom.xml @@ -4,12 +4,12 @@ sonia.scm.support scm-support - 1.47-SNAPSHOT + 1.47 sonia.scm scm-support-btrace - 1.47-SNAPSHOT + 1.47 jar scm-support-btrace @@ -18,7 +18,7 @@ sonia.scm scm-core - 1.47-SNAPSHOT + 1.47 From 2ac56dc110e2474a2b00e28555fb64963be5a897 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 31 May 2016 11:12:37 +0200 Subject: [PATCH 095/171] [maven-release-plugin] copy for tag 1.47 From db6ef804dcb8acbdcbb8a45a027be9cd5e1986b3 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 31 May 2016 11:12:38 +0200 Subject: [PATCH 096/171] [maven-release-plugin] prepare for next development iteration --- maven/pom.xml | 4 ++-- maven/scm-maven-plugin/pom.xml | 4 ++-- maven/scm-plugin-archetype/pom.xml | 4 ++-- pom.xml | 4 ++-- scm-clients/pom.xml | 6 +++--- scm-clients/scm-cli-client/pom.xml | 6 +++--- scm-clients/scm-client-api/pom.xml | 4 ++-- scm-clients/scm-client-impl/pom.xml | 8 ++++---- scm-core/pom.xml | 4 ++-- scm-dao-orientdb/pom.xml | 8 ++++---- scm-dao-xml/pom.xml | 8 ++++---- scm-plugin-backend/pom.xml | 6 +++--- scm-plugins/pom.xml | 8 ++++---- scm-plugins/scm-git-plugin/pom.xml | 6 +++--- scm-plugins/scm-hg-plugin/pom.xml | 6 +++--- scm-plugins/scm-svn-plugin/pom.xml | 6 +++--- scm-samples/pom.xml | 4 ++-- scm-samples/scm-sample-auth/pom.xml | 6 +++--- scm-samples/scm-sample-hello/pom.xml | 6 +++--- scm-server/pom.xml | 4 ++-- scm-test/pom.xml | 6 +++--- scm-webapp/pom.xml | 24 ++++++++++++------------ support/pom.xml | 4 ++-- support/scm-support-btrace/pom.xml | 6 +++--- 24 files changed, 76 insertions(+), 76 deletions(-) diff --git a/maven/pom.xml b/maven/pom.xml index b142855c2b..1268acc7e4 100644 --- a/maven/pom.xml +++ b/maven/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.47 + 1.48-SNAPSHOT sonia.scm.maven scm-maven-plugins pom - 1.47 + 1.48-SNAPSHOT scm-maven-plugins diff --git a/maven/scm-maven-plugin/pom.xml b/maven/scm-maven-plugin/pom.xml index 278ed8b9cf..9a818023da 100644 --- a/maven/scm-maven-plugin/pom.xml +++ b/maven/scm-maven-plugin/pom.xml @@ -6,12 +6,12 @@ scm-maven-plugins sonia.scm.maven - 1.47 + 1.48-SNAPSHOT sonia.scm.maven scm-maven-plugin - 1.47 + 1.48-SNAPSHOT maven-plugin scm-maven-plugin diff --git a/maven/scm-plugin-archetype/pom.xml b/maven/scm-plugin-archetype/pom.xml index 894b947d0e..98d3ab5439 100644 --- a/maven/scm-plugin-archetype/pom.xml +++ b/maven/scm-plugin-archetype/pom.xml @@ -6,12 +6,12 @@ scm-maven-plugins sonia.scm.maven - 1.47 + 1.48-SNAPSHOT sonia.scm.maven scm-plugin-archetype - 1.47 + 1.48-SNAPSHOT scm-plugin-archetype diff --git a/pom.xml b/pom.xml index f16529bb70..01aed23935 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ sonia.scm scm pom - 1.47 + 1.48-SNAPSHOT The easiest way to share your Git, Mercurial and Subversion repositories over http. @@ -36,7 +36,7 @@ scm:hg:http://bitbucket.org/sdorra/scm-manager scm:hg:https://bitbucket.org/sdorra/scm-manager http://bitbucket.org/sdorra/scm-manager - 1.47 + HEAD diff --git a/scm-clients/pom.xml b/scm-clients/pom.xml index 736ee82d1d..143ae58fdf 100644 --- a/scm-clients/pom.xml +++ b/scm-clients/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.47 + 1.48-SNAPSHOT sonia.scm.clients scm-clients pom - 1.47 + 1.48-SNAPSHOT scm-clients @@ -32,7 +32,7 @@ scm-core sonia.scm jar - 1.47 + 1.48-SNAPSHOT shiro-core diff --git a/scm-clients/scm-cli-client/pom.xml b/scm-clients/scm-cli-client/pom.xml index 7f3d7a7ba5..72f83e1d82 100644 --- a/scm-clients/scm-cli-client/pom.xml +++ b/scm-clients/scm-cli-client/pom.xml @@ -6,12 +6,12 @@ scm-clients sonia.scm.clients - 1.47 + 1.48-SNAPSHOT sonia.scm.clients scm-cli-client - 1.47 + 1.48-SNAPSHOT scm-cli-client @@ -34,7 +34,7 @@ sonia.scm.clients scm-client-impl - 1.47 + 1.48-SNAPSHOT diff --git a/scm-clients/scm-client-api/pom.xml b/scm-clients/scm-client-api/pom.xml index 69fc7799c9..babe71c9bc 100644 --- a/scm-clients/scm-client-api/pom.xml +++ b/scm-clients/scm-client-api/pom.xml @@ -6,13 +6,13 @@ sonia.scm.clients scm-clients - 1.47 + 1.48-SNAPSHOT sonia.scm.clients scm-client-api jar - 1.47 + 1.48-SNAPSHOT scm-client-api diff --git a/scm-clients/scm-client-impl/pom.xml b/scm-clients/scm-client-impl/pom.xml index 46d001f154..911a962f7a 100644 --- a/scm-clients/scm-client-impl/pom.xml +++ b/scm-clients/scm-client-impl/pom.xml @@ -6,13 +6,13 @@ sonia.scm.clients scm-clients - 1.47 + 1.48-SNAPSHOT sonia.scm.clients scm-client-impl jar - 1.47 + 1.48-SNAPSHOT scm-client-impl @@ -36,7 +36,7 @@ sonia.scm.clients scm-client-api - 1.47 + 1.48-SNAPSHOT @@ -70,7 +70,7 @@ sonia.scm scm-test - 1.47 + 1.48-SNAPSHOT test diff --git a/scm-core/pom.xml b/scm-core/pom.xml index 646a5b8f8c..a3f56f355a 100644 --- a/scm-core/pom.xml +++ b/scm-core/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.47 + 1.48-SNAPSHOT sonia.scm scm-core - 1.47 + 1.48-SNAPSHOT scm-core diff --git a/scm-dao-orientdb/pom.xml b/scm-dao-orientdb/pom.xml index 5c7452137e..b2345d6c8d 100644 --- a/scm-dao-orientdb/pom.xml +++ b/scm-dao-orientdb/pom.xml @@ -6,12 +6,12 @@ sonia.scm scm - 1.47 + 1.48-SNAPSHOT sonia.scm scm-dao-orientdb - 1.47 + 1.48-SNAPSHOT scm-dao-orientdb @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.47 + 1.48-SNAPSHOT @@ -52,7 +52,7 @@ sonia.scm scm-test - 1.47 + 1.48-SNAPSHOT test diff --git a/scm-dao-xml/pom.xml b/scm-dao-xml/pom.xml index 0c5014752f..5e61796443 100644 --- a/scm-dao-xml/pom.xml +++ b/scm-dao-xml/pom.xml @@ -6,12 +6,12 @@ sonia.scm scm - 1.47 + 1.48-SNAPSHOT sonia.scm scm-dao-xml - 1.47 + 1.48-SNAPSHOT scm-dao-xml @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.47 + 1.48-SNAPSHOT @@ -34,7 +34,7 @@ sonia.scm scm-test - 1.47 + 1.48-SNAPSHOT test diff --git a/scm-plugin-backend/pom.xml b/scm-plugin-backend/pom.xml index b5fe8c0e67..788a0334fc 100644 --- a/scm-plugin-backend/pom.xml +++ b/scm-plugin-backend/pom.xml @@ -6,13 +6,13 @@ scm sonia.scm - 1.47 + 1.48-SNAPSHOT sonia.scm scm-plugin-backend war - 1.47 + 1.48-SNAPSHOT ${project.artifactId} @@ -62,7 +62,7 @@ sonia.scm scm-core - 1.47 + 1.48-SNAPSHOT diff --git a/scm-plugins/pom.xml b/scm-plugins/pom.xml index 7c396ae813..24150b2da9 100644 --- a/scm-plugins/pom.xml +++ b/scm-plugins/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.47 + 1.48-SNAPSHOT sonia.scm.plugins scm-plugins pom - 1.47 + 1.48-SNAPSHOT scm-plugins @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.47 + 1.48-SNAPSHOT @@ -59,7 +59,7 @@ sonia.scm.maven scm-maven-plugin - 1.47 + 1.48-SNAPSHOT process-resources diff --git a/scm-plugins/scm-git-plugin/pom.xml b/scm-plugins/scm-git-plugin/pom.xml index cd2c14d7a3..2d5520a958 100644 --- a/scm-plugins/scm-git-plugin/pom.xml +++ b/scm-plugins/scm-git-plugin/pom.xml @@ -6,12 +6,12 @@ scm-plugins sonia.scm.plugins - 1.47 + 1.48-SNAPSHOT sonia.scm.plugins scm-git-plugin - 1.47 + 1.48-SNAPSHOT scm-git-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Git @@ -48,7 +48,7 @@ sonia.scm scm-test - 1.47 + 1.48-SNAPSHOT test diff --git a/scm-plugins/scm-hg-plugin/pom.xml b/scm-plugins/scm-hg-plugin/pom.xml index 797ff58174..6003a7ba47 100644 --- a/scm-plugins/scm-hg-plugin/pom.xml +++ b/scm-plugins/scm-hg-plugin/pom.xml @@ -6,12 +6,12 @@ sonia.scm.plugins scm-plugins - 1.47 + 1.48-SNAPSHOT sonia.scm.plugins scm-hg-plugin - 1.47 + 1.48-SNAPSHOT scm-hg-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Mercurial @@ -42,7 +42,7 @@ sonia.scm scm-test - 1.47 + 1.48-SNAPSHOT test diff --git a/scm-plugins/scm-svn-plugin/pom.xml b/scm-plugins/scm-svn-plugin/pom.xml index 7d430856bf..59efbfd272 100644 --- a/scm-plugins/scm-svn-plugin/pom.xml +++ b/scm-plugins/scm-svn-plugin/pom.xml @@ -6,12 +6,12 @@ scm-plugins sonia.scm.plugins - 1.47 + 1.48-SNAPSHOT sonia.scm.plugins scm-svn-plugin - 1.47 + 1.48-SNAPSHOT scm-svn-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Subversion @@ -48,7 +48,7 @@ sonia.scm scm-test - 1.47 + 1.48-SNAPSHOT test diff --git a/scm-samples/pom.xml b/scm-samples/pom.xml index 03ccf84e8f..af9c057725 100644 --- a/scm-samples/pom.xml +++ b/scm-samples/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.47 + 1.48-SNAPSHOT sonia.scm.samples scm-samples pom - 1.47 + 1.48-SNAPSHOT scm-samples diff --git a/scm-samples/scm-sample-auth/pom.xml b/scm-samples/scm-sample-auth/pom.xml index 0951c9e5f8..250bc1e5d7 100644 --- a/scm-samples/scm-sample-auth/pom.xml +++ b/scm-samples/scm-sample-auth/pom.xml @@ -6,12 +6,12 @@ scm-samples sonia.scm.samples - 1.47 + 1.48-SNAPSHOT sonia.scm.sample scm-sample-auth - 1.47 + 1.48-SNAPSHOT scm-sample-auth Sample Authentication Plugin https://bitbucket.org/sdorra/scm-manager @@ -28,7 +28,7 @@ sonia.scm scm-core - 1.47 + 1.48-SNAPSHOT diff --git a/scm-samples/scm-sample-hello/pom.xml b/scm-samples/scm-sample-hello/pom.xml index 06300ba98e..7592cb18fd 100644 --- a/scm-samples/scm-sample-hello/pom.xml +++ b/scm-samples/scm-sample-hello/pom.xml @@ -6,12 +6,12 @@ scm-samples sonia.scm.samples - 1.47 + 1.48-SNAPSHOT sonia.scm.sample scm-sample-hello - 1.47 + 1.48-SNAPSHOT scm-sample-hello A simple hello world plugin https://bitbucket.org/sdorra/scm-manager @@ -28,7 +28,7 @@ sonia.scm scm-core - 1.47 + 1.48-SNAPSHOT diff --git a/scm-server/pom.xml b/scm-server/pom.xml index 0aa2b6b5bf..1a672a31cf 100644 --- a/scm-server/pom.xml +++ b/scm-server/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.47 + 1.48-SNAPSHOT sonia.scm scm-server - 1.47 + 1.48-SNAPSHOT scm-server jar diff --git a/scm-test/pom.xml b/scm-test/pom.xml index d61a08e4f9..fe95e257a7 100644 --- a/scm-test/pom.xml +++ b/scm-test/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.47 + 1.48-SNAPSHOT sonia.scm scm-test - 1.47 + 1.48-SNAPSHOT scm-test @@ -25,7 +25,7 @@ sonia.scm scm-core - 1.47 + 1.48-SNAPSHOT diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index c1283c02e4..b30833a968 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.47 + 1.48-SNAPSHOT sonia.scm scm-webapp war - 1.47 + 1.48-SNAPSHOT scm-webapp @@ -38,31 +38,31 @@ sonia.scm scm-core - 1.47 + 1.48-SNAPSHOT sonia.scm scm-dao-xml - 1.47 + 1.48-SNAPSHOT sonia.scm.plugins scm-hg-plugin - 1.47 + 1.48-SNAPSHOT sonia.scm.plugins scm-svn-plugin - 1.47 + 1.48-SNAPSHOT sonia.scm.plugins scm-git-plugin - 1.47 + 1.48-SNAPSHOT @@ -286,7 +286,7 @@ sonia.scm scm-test - 1.47 + 1.48-SNAPSHOT test @@ -299,7 +299,7 @@ sonia.scm.plugins scm-git-plugin - 1.47 + 1.48-SNAPSHOT tests test @@ -307,7 +307,7 @@ sonia.scm.plugins scm-hg-plugin - 1.47 + 1.48-SNAPSHOT tests test @@ -315,7 +315,7 @@ sonia.scm.plugins scm-svn-plugin - 1.47 + 1.48-SNAPSHOT tests test @@ -541,7 +541,7 @@ sonia.scm scm-dao-orientdb - 1.47 + 1.48-SNAPSHOT diff --git a/support/pom.xml b/support/pom.xml index b4bf09d02a..d2b787d62c 100644 --- a/support/pom.xml +++ b/support/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.47 + 1.48-SNAPSHOT sonia.scm.support scm-support pom - 1.47 + 1.48-SNAPSHOT scm-support diff --git a/support/scm-support-btrace/pom.xml b/support/scm-support-btrace/pom.xml index b7f5650017..9f8264251f 100644 --- a/support/scm-support-btrace/pom.xml +++ b/support/scm-support-btrace/pom.xml @@ -4,12 +4,12 @@ sonia.scm.support scm-support - 1.47 + 1.48-SNAPSHOT sonia.scm scm-support-btrace - 1.47 + 1.48-SNAPSHOT jar scm-support-btrace @@ -18,7 +18,7 @@ sonia.scm scm-core - 1.47 + 1.48-SNAPSHOT From 981d203723f7bbdfa887dffa51fbf7a8a59289f4 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 23 Jun 2016 14:06:03 +0200 Subject: [PATCH 097/171] added request uri to mdc logging context --- scm-webapp/pom.xml | 6 + .../main/java/sonia/scm/filter/MDCFilter.java | 16 ++- .../java/sonia/scm/filter/MDCFilterTest.java | 128 ++++++++++++++++++ .../test/resources/sonia/scm/shiro-001.ini | 6 + 4 files changed, 153 insertions(+), 3 deletions(-) create mode 100644 scm-webapp/src/test/java/sonia/scm/filter/MDCFilterTest.java create mode 100644 scm-webapp/src/test/resources/sonia/scm/shiro-001.ini diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index b30833a968..c7218797eb 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -348,6 +348,12 @@ test + + com.github.sdorra + shiro-unit + 1.0.0 + + diff --git a/scm-webapp/src/main/java/sonia/scm/filter/MDCFilter.java b/scm-webapp/src/main/java/sonia/scm/filter/MDCFilter.java index 6630543bc3..5c14269ffb 100644 --- a/scm-webapp/src/main/java/sonia/scm/filter/MDCFilter.java +++ b/scm-webapp/src/main/java/sonia/scm/filter/MDCFilter.java @@ -33,6 +33,7 @@ package sonia.scm.filter; //~--- non-JDK imports -------------------------------------------------------- +import com.google.common.annotations.VisibleForTesting; import com.google.inject.Singleton; import org.apache.shiro.SecurityUtils; @@ -61,13 +62,20 @@ public class MDCFilter extends HttpFilter { /** Field description */ - private static final String MDC_CLIEN_HOST = "client_host"; + @VisibleForTesting + static final String MDC_CLIEN_HOST = "client_host"; /** Field description */ - private static final String MDC_CLIEN_IP = "client_ip"; + @VisibleForTesting + static final String MDC_CLIEN_IP = "client_ip"; + + /** url of the current request */ + @VisibleForTesting + static final String MDC_REQUEST_URI = "request_uri"; /** Field description */ - private static final String MDC_USERNAME = "username"; + @VisibleForTesting + static final String MDC_USERNAME = "username"; //~--- methods -------------------------------------------------------------- @@ -90,6 +98,7 @@ public class MDCFilter extends HttpFilter MDC.put(MDC_USERNAME, getUsername()); MDC.put(MDC_CLIEN_IP, request.getRemoteAddr()); MDC.put(MDC_CLIEN_HOST, request.getRemoteHost()); + MDC.put(MDC_REQUEST_URI, request.getRequestURI()); try { @@ -100,6 +109,7 @@ public class MDCFilter extends HttpFilter MDC.remove(MDC_USERNAME); MDC.remove(MDC_CLIEN_IP); MDC.remove(MDC_CLIEN_HOST); + MDC.remove(MDC_REQUEST_URI); } } diff --git a/scm-webapp/src/test/java/sonia/scm/filter/MDCFilterTest.java b/scm-webapp/src/test/java/sonia/scm/filter/MDCFilterTest.java new file mode 100644 index 0000000000..b5e1753c52 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/filter/MDCFilterTest.java @@ -0,0 +1,128 @@ +/*** + * Copyright (c) 2015, 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. + * + * https://bitbucket.org/sdorra/scm-manager + * + */ + +package sonia.scm.filter; + +import com.github.sdorra.shiro.ShiroRule; +import com.github.sdorra.shiro.SubjectAware; +import java.io.IOException; +import java.util.Map; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.junit.Test; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; +import org.junit.Rule; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.slf4j.MDC; +import sonia.scm.AbstractTestBase; + +/** + * Unit tests for {@link MDCFilter}. + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +public class MDCFilterTest extends AbstractTestBase { + + @Rule + public ShiroRule shiro = new ShiroRule(); + + @Mock + private HttpServletRequest request; + + @Mock + private HttpServletResponse response; + + private final MDCFilter filter = new MDCFilter(); + + /** + * Tests {@link MDCFilter#doFilter(HttpServletRequest, HttpServletResponse, FilterChain)}. + * + * @throws IOException + * @throws ServletException + */ + @Test + @SubjectAware( + username = "trillian", + password = "secret", + configuration = "classpath:sonia/scm/shiro-001.ini" + ) + public void testDoFilter() throws IOException, ServletException + { + when(request.getRequestURI()).thenReturn("api/v1/repositories"); + when(request.getRemoteAddr()).thenReturn("127.0.0.1"); + when(request.getRemoteHost()).thenReturn("localhost"); + + MDCCapturingFilterChain chain = new MDCCapturingFilterChain(); + filter.doFilter(request, response, chain); + + assertNotNull(chain.ctx); + assertEquals("trillian", chain.ctx.get(MDCFilter.MDC_USERNAME)); + assertEquals("api/v1/repositories", chain.ctx.get(MDCFilter.MDC_REQUEST_URI)); + assertEquals("127.0.0.1", chain.ctx.get(MDCFilter.MDC_CLIEN_IP)); + assertEquals("localhost", chain.ctx.get(MDCFilter.MDC_CLIEN_HOST)); + } + + /** + * Tests {@link MDCFilter#doFilter(HttpServletRequest, HttpServletResponse, FilterChain)} as anonymous user. + * + * @throws IOException + * @throws ServletException + */ + @Test + @SubjectAware + public void testDoFilterAsAnonymous() throws IOException, ServletException { + MDCCapturingFilterChain chain = new MDCCapturingFilterChain(); + filter.doFilter(request, response, chain); + + assertNotNull(chain.ctx); + assertEquals("anonymous", chain.ctx.get(MDCFilter.MDC_USERNAME)); + } + + private static class MDCCapturingFilterChain implements FilterChain { + + private Map ctx; + + @Override + public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { + this.ctx = MDC.getCopyOfContextMap(); + } + + } + +} \ No newline at end of file diff --git a/scm-webapp/src/test/resources/sonia/scm/shiro-001.ini b/scm-webapp/src/test/resources/sonia/scm/shiro-001.ini new file mode 100644 index 0000000000..0202162d00 --- /dev/null +++ b/scm-webapp/src/test/resources/sonia/scm/shiro-001.ini @@ -0,0 +1,6 @@ +[users] +trillian = secret, user + +[roles] +admin = * +user = something:* From f5e4f4ae717a498d6987d75e613724711ec406cb Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 23 Jun 2016 14:07:12 +0200 Subject: [PATCH 098/171] fix typo in log message --- .../main/java/sonia/scm/security/AuthorizationCollector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java b/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java index 192646f8cf..942a431f28 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java +++ b/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java @@ -377,7 +377,7 @@ public class AuthorizationCollector } else if (logger.isTraceEnabled()) { - logger.trace("repository {} has not permission entries", + logger.trace("repository {} has no permission entries", repository.getName()); } } From b5383f40736d4c59ebafe775f8a29c65099defa2 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Fri, 24 Jun 2016 17:58:02 +0200 Subject: [PATCH 099/171] fix loading of repository handler informations on some systems, see issue #841 --- .../main/java/sonia/scm/repository/GitRepositoryHandler.java | 2 +- .../src/main/java/sonia/scm/repository/HgRepositoryHandler.java | 2 +- .../main/java/sonia/scm/repository/SvnRepositoryHandler.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHandler.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHandler.java index d919eacb68..b5cb96c290 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHandler.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHandler.java @@ -77,7 +77,7 @@ public class GitRepositoryHandler /** Field description */ public static final String RESOURCE_VERSION = - "/sonia/scm/version/scm-git-plugin"; + "sonia/scm/version/scm-git-plugin"; /** Field description */ public static final String TYPE_DISPLAYNAME = "Git"; diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java index 6cf9b7ca70..bbfb555da1 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java @@ -90,7 +90,7 @@ public class HgRepositoryHandler /** Field description */ public static final String RESOURCE_VERSION = - "/sonia/scm/version/scm-hg-plugin"; + "sonia/scm/version/scm-hg-plugin"; /** Field description */ public static final String TYPE_DISPLAYNAME = "Mercurial"; diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnRepositoryHandler.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnRepositoryHandler.java index 3cea8e83d9..e8357b565d 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnRepositoryHandler.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnRepositoryHandler.java @@ -79,7 +79,7 @@ public class SvnRepositoryHandler /** Field description */ public static final String RESOURCE_VERSION = - "/sonia/scm/version/scm-svn-plugin"; + "sonia/scm/version/scm-svn-plugin"; /** Field description */ public static final String TYPE_DISPLAYNAME = "Subversion"; From 7873cd556d742287f23c72ddb873baac123bb33c Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Fri, 24 Jun 2016 19:32:45 +0200 Subject: [PATCH 100/171] improve trace logging of AuthorizationCollector --- .../scm/security/AuthorizationCollector.java | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java b/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java index 942a431f28..26e22c9e0c 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java +++ b/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java @@ -281,11 +281,6 @@ public class AuthorizationCollector private void collectGlobalPermissions(Builder builder, final User user, final GroupNames groups) { - if (logger.isTraceEnabled()) - { - logger.trace("collect global permissions for user {}", user.getName()); - } - List globalPermissions = securitySystem.getPermissions(new Predicate() { @@ -293,7 +288,7 @@ public class AuthorizationCollector @Override public boolean apply(AssignedPermission input) { - return isUserPermission(user, groups, input); + return isUserPermitted(user, groups, input); } }); @@ -329,12 +324,6 @@ public class AuthorizationCollector { for (Repository repository : repositoryDAO.getAll()) { - if (logger.isTraceEnabled()) - { - logger.trace("collect permissions for repository {} and user {}", - repository.getName(), user.getName()); - } - collectRepositoryPermissions(builder, repository, user, groups); } } @@ -357,23 +346,29 @@ public class AuthorizationCollector if (Util.isNotEmpty(repositoryPermissions)) { - + boolean hasPermission = false; for (sonia.scm.repository.Permission permission : repositoryPermissions) { - if (isUserPermission(user, groups, permission)) + if (isUserPermitted(user, groups, permission)) { + hasPermission = true; RepositoryPermission rp = new RepositoryPermission(repository, permission.getType()); if (logger.isTraceEnabled()) { - logger.trace("add repository permission {} for user {}", rp, - user.getName()); + logger.trace("add repository permission {} for user {} at repository {}", rp, + user.getName(), repository.getName()); } builder.add(rp); } } + + if (!hasPermission && logger.isTraceEnabled()) + { + logger.trace("no permission for user {} defined at repository {}", user.getName(), repository.getName()); + } } else if (logger.isTraceEnabled()) { @@ -445,7 +440,7 @@ public class AuthorizationCollector * * @return */ - private boolean isUserPermission(User user, GroupNames groups, + private boolean isUserPermitted(User user, GroupNames groups, PermissionObject perm) { //J- From c86ec11f090a24d1f05395eea35009e8afaaa72a Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Fri, 24 Jun 2016 19:46:16 +0200 Subject: [PATCH 101/171] start implementation of unit tests for AuthorizationCollector --- .../security/AuthorizationCollectorTest.java | 147 ++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 scm-webapp/src/test/java/sonia/scm/security/AuthorizationCollectorTest.java diff --git a/scm-webapp/src/test/java/sonia/scm/security/AuthorizationCollectorTest.java b/scm-webapp/src/test/java/sonia/scm/security/AuthorizationCollectorTest.java new file mode 100644 index 0000000000..3e7986eaf6 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/security/AuthorizationCollectorTest.java @@ -0,0 +1,147 @@ +/** + * 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.security; + +import org.apache.shiro.authz.permission.PermissionResolver; +import org.junit.Test; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import static org.mockito.Mockito.*; +import org.mockito.runners.MockitoJUnitRunner; +import sonia.scm.HandlerEvent; +import sonia.scm.cache.Cache; +import sonia.scm.cache.CacheManager; +import sonia.scm.group.Group; +import sonia.scm.group.GroupEvent; +import sonia.scm.repository.Repository; +import sonia.scm.repository.RepositoryDAO; +import sonia.scm.repository.RepositoryEvent; +import sonia.scm.repository.RepositoryTestData; +import sonia.scm.user.User; +import sonia.scm.user.UserEvent; +import sonia.scm.user.UserTestData; + +/** + * Unit tests for {@link AuthorizationCollector}. + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +public class AuthorizationCollectorTest { + + @Mock + private Cache cache; + + @Mock + private CacheManager cacheManager; + + @Mock + private RepositoryDAO repositoryDAO; + + @Mock + private PermissionResolver resolver; + + @Mock + private SecuritySystem securitySystem; + + private AuthorizationCollector collector; + + /** + * Set up object to test. + */ + @Before + public void setUp(){ + when(cacheManager.getCache(Mockito.any(Class.class), Mockito.any(Class.class), Mockito.any(String.class))) + .thenReturn(cache); + + collector = new AuthorizationCollector(cacheManager, repositoryDAO, securitySystem, resolver); + } + + /** + * Tests {@link AuthorizationCollector#onEvent(sonia.scm.user.UserEvent)}. + */ + @Test + public void testOnUserEvent() + { + User user = UserTestData.createDent(); + collector.onEvent(new UserEvent(user, HandlerEvent.BEFORE_CREATE)); + verify(cache, never()).clear(); + + collector.onEvent(new UserEvent(user, HandlerEvent.CREATE)); + verify(cache).clear(); + } + + /** + * Tests {@link AuthorizationCollector#onEvent(sonia.scm.group.GroupEvent)}. + */ + @Test + public void testOnGroupEvent() + { + Group group = new Group("xml", "base"); + collector.onEvent(new GroupEvent(group, HandlerEvent.BEFORE_CREATE)); + verify(cache, never()).clear(); + + collector.onEvent(new GroupEvent(group, HandlerEvent.CREATE)); + verify(cache).clear(); + } + + /** + * Tests {@link AuthorizationCollector#onEvent(sonia.scm.repository.RepositoryEvent)}. + */ + @Test + public void testOnRepositoryEvent() + { + Repository repository = RepositoryTestData.createHeartOfGold(); + collector.onEvent(new RepositoryEvent(repository, HandlerEvent.BEFORE_CREATE)); + verify(cache, never()).clear(); + + collector.onEvent(new RepositoryEvent(repository, HandlerEvent.CREATE)); + verify(cache).clear(); + } + + /** + * Tests {@link AuthorizationCollector#onEvent(sonia.scm.security.StoredAssignedPermissionEvent)}. + */ + @Test + public void testOnStoredAssignedPermissionEvent() + { + StoredAssignedPermission permission = new StoredAssignedPermission(); + collector.onEvent(new StoredAssignedPermissionEvent(HandlerEvent.BEFORE_CREATE, permission)); + verify(cache, never()).clear(); + + collector.onEvent(new StoredAssignedPermissionEvent(HandlerEvent.CREATE, permission)); + verify(cache).clear(); + } + +} \ No newline at end of file From 1eb5a6c9adcf155e6b030032b6208583ef19dea0 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sat, 25 Jun 2016 14:02:18 +0200 Subject: [PATCH 102/171] unit tests for AuthorizationCollector --- .../security/AuthorizationCollectorTest.java | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) diff --git a/scm-webapp/src/test/java/sonia/scm/security/AuthorizationCollectorTest.java b/scm-webapp/src/test/java/sonia/scm/security/AuthorizationCollectorTest.java index 3e7986eaf6..ad63fcd730 100644 --- a/scm-webapp/src/test/java/sonia/scm/security/AuthorizationCollectorTest.java +++ b/scm-webapp/src/test/java/sonia/scm/security/AuthorizationCollectorTest.java @@ -31,19 +31,35 @@ package sonia.scm.security; +import com.github.sdorra.shiro.ShiroRule; +import com.github.sdorra.shiro.SubjectAware; +import com.google.common.base.Predicate; +import com.google.common.collect.Lists; +import org.apache.shiro.authz.AuthorizationInfo; +import org.apache.shiro.authz.Permission; +import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.authz.permission.PermissionResolver; +import org.apache.shiro.authz.permission.WildcardPermission; +import org.apache.shiro.subject.SimplePrincipalCollection; +import org.apache.shiro.subject.Subject; +import org.hamcrest.Matchers; import org.junit.Test; import org.junit.Before; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import static org.mockito.Mockito.*; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; +import org.junit.Rule; import org.mockito.runners.MockitoJUnitRunner; import sonia.scm.HandlerEvent; import sonia.scm.cache.Cache; import sonia.scm.cache.CacheManager; import sonia.scm.group.Group; import sonia.scm.group.GroupEvent; +import sonia.scm.group.GroupNames; +import sonia.scm.repository.PermissionType; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryDAO; import sonia.scm.repository.RepositoryEvent; @@ -77,6 +93,9 @@ public class AuthorizationCollectorTest { private AuthorizationCollector collector; + @Rule + public ShiroRule shiro = new ShiroRule(); + /** * Set up object to test. */ @@ -143,5 +162,152 @@ public class AuthorizationCollectorTest { collector.onEvent(new StoredAssignedPermissionEvent(HandlerEvent.CREATE, permission)); verify(cache).clear(); } + + /** + * Tests {@link AuthorizationCollector#collect()} without user role. + */ + @Test + @SubjectAware + public void testCollectWithoutUserRole() + { + AuthorizationInfo authInfo = collector.collect(); + assertThat(authInfo.getRoles(), nullValue()); + assertThat(authInfo.getStringPermissions(), nullValue()); + assertThat(authInfo.getObjectPermissions(), nullValue()); + } + + /** + * Tests {@link AuthorizationCollector#collect()} from cache. + */ + @Test + @SubjectAware( + configuration = "classpath:sonia/scm/shiro-001.ini" + ) + public void testCollectFromCache() + { + AuthorizationInfo info = new SimpleAuthorizationInfo(); + when(cache.get(anyObject())).thenReturn(info); + authenticate(UserTestData.createTrillian(), "main"); + + AuthorizationInfo authInfo = collector.collect(); + assertSame(info, authInfo); + } + + /** + * Tests {@link AuthorizationCollector#collect()} with cache. + */ + @Test + @SubjectAware( + configuration = "classpath:sonia/scm/shiro-001.ini" + ) + public void testCollectWithCache(){ + authenticate(UserTestData.createTrillian(), "main"); + + AuthorizationInfo authInfo = collector.collect(); + verify(cache).put(any(), any()); + } + + /** + * Tests {@link AuthorizationCollector#collect()} without permissions. + */ + @Test + @SubjectAware( + configuration = "classpath:sonia/scm/shiro-001.ini" + ) + public void testCollectWithoutPermissions() + { + authenticate(UserTestData.createTrillian(), "main"); + + AuthorizationInfo authInfo = collector.collect(); + assertThat(authInfo.getRoles(), Matchers.contains(Role.USER)); + assertThat(authInfo.getStringPermissions(), nullValue()); + assertThat(authInfo.getObjectPermissions(), hasSize(0)); + } + + /** + * Tests {@link AuthorizationCollector#collect()} as admin. + */ + @Test + @SubjectAware( + configuration = "classpath:sonia/scm/shiro-001.ini" + ) + public void testCollectAsAdmin() + { + User trillian = UserTestData.createTrillian(); + trillian.setAdmin(true); + authenticate(trillian, "main"); + + AuthorizationInfo authInfo = collector.collect(); + assertThat(authInfo.getRoles(), Matchers.containsInAnyOrder(Role.USER, Role.ADMIN)); + assertThat(authInfo.getStringPermissions(), nullValue()); + Permission permission = new RepositoryPermission(RepositoryPermission.WILDCARD, PermissionType.OWNER); + assertThat(authInfo.getObjectPermissions(), contains(permission)); + } + + /** + * Tests {@link AuthorizationCollector#collect()} with repository permissions. + */ + @Test + @SubjectAware( + configuration = "classpath:sonia/scm/shiro-001.ini" + ) + public void testCollectWithRepositoryPermissions() + { + String group = "heart-of-gold-crew"; + authenticate(UserTestData.createTrillian(), group); + Repository heartOfGold = RepositoryTestData.createHeartOfGold(); + heartOfGold.setId("one"); + heartOfGold.setPermissions(Lists.newArrayList(new sonia.scm.repository.Permission("trillian"))); + Repository puzzle42 = RepositoryTestData.create42Puzzle(); + puzzle42.setId("two"); + sonia.scm.repository.Permission permission = new sonia.scm.repository.Permission(group, PermissionType.WRITE, true); + puzzle42.setPermissions(Lists.newArrayList(permission)); + when(repositoryDAO.getAll()).thenReturn(Lists.newArrayList(heartOfGold, puzzle42)); + + // execute and assert + AuthorizationInfo authInfo = collector.collect(); + assertThat(authInfo.getRoles(), Matchers.containsInAnyOrder(Role.USER)); + assertThat(authInfo.getStringPermissions(), nullValue()); + Permission p1 = new RepositoryPermission("one", PermissionType.READ); + Permission p2 = new RepositoryPermission("two", PermissionType.WRITE); + assertThat(authInfo.getObjectPermissions(), containsInAnyOrder(p1, p2)); + } + + /** + * Tests {@link AuthorizationCollector#collect()} with global permissions. + */ + @Test + @SubjectAware( + configuration = "classpath:sonia/scm/shiro-001.ini" + ) + public void testCollectWithGlobalPermissions(){ + authenticate(UserTestData.createTrillian(), "main"); + + StoredAssignedPermission p1 = new StoredAssignedPermission("one", new AssignedPermission("one", "one:one")); + StoredAssignedPermission p2 = new StoredAssignedPermission("two", new AssignedPermission("two", "two:two")); + when(securitySystem.getPermissions(Mockito.any(Predicate.class))).thenReturn(Lists.newArrayList(p1, p2)); + + Permission wp1 = new WildcardPermission("one"); + when(resolver.resolvePermission("one:one")).thenReturn(wp1); + Permission wp2 = new WildcardPermission("two"); + when(resolver.resolvePermission("two:two")).thenReturn(wp2); + + // execute and assert + AuthorizationInfo authInfo = collector.collect(); + assertThat(authInfo.getRoles(), Matchers.containsInAnyOrder(Role.USER)); + assertThat(authInfo.getStringPermissions(), nullValue()); + + assertThat(authInfo.getObjectPermissions(), containsInAnyOrder(wp1, wp2)); + } + + private void authenticate(User user, String group, String... groups) + { + SimplePrincipalCollection spc = new SimplePrincipalCollection(); + spc.add(user.getName(), "unit"); + spc.add(user, "unit"); + spc.add(new GroupNames(group, groups), "unit"); + Subject subject = new Subject.Builder().authenticated(true).principals(spc).buildSubject(); + shiro.setSubject(subject); + } } \ No newline at end of file From 8e3c3e4b63fbaafb573a72173cf30428bac45f52 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sat, 25 Jun 2016 21:48:53 +0200 Subject: [PATCH 103/171] improve modification events to pass the item before it was modified to the subscriber --- .../sonia/scm/ModificationHandlerEvent.java | 51 ++++++++++++++ .../sonia/scm/group/AbstractGroupManager.java | 23 +++++-- .../main/java/sonia/scm/group/GroupEvent.java | 2 +- .../scm/group/GroupModificationEvent.java | 66 ++++++++++++++++++ .../repository/AbstractRepositoryManager.java | 22 ++++-- .../sonia/scm/repository/RepositoryEvent.java | 2 +- .../RepositoryModificationEvent.java | 68 +++++++++++++++++++ .../sonia/scm/user/AbstractUserManager.java | 21 ++++-- .../main/java/sonia/scm/user/UserEvent.java | 2 +- .../sonia/scm/user/UserModificationEvent.java | 68 +++++++++++++++++++ .../sonia/scm/group/DefaultGroupManager.java | 7 +- .../repository/DefaultRepositoryManager.java | 4 +- .../sonia/scm/user/DefaultUserManager.java | 9 +-- 13 files changed, 317 insertions(+), 28 deletions(-) create mode 100644 scm-core/src/main/java/sonia/scm/ModificationHandlerEvent.java create mode 100644 scm-core/src/main/java/sonia/scm/group/GroupModificationEvent.java create mode 100644 scm-core/src/main/java/sonia/scm/repository/RepositoryModificationEvent.java create mode 100644 scm-core/src/main/java/sonia/scm/user/UserModificationEvent.java diff --git a/scm-core/src/main/java/sonia/scm/ModificationHandlerEvent.java b/scm-core/src/main/java/sonia/scm/ModificationHandlerEvent.java new file mode 100644 index 0000000000..18b9a9d6b3 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/ModificationHandlerEvent.java @@ -0,0 +1,51 @@ +/** + * 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; + +import sonia.scm.event.HandlerEventBase; + +/** + * Extension to the {@link ModificationHandlerEvent}. + * + * @param type of changed item + * + * @author Sebastian Sdorra + * @since 1.48 + */ +public interface ModificationHandlerEvent extends HandlerEventBase +{ + /** + * Returns item, before it was modified. + * + * @return item before modification + */ + public T getItemBeforeModification(); +} diff --git a/scm-core/src/main/java/sonia/scm/group/AbstractGroupManager.java b/scm-core/src/main/java/sonia/scm/group/AbstractGroupManager.java index aea5258bf2..a4d4e4b355 100644 --- a/scm-core/src/main/java/sonia/scm/group/AbstractGroupManager.java +++ b/scm-core/src/main/java/sonia/scm/group/AbstractGroupManager.java @@ -90,21 +90,32 @@ public abstract class AbstractGroupManager implements GroupManager } /** - * Calls the {@link GroupListener#onEvent(Group,sonia.scm.HandlerEvent)} - * method of all registered listeners and send a {@link GroupEvent} to - * the {@link ScmEventBus}. - * + * Creates a new {@link GroupEvent} and calls {@link #fireEvent(sonia.scm.group.GroupEvent)}. + * * @param group group that has changed * @param event type of change event */ protected void fireEvent(Group group, HandlerEvent event) + { + fireEvent(new GroupEvent(group, event)); + } + + /** + * Calls the {@link GroupListener#onEvent(Group,sonia.scm.HandlerEvent)} + * method of all registered listeners and send a {@link GroupEvent} to + * the {@link ScmEventBus}. + * + * @param event type of change event + * @since 1.148 + */ + protected void fireEvent(GroupEvent event) { for (GroupListener listener : listenerSet) { - listener.onEvent(group, event); + listener.onEvent(event.getItem(), event.getEventType()); } - ScmEventBus.getInstance().post(new GroupEvent(group, event)); + ScmEventBus.getInstance().post(event); } //~--- fields --------------------------------------------------------------- diff --git a/scm-core/src/main/java/sonia/scm/group/GroupEvent.java b/scm-core/src/main/java/sonia/scm/group/GroupEvent.java index f13ed098d9..b22b7b2ae6 100644 --- a/scm-core/src/main/java/sonia/scm/group/GroupEvent.java +++ b/scm-core/src/main/java/sonia/scm/group/GroupEvent.java @@ -48,7 +48,7 @@ import sonia.scm.event.HandlerEventBase; * @since 1.23 */ @Event -public final class GroupEvent implements HandlerEventBase +public class GroupEvent implements HandlerEventBase { /** diff --git a/scm-core/src/main/java/sonia/scm/group/GroupModificationEvent.java b/scm-core/src/main/java/sonia/scm/group/GroupModificationEvent.java new file mode 100644 index 0000000000..50c9f143ce --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/group/GroupModificationEvent.java @@ -0,0 +1,66 @@ +/** + * 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.group; + +import sonia.scm.HandlerEvent; +import sonia.scm.ModificationHandlerEvent; + +/** + * Event which is fired whenever a group is modified. + * + * @author Sebastian Sdorra + * @since 1.48 + */ +public class GroupModificationEvent extends GroupEvent implements ModificationHandlerEvent +{ + + private final Group itemBeforeModification; + + /** + * Constructs a new {@link GroupModificationEvent}. + * + * @param item changed group + * @param itemBeforeModification changed group before it was modified + * @param eventType type of event + */ + public GroupModificationEvent(Group item, Group itemBeforeModification, HandlerEvent eventType) + { + super(item, eventType); + this.itemBeforeModification = itemBeforeModification; + } + + @Override + public Group getItemBeforeModification() + { + return itemBeforeModification; + } + +} diff --git a/scm-core/src/main/java/sonia/scm/repository/AbstractRepositoryManager.java b/scm-core/src/main/java/sonia/scm/repository/AbstractRepositoryManager.java index e2955d1e64..1468ac3ec1 100644 --- a/scm-core/src/main/java/sonia/scm/repository/AbstractRepositoryManager.java +++ b/scm-core/src/main/java/sonia/scm/repository/AbstractRepositoryManager.java @@ -231,22 +231,34 @@ public abstract class AbstractRepositoryManager implements RepositoryManager listenerSet.remove(listener); } + /** + * Creates an {@link RepositoryEvent} and calls {@link #fireEvent(sonia.scm.repository.RepositoryEvent)}. + * + * @param repository changed repository + * @param event event type + */ + protected void fireEvent(Repository repository, HandlerEvent event) + { + fireEvent(new RepositoryEvent(repository, event)); + } + /** * Calls the {@link RepositoryListener#onEvent(Repository,sonia.scm.HandlerEvent)} * method of all registered listeners and send a {@link RepositoryEvent} to * the {@link ScmEventBus}. * - * @param repository repository that has changed - * @param event type of change event + * @param event repository event + * + * @since 1.48 */ - protected void fireEvent(Repository repository, HandlerEvent event) + protected void fireEvent(RepositoryEvent event) { for (RepositoryListener listener : listenerSet) { - listener.onEvent(repository, event); + listener.onEvent(event.getItem(), event.getEventType()); } - ScmEventBus.getInstance().post(new RepositoryEvent(repository, event)); + ScmEventBus.getInstance().post(event); } /** diff --git a/scm-core/src/main/java/sonia/scm/repository/RepositoryEvent.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryEvent.java index b6e8412696..3bf3a44c7a 100644 --- a/scm-core/src/main/java/sonia/scm/repository/RepositoryEvent.java +++ b/scm-core/src/main/java/sonia/scm/repository/RepositoryEvent.java @@ -47,7 +47,7 @@ import sonia.scm.event.HandlerEventBase; * @since 1.23 */ @Event -public final class RepositoryEvent implements HandlerEventBase +public class RepositoryEvent implements HandlerEventBase { /** diff --git a/scm-core/src/main/java/sonia/scm/repository/RepositoryModificationEvent.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryModificationEvent.java new file mode 100644 index 0000000000..f8d571b4e5 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/repository/RepositoryModificationEvent.java @@ -0,0 +1,68 @@ +/** + * 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; + +import sonia.scm.HandlerEvent; +import sonia.scm.ModificationHandlerEvent; +import sonia.scm.event.Event; + +/** + * Event which is fired whenever a repository is modified. + * + * @author Sebastian Sdorra + * @since 1.48 + */ +@Event +public final class RepositoryModificationEvent extends RepositoryEvent implements ModificationHandlerEvent +{ + + private final Repository itemBeforeModification; + + /** + * Constructs a new {@link RepositoryModificationEvent}. + * + * @param item changed repository + * @param itemBeforeModification repository before it was modified + * @param eventType event type + */ + public RepositoryModificationEvent(Repository item, Repository itemBeforeModification, HandlerEvent eventType) + { + super(item, eventType); + this.itemBeforeModification = itemBeforeModification; + } + + @Override + public Repository getItemBeforeModification() + { + return itemBeforeModification; + } + +} diff --git a/scm-core/src/main/java/sonia/scm/user/AbstractUserManager.java b/scm-core/src/main/java/sonia/scm/user/AbstractUserManager.java index 2c3cb0b17d..c1020e0bb6 100644 --- a/scm-core/src/main/java/sonia/scm/user/AbstractUserManager.java +++ b/scm-core/src/main/java/sonia/scm/user/AbstractUserManager.java @@ -90,21 +90,32 @@ public abstract class AbstractUserManager implements UserManager } /** - * Calls the {@link UserListener#onEvent(User,sonia.scm.HandlerEvent)} - * method of all registered listeners and send a {@link UserEvent} to - * the {@link ScmEventBus}. + * Creates a new {@link UserEvent} and calls {@link #fireEvent(sonia.scm.user.UserEvent)}. * * @param user user that has changed * @param event type of change event */ protected void fireEvent(User user, HandlerEvent event) + { + fireEvent(new UserEvent(user, event)); + } + + /** + * Calls the {@link UserListener#onEvent(User,sonia.scm.HandlerEvent)} + * method of all registered listeners and send a {@link UserEvent} to + * the {@link ScmEventBus}. + * + * @param event user event + * @since 1.48 + */ + protected void fireEvent(UserEvent event) { for (UserListener listener : listenerSet) { - listener.onEvent(user, event); + listener.onEvent(event.getItem(), event.getEventType()); } - ScmEventBus.getInstance().post(new UserEvent(user, event)); + ScmEventBus.getInstance().post(event); } //~--- fields --------------------------------------------------------------- diff --git a/scm-core/src/main/java/sonia/scm/user/UserEvent.java b/scm-core/src/main/java/sonia/scm/user/UserEvent.java index 276cd5664a..ef1d99c000 100644 --- a/scm-core/src/main/java/sonia/scm/user/UserEvent.java +++ b/scm-core/src/main/java/sonia/scm/user/UserEvent.java @@ -47,7 +47,7 @@ import sonia.scm.event.HandlerEventBase; * @since 1.23 */ @Event -public final class UserEvent implements HandlerEventBase +public class UserEvent implements HandlerEventBase { /** diff --git a/scm-core/src/main/java/sonia/scm/user/UserModificationEvent.java b/scm-core/src/main/java/sonia/scm/user/UserModificationEvent.java new file mode 100644 index 0000000000..72c8e23e31 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/user/UserModificationEvent.java @@ -0,0 +1,68 @@ +/** + * 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.user; + +import sonia.scm.HandlerEvent; +import sonia.scm.ModificationHandlerEvent; +import sonia.scm.event.Event; + +/** + * Event which is fired whenever a user is modified. + * + * @author Sebastian Sdorra + * @since 1.48 + */ +@Event +public class UserModificationEvent extends UserEvent implements ModificationHandlerEvent +{ + + private final User itemBeforeModification; + + /** + * Constructs a new {@link UserModificationEvent}. + * + * @param item changed user + * @param itemBeforeModification changed user before it was modified + * @param eventType type of event + */ + public UserModificationEvent(User item, User itemBeforeModification, HandlerEvent eventType) + { + super(item, eventType); + this.itemBeforeModification = itemBeforeModification; + } + + @Override + public User getItemBeforeModification() + { + return itemBeforeModification; + } + +} diff --git a/scm-webapp/src/main/java/sonia/scm/group/DefaultGroupManager.java b/scm-webapp/src/main/java/sonia/scm/group/DefaultGroupManager.java index b85bad40c5..542add65eb 100644 --- a/scm-webapp/src/main/java/sonia/scm/group/DefaultGroupManager.java +++ b/scm-webapp/src/main/java/sonia/scm/group/DefaultGroupManager.java @@ -222,13 +222,14 @@ public class DefaultGroupManager extends AbstractGroupManager String name = group.getName(); - if (groupDAO.contains(name)) + Group notModified = groupDAO.get(name); + if (notModified != null) { removeDuplicateMembers(group); + fireEvent(new GroupModificationEvent(group, notModified, HandlerEvent.BEFORE_MODIFY)); group.setLastModified(System.currentTimeMillis()); - fireEvent(group, HandlerEvent.BEFORE_MODIFY); groupDAO.modify(group); - fireEvent(group, HandlerEvent.MODIFY); + fireEvent(new GroupModificationEvent(group, notModified, HandlerEvent.MODIFY)); } else { diff --git a/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java b/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java index da5af847de..672a8d7a46 100644 --- a/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java +++ b/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java @@ -373,7 +373,7 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager if (notModifiedRepository != null) { assertIsOwner(notModifiedRepository); - fireEvent(repository, HandlerEvent.BEFORE_MODIFY); + fireEvent(new RepositoryModificationEvent(repository, notModifiedRepository, HandlerEvent.BEFORE_MODIFY)); repository.setLastModified(System.currentTimeMillis()); getHandler(repository).modify(repository); repositoryDAO.modify(repository); @@ -384,7 +384,7 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager "repository ".concat(repository.getName()).concat(" not found")); } - fireEvent(repository, HandlerEvent.MODIFY); + fireEvent(new RepositoryModificationEvent(repository, notModifiedRepository, HandlerEvent.MODIFY)); } /** diff --git a/scm-webapp/src/main/java/sonia/scm/user/DefaultUserManager.java b/scm-webapp/src/main/java/sonia/scm/user/DefaultUserManager.java index 4dce1df549..7eaf9a25b8 100644 --- a/scm-webapp/src/main/java/sonia/scm/user/DefaultUserManager.java +++ b/scm-webapp/src/main/java/sonia/scm/user/DefaultUserManager.java @@ -280,14 +280,15 @@ public class DefaultUserManager extends AbstractUserManager } String name = user.getName(); - - if (userDAO.contains(name)) + + User notModified = userDAO.get(name); + if (notModified != null) { AssertUtil.assertIsValid(user); + fireEvent(new UserModificationEvent(user, notModified, HandlerEvent.BEFORE_MODIFY)); user.setLastModified(System.currentTimeMillis()); - fireEvent(user, HandlerEvent.BEFORE_MODIFY); userDAO.modify(user); - fireEvent(user, HandlerEvent.MODIFY); + fireEvent(new UserModificationEvent(user, notModified, HandlerEvent.MODIFY)); } else { From 64282455064868bfc1d8db6e263a2c592e7a2c1a Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 26 Jun 2016 12:20:32 +0200 Subject: [PATCH 104/171] improve cache invalidation on user events --- .../scm/security/AuthorizationCollector.java | 46 +++++++++++++++---- .../security/AuthorizationCollectorTest.java | 29 +++++++++++- 2 files changed, 64 insertions(+), 11 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java b/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java index 26e22c9e0c..40a8f4f130 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java +++ b/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java @@ -71,6 +71,8 @@ import sonia.scm.util.Util; import java.util.List; import java.util.Set; +import sonia.scm.Filter; +import sonia.scm.user.UserModificationEvent; /** * @@ -139,8 +141,13 @@ public class AuthorizationCollector } /** - * Method description - * + * Invalidates the cache of a user which was modified. The cache entries for the user will be invalidated for the + * following reasons: + *
      + *
    • Admin or Active flag was modified.
    • + *
    • New user created, for the case of old cache values
    • + *
    • User deleted
    • + *
    * * @param event */ @@ -150,18 +157,37 @@ public class AuthorizationCollector if (event.getEventType().isPost()) { User user = event.getItem(); - - if (logger.isDebugEnabled()) + String username = user.getId(); + if (event instanceof UserModificationEvent) { - logger.debug( - "clear cache of user {}, because user properties have changed", - user.getName()); + User beforeModification = ((UserModificationEvent) event).getItemBeforeModification(); + if ( user.isAdmin() != beforeModification.isAdmin() || user.isActive() != beforeModification.isActive() ) + { + invalidateUserCache(username); + } + else + { + logger.debug("cache of user {} is not invalidated, because admin and active flag has not changed", username); + } + } + else + { + invalidateUserCache(username); } - - // check if this is neccessary - cache.clear(); } } + + private void invalidateUserCache(final String username){ + logger.debug("invalidate cache of user {}, because user properties have changed", username); + cache.removeAll(new Filter() + { + @Override + public boolean accept(CacheKey item) + { + return username.equalsIgnoreCase(item.username); + } + }); + } /** * Method description diff --git a/scm-webapp/src/test/java/sonia/scm/security/AuthorizationCollectorTest.java b/scm-webapp/src/test/java/sonia/scm/security/AuthorizationCollectorTest.java index ad63fcd730..6cbd68bd23 100644 --- a/scm-webapp/src/test/java/sonia/scm/security/AuthorizationCollectorTest.java +++ b/scm-webapp/src/test/java/sonia/scm/security/AuthorizationCollectorTest.java @@ -53,6 +53,7 @@ import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import org.junit.Rule; import org.mockito.runners.MockitoJUnitRunner; +import sonia.scm.Filter; import sonia.scm.HandlerEvent; import sonia.scm.cache.Cache; import sonia.scm.cache.CacheManager; @@ -66,6 +67,7 @@ import sonia.scm.repository.RepositoryEvent; import sonia.scm.repository.RepositoryTestData; import sonia.scm.user.User; import sonia.scm.user.UserEvent; +import sonia.scm.user.UserModificationEvent; import sonia.scm.user.UserTestData; /** @@ -118,7 +120,32 @@ public class AuthorizationCollectorTest { verify(cache, never()).clear(); collector.onEvent(new UserEvent(user, HandlerEvent.CREATE)); - verify(cache).clear(); + verify(cache).removeAll(Mockito.any(Filter.class)); + } + + /** + * Tests {@link AuthorizationCollector#onEvent(sonia.scm.user.UserEvent)} with modified user. + */ + @Test + public void testOnUserModificationEvent() + { + User user = UserTestData.createDent(); + User userModified = UserTestData.createDent(); + userModified.setDisplayName("Super Dent"); + + collector.onEvent(new UserModificationEvent(userModified, user, HandlerEvent.BEFORE_CREATE)); + verify(cache, never()).removeAll(Mockito.any(Filter.class)); + + collector.onEvent(new UserModificationEvent(userModified, user, HandlerEvent.CREATE)); + verify(cache, never()).removeAll(Mockito.any(Filter.class)); + + userModified.setAdmin(true); + + collector.onEvent(new UserModificationEvent(userModified, user, HandlerEvent.BEFORE_CREATE)); + verify(cache, never()).removeAll(Mockito.any(Filter.class)); + + collector.onEvent(new UserModificationEvent(userModified, user, HandlerEvent.CREATE)); + verify(cache).removeAll(Mockito.any(Filter.class)); } /** From a592484f0f05f55df55bb97496b489b67e649544 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 26 Jun 2016 12:41:00 +0200 Subject: [PATCH 105/171] improve cache invalidation on repository events --- .../scm/security/AuthorizationCollector.java | 41 +++++++++++++++++-- .../security/AuthorizationCollectorTest.java | 29 +++++++++++++ 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java b/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java index 40a8f4f130..5c8c45cf28 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java +++ b/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java @@ -72,6 +72,7 @@ import sonia.scm.util.Util; import java.util.List; import java.util.Set; import sonia.scm.Filter; +import sonia.scm.repository.RepositoryModificationEvent; import sonia.scm.user.UserModificationEvent; /** @@ -161,7 +162,7 @@ public class AuthorizationCollector if (event instanceof UserModificationEvent) { User beforeModification = ((UserModificationEvent) event).getItemBeforeModification(); - if ( user.isAdmin() != beforeModification.isAdmin() || user.isActive() != beforeModification.isActive() ) + if ( shouldCacheBeCleared(user, beforeModification) ) { invalidateUserCache(username); } @@ -177,6 +178,11 @@ public class AuthorizationCollector } } + private boolean shouldCacheBeCleared(User user, User beforeModification) + { + return user.isAdmin() != beforeModification.isAdmin() || user.isActive() != beforeModification.isActive(); + } + private void invalidateUserCache(final String username){ logger.debug("invalidate cache of user {}, because user properties have changed", username); cache.removeAll(new Filter() @@ -200,15 +206,42 @@ public class AuthorizationCollector { if (event.getEventType().isPost()) { + Repository repository = event.getItem(); if (logger.isDebugEnabled()) { - logger.debug("clear cache, because repository {} has changed", - event.getItem().getName()); + logger.debug("clear cache, because repository {} has changed", repository.getName()); } - cache.clear(); + if (event instanceof RepositoryModificationEvent) + { + Repository beforeModification = ((RepositoryModificationEvent) event).getItemBeforeModification(); + if (shouldCacheBeCleared(repository, beforeModification)) + { + logger.debug("clear cache, because a relevant field of repository {} has changed", repository.getName()); + cache.clear(); + } + else + { + logger.debug( + "cache of repository {} is not invalidated, because non relevant fields have changed", + repository.getName() + ); + } + } + else + { + logger.debug("clear cache, because repository {} has changed", repository.getName()); + cache.clear(); + } } } + + private boolean shouldCacheBeCleared(Repository repository, Repository beforeModification) + { + return repository.isArchived() != beforeModification.isArchived() + || repository.isPublicReadable() != beforeModification.isPublicReadable() + || ! repository.getPermissions().equals(beforeModification.getPermissions()); + } /** * Method description diff --git a/scm-webapp/src/test/java/sonia/scm/security/AuthorizationCollectorTest.java b/scm-webapp/src/test/java/sonia/scm/security/AuthorizationCollectorTest.java index 6cbd68bd23..75e282fc4f 100644 --- a/scm-webapp/src/test/java/sonia/scm/security/AuthorizationCollectorTest.java +++ b/scm-webapp/src/test/java/sonia/scm/security/AuthorizationCollectorTest.java @@ -64,6 +64,7 @@ import sonia.scm.repository.PermissionType; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryDAO; import sonia.scm.repository.RepositoryEvent; +import sonia.scm.repository.RepositoryModificationEvent; import sonia.scm.repository.RepositoryTestData; import sonia.scm.user.User; import sonia.scm.user.UserEvent; @@ -176,6 +177,34 @@ public class AuthorizationCollectorTest { verify(cache).clear(); } + /** + * Tests {@link AuthorizationCollector#onEvent(sonia.scm.repository.RepositoryEvent)} with modified repository. + */ + @Test + public void testOnRepositoryModificationEvent() + { + Repository repositoryModified = RepositoryTestData.createHeartOfGold(); + repositoryModified.setName("test123"); + repositoryModified.setPermissions(Lists.newArrayList(new sonia.scm.repository.Permission("test"))); + + Repository repository = RepositoryTestData.createHeartOfGold(); + repository.setPermissions(Lists.newArrayList(new sonia.scm.repository.Permission("test"))); + + collector.onEvent(new RepositoryModificationEvent(repositoryModified, repository, HandlerEvent.BEFORE_CREATE)); + verify(cache, never()).clear(); + + collector.onEvent(new RepositoryModificationEvent(repositoryModified, repository, HandlerEvent.CREATE)); + verify(cache, never()).clear(); + + repositoryModified.setPermissions(Lists.newArrayList(new sonia.scm.repository.Permission("test"))); + collector.onEvent(new RepositoryModificationEvent(repositoryModified, repository, HandlerEvent.CREATE)); + verify(cache, never()).clear(); + + repositoryModified.setPermissions(Lists.newArrayList(new sonia.scm.repository.Permission("test123"))); + collector.onEvent(new RepositoryModificationEvent(repositoryModified, repository, HandlerEvent.CREATE)); + verify(cache).clear(); + } + /** * Tests {@link AuthorizationCollector#onEvent(sonia.scm.security.StoredAssignedPermissionEvent)}. */ From 7bc793ecd50d4574a1c6010bfb203ff91741d5f0 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 26 Jun 2016 12:44:13 +0200 Subject: [PATCH 106/171] improve javadoc --- .../sonia/scm/security/AuthorizationCollector.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java b/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java index 5c8c45cf28..87803a9dc6 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java +++ b/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java @@ -150,7 +150,7 @@ public class AuthorizationCollector *
  • User deleted
  • * * - * @param event + * @param event user event */ @Subscribe public void onEvent(UserEvent event) @@ -196,10 +196,14 @@ public class AuthorizationCollector } /** - * Method description + * Invalidates the whole cache, if a repository has changed. The cache get cleared for one of the following reasons: + *
      + *
    • New repository created
    • + *
    • Repository was removed
    • + *
    • Archived, Public readable or permission field of the repository was modified
    • + *
    * - * - * @param event + * @param event repository event */ @Subscribe public void onEvent(RepositoryEvent event) From 89660e8ac3327dc30d90794881882a58610fa364 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 26 Jun 2016 12:53:41 +0200 Subject: [PATCH 107/171] improve cache invalidation on permission change events --- .../scm/security/AuthorizationCollector.java | 18 +++++++++++++---- .../security/AuthorizationCollectorTest.java | 20 ++++++++++++++++--- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java b/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java index 87803a9dc6..e0e26740cd 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java +++ b/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java @@ -184,7 +184,7 @@ public class AuthorizationCollector } private void invalidateUserCache(final String username){ - logger.debug("invalidate cache of user {}, because user properties have changed", username); + logger.debug("invalidate cache of user {}, because of a event which could change the permissions", username); cache.removeAll(new Filter() { @Override @@ -248,10 +248,11 @@ public class AuthorizationCollector } /** - * Method description + * Invalidates the whole cache if a group permission has changed and invalidates the cached entries of a user, if a + * user permission has changed. * * - * @param event + * @param event permission event */ @Subscribe public void onEvent(StoredAssignedPermissionEvent event) @@ -264,7 +265,16 @@ public class AuthorizationCollector event.getPermission().getId()); } - cache.clear(); + StoredAssignedPermission permission = event.getPermission(); + if (permission.isGroupPermission()) + { + logger.debug("clears the whole cache, because global group permission {} has changed", permission.getId()); + cache.clear(); + } + else + { + invalidateUserCache(permission.getName()); + } } } diff --git a/scm-webapp/src/test/java/sonia/scm/security/AuthorizationCollectorTest.java b/scm-webapp/src/test/java/sonia/scm/security/AuthorizationCollectorTest.java index 75e282fc4f..ea8a280987 100644 --- a/scm-webapp/src/test/java/sonia/scm/security/AuthorizationCollectorTest.java +++ b/scm-webapp/src/test/java/sonia/scm/security/AuthorizationCollectorTest.java @@ -211,11 +211,25 @@ public class AuthorizationCollectorTest { @Test public void testOnStoredAssignedPermissionEvent() { - StoredAssignedPermission permission = new StoredAssignedPermission(); - collector.onEvent(new StoredAssignedPermissionEvent(HandlerEvent.BEFORE_CREATE, permission)); + StoredAssignedPermission groupPermission = new StoredAssignedPermission( + "123", new AssignedPermission("_authenticated", true, "repository:read:*") + ); + collector.onEvent(new StoredAssignedPermissionEvent(HandlerEvent.BEFORE_CREATE, groupPermission)); verify(cache, never()).clear(); - collector.onEvent(new StoredAssignedPermissionEvent(HandlerEvent.CREATE, permission)); + collector.onEvent(new StoredAssignedPermissionEvent(HandlerEvent.CREATE, groupPermission)); + verify(cache).clear(); + + + StoredAssignedPermission userPermission = new StoredAssignedPermission( + "123", new AssignedPermission("trillian", false, "repository:read:*") + ); + collector.onEvent(new StoredAssignedPermissionEvent(HandlerEvent.BEFORE_CREATE, userPermission)); + verify(cache, never()).removeAll(Mockito.any(Filter.class)); + verify(cache).clear(); + + collector.onEvent(new StoredAssignedPermissionEvent(HandlerEvent.CREATE, userPermission)); + verify(cache).removeAll(Mockito.any(Filter.class)); verify(cache).clear(); } From 9dc1c6fd8e5b94ac5a192f39f98f01d5f7b8274c Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 26 Jun 2016 15:03:28 +0200 Subject: [PATCH 108/171] improve cache invalidation on group events --- .../scm/security/AuthorizationCollector.java | 100 +++++++++++------- .../security/AuthorizationCollectorTest.java | 20 ++++ 2 files changed, 83 insertions(+), 37 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java b/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java index e0e26740cd..d7db55ac59 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java +++ b/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java @@ -72,6 +72,8 @@ import sonia.scm.util.Util; import java.util.List; import java.util.Set; import sonia.scm.Filter; +import sonia.scm.group.Group; +import sonia.scm.group.GroupModificationEvent; import sonia.scm.repository.RepositoryModificationEvent; import sonia.scm.user.UserModificationEvent; @@ -162,28 +164,29 @@ public class AuthorizationCollector if (event instanceof UserModificationEvent) { User beforeModification = ((UserModificationEvent) event).getItemBeforeModification(); - if ( shouldCacheBeCleared(user, beforeModification) ) + if (shouldCacheBeCleared(user, beforeModification)) { invalidateUserCache(username); - } - else + } + else { logger.debug("cache of user {} is not invalidated, because admin and active flag has not changed", username); } - } - else + } + else { invalidateUserCache(username); } } } - + private boolean shouldCacheBeCleared(User user, User beforeModification) { return user.isAdmin() != beforeModification.isAdmin() || user.isActive() != beforeModification.isActive(); } - - private void invalidateUserCache(final String username){ + + private void invalidateUserCache(final String username) + { logger.debug("invalidate cache of user {}, because of a event which could change the permissions", username); cache.removeAll(new Filter() { @@ -223,26 +226,26 @@ public class AuthorizationCollector { logger.debug("clear cache, because a relevant field of repository {} has changed", repository.getName()); cache.clear(); - } - else + } + else { logger.debug( - "cache of repository {} is not invalidated, because non relevant fields have changed", + "cache of repository {} is not invalidated, because non relevant fields have changed", repository.getName() ); } - } - else + } + else { logger.debug("clear cache, because repository {} has changed", repository.getName()); cache.clear(); } } } - + private boolean shouldCacheBeCleared(Repository repository, Repository beforeModification) { - return repository.isArchived() != beforeModification.isArchived() + return repository.isArchived() != beforeModification.isArchived() || repository.isPublicReadable() != beforeModification.isPublicReadable() || ! repository.getPermissions().equals(beforeModification.getPermissions()); } @@ -271,7 +274,7 @@ public class AuthorizationCollector logger.debug("clears the whole cache, because global group permission {} has changed", permission.getId()); cache.clear(); } - else + else { invalidateUserCache(permission.getName()); } @@ -279,26 +282,50 @@ public class AuthorizationCollector } /** - * Method description + * Invalidates the whole cache, if a group has changed. The cache get cleared for one of the following reasons: + *
      + *
    • New group created
    • + *
    • Group was removed
    • + *
    • Group members was modified
    • + *
    * - * - * @param event + * @param event group event */ @Subscribe public void onEvent(GroupEvent event) { if (event.getEventType().isPost()) { - if (logger.isDebugEnabled()) + Group group = event.getItem(); + if (event instanceof GroupModificationEvent) { - logger.debug("clear cache, because group {} has changed", - event.getItem().getId()); + Group beforeModification = ((GroupModificationEvent) event).getItemBeforeModification(); + if (shouldCacheBeCleared(group, beforeModification)) + { + logger.debug("clear cache, because group {} has changed", group.getId()); + cache.clear(); + } + else + { + logger.debug( + "cache of group {} is not invalidated, because non relevant fields have changed", + group.getId() + ); + } + } + else + { + logger.debug("clear cache, because group {} has changed", group.getId()); + cache.clear(); } - - cache.clear(); } } + private boolean shouldCacheBeCleared(Group group, Group beforeModification) + { + return !group.getMembers().equals(beforeModification.getMembers()); + } + /** * Method description * @@ -356,14 +383,14 @@ public class AuthorizationCollector { List globalPermissions = securitySystem.getPermissions(new Predicate() - { - - @Override - public boolean apply(AssignedPermission input) { - return isUserPermitted(user, groups, input); - } - }); + + @Override + public boolean apply(AssignedPermission input) + { + return isUserPermitted(user, groups, input); + } + }); for (StoredAssignedPermission gp : globalPermissions) { @@ -414,8 +441,8 @@ public class AuthorizationCollector private void collectRepositoryPermissions(Builder builder, Repository repository, User user, GroupNames groups) { - List repositoryPermissions = - repository.getPermissions(); + List repositoryPermissions + = repository.getPermissions(); if (Util.isNotEmpty(repositoryPermissions)) { @@ -426,7 +453,7 @@ public class AuthorizationCollector { hasPermission = true; RepositoryPermission rp = new RepositoryPermission(repository, - permission.getType()); + permission.getType()); if (logger.isTraceEnabled()) { @@ -437,7 +464,7 @@ public class AuthorizationCollector builder.add(rp); } } - + if (!hasPermission && logger.isTraceEnabled()) { logger.trace("no permission for user {} defined at repository {}", user.getName(), repository.getName()); @@ -517,7 +544,7 @@ public class AuthorizationCollector PermissionObject perm) { //J- - return (perm.isGroupPermission() && groups.contains(perm.getName())) + return (perm.isGroupPermission() && groups.contains(perm.getName())) || ((!perm.isGroupPermission()) && user.getName().equals(perm.getName())); //J+ } @@ -589,7 +616,6 @@ public class AuthorizationCollector private final String username; } - //~--- fields --------------------------------------------------------------- /** Field description */ diff --git a/scm-webapp/src/test/java/sonia/scm/security/AuthorizationCollectorTest.java b/scm-webapp/src/test/java/sonia/scm/security/AuthorizationCollectorTest.java index ea8a280987..cfa18960c5 100644 --- a/scm-webapp/src/test/java/sonia/scm/security/AuthorizationCollectorTest.java +++ b/scm-webapp/src/test/java/sonia/scm/security/AuthorizationCollectorTest.java @@ -59,6 +59,7 @@ import sonia.scm.cache.Cache; import sonia.scm.cache.CacheManager; import sonia.scm.group.Group; import sonia.scm.group.GroupEvent; +import sonia.scm.group.GroupModificationEvent; import sonia.scm.group.GroupNames; import sonia.scm.repository.PermissionType; import sonia.scm.repository.Repository; @@ -163,6 +164,25 @@ public class AuthorizationCollectorTest { verify(cache).clear(); } + /** + * Tests {@link AuthorizationCollector#onEvent(sonia.scm.group.GroupEvent)} with modified groups. + */ + @Test + public void testOnGroupModificationEvent() + { + Group group = new Group("xml", "base"); + Group modifiedGroup = new Group("xml", "base"); + collector.onEvent(new GroupModificationEvent(modifiedGroup, group, HandlerEvent.BEFORE_MODIFY)); + verify(cache, never()).clear(); + + collector.onEvent(new GroupModificationEvent(modifiedGroup, group, HandlerEvent.MODIFY)); + verify(cache, never()).clear(); + + modifiedGroup.add("test"); + collector.onEvent(new GroupModificationEvent(modifiedGroup, group, HandlerEvent.MODIFY)); + verify(cache).clear(); + } + /** * Tests {@link AuthorizationCollector#onEvent(sonia.scm.repository.RepositoryEvent)}. */ From a19790208970be431dac0597e93907dba310b163 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 26 Jun 2016 15:04:55 +0200 Subject: [PATCH 109/171] added editorconfig file --- .editorconfig | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..7bd36e0adc --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line=lf +insert_final_newline = true +indent_style = space +indent_size = 2 +charset = utf-8 + +# 4 space indentation +[*.py] +indent_style = space +indent_size = 4 From ae8668522c9ceeffa654565e2edb15337ee4a896 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 26 Jun 2016 15:21:07 +0200 Subject: [PATCH 110/171] update apache shiro to version 1.2.5 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 01aed23935..d572cd1fa3 100644 --- a/pom.xml +++ b/pom.xml @@ -421,7 +421,7 @@ 7.6.16.v20140903 - 1.2.4 + 1.2.5 3.7.1.201504261725-r-scm1 From 4ae453085043455dd929462c9c9d5e69b8e91fcb Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 28 Jun 2016 09:20:47 +0200 Subject: [PATCH 111/171] fixed syntax highlight for bash/sh, see issue #843 --- .../resources/js/panel/sonia.panel.syntaxhighlighterpanel.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scm-webapp/src/main/webapp/resources/js/panel/sonia.panel.syntaxhighlighterpanel.js b/scm-webapp/src/main/webapp/resources/js/panel/sonia.panel.syntaxhighlighterpanel.js index 686df63cbb..fab21ac806 100644 --- a/scm-webapp/src/main/webapp/resources/js/panel/sonia.panel.syntaxhighlighterpanel.js +++ b/scm-webapp/src/main/webapp/resources/js/panel/sonia.panel.syntaxhighlighterpanel.js @@ -41,6 +41,7 @@ Sonia.panel.SyntaxHighlighterPanel = Ext.extend(Ext.Panel, { fileName: 'shBrushAppleScript.js' },{ name: 'Bash/shell', + brush: 'shell', aliases: ['sh', 'bash', 'shell'], fileName: 'shBrushBash.js' },{ From 8e38d95dcf2ec79a72ce7b721c013137e9ff5e29 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 28 Jun 2016 09:32:25 +0200 Subject: [PATCH 112/171] added name of the repository to access denied exceptions --- .../java/sonia/scm/repository/DefaultRepositoryManager.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java b/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java index 672a8d7a46..bbe4f80115 100644 --- a/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java +++ b/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java @@ -918,7 +918,8 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager if (!isPermitted(repository, PermissionType.OWNER)) { throw new ScmSecurityException( - "owner permission is required, access denied"); + "owner permission is required, owner access to repository " + repository.getName() + " denied" + ); } } @@ -933,7 +934,8 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager if (!isReader(repository)) { throw new ScmSecurityException( - "reader permission is required, access denied"); + "reader permission is required, access to repository " + repository.getName() + " denied" + ); } } From 353e4c4f7b6a19553a9b19a30b346aac8361db87 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 28 Jun 2016 10:15:59 +0200 Subject: [PATCH 113/171] removed duplicate clear cache log message --- .../java/sonia/scm/security/AuthorizationCollector.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java b/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java index d7db55ac59..1a1f7fc55d 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java +++ b/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java @@ -214,11 +214,7 @@ public class AuthorizationCollector if (event.getEventType().isPost()) { Repository repository = event.getItem(); - if (logger.isDebugEnabled()) - { - logger.debug("clear cache, because repository {} has changed", repository.getName()); - } - + if (event instanceof RepositoryModificationEvent) { Repository beforeModification = ((RepositoryModificationEvent) event).getItemBeforeModification(); From 54333176926d8b78162d6af8aabd211cf71ddbe5 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 28 Jun 2016 10:26:01 +0200 Subject: [PATCH 114/171] improve logging of AuthorizationCollector --- .../scm/security/AuthorizationCollector.java | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java b/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java index 1a1f7fc55d..0b9e565c32 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java +++ b/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java @@ -166,15 +166,17 @@ public class AuthorizationCollector User beforeModification = ((UserModificationEvent) event).getItemBeforeModification(); if (shouldCacheBeCleared(user, beforeModification)) { + logger.debug("invalidate cache of user {}, because of a permission relevant field has changed", username); invalidateUserCache(username); } else { - logger.debug("cache of user {} is not invalidated, because admin and active flag has not changed", username); + logger.debug("cache of user {} is not invalidated, because no permission relevant field has changed", username); } } else { + logger.debug("invalidate cache of user {}, because of a user event", username); invalidateUserCache(username); } } @@ -187,7 +189,6 @@ public class AuthorizationCollector private void invalidateUserCache(final String username) { - logger.debug("invalidate cache of user {}, because of a event which could change the permissions", username); cache.removeAll(new Filter() { @Override @@ -260,8 +261,7 @@ public class AuthorizationCollector { if (logger.isDebugEnabled()) { - logger.debug("clear cache, because permission {} has changed", - event.getPermission().getId()); + } StoredAssignedPermission permission = event.getPermission(); @@ -272,6 +272,10 @@ public class AuthorizationCollector } else { + logger.debug( + "clear cache of user {}, because permission {} has changed", + permission.getName(), event.getPermission().getId() + ); invalidateUserCache(permission.getName()); } } @@ -347,18 +351,13 @@ public class AuthorizationCollector if (info == null) { - if (logger.isTraceEnabled()) - { - logger.trace("collect AuthorizationInfo for user {}", user.getName()); - } - + logger.trace("collect AuthorizationInfo for user {}", user.getName()); info = createAuthorizationInfo(user, groupNames); cache.put(cacheKey, info); } else if (logger.isTraceEnabled()) { - logger.trace("retrieve AuthorizationInfo for user {} from cache", - user.getName()); + logger.trace("retrieve AuthorizationInfo for user {} from cache", user.getName()); } return info; @@ -518,7 +517,6 @@ public class AuthorizationCollector } SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles); - info.addObjectPermissions(permissions); return info; From 1529ef99d79e52ab29a06e63cc04fee11adf6043 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 28 Jun 2016 10:54:14 +0200 Subject: [PATCH 115/171] log authorization summary to trace level --- .../scm/security/AuthorizationCollector.java | 31 +++++++++++++++++++ .../src/main/resources/logback.default.xml | 3 ++ 2 files changed, 34 insertions(+) diff --git a/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java b/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java index 0b9e565c32..94662b878e 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java +++ b/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java @@ -35,6 +35,7 @@ package sonia.scm.security; //~--- non-JDK imports -------------------------------------------------------- +import com.google.common.base.Joiner; import com.google.common.base.Objects; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; @@ -85,6 +86,9 @@ import sonia.scm.user.UserModificationEvent; public class AuthorizationCollector { + // TODO move to util class + private static final String SEPARATOR = System.getProperty("line.separator", "\n"); + /** Field description */ private static final String CACHE_NAME = "sonia.cache.authorizing"; @@ -359,9 +363,36 @@ public class AuthorizationCollector { logger.trace("retrieve AuthorizationInfo for user {} from cache", user.getName()); } + + if (logger.isTraceEnabled()){ + logger.trace(createAuthorizationSummary(user, groupNames, info)); + } return info; } + + private String createAuthorizationSummary(User user, GroupNames groups, AuthorizationInfo authzInfo) + { + StringBuilder buffer = new StringBuilder("authorization summary: "); + buffer.append(SEPARATOR).append("username : ").append(user.getName()); + buffer.append(SEPARATOR).append("groups : "); + append(buffer, groups); + buffer.append(SEPARATOR).append("roles : "); + append(buffer, authzInfo.getRoles()); + buffer.append(SEPARATOR).append("permissions:"); + append(buffer, authzInfo.getStringPermissions()); + append(buffer, authzInfo.getObjectPermissions()); + return buffer.toString(); + } + + private void append(StringBuilder buffer, Iterable iterable){ + if (iterable != null){ + for ( Object item : iterable ) + { + buffer.append(SEPARATOR).append(" - ").append(item); + } + } + } /** * Method description diff --git a/scm-webapp/src/main/resources/logback.default.xml b/scm-webapp/src/main/resources/logback.default.xml index 290a04a9d1..b384437c72 100644 --- a/scm-webapp/src/main/resources/logback.default.xml +++ b/scm-webapp/src/main/resources/logback.default.xml @@ -52,6 +52,9 @@ + + + From f52b27b58d31ff203aab49cf791eb9b5f9ccac3f Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 28 Jun 2016 11:24:33 +0200 Subject: [PATCH 116/171] fixed bug in equals method of Permission object --- scm-core/src/main/java/sonia/scm/repository/Permission.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/repository/Permission.java b/scm-core/src/main/java/sonia/scm/repository/Permission.java index 60c6628d61..b5de810f75 100644 --- a/scm-core/src/main/java/sonia/scm/repository/Permission.java +++ b/scm-core/src/main/java/sonia/scm/repository/Permission.java @@ -136,8 +136,9 @@ public class Permission implements PermissionObject, Serializable final Permission other = (Permission) obj; - return Objects.equal(name, other.name) && Objects.equal(type, other.type) - && Objects.equal(groupPermission, groupPermission); + return Objects.equal(name, other.name) + && Objects.equal(type, other.type) + && Objects.equal(groupPermission, other.groupPermission); } /** From f8133f4c6b99baff1b90b2355c40acc3aaf8c4ef Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 28 Jun 2016 11:32:30 +0200 Subject: [PATCH 117/171] improve logging --- .../scm/security/AuthorizationCollector.java | 69 +++++++++---------- .../src/main/resources/logback.default.xml | 1 + .../security/AuthorizationCollectorTest.java | 12 ++++ 3 files changed, 47 insertions(+), 35 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java b/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java index 94662b878e..4ad23d3fa7 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java +++ b/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java @@ -35,7 +35,6 @@ package sonia.scm.security; //~--- non-JDK imports -------------------------------------------------------- -import com.google.common.base.Joiner; import com.google.common.base.Objects; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; @@ -180,7 +179,7 @@ public class AuthorizationCollector } else { - logger.debug("invalidate cache of user {}, because of a user event", username); + logger.debug("invalidate cache of user {}, because of user {} event", username, event.getEventType()); invalidateUserCache(username); } } @@ -231,14 +230,14 @@ public class AuthorizationCollector else { logger.debug( - "cache of repository {} is not invalidated, because non relevant fields have changed", + "cache is not invalidated, because non relevant field of repository {} has changed", repository.getName() ); } } else { - logger.debug("clear cache, because repository {} has changed", repository.getName()); + logger.debug("clear cache, received {} event of repository {}", event.getEventType(), repository.getName()); cache.clear(); } } @@ -271,7 +270,7 @@ public class AuthorizationCollector StoredAssignedPermission permission = event.getPermission(); if (permission.isGroupPermission()) { - logger.debug("clears the whole cache, because global group permission {} has changed", permission.getId()); + logger.debug("clear cache, because global group permission {} has changed", permission.getId()); cache.clear(); } else @@ -312,14 +311,14 @@ public class AuthorizationCollector else { logger.debug( - "cache of group {} is not invalidated, because non relevant fields have changed", + "cache is not invalidated, because non relevant field of group {} has changed", group.getId() ); } } else { - logger.debug("clear cache, because group {} has changed", group.getId()); + logger.debug("clear cache, received group event {} for group {}", event.getEventType(), group.getId()); cache.clear(); } } @@ -363,36 +362,9 @@ public class AuthorizationCollector { logger.trace("retrieve AuthorizationInfo for user {} from cache", user.getName()); } - - if (logger.isTraceEnabled()){ - logger.trace(createAuthorizationSummary(user, groupNames, info)); - } return info; } - - private String createAuthorizationSummary(User user, GroupNames groups, AuthorizationInfo authzInfo) - { - StringBuilder buffer = new StringBuilder("authorization summary: "); - buffer.append(SEPARATOR).append("username : ").append(user.getName()); - buffer.append(SEPARATOR).append("groups : "); - append(buffer, groups); - buffer.append(SEPARATOR).append("roles : "); - append(buffer, authzInfo.getRoles()); - buffer.append(SEPARATOR).append("permissions:"); - append(buffer, authzInfo.getStringPermissions()); - append(buffer, authzInfo.getObjectPermissions()); - return buffer.toString(); - } - - private void append(StringBuilder buffer, Iterable iterable){ - if (iterable != null){ - for ( Object item : iterable ) - { - buffer.append(SEPARATOR).append(" - ").append(item); - } - } - } /** * Method description @@ -549,9 +521,36 @@ public class AuthorizationCollector SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles); info.addObjectPermissions(permissions); - + + if (logger.isTraceEnabled()){ + logger.trace(createAuthorizationSummary(user, groups, info)); + } + return info; } + + private String createAuthorizationSummary(User user, GroupNames groups, AuthorizationInfo authzInfo) + { + StringBuilder buffer = new StringBuilder("authorization summary: "); + buffer.append(SEPARATOR).append("username : ").append(user.getName()); + buffer.append(SEPARATOR).append("groups : "); + append(buffer, groups); + buffer.append(SEPARATOR).append("roles : "); + append(buffer, authzInfo.getRoles()); + buffer.append(SEPARATOR).append("permissions:"); + append(buffer, authzInfo.getStringPermissions()); + append(buffer, authzInfo.getObjectPermissions()); + return buffer.toString(); + } + + private void append(StringBuilder buffer, Iterable iterable){ + if (iterable != null){ + for ( Object item : iterable ) + { + buffer.append(SEPARATOR).append(" - ").append(item); + } + } + } //~--- get methods ---------------------------------------------------------- diff --git a/scm-webapp/src/main/resources/logback.default.xml b/scm-webapp/src/main/resources/logback.default.xml index b384437c72..3550607db6 100644 --- a/scm-webapp/src/main/resources/logback.default.xml +++ b/scm-webapp/src/main/resources/logback.default.xml @@ -54,6 +54,7 @@ + diff --git a/scm-webapp/src/test/java/sonia/scm/security/AuthorizationCollectorTest.java b/scm-webapp/src/test/java/sonia/scm/security/AuthorizationCollectorTest.java index cfa18960c5..269f39f833 100644 --- a/scm-webapp/src/test/java/sonia/scm/security/AuthorizationCollectorTest.java +++ b/scm-webapp/src/test/java/sonia/scm/security/AuthorizationCollectorTest.java @@ -223,6 +223,18 @@ public class AuthorizationCollectorTest { repositoryModified.setPermissions(Lists.newArrayList(new sonia.scm.repository.Permission("test123"))); collector.onEvent(new RepositoryModificationEvent(repositoryModified, repository, HandlerEvent.CREATE)); verify(cache).clear(); + + repositoryModified.setPermissions( + Lists.newArrayList(new sonia.scm.repository.Permission("test", PermissionType.READ, true)) + ); + collector.onEvent(new RepositoryModificationEvent(repositoryModified, repository, HandlerEvent.CREATE)); + verify(cache, times(2)).clear(); + + repositoryModified.setPermissions( + Lists.newArrayList(new sonia.scm.repository.Permission("test", PermissionType.WRITE)) + ); + collector.onEvent(new RepositoryModificationEvent(repositoryModified, repository, HandlerEvent.CREATE)); + verify(cache, times(3)).clear(); } /** From 7ef8e1ebd53b5de1b0d5a194ded022e3fbc986f0 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 28 Jun 2016 12:06:02 +0200 Subject: [PATCH 118/171] removed unnecessary log level check --- .../main/java/sonia/scm/security/AuthorizationCollector.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java b/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java index 4ad23d3fa7..315f19d1e5 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java +++ b/scm-webapp/src/main/java/sonia/scm/security/AuthorizationCollector.java @@ -262,11 +262,6 @@ public class AuthorizationCollector { if (event.getEventType().isPost()) { - if (logger.isDebugEnabled()) - { - - } - StoredAssignedPermission permission = event.getPermission(); if (permission.isGroupPermission()) { From 7d8613b6bb0856d6c652632ecfc9839d640f6619 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 28 Jun 2016 12:06:28 +0200 Subject: [PATCH 119/171] added request method to mdc filter --- scm-webapp/src/main/java/sonia/scm/filter/MDCFilter.java | 6 ++++++ .../src/test/java/sonia/scm/filter/MDCFilterTest.java | 2 ++ 2 files changed, 8 insertions(+) diff --git a/scm-webapp/src/main/java/sonia/scm/filter/MDCFilter.java b/scm-webapp/src/main/java/sonia/scm/filter/MDCFilter.java index 5c14269ffb..f16e7ca548 100644 --- a/scm-webapp/src/main/java/sonia/scm/filter/MDCFilter.java +++ b/scm-webapp/src/main/java/sonia/scm/filter/MDCFilter.java @@ -72,6 +72,10 @@ public class MDCFilter extends HttpFilter /** url of the current request */ @VisibleForTesting static final String MDC_REQUEST_URI = "request_uri"; + + /** request method */ + @VisibleForTesting + static final String MDC_REQUEST_METHOD = "request_method"; /** Field description */ @VisibleForTesting @@ -98,6 +102,7 @@ public class MDCFilter extends HttpFilter MDC.put(MDC_USERNAME, getUsername()); MDC.put(MDC_CLIEN_IP, request.getRemoteAddr()); MDC.put(MDC_CLIEN_HOST, request.getRemoteHost()); + MDC.put(MDC_REQUEST_METHOD, request.getMethod()); MDC.put(MDC_REQUEST_URI, request.getRequestURI()); try @@ -109,6 +114,7 @@ public class MDCFilter extends HttpFilter MDC.remove(MDC_USERNAME); MDC.remove(MDC_CLIEN_IP); MDC.remove(MDC_CLIEN_HOST); + MDC.remove(MDC_REQUEST_METHOD); MDC.remove(MDC_REQUEST_URI); } } diff --git a/scm-webapp/src/test/java/sonia/scm/filter/MDCFilterTest.java b/scm-webapp/src/test/java/sonia/scm/filter/MDCFilterTest.java index b5e1753c52..010c9bc1cf 100644 --- a/scm-webapp/src/test/java/sonia/scm/filter/MDCFilterTest.java +++ b/scm-webapp/src/test/java/sonia/scm/filter/MDCFilterTest.java @@ -87,6 +87,7 @@ public class MDCFilterTest extends AbstractTestBase { when(request.getRequestURI()).thenReturn("api/v1/repositories"); when(request.getRemoteAddr()).thenReturn("127.0.0.1"); when(request.getRemoteHost()).thenReturn("localhost"); + when(request.getMethod()).thenReturn("GET"); MDCCapturingFilterChain chain = new MDCCapturingFilterChain(); filter.doFilter(request, response, chain); @@ -96,6 +97,7 @@ public class MDCFilterTest extends AbstractTestBase { assertEquals("api/v1/repositories", chain.ctx.get(MDCFilter.MDC_REQUEST_URI)); assertEquals("127.0.0.1", chain.ctx.get(MDCFilter.MDC_CLIEN_IP)); assertEquals("localhost", chain.ctx.get(MDCFilter.MDC_CLIEN_HOST)); + assertEquals("GET", chain.ctx.get(MDCFilter.MDC_REQUEST_METHOD)); } /** From 850b846293fe8d89450b34ece85dd0f2c6f5139e Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 28 Jun 2016 13:11:13 +0200 Subject: [PATCH 120/171] [maven-release-plugin] prepare release 1.48 --- maven/pom.xml | 4 ++-- maven/scm-maven-plugin/pom.xml | 4 ++-- maven/scm-plugin-archetype/pom.xml | 4 ++-- pom.xml | 4 ++-- scm-clients/pom.xml | 6 +++--- scm-clients/scm-cli-client/pom.xml | 6 +++--- scm-clients/scm-client-api/pom.xml | 4 ++-- scm-clients/scm-client-impl/pom.xml | 8 ++++---- scm-core/pom.xml | 4 ++-- scm-dao-orientdb/pom.xml | 8 ++++---- scm-dao-xml/pom.xml | 8 ++++---- scm-plugin-backend/pom.xml | 6 +++--- scm-plugins/pom.xml | 8 ++++---- scm-plugins/scm-git-plugin/pom.xml | 6 +++--- scm-plugins/scm-hg-plugin/pom.xml | 6 +++--- scm-plugins/scm-svn-plugin/pom.xml | 6 +++--- scm-samples/pom.xml | 4 ++-- scm-samples/scm-sample-auth/pom.xml | 6 +++--- scm-samples/scm-sample-hello/pom.xml | 6 +++--- scm-server/pom.xml | 4 ++-- scm-test/pom.xml | 6 +++--- scm-webapp/pom.xml | 24 ++++++++++++------------ support/pom.xml | 4 ++-- support/scm-support-btrace/pom.xml | 6 +++--- 24 files changed, 76 insertions(+), 76 deletions(-) diff --git a/maven/pom.xml b/maven/pom.xml index 1268acc7e4..2eb1e4eca4 100644 --- a/maven/pom.xml +++ b/maven/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.48-SNAPSHOT + 1.48 sonia.scm.maven scm-maven-plugins pom - 1.48-SNAPSHOT + 1.48 scm-maven-plugins diff --git a/maven/scm-maven-plugin/pom.xml b/maven/scm-maven-plugin/pom.xml index 9a818023da..55d4c181bc 100644 --- a/maven/scm-maven-plugin/pom.xml +++ b/maven/scm-maven-plugin/pom.xml @@ -6,12 +6,12 @@ scm-maven-plugins sonia.scm.maven - 1.48-SNAPSHOT + 1.48 sonia.scm.maven scm-maven-plugin - 1.48-SNAPSHOT + 1.48 maven-plugin scm-maven-plugin diff --git a/maven/scm-plugin-archetype/pom.xml b/maven/scm-plugin-archetype/pom.xml index 98d3ab5439..52ed9e96c0 100644 --- a/maven/scm-plugin-archetype/pom.xml +++ b/maven/scm-plugin-archetype/pom.xml @@ -6,12 +6,12 @@ scm-maven-plugins sonia.scm.maven - 1.48-SNAPSHOT + 1.48 sonia.scm.maven scm-plugin-archetype - 1.48-SNAPSHOT + 1.48 scm-plugin-archetype diff --git a/pom.xml b/pom.xml index d572cd1fa3..2ccb0aaaa2 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ sonia.scm scm pom - 1.48-SNAPSHOT + 1.48 The easiest way to share your Git, Mercurial and Subversion repositories over http. @@ -36,7 +36,7 @@ scm:hg:http://bitbucket.org/sdorra/scm-manager scm:hg:https://bitbucket.org/sdorra/scm-manager http://bitbucket.org/sdorra/scm-manager - HEAD + 1.48 diff --git a/scm-clients/pom.xml b/scm-clients/pom.xml index 143ae58fdf..e0db189fd9 100644 --- a/scm-clients/pom.xml +++ b/scm-clients/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.48-SNAPSHOT + 1.48 sonia.scm.clients scm-clients pom - 1.48-SNAPSHOT + 1.48 scm-clients @@ -32,7 +32,7 @@ scm-core sonia.scm jar - 1.48-SNAPSHOT + 1.48 shiro-core diff --git a/scm-clients/scm-cli-client/pom.xml b/scm-clients/scm-cli-client/pom.xml index 72f83e1d82..2c98e36243 100644 --- a/scm-clients/scm-cli-client/pom.xml +++ b/scm-clients/scm-cli-client/pom.xml @@ -6,12 +6,12 @@ scm-clients sonia.scm.clients - 1.48-SNAPSHOT + 1.48 sonia.scm.clients scm-cli-client - 1.48-SNAPSHOT + 1.48 scm-cli-client @@ -34,7 +34,7 @@ sonia.scm.clients scm-client-impl - 1.48-SNAPSHOT + 1.48 diff --git a/scm-clients/scm-client-api/pom.xml b/scm-clients/scm-client-api/pom.xml index babe71c9bc..dc115f1770 100644 --- a/scm-clients/scm-client-api/pom.xml +++ b/scm-clients/scm-client-api/pom.xml @@ -6,13 +6,13 @@ sonia.scm.clients scm-clients - 1.48-SNAPSHOT + 1.48 sonia.scm.clients scm-client-api jar - 1.48-SNAPSHOT + 1.48 scm-client-api diff --git a/scm-clients/scm-client-impl/pom.xml b/scm-clients/scm-client-impl/pom.xml index 911a962f7a..642e6395f1 100644 --- a/scm-clients/scm-client-impl/pom.xml +++ b/scm-clients/scm-client-impl/pom.xml @@ -6,13 +6,13 @@ sonia.scm.clients scm-clients - 1.48-SNAPSHOT + 1.48 sonia.scm.clients scm-client-impl jar - 1.48-SNAPSHOT + 1.48 scm-client-impl @@ -36,7 +36,7 @@ sonia.scm.clients scm-client-api - 1.48-SNAPSHOT + 1.48 @@ -70,7 +70,7 @@ sonia.scm scm-test - 1.48-SNAPSHOT + 1.48 test diff --git a/scm-core/pom.xml b/scm-core/pom.xml index a3f56f355a..93b64e058c 100644 --- a/scm-core/pom.xml +++ b/scm-core/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.48-SNAPSHOT + 1.48 sonia.scm scm-core - 1.48-SNAPSHOT + 1.48 scm-core diff --git a/scm-dao-orientdb/pom.xml b/scm-dao-orientdb/pom.xml index b2345d6c8d..ce9e165cd9 100644 --- a/scm-dao-orientdb/pom.xml +++ b/scm-dao-orientdb/pom.xml @@ -6,12 +6,12 @@ sonia.scm scm - 1.48-SNAPSHOT + 1.48 sonia.scm scm-dao-orientdb - 1.48-SNAPSHOT + 1.48 scm-dao-orientdb @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.48-SNAPSHOT + 1.48 @@ -52,7 +52,7 @@ sonia.scm scm-test - 1.48-SNAPSHOT + 1.48 test diff --git a/scm-dao-xml/pom.xml b/scm-dao-xml/pom.xml index 5e61796443..3fc3b18155 100644 --- a/scm-dao-xml/pom.xml +++ b/scm-dao-xml/pom.xml @@ -6,12 +6,12 @@ sonia.scm scm - 1.48-SNAPSHOT + 1.48 sonia.scm scm-dao-xml - 1.48-SNAPSHOT + 1.48 scm-dao-xml @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.48-SNAPSHOT + 1.48 @@ -34,7 +34,7 @@ sonia.scm scm-test - 1.48-SNAPSHOT + 1.48 test diff --git a/scm-plugin-backend/pom.xml b/scm-plugin-backend/pom.xml index 788a0334fc..94d09ebd3b 100644 --- a/scm-plugin-backend/pom.xml +++ b/scm-plugin-backend/pom.xml @@ -6,13 +6,13 @@ scm sonia.scm - 1.48-SNAPSHOT + 1.48 sonia.scm scm-plugin-backend war - 1.48-SNAPSHOT + 1.48 ${project.artifactId} @@ -62,7 +62,7 @@ sonia.scm scm-core - 1.48-SNAPSHOT + 1.48 diff --git a/scm-plugins/pom.xml b/scm-plugins/pom.xml index 24150b2da9..5aa335908d 100644 --- a/scm-plugins/pom.xml +++ b/scm-plugins/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.48-SNAPSHOT + 1.48 sonia.scm.plugins scm-plugins pom - 1.48-SNAPSHOT + 1.48 scm-plugins @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.48-SNAPSHOT + 1.48 @@ -59,7 +59,7 @@ sonia.scm.maven scm-maven-plugin - 1.48-SNAPSHOT + 1.48 process-resources diff --git a/scm-plugins/scm-git-plugin/pom.xml b/scm-plugins/scm-git-plugin/pom.xml index 2d5520a958..c1b0af2614 100644 --- a/scm-plugins/scm-git-plugin/pom.xml +++ b/scm-plugins/scm-git-plugin/pom.xml @@ -6,12 +6,12 @@ scm-plugins sonia.scm.plugins - 1.48-SNAPSHOT + 1.48 sonia.scm.plugins scm-git-plugin - 1.48-SNAPSHOT + 1.48 scm-git-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Git @@ -48,7 +48,7 @@ sonia.scm scm-test - 1.48-SNAPSHOT + 1.48 test diff --git a/scm-plugins/scm-hg-plugin/pom.xml b/scm-plugins/scm-hg-plugin/pom.xml index 6003a7ba47..5eb8f0c005 100644 --- a/scm-plugins/scm-hg-plugin/pom.xml +++ b/scm-plugins/scm-hg-plugin/pom.xml @@ -6,12 +6,12 @@ sonia.scm.plugins scm-plugins - 1.48-SNAPSHOT + 1.48 sonia.scm.plugins scm-hg-plugin - 1.48-SNAPSHOT + 1.48 scm-hg-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Mercurial @@ -42,7 +42,7 @@ sonia.scm scm-test - 1.48-SNAPSHOT + 1.48 test diff --git a/scm-plugins/scm-svn-plugin/pom.xml b/scm-plugins/scm-svn-plugin/pom.xml index 59efbfd272..afad77ff1d 100644 --- a/scm-plugins/scm-svn-plugin/pom.xml +++ b/scm-plugins/scm-svn-plugin/pom.xml @@ -6,12 +6,12 @@ scm-plugins sonia.scm.plugins - 1.48-SNAPSHOT + 1.48 sonia.scm.plugins scm-svn-plugin - 1.48-SNAPSHOT + 1.48 scm-svn-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Subversion @@ -48,7 +48,7 @@ sonia.scm scm-test - 1.48-SNAPSHOT + 1.48 test diff --git a/scm-samples/pom.xml b/scm-samples/pom.xml index af9c057725..9467277b39 100644 --- a/scm-samples/pom.xml +++ b/scm-samples/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.48-SNAPSHOT + 1.48 sonia.scm.samples scm-samples pom - 1.48-SNAPSHOT + 1.48 scm-samples diff --git a/scm-samples/scm-sample-auth/pom.xml b/scm-samples/scm-sample-auth/pom.xml index 250bc1e5d7..d92808bb91 100644 --- a/scm-samples/scm-sample-auth/pom.xml +++ b/scm-samples/scm-sample-auth/pom.xml @@ -6,12 +6,12 @@ scm-samples sonia.scm.samples - 1.48-SNAPSHOT + 1.48 sonia.scm.sample scm-sample-auth - 1.48-SNAPSHOT + 1.48 scm-sample-auth Sample Authentication Plugin https://bitbucket.org/sdorra/scm-manager @@ -28,7 +28,7 @@ sonia.scm scm-core - 1.48-SNAPSHOT + 1.48 diff --git a/scm-samples/scm-sample-hello/pom.xml b/scm-samples/scm-sample-hello/pom.xml index 7592cb18fd..19816e85c1 100644 --- a/scm-samples/scm-sample-hello/pom.xml +++ b/scm-samples/scm-sample-hello/pom.xml @@ -6,12 +6,12 @@ scm-samples sonia.scm.samples - 1.48-SNAPSHOT + 1.48 sonia.scm.sample scm-sample-hello - 1.48-SNAPSHOT + 1.48 scm-sample-hello A simple hello world plugin https://bitbucket.org/sdorra/scm-manager @@ -28,7 +28,7 @@ sonia.scm scm-core - 1.48-SNAPSHOT + 1.48 diff --git a/scm-server/pom.xml b/scm-server/pom.xml index 1a672a31cf..15c6c3bb11 100644 --- a/scm-server/pom.xml +++ b/scm-server/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.48-SNAPSHOT + 1.48 sonia.scm scm-server - 1.48-SNAPSHOT + 1.48 scm-server jar diff --git a/scm-test/pom.xml b/scm-test/pom.xml index fe95e257a7..7bff7a3905 100644 --- a/scm-test/pom.xml +++ b/scm-test/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.48-SNAPSHOT + 1.48 sonia.scm scm-test - 1.48-SNAPSHOT + 1.48 scm-test @@ -25,7 +25,7 @@ sonia.scm scm-core - 1.48-SNAPSHOT + 1.48 diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index c7218797eb..e5a03189e6 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.48-SNAPSHOT + 1.48 sonia.scm scm-webapp war - 1.48-SNAPSHOT + 1.48 scm-webapp @@ -38,31 +38,31 @@ sonia.scm scm-core - 1.48-SNAPSHOT + 1.48 sonia.scm scm-dao-xml - 1.48-SNAPSHOT + 1.48 sonia.scm.plugins scm-hg-plugin - 1.48-SNAPSHOT + 1.48 sonia.scm.plugins scm-svn-plugin - 1.48-SNAPSHOT + 1.48 sonia.scm.plugins scm-git-plugin - 1.48-SNAPSHOT + 1.48 @@ -286,7 +286,7 @@ sonia.scm scm-test - 1.48-SNAPSHOT + 1.48 test @@ -299,7 +299,7 @@ sonia.scm.plugins scm-git-plugin - 1.48-SNAPSHOT + 1.48 tests test @@ -307,7 +307,7 @@ sonia.scm.plugins scm-hg-plugin - 1.48-SNAPSHOT + 1.48 tests test @@ -315,7 +315,7 @@ sonia.scm.plugins scm-svn-plugin - 1.48-SNAPSHOT + 1.48 tests test @@ -547,7 +547,7 @@ sonia.scm scm-dao-orientdb - 1.48-SNAPSHOT + 1.48 diff --git a/support/pom.xml b/support/pom.xml index d2b787d62c..5bff52d7ac 100644 --- a/support/pom.xml +++ b/support/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.48-SNAPSHOT + 1.48 sonia.scm.support scm-support pom - 1.48-SNAPSHOT + 1.48 scm-support diff --git a/support/scm-support-btrace/pom.xml b/support/scm-support-btrace/pom.xml index 9f8264251f..ce0aa58dca 100644 --- a/support/scm-support-btrace/pom.xml +++ b/support/scm-support-btrace/pom.xml @@ -4,12 +4,12 @@ sonia.scm.support scm-support - 1.48-SNAPSHOT + 1.48 sonia.scm scm-support-btrace - 1.48-SNAPSHOT + 1.48 jar scm-support-btrace @@ -18,7 +18,7 @@ sonia.scm scm-core - 1.48-SNAPSHOT + 1.48 From c3505f16ec14c11bfab5f9ac7aa81fc3d93ea4be Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 28 Jun 2016 13:11:13 +0200 Subject: [PATCH 121/171] [maven-release-plugin] copy for tag 1.48 From df1c0358e381c452bc5edc8c86d47bb7e1e0bf75 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 28 Jun 2016 13:11:14 +0200 Subject: [PATCH 122/171] [maven-release-plugin] prepare for next development iteration --- maven/pom.xml | 4 ++-- maven/scm-maven-plugin/pom.xml | 4 ++-- maven/scm-plugin-archetype/pom.xml | 4 ++-- pom.xml | 4 ++-- scm-clients/pom.xml | 6 +++--- scm-clients/scm-cli-client/pom.xml | 6 +++--- scm-clients/scm-client-api/pom.xml | 4 ++-- scm-clients/scm-client-impl/pom.xml | 8 ++++---- scm-core/pom.xml | 4 ++-- scm-dao-orientdb/pom.xml | 8 ++++---- scm-dao-xml/pom.xml | 8 ++++---- scm-plugin-backend/pom.xml | 6 +++--- scm-plugins/pom.xml | 8 ++++---- scm-plugins/scm-git-plugin/pom.xml | 6 +++--- scm-plugins/scm-hg-plugin/pom.xml | 6 +++--- scm-plugins/scm-svn-plugin/pom.xml | 6 +++--- scm-samples/pom.xml | 4 ++-- scm-samples/scm-sample-auth/pom.xml | 6 +++--- scm-samples/scm-sample-hello/pom.xml | 6 +++--- scm-server/pom.xml | 4 ++-- scm-test/pom.xml | 6 +++--- scm-webapp/pom.xml | 24 ++++++++++++------------ support/pom.xml | 4 ++-- support/scm-support-btrace/pom.xml | 6 +++--- 24 files changed, 76 insertions(+), 76 deletions(-) diff --git a/maven/pom.xml b/maven/pom.xml index 2eb1e4eca4..cf788a2471 100644 --- a/maven/pom.xml +++ b/maven/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.48 + 1.49-SNAPSHOT sonia.scm.maven scm-maven-plugins pom - 1.48 + 1.49-SNAPSHOT scm-maven-plugins diff --git a/maven/scm-maven-plugin/pom.xml b/maven/scm-maven-plugin/pom.xml index 55d4c181bc..4f376e411c 100644 --- a/maven/scm-maven-plugin/pom.xml +++ b/maven/scm-maven-plugin/pom.xml @@ -6,12 +6,12 @@ scm-maven-plugins sonia.scm.maven - 1.48 + 1.49-SNAPSHOT sonia.scm.maven scm-maven-plugin - 1.48 + 1.49-SNAPSHOT maven-plugin scm-maven-plugin diff --git a/maven/scm-plugin-archetype/pom.xml b/maven/scm-plugin-archetype/pom.xml index 52ed9e96c0..0a247d2cfb 100644 --- a/maven/scm-plugin-archetype/pom.xml +++ b/maven/scm-plugin-archetype/pom.xml @@ -6,12 +6,12 @@ scm-maven-plugins sonia.scm.maven - 1.48 + 1.49-SNAPSHOT sonia.scm.maven scm-plugin-archetype - 1.48 + 1.49-SNAPSHOT scm-plugin-archetype diff --git a/pom.xml b/pom.xml index 2ccb0aaaa2..beedafee1e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ sonia.scm scm pom - 1.48 + 1.49-SNAPSHOT The easiest way to share your Git, Mercurial and Subversion repositories over http. @@ -36,7 +36,7 @@ scm:hg:http://bitbucket.org/sdorra/scm-manager scm:hg:https://bitbucket.org/sdorra/scm-manager http://bitbucket.org/sdorra/scm-manager - 1.48 + HEAD diff --git a/scm-clients/pom.xml b/scm-clients/pom.xml index e0db189fd9..e946a2fc8f 100644 --- a/scm-clients/pom.xml +++ b/scm-clients/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.48 + 1.49-SNAPSHOT sonia.scm.clients scm-clients pom - 1.48 + 1.49-SNAPSHOT scm-clients @@ -32,7 +32,7 @@ scm-core sonia.scm jar - 1.48 + 1.49-SNAPSHOT shiro-core diff --git a/scm-clients/scm-cli-client/pom.xml b/scm-clients/scm-cli-client/pom.xml index 2c98e36243..aa3d6a807b 100644 --- a/scm-clients/scm-cli-client/pom.xml +++ b/scm-clients/scm-cli-client/pom.xml @@ -6,12 +6,12 @@ scm-clients sonia.scm.clients - 1.48 + 1.49-SNAPSHOT sonia.scm.clients scm-cli-client - 1.48 + 1.49-SNAPSHOT scm-cli-client @@ -34,7 +34,7 @@ sonia.scm.clients scm-client-impl - 1.48 + 1.49-SNAPSHOT diff --git a/scm-clients/scm-client-api/pom.xml b/scm-clients/scm-client-api/pom.xml index dc115f1770..3285997063 100644 --- a/scm-clients/scm-client-api/pom.xml +++ b/scm-clients/scm-client-api/pom.xml @@ -6,13 +6,13 @@ sonia.scm.clients scm-clients - 1.48 + 1.49-SNAPSHOT sonia.scm.clients scm-client-api jar - 1.48 + 1.49-SNAPSHOT scm-client-api diff --git a/scm-clients/scm-client-impl/pom.xml b/scm-clients/scm-client-impl/pom.xml index 642e6395f1..0173942ed2 100644 --- a/scm-clients/scm-client-impl/pom.xml +++ b/scm-clients/scm-client-impl/pom.xml @@ -6,13 +6,13 @@ sonia.scm.clients scm-clients - 1.48 + 1.49-SNAPSHOT sonia.scm.clients scm-client-impl jar - 1.48 + 1.49-SNAPSHOT scm-client-impl @@ -36,7 +36,7 @@ sonia.scm.clients scm-client-api - 1.48 + 1.49-SNAPSHOT @@ -70,7 +70,7 @@ sonia.scm scm-test - 1.48 + 1.49-SNAPSHOT test diff --git a/scm-core/pom.xml b/scm-core/pom.xml index 93b64e058c..5ee1f110ab 100644 --- a/scm-core/pom.xml +++ b/scm-core/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.48 + 1.49-SNAPSHOT sonia.scm scm-core - 1.48 + 1.49-SNAPSHOT scm-core diff --git a/scm-dao-orientdb/pom.xml b/scm-dao-orientdb/pom.xml index ce9e165cd9..5b54b2361e 100644 --- a/scm-dao-orientdb/pom.xml +++ b/scm-dao-orientdb/pom.xml @@ -6,12 +6,12 @@ sonia.scm scm - 1.48 + 1.49-SNAPSHOT sonia.scm scm-dao-orientdb - 1.48 + 1.49-SNAPSHOT scm-dao-orientdb @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.48 + 1.49-SNAPSHOT @@ -52,7 +52,7 @@ sonia.scm scm-test - 1.48 + 1.49-SNAPSHOT test diff --git a/scm-dao-xml/pom.xml b/scm-dao-xml/pom.xml index 3fc3b18155..009b20f559 100644 --- a/scm-dao-xml/pom.xml +++ b/scm-dao-xml/pom.xml @@ -6,12 +6,12 @@ sonia.scm scm - 1.48 + 1.49-SNAPSHOT sonia.scm scm-dao-xml - 1.48 + 1.49-SNAPSHOT scm-dao-xml @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.48 + 1.49-SNAPSHOT @@ -34,7 +34,7 @@ sonia.scm scm-test - 1.48 + 1.49-SNAPSHOT test diff --git a/scm-plugin-backend/pom.xml b/scm-plugin-backend/pom.xml index 94d09ebd3b..9f9024def5 100644 --- a/scm-plugin-backend/pom.xml +++ b/scm-plugin-backend/pom.xml @@ -6,13 +6,13 @@ scm sonia.scm - 1.48 + 1.49-SNAPSHOT sonia.scm scm-plugin-backend war - 1.48 + 1.49-SNAPSHOT ${project.artifactId} @@ -62,7 +62,7 @@ sonia.scm scm-core - 1.48 + 1.49-SNAPSHOT diff --git a/scm-plugins/pom.xml b/scm-plugins/pom.xml index 5aa335908d..35c64fb10c 100644 --- a/scm-plugins/pom.xml +++ b/scm-plugins/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.48 + 1.49-SNAPSHOT sonia.scm.plugins scm-plugins pom - 1.48 + 1.49-SNAPSHOT scm-plugins @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.48 + 1.49-SNAPSHOT @@ -59,7 +59,7 @@ sonia.scm.maven scm-maven-plugin - 1.48 + 1.49-SNAPSHOT process-resources diff --git a/scm-plugins/scm-git-plugin/pom.xml b/scm-plugins/scm-git-plugin/pom.xml index c1b0af2614..82515c13cd 100644 --- a/scm-plugins/scm-git-plugin/pom.xml +++ b/scm-plugins/scm-git-plugin/pom.xml @@ -6,12 +6,12 @@ scm-plugins sonia.scm.plugins - 1.48 + 1.49-SNAPSHOT sonia.scm.plugins scm-git-plugin - 1.48 + 1.49-SNAPSHOT scm-git-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Git @@ -48,7 +48,7 @@ sonia.scm scm-test - 1.48 + 1.49-SNAPSHOT test diff --git a/scm-plugins/scm-hg-plugin/pom.xml b/scm-plugins/scm-hg-plugin/pom.xml index 5eb8f0c005..3a2611d6da 100644 --- a/scm-plugins/scm-hg-plugin/pom.xml +++ b/scm-plugins/scm-hg-plugin/pom.xml @@ -6,12 +6,12 @@ sonia.scm.plugins scm-plugins - 1.48 + 1.49-SNAPSHOT sonia.scm.plugins scm-hg-plugin - 1.48 + 1.49-SNAPSHOT scm-hg-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Mercurial @@ -42,7 +42,7 @@ sonia.scm scm-test - 1.48 + 1.49-SNAPSHOT test diff --git a/scm-plugins/scm-svn-plugin/pom.xml b/scm-plugins/scm-svn-plugin/pom.xml index afad77ff1d..3de56a1a92 100644 --- a/scm-plugins/scm-svn-plugin/pom.xml +++ b/scm-plugins/scm-svn-plugin/pom.xml @@ -6,12 +6,12 @@ scm-plugins sonia.scm.plugins - 1.48 + 1.49-SNAPSHOT sonia.scm.plugins scm-svn-plugin - 1.48 + 1.49-SNAPSHOT scm-svn-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Subversion @@ -48,7 +48,7 @@ sonia.scm scm-test - 1.48 + 1.49-SNAPSHOT test diff --git a/scm-samples/pom.xml b/scm-samples/pom.xml index 9467277b39..b647b307af 100644 --- a/scm-samples/pom.xml +++ b/scm-samples/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.48 + 1.49-SNAPSHOT sonia.scm.samples scm-samples pom - 1.48 + 1.49-SNAPSHOT scm-samples diff --git a/scm-samples/scm-sample-auth/pom.xml b/scm-samples/scm-sample-auth/pom.xml index d92808bb91..53f30cc74d 100644 --- a/scm-samples/scm-sample-auth/pom.xml +++ b/scm-samples/scm-sample-auth/pom.xml @@ -6,12 +6,12 @@ scm-samples sonia.scm.samples - 1.48 + 1.49-SNAPSHOT sonia.scm.sample scm-sample-auth - 1.48 + 1.49-SNAPSHOT scm-sample-auth Sample Authentication Plugin https://bitbucket.org/sdorra/scm-manager @@ -28,7 +28,7 @@ sonia.scm scm-core - 1.48 + 1.49-SNAPSHOT diff --git a/scm-samples/scm-sample-hello/pom.xml b/scm-samples/scm-sample-hello/pom.xml index 19816e85c1..04ff972b50 100644 --- a/scm-samples/scm-sample-hello/pom.xml +++ b/scm-samples/scm-sample-hello/pom.xml @@ -6,12 +6,12 @@ scm-samples sonia.scm.samples - 1.48 + 1.49-SNAPSHOT sonia.scm.sample scm-sample-hello - 1.48 + 1.49-SNAPSHOT scm-sample-hello A simple hello world plugin https://bitbucket.org/sdorra/scm-manager @@ -28,7 +28,7 @@ sonia.scm scm-core - 1.48 + 1.49-SNAPSHOT diff --git a/scm-server/pom.xml b/scm-server/pom.xml index 15c6c3bb11..df5ec69a0f 100644 --- a/scm-server/pom.xml +++ b/scm-server/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.48 + 1.49-SNAPSHOT sonia.scm scm-server - 1.48 + 1.49-SNAPSHOT scm-server jar diff --git a/scm-test/pom.xml b/scm-test/pom.xml index 7bff7a3905..4d700407cf 100644 --- a/scm-test/pom.xml +++ b/scm-test/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.48 + 1.49-SNAPSHOT sonia.scm scm-test - 1.48 + 1.49-SNAPSHOT scm-test @@ -25,7 +25,7 @@ sonia.scm scm-core - 1.48 + 1.49-SNAPSHOT diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index e5a03189e6..9a7ab485a8 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.48 + 1.49-SNAPSHOT sonia.scm scm-webapp war - 1.48 + 1.49-SNAPSHOT scm-webapp @@ -38,31 +38,31 @@ sonia.scm scm-core - 1.48 + 1.49-SNAPSHOT sonia.scm scm-dao-xml - 1.48 + 1.49-SNAPSHOT sonia.scm.plugins scm-hg-plugin - 1.48 + 1.49-SNAPSHOT sonia.scm.plugins scm-svn-plugin - 1.48 + 1.49-SNAPSHOT sonia.scm.plugins scm-git-plugin - 1.48 + 1.49-SNAPSHOT @@ -286,7 +286,7 @@ sonia.scm scm-test - 1.48 + 1.49-SNAPSHOT test @@ -299,7 +299,7 @@ sonia.scm.plugins scm-git-plugin - 1.48 + 1.49-SNAPSHOT tests test @@ -307,7 +307,7 @@ sonia.scm.plugins scm-hg-plugin - 1.48 + 1.49-SNAPSHOT tests test @@ -315,7 +315,7 @@ sonia.scm.plugins scm-svn-plugin - 1.48 + 1.49-SNAPSHOT tests test @@ -547,7 +547,7 @@ sonia.scm scm-dao-orientdb - 1.48 + 1.49-SNAPSHOT diff --git a/support/pom.xml b/support/pom.xml index 5bff52d7ac..f8a2a61627 100644 --- a/support/pom.xml +++ b/support/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.48 + 1.49-SNAPSHOT sonia.scm.support scm-support pom - 1.48 + 1.49-SNAPSHOT scm-support diff --git a/support/scm-support-btrace/pom.xml b/support/scm-support-btrace/pom.xml index ce0aa58dca..aa4caaf32c 100644 --- a/support/scm-support-btrace/pom.xml +++ b/support/scm-support-btrace/pom.xml @@ -4,12 +4,12 @@ sonia.scm.support scm-support - 1.48 + 1.49-SNAPSHOT sonia.scm scm-support-btrace - 1.48 + 1.49-SNAPSHOT jar scm-support-btrace @@ -18,7 +18,7 @@ sonia.scm scm-core - 1.48 + 1.49-SNAPSHOT From ef7a94353f74227e5f1cac4bc6b31c371bef9dee Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 29 Jun 2016 20:24:35 +0200 Subject: [PATCH 123/171] close branch issue-841 From 8d5c3e709f8e7ccdd0157a3a89be7055c34c0466 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 29 Jun 2016 20:28:33 +0200 Subject: [PATCH 124/171] remove debug output, which seems to break ui on ie #844 --- scm-webapp/src/main/webapp/resources/js/sonia.global.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/scm-webapp/src/main/webapp/resources/js/sonia.global.js b/scm-webapp/src/main/webapp/resources/js/sonia.global.js index 4513a05e6e..a43feb824b 100644 --- a/scm-webapp/src/main/webapp/resources/js/sonia.global.js +++ b/scm-webapp/src/main/webapp/resources/js/sonia.global.js @@ -40,8 +40,6 @@ Ext.Ajax.defaultHeaders = { // XSRF protection Ext.Ajax.on('beforerequest', function(conn, options){ var token = Ext.util.Cookies.get('X-XSRF-Token'); - console.log(token); - console.log(options); if (token){ if (!options.headers){ options.headers = {}; From d1193c39df72c4aec4102f2fcaf5643159ae2cf2 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Fri, 8 Jul 2016 14:29:09 +0200 Subject: [PATCH 125/171] assign revision field in constructor FileObjectWrapper, fix issue 846 --- .../src/main/java/sonia/scm/client/FileObjectWrapper.java | 1 + 1 file changed, 1 insertion(+) diff --git a/scm-clients/scm-client-api/src/main/java/sonia/scm/client/FileObjectWrapper.java b/scm-clients/scm-client-api/src/main/java/sonia/scm/client/FileObjectWrapper.java index 1c6c0aaeda..ddbeef69a3 100644 --- a/scm-clients/scm-client-api/src/main/java/sonia/scm/client/FileObjectWrapper.java +++ b/scm-clients/scm-client-api/src/main/java/sonia/scm/client/FileObjectWrapper.java @@ -66,6 +66,7 @@ public class FileObjectWrapper String revision, FileObject file) { this.repositoryBrowser = repositoryBrowser; + this.revision = revision; this.file = file; } From 73c566d9bf71e8cfa4ba6b53a0e6fb2139ca9ef5 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Fri, 8 Jul 2016 14:50:20 +0200 Subject: [PATCH 126/171] escape url parameters ub UrlBuilder in order to fix issue #847 --- scm-core/src/main/java/sonia/scm/util/UrlBuilder.java | 8 ++++++-- scm-core/src/test/java/sonia/scm/util/UrlBuilderTest.java | 5 +++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/util/UrlBuilder.java b/scm-core/src/main/java/sonia/scm/util/UrlBuilder.java index cd39723ec1..c844f65f11 100644 --- a/scm-core/src/main/java/sonia/scm/util/UrlBuilder.java +++ b/scm-core/src/main/java/sonia/scm/util/UrlBuilder.java @@ -35,6 +35,7 @@ package sonia.scm.util; //~--- JDK imports ------------------------------------------------------------ +import com.google.common.net.UrlEscapers; import java.net.MalformedURLException; import java.net.URL; @@ -138,8 +139,11 @@ public class UrlBuilder { if (Util.isNotEmpty(name) && Util.isNotEmpty(value)) { - url = new StringBuilder(url).append(separator).append(name).append( - HttpUtil.SEPARATOR_PARAMETER_VALUE).append(value).toString(); + url = new StringBuilder(url) + .append(separator).append(name) + .append(HttpUtil.SEPARATOR_PARAMETER_VALUE) + .append(UrlEscapers.urlFragmentEscaper().escape(value)) + .toString(); separator = HttpUtil.SEPARATOR_PARAMETER; parameterAdded = true; } diff --git a/scm-core/src/test/java/sonia/scm/util/UrlBuilderTest.java b/scm-core/src/test/java/sonia/scm/util/UrlBuilderTest.java index fe64cb2d4f..16d4b59ffc 100644 --- a/scm-core/src/test/java/sonia/scm/util/UrlBuilderTest.java +++ b/scm-core/src/test/java/sonia/scm/util/UrlBuilderTest.java @@ -57,8 +57,9 @@ public class UrlBuilderTest builder.appendParameter("i", 123).appendParameter("s", "abc"); builder.appendParameter("b", true).appendParameter("l", 321l); - assertEquals("http://www.short.de?i=123&s=abc&b=true&l=321", - builder.toString()); + assertEquals("http://www.short.de?i=123&s=abc&b=true&l=321", builder.toString()); + builder.appendParameter("c", "a b"); + assertEquals("http://www.short.de?i=123&s=abc&b=true&l=321&c=a%20b", builder.toString()); } /** From 537a7e1509edf791691374ec642ec9b1dbd5410e Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Fri, 8 Jul 2016 14:51:48 +0200 Subject: [PATCH 127/171] close branch issue-844 From f61380097013755de1f9a0404abd69134701ae68 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 14 Jul 2016 22:09:16 +0200 Subject: [PATCH 128/171] set java version to 1.7 and updated jgit 4.4.0.201606070830-r-scm1 --- pom.xml | 6 +++--- .../src/main/java/sonia/scm/repository/GitUtil.java | 6 +++--- .../scm/repository/client/GitRepositoryClient.java | 13 ++----------- 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/pom.xml b/pom.xml index beedafee1e..843f85d1a8 100644 --- a/pom.xml +++ b/pom.xml @@ -146,7 +146,7 @@ org.codehaus.mojo.signature - java16 + java17 1.0 @@ -424,7 +424,7 @@ 1.2.5 - 3.7.1.201504261725-r-scm1 + 4.4.0.201606070830-r-scm1 1.8.5-scm2 @@ -432,7 +432,7 @@ 2.2.3 - 1.6 + 1.7 UTF-8 SCM-BSD diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java index 8985966220..16a6f05541 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java @@ -264,7 +264,7 @@ public final class GitUtil { if (formatter != null) { - formatter.release(); + formatter.close(); } } @@ -278,7 +278,7 @@ public final class GitUtil { if (walk != null) { - walk.release(); + walk.close(); } } @@ -292,7 +292,7 @@ public final class GitUtil { if (walk != null) { - walk.release(); + walk.close();; } } diff --git a/scm-test/src/main/java/sonia/scm/repository/client/GitRepositoryClient.java b/scm-test/src/main/java/sonia/scm/repository/client/GitRepositoryClient.java index 9f73fcb21a..9eba91d0e0 100644 --- a/scm-test/src/main/java/sonia/scm/repository/client/GitRepositoryClient.java +++ b/scm-test/src/main/java/sonia/scm/repository/client/GitRepositoryClient.java @@ -476,19 +476,10 @@ public class GitRepositoryClient extends AbstractRepositoryClient private RevCommit parseCommit(Ref branch) throws MissingObjectException, IncorrectObjectTypeException, IOException { - final RevWalk rw = new RevWalk(repository); - final RevCommit commit; - - try + try (RevWalk rw = new RevWalk(repository)) { - commit = rw.parseCommit(branch.getObjectId()); + return rw.parseCommit(branch.getObjectId()); } - finally - { - rw.release(); - } - - return commit; } /** From 0fbe51f5a343baa2f58242262d65cf9446a6ec88 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Mon, 25 Jul 2016 12:09:41 +0200 Subject: [PATCH 129/171] close branhc issue-848 From 8a19193799568f1646e2ac03ff3b8cfac82071d3 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Mon, 25 Jul 2016 18:27:47 +0200 Subject: [PATCH 130/171] added RepositoryHookITCase to test repository post receive hooks --- scm-webapp/pom.xml | 4 + .../java/sonia/scm/ScmContextListener.java | 5 + .../main/java/sonia/scm/debug/DebugHook.java | 85 +++++++ .../java/sonia/scm/debug/DebugHookData.java | 90 ++++++++ .../java/sonia/scm/debug/DebugModule.java | 51 +++++ .../java/sonia/scm/debug/DebugResource.java | 89 ++++++++ .../java/sonia/scm/debug/DebugService.java | 92 ++++++++ .../sonia/scm/it/IntegrationTestUtil.java | 11 +- .../sonia/scm/it/RepositoryHookITCase.java | 208 ++++++++++++++++++ 9 files changed, 631 insertions(+), 4 deletions(-) create mode 100644 scm-webapp/src/main/java/sonia/scm/debug/DebugHook.java create mode 100644 scm-webapp/src/main/java/sonia/scm/debug/DebugHookData.java create mode 100644 scm-webapp/src/main/java/sonia/scm/debug/DebugModule.java create mode 100644 scm-webapp/src/main/java/sonia/scm/debug/DebugResource.java create mode 100644 scm-webapp/src/main/java/sonia/scm/debug/DebugService.java create mode 100644 scm-webapp/src/test/java/sonia/scm/it/RepositoryHookITCase.java diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index 9a7ab485a8..e4245649f1 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -644,6 +644,10 @@ scm.home target/scm-it + + scm.stage + ${scm.stage} + diff --git a/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java b/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java index 2c2efff045..555fb7682c 100644 --- a/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java +++ b/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java @@ -59,6 +59,7 @@ import java.util.List; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; +import sonia.scm.debug.DebugModule; import sonia.scm.repository.HealthCheckContextListener; import sonia.scm.schedule.Scheduler; @@ -198,6 +199,10 @@ public class ScmContextListener extends GuiceServletContextListener moduleList.add(new ScmSecurityModule(servletContext)); moduleList.addAll(pluginLoader.getModuleSet()); moduleList.addAll(overrides.getModules()); + + if (SCMContext.getContext().getStage() == Stage.DEVELOPMENT){ + moduleList.add(new DebugModule()); + } return Guice.createInjector(moduleList); } diff --git a/scm-webapp/src/main/java/sonia/scm/debug/DebugHook.java b/scm-webapp/src/main/java/sonia/scm/debug/DebugHook.java new file mode 100644 index 0000000000..b225090da0 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/debug/DebugHook.java @@ -0,0 +1,85 @@ +/** + * 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.debug; + +import com.google.common.base.Function; +import com.google.common.collect.Collections2; +import com.google.common.eventbus.Subscribe; +import javax.inject.Inject; +import sonia.scm.EagerSingleton; +import sonia.scm.repository.Changeset; +import sonia.scm.repository.PostReceiveRepositoryHookEvent; + +/** + * {@link PostReceiveRepositoryHookEvent} which stores receives data and passes it to the {@link DebugService}. + * + * @author Sebastian Sdorra + */ +@EagerSingleton +public final class DebugHook +{ + + private final DebugService debugService; + + /** + * Constructs a new instance. + * + * @param debugService debug service + */ + @Inject + public DebugHook(DebugService debugService) + { + this.debugService = debugService; + } + + /** + * Processes the received {@link PostReceiveRepositoryHookEvent} and transforms it to a {@link DebugHookData} and + * passes it to the {@link DebugService}. + * + * @param event received event + */ + @Subscribe + public void processEvent(PostReceiveRepositoryHookEvent event){ + debugService.put( + event.getRepository().getId(), + new DebugHookData(Collections2.transform(event.getChangesets(), IDEXTRACTOR)) + ); + } + + private static final Function IDEXTRACTOR = new Function(){ + + @Override + public String apply(Changeset input) + { + return input.getId(); + } + }; +} diff --git a/scm-webapp/src/main/java/sonia/scm/debug/DebugHookData.java b/scm-webapp/src/main/java/sonia/scm/debug/DebugHookData.java new file mode 100644 index 0000000000..bb8cfa42e7 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/debug/DebugHookData.java @@ -0,0 +1,90 @@ +/** + * 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.debug; + +import java.util.Collection; +import java.util.Date; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * Received data from repository hook event. + * + * @author Sebastian Sdorra + */ +@XmlRootElement(name = "hook") +@XmlAccessorType(XmlAccessType.FIELD) +public class DebugHookData +{ + private Date date; + private Collection changesets; + + /** + * Constructs a new instance. This constructor should only be used by JAXB. + */ + public DebugHookData() + { + } + + /** + * Constructs a new instance. + * + * @param changesets collection of changeset ids + */ + public DebugHookData(Collection changesets) + { + this.date = new Date(); + this.changesets = changesets; + } + + /** + * Returns the receiving date. + * + * @return receiving date + */ + public Date getDate() + { + return date; + } + + /** + * Return collection of changeset ids. + * + * @return collection of changeset ids + */ + public Collection getChangesets() + { + return changesets; + } + + +} diff --git a/scm-webapp/src/main/java/sonia/scm/debug/DebugModule.java b/scm-webapp/src/main/java/sonia/scm/debug/DebugModule.java new file mode 100644 index 0000000000..c0dc231341 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/debug/DebugModule.java @@ -0,0 +1,51 @@ +/** + * 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.debug; + +import com.google.inject.AbstractModule; + +/** + * DebugModule binds all required classes around the {@link DebugService}. + * + * @author Sebastian Sdorra + */ +public final class DebugModule extends AbstractModule +{ + + @Override + protected void configure() + { + bind(DebugService.class); + bind(DebugHook.class); + bind(DebugResource.class); + } + +} diff --git a/scm-webapp/src/main/java/sonia/scm/debug/DebugResource.java b/scm-webapp/src/main/java/sonia/scm/debug/DebugResource.java new file mode 100644 index 0000000000..f65e0c7708 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/debug/DebugResource.java @@ -0,0 +1,89 @@ +/** + * 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.debug; + +import java.util.Collection; +import javax.inject.Inject; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +/** + * Rest api resource for the {@link DebugService}. + * + * @author Sebastian Sdorra + */ +@Path("debug/{repository}/post-receive") +public final class DebugResource +{ + private final DebugService debugService; + + /** + * Constructs a new instance. + * + * @param debugService debug service + */ + @Inject + public DebugResource(DebugService debugService) + { + this.debugService = debugService; + } + + /** + * Returns all received hook data for the given repository. + * + * @param repository repository id + * + * @return all received hook data for the given repository + */ + @GET + @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) + public Collection getAll(@PathParam("repository") String repository){ + return debugService.getAll(repository); + } + + /** + * Returns the last received hook data for the given repository. + * + * @param repository repository id + * + * @return the last received hook data for the given repository + */ + @GET + @Path("last") + @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) + public DebugHookData getLast(@PathParam("repository") String repository){ + return debugService.getLast(repository); + } + +} diff --git a/scm-webapp/src/main/java/sonia/scm/debug/DebugService.java b/scm-webapp/src/main/java/sonia/scm/debug/DebugService.java new file mode 100644 index 0000000000..807b9c0abc --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/debug/DebugService.java @@ -0,0 +1,92 @@ +/** + * 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.debug; + +import com.google.common.collect.Iterables; +import com.google.common.collect.LinkedListMultimap; +import com.google.common.collect.Multimap; +import com.google.inject.Singleton; +import java.util.Collection; +import org.apache.shiro.SecurityUtils; +import sonia.scm.security.Role; + +/** + * The DebugService stores and returns received data from repository hook events. + * + * @author Sebastian Sdorra + */ +@Singleton +public final class DebugService +{ + + private final Multimap receivedHooks = LinkedListMultimap.create(); + + /** + * + * @param repository repository id + * @param hookData received hook data + */ + public void put(String repository, DebugHookData hookData) + { + SecurityUtils.getSubject().checkRole(Role.ADMIN); + receivedHooks.put(repository, hookData); + } + + /** + * Returns the last received hook data for the given repository. + * + * @param repository repository id + * + * @return the last received hook data for the given repository + */ + public DebugHookData getLast(String repository){ + SecurityUtils.getSubject().checkRole(Role.ADMIN); + DebugHookData hookData = null; + Collection receivedHookData = receivedHooks.get(repository); + if (receivedHookData != null && ! receivedHookData.isEmpty()){ + hookData = Iterables.getLast(receivedHookData); + } + return hookData; + } + + /** + * Returns all received hook data for the given repository. + * + * @param repository repository id + * + * @return all received hook data for the given repository + */ + public Collection getAll(String repository){ + SecurityUtils.getSubject().checkRole(Role.ADMIN); + return receivedHooks.get(repository); + } + +} diff --git a/scm-webapp/src/test/java/sonia/scm/it/IntegrationTestUtil.java b/scm-webapp/src/test/java/sonia/scm/it/IntegrationTestUtil.java index 5d4b1fbe48..9bb2b4c16d 100644 --- a/scm-webapp/src/test/java/sonia/scm/it/IntegrationTestUtil.java +++ b/scm-webapp/src/test/java/sonia/scm/it/IntegrationTestUtil.java @@ -76,9 +76,12 @@ public final class IntegrationTestUtil /** Field description */ public static final String ADMIN_USERNAME = "scmadmin"; - - /** Field description */ - public static final String BASE_URL = "http://localhost:8081/scm/api/rest/"; + + /** scm-manager base url */ + public static final String BASE_URL = "http://localhost:8081/scm/"; + + /** scm-manager base url for the rest api */ + public static final String REST_BASE_URL = BASE_URL.concat("api/rest/"); /** Field description */ public static final String EXTENSION = ".xml"; @@ -253,7 +256,7 @@ public final class IntegrationTestUtil */ public static String createResourceUrl(String url) { - return BASE_URL.concat(url).concat(EXTENSION); + return REST_BASE_URL.concat(url).concat(EXTENSION); } /** diff --git a/scm-webapp/src/test/java/sonia/scm/it/RepositoryHookITCase.java b/scm-webapp/src/test/java/sonia/scm/it/RepositoryHookITCase.java new file mode 100644 index 0000000000..60bf5d4aa9 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/it/RepositoryHookITCase.java @@ -0,0 +1,208 @@ +/** + * 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.it; + +import com.google.common.base.Charsets; +import com.google.common.collect.Lists; +import com.google.common.io.Files; +import com.sun.jersey.api.client.WebResource; +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import org.junit.After; +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import sonia.scm.debug.DebugHookData; +import static sonia.scm.it.IntegrationTestUtil.createResource; +import static sonia.scm.it.RepositoryITUtil.createRepository; +import static sonia.scm.it.RepositoryITUtil.deleteRepository; +import sonia.scm.repository.Changeset; +import sonia.scm.repository.Person; +import sonia.scm.repository.Repository; +import sonia.scm.repository.RepositoryTestData; +import sonia.scm.repository.client.api.RepositoryClient; +import sonia.scm.repository.client.api.RepositoryClientFactory; + +/** + * Integration tests for repository hooks. + * + * @author Sebastian Sdorra + */ +@RunWith(Parameterized.class) +public class RepositoryHookITCase extends AbstractAdminITCaseBase +{ + + private static final RepositoryClientFactory REPOSITORY_CLIENT_FACTORY = new RepositoryClientFactory(); + + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder(); + + private final String repositoryType; + private Repository repository; + private File workingCopy; + private RepositoryClient repositoryClient; + + /** + * Constructs a new instance with a repository type. + * + * @param repositoryType repository type + */ + public RepositoryHookITCase(String repositoryType) + { + this.repositoryType = repositoryType; + } + + /** + * Creates a test repository. + * + * @throws IOException + */ + @Before + public void setUpTestRepository() throws IOException + { + repository = RepositoryTestData.createHeartOfGold(repositoryType); + repository = createRepository(client, repository); + workingCopy = tempFolder.newFolder(); + repositoryClient = createRepositoryClient(); + } + + /** + * Removes the tests repository. + */ + @After + public void removeTestRepository() + { + deleteRepository(client, repository.getId()); + } + + /** + * Tests that the debug service has received the commit. + * + * @throws IOException + * @throws InterruptedException + */ + @Test + public void testSimpleHook() throws IOException, InterruptedException + { + // push commit + Files.write("a", new File(workingCopy, "a.txt"), Charsets.UTF_8); + repositoryClient.getAddCommand().add("a.txt"); + Changeset changeset = repositoryClient.getCommitCommand().commit( + new Person("scmadmin", "scmadmin@scm-manager.org"), "added a" + ); + repositoryClient.getPushCommand().push(); + + // wait some time, because the debug hook is asnychron + Thread.sleep(125); + + // check debug servlet for pushed commit + WebResource wr = createResource(client, "debug/" + repository.getId() + "/post-receive/last"); + DebugHookData data = wr.get(DebugHookData.class); + assertNotNull(data); + assertThat(data.getChangesets(), contains(changeset.getId())); + } + + /** + * Tests that the debug service receives only new commits. + * + * @throws IOException + * @throws InterruptedException + */ + @Test + public void testOnlyNewCommit() throws IOException, InterruptedException + { + // push commit + Files.write("a", new File(workingCopy, "a.txt"), Charsets.UTF_8); + repositoryClient.getAddCommand().add("a.txt"); + Changeset a = repositoryClient.getCommitCommand().commit( + new Person("scmadmin", "scmadmin@scm-manager.org"), "added a" + ); + repositoryClient.getPushCommand().push(); + + // create branch, commit and push again + repositoryClient.getBranchCommand().branch("feature/added-b"); + Files.write("b", new File(workingCopy, "b.txt"), Charsets.UTF_8); + repositoryClient.getAddCommand().add("a.txt"); + Changeset b = repositoryClient.getCommitCommand().commit( + new Person("scmadmin", "scmadmin@scm-manager.org"), "added b" + ); + repositoryClient.getPushCommand().push(); + + // wait some time, because the debug hook is asnychron + Thread.sleep(125); + + // check debug servlet that only one commit is present + WebResource wr = createResource(client, "debug/" + repository.getId() + "/post-receive/last"); + DebugHookData data = wr.get(DebugHookData.class); + assertNotNull(data); + assertThat(data.getChangesets(), allOf( + contains(b.getId()), + not( + contains(a.getId()) + ) + )); + } + + private RepositoryClient createRepositoryClient() throws IOException + { + return REPOSITORY_CLIENT_FACTORY.create(repositoryType, + IntegrationTestUtil.BASE_URL + repositoryType + "/" + repository.getName(), + IntegrationTestUtil.ADMIN_USERNAME, IntegrationTestUtil.ADMIN_PASSWORD, workingCopy + ); + } + + + /** + * Returns repository types a test parameter. + * + * @return repository types test parameter + */ + @Parameters + public static Collection createParameters() + { + Collection params = Lists.newArrayList(); + params.add(new String[] { "git" }); + // params.add(new String[] { "svn" }); + // if (IOUtil.search("hg") != null) + // { + // params.add(new String[] { "hg" }); + // } + return params; + } + +} From 37228c329cde3837b2af375961faedb59f70cc3f Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Mon, 25 Jul 2016 18:28:58 +0200 Subject: [PATCH 131/171] improve javadoc --- scm-webapp/src/main/java/sonia/scm/debug/DebugModule.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scm-webapp/src/main/java/sonia/scm/debug/DebugModule.java b/scm-webapp/src/main/java/sonia/scm/debug/DebugModule.java index c0dc231341..3c767a1ddb 100644 --- a/scm-webapp/src/main/java/sonia/scm/debug/DebugModule.java +++ b/scm-webapp/src/main/java/sonia/scm/debug/DebugModule.java @@ -33,7 +33,8 @@ package sonia.scm.debug; import com.google.inject.AbstractModule; /** - * DebugModule binds all required classes around the {@link DebugService}. + * DebugModule binds all required classes around the {@link DebugService}. The module will only be activated, if the + * application was started in development stage. * * @author Sebastian Sdorra */ From 5340bea7f1f1a2af48e344dc5e9436bba83e2e25 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Mon, 25 Jul 2016 18:49:57 +0200 Subject: [PATCH 132/171] re enable repository it cases --- .../java/sonia/scm/it/RepositoryITCase.java | 48 ++++++++----------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/scm-webapp/src/test/java/sonia/scm/it/RepositoryITCase.java b/scm-webapp/src/test/java/sonia/scm/it/RepositoryITCase.java index d1de59feac..f0f7b65f12 100644 --- a/scm-webapp/src/test/java/sonia/scm/it/RepositoryITCase.java +++ b/scm-webapp/src/test/java/sonia/scm/it/RepositoryITCase.java @@ -35,8 +35,7 @@ package sonia.scm.it; //~--- non-JDK imports -------------------------------------------------------- -import org.junit.AfterClass; -import org.junit.Ignore; +import com.google.common.collect.Lists; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -48,13 +47,13 @@ import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryTestData; import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; import static sonia.scm.it.IntegrationTestUtil.*; import static sonia.scm.it.RepositoryITUtil.*; //~--- JDK imports ------------------------------------------------------------ -import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.GenericType; import com.sun.jersey.api.client.WebResource; @@ -62,6 +61,8 @@ import com.sun.jersey.api.client.WebResource; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import org.junit.After; +import sonia.scm.util.IOUtil; /** * @@ -88,13 +89,9 @@ public class RepositoryITCase extends AbstractAdminITCaseBase * Method description * */ - @AfterClass - public static void cleanup() + @After + public void cleanup() { - Client client = createClient(); - - authenticateAdmin(client); - Collection repositories = createResource(client, "repositories").get(new GenericType>() {} @@ -120,18 +117,15 @@ public class RepositoryITCase extends AbstractAdminITCaseBase @Parameters public static Collection createParameters() { - Collection params = new ArrayList(); + Collection params = Lists.newArrayList(); params.add(new String[] { "git" }); - - /* - * params.add(new String[] { "svn" }); - * - * if (IOUtil.search("hg") != null) - * { - * params.add(new String[] { "hg" }); - * } - */ + params.add(new String[] { "svn" }); + + if (IOUtil.search("hg") != null) + { + params.add(new String[] { "hg" }); + } return params; } @@ -141,7 +135,6 @@ public class RepositoryITCase extends AbstractAdminITCaseBase * */ @Test - @Ignore public void create() { Repository repository = @@ -155,7 +148,6 @@ public class RepositoryITCase extends AbstractAdminITCaseBase * */ @Test - @Ignore public void delete() { Repository repository = @@ -169,15 +161,18 @@ public class RepositoryITCase extends AbstractAdminITCaseBase * Method description * */ - - // @Test + @Test public void doubleCreate() { Repository repository = RepositoryTestData.create42Puzzle(repositoryType); repository = createRepository(client, repository); - - // repository = createRepository(repository); + + WebResource wr = createResource(client, "repositories"); + ClientResponse response = wr.post(ClientResponse.class, repository); + + assertNotNull(response); + assertThat(response.getStatus(), not(lessThanOrEqualTo(400))); } /** @@ -185,7 +180,6 @@ public class RepositoryITCase extends AbstractAdminITCaseBase * */ @Test - @Ignore public void modify() { Repository repository = @@ -259,5 +253,5 @@ public class RepositoryITCase extends AbstractAdminITCaseBase //~--- fields --------------------------------------------------------------- /** Field description */ - private String repositoryType; + private final String repositoryType; } From a729f0f207661cc5193e143915eca30c0bf91b11 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Mon, 25 Jul 2016 18:59:33 +0200 Subject: [PATCH 133/171] fix debug service hook for non admin users --- scm-webapp/src/main/java/sonia/scm/debug/DebugService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/debug/DebugService.java b/scm-webapp/src/main/java/sonia/scm/debug/DebugService.java index 807b9c0abc..31282b6b08 100644 --- a/scm-webapp/src/main/java/sonia/scm/debug/DebugService.java +++ b/scm-webapp/src/main/java/sonia/scm/debug/DebugService.java @@ -50,13 +50,13 @@ public final class DebugService private final Multimap receivedHooks = LinkedListMultimap.create(); /** + * Stores {@link DebugHookData} for the given repository. * * @param repository repository id * @param hookData received hook data */ - public void put(String repository, DebugHookData hookData) + void put(String repository, DebugHookData hookData) { - SecurityUtils.getSubject().checkRole(Role.ADMIN); receivedHooks.put(repository, hookData); } From 2b97f285ac712471366b528e5997b50e902ef862 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Mon, 25 Jul 2016 18:59:59 +0200 Subject: [PATCH 134/171] reduce event bus logging --- .../src/main/java/sonia/scm/event/GuavaScmEventBus.java | 4 ++-- scm-webapp/src/main/resources/logback.default.xml | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/event/GuavaScmEventBus.java b/scm-webapp/src/main/java/sonia/scm/event/GuavaScmEventBus.java index aba28c8186..fa2e6a1e67 100644 --- a/scm-webapp/src/main/java/sonia/scm/event/GuavaScmEventBus.java +++ b/scm-webapp/src/main/java/sonia/scm/event/GuavaScmEventBus.java @@ -123,7 +123,7 @@ public class GuavaScmEventBus extends ScmEventBus @Override public void register(Object object, boolean async) { - logger.debug("register {} to event bus, async = {}", object, async); + logger.trace("register {} to event bus, async = {}", object, async); if (async) { @@ -144,7 +144,7 @@ public class GuavaScmEventBus extends ScmEventBus @Override public void unregister(Object object) { - logger.debug("unregister {} from event bus", object); + logger.trace("unregister {} from event bus", object); unregister(asyncEventBus, object); unregister(eventBus, object); } diff --git a/scm-webapp/src/main/resources/logback.default.xml b/scm-webapp/src/main/resources/logback.default.xml index 3550607db6..318cd6112a 100644 --- a/scm-webapp/src/main/resources/logback.default.xml +++ b/scm-webapp/src/main/resources/logback.default.xml @@ -61,7 +61,10 @@ + + + From 9b600637ad7cfbe3d2fb436e3e587ceb97793cb9 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Mon, 25 Jul 2016 19:12:49 +0200 Subject: [PATCH 135/171] update apache shiro to version 1.3.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 843f85d1a8..15049ca050 100644 --- a/pom.xml +++ b/pom.xml @@ -421,7 +421,7 @@ 7.6.16.v20140903 - 1.2.5 + 1.3.0 4.4.0.201606070830-r-scm1 From 1cf399ab2d6879b1e0c5159bdd1d2214ec02a6b2 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 26 Jul 2016 00:16:14 +0200 Subject: [PATCH 136/171] implement mercurial repository test client --- .../repository/client/spi/HgAddCommand.java | 57 ++++++++ .../client/spi/HgBranchCommand.java | 59 +++++++++ .../client/spi/HgCommitCommand.java | 79 ++++++++++++ .../repository/client/spi/HgPushCommand.java | 61 +++++++++ .../client/spi/HgRemoveCommand.java | 57 ++++++++ .../HgRepositoryClientFactoryProvider.java | 122 ++++++++++++++++++ .../spi/HgRepositoryClientProvider.java | 114 ++++++++++++++++ .../repository/client/spi/HgTagCommand.java | 64 +++++++++ ...client.spi.RepositoryClientFactoryProvider | 1 + .../sonia/scm/it/RepositoryHookITCase.java | 9 +- 10 files changed, 619 insertions(+), 4 deletions(-) create mode 100644 scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgAddCommand.java create mode 100644 scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgBranchCommand.java create mode 100644 scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgCommitCommand.java create mode 100644 scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgPushCommand.java create mode 100644 scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgRemoveCommand.java create mode 100644 scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgRepositoryClientFactoryProvider.java create mode 100644 scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgRepositoryClientProvider.java create mode 100644 scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgTagCommand.java create mode 100644 scm-plugins/scm-hg-plugin/src/test/resources/META-INF/services/sonia.scm.repository.client.spi.RepositoryClientFactoryProvider diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgAddCommand.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgAddCommand.java new file mode 100644 index 0000000000..aec21d87ad --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgAddCommand.java @@ -0,0 +1,57 @@ +/** + * 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.client.spi; + +import com.aragost.javahg.Repository; +import java.io.IOException; + +/** + * Mercurial implementation of the {@link AddCommand}. + * + * @author Sebastian Sdorra + */ +public final class HgAddCommand implements AddCommand +{ + + private final Repository repository; + + HgAddCommand(Repository repository) + { + this.repository = repository; + } + + @Override + public void add(String path) throws IOException + { + com.aragost.javahg.commands.AddCommand.on(repository).execute(path); + } + +} diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgBranchCommand.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgBranchCommand.java new file mode 100644 index 0000000000..74d1b1f742 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgBranchCommand.java @@ -0,0 +1,59 @@ +/** + * 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.client.spi; + +import com.aragost.javahg.Repository; +import java.io.IOException; +import sonia.scm.repository.Branch; + +/** + * Mercurial implementation of the {@link BranchCommand}. + * + * @author Sebastian Sdorra + */ +public class HgBranchCommand implements BranchCommand +{ + + private final Repository repository; + + HgBranchCommand(Repository repository) + { + this.repository = repository; + } + + @Override + public Branch branch(String name) throws IOException + { + com.aragost.javahg.commands.BranchCommand.on(repository).set(name); + return new Branch(name, repository.tip().getNode()); + } + +} diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgCommitCommand.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgCommitCommand.java new file mode 100644 index 0000000000..aa611a8ba4 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgCommitCommand.java @@ -0,0 +1,79 @@ +/** + * 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.client.spi; + +import com.aragost.javahg.Repository; +import com.google.common.collect.Lists; +import java.io.IOException; +import sonia.scm.repository.Changeset; +import sonia.scm.repository.Modifications; +import sonia.scm.repository.Person; + +/** + * Mercurial implementation of the {@link CommitCommand}. + * + * @author Sebastian Sdorra + */ +public class HgCommitCommand implements CommitCommand +{ + + private final Repository repository; + + HgCommitCommand(Repository repository) + { + this.repository = repository; + } + + @Override + public Changeset commit(CommitRequest request) throws IOException + { + com.aragost.javahg.Changeset c = com.aragost.javahg.commands.CommitCommand + .on(repository) + .user(request.getAuthor().toString()) + .message(request.getMessage()) + .execute(); + + Changeset changeset = new Changeset( + c.getNode(), + c.getTimestamp().getDate().getTime(), + Person.toPerson(c.getUser()), + c.getMessage() + ); + + changeset.setBranches(Lists.newArrayList(c.getBranch())); + changeset.setTags(c.tags()); + changeset.setModifications( + new Modifications(c.getAddedFiles(), c.getModifiedFiles(), c.getDeletedFiles()) + ); + return changeset; + } + +} diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgPushCommand.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgPushCommand.java new file mode 100644 index 0000000000..47144dad35 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgPushCommand.java @@ -0,0 +1,61 @@ +/** + * 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.client.spi; + +import com.aragost.javahg.Repository; +import java.io.IOException; + +/** + * Mercurial implementation of the {@link PushCommand}. + * + * @author Sebastian Sdorra + */ +public class HgPushCommand implements PushCommand +{ + + private final Repository repository; + private final String url; + + HgPushCommand(Repository repository, String url) + { + this.repository = repository; + this.url = url; + } + + @Override + public void push() throws IOException + { + com.aragost.javahg.commands.PushCommand cmd = com.aragost.javahg.commands.PushCommand.on(repository); + cmd.cmdAppend("--new-branch"); + cmd.execute(url); + } + +} diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgRemoveCommand.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgRemoveCommand.java new file mode 100644 index 0000000000..88d6ef04a8 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgRemoveCommand.java @@ -0,0 +1,57 @@ +/** + * 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.client.spi; + +import com.aragost.javahg.Repository; +import java.io.IOException; + +/** + * Mercurial implementation of the {@link RemoveCommand}. + * + * @author Sebastian Sdorra + */ +public class HgRemoveCommand implements RemoveCommand +{ + + private final Repository repository; + + HgRemoveCommand(Repository repository) + { + this.repository = repository; + } + + @Override + public void remove(String path) throws IOException + { + com.aragost.javahg.commands.RemoveCommand.on(repository).execute(path); + } + +} diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgRepositoryClientFactoryProvider.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgRepositoryClientFactoryProvider.java new file mode 100644 index 0000000000..ad83abc97b --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgRepositoryClientFactoryProvider.java @@ -0,0 +1,122 @@ +/** + * 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.client.spi; + +import com.aragost.javahg.Repository; +import com.aragost.javahg.RepositoryConfiguration; +import com.aragost.javahg.commands.PullCommand; +import com.google.common.base.Strings; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import sonia.scm.io.INIConfiguration; +import sonia.scm.io.INIConfigurationWriter; +import sonia.scm.io.INISection; +import sonia.scm.util.IOUtil; + +/** + * Mercurial implementation of the {@link RepositoryClientFactoryProvider}. + * + * @author Sebastian Sdorra + */ +public class HgRepositoryClientFactoryProvider implements RepositoryClientFactoryProvider +{ + + private static final String TYPE = "hg"; + + @Override + public RepositoryClientProvider create(File main, File workingCopy) throws IOException + { + return create(main.toURI().toString(), null, null, workingCopy); + } + + @Override + public RepositoryClientProvider create(String url, String username, String password, File workingCopy) + throws IOException + { + RepositoryConfiguration configuration = new RepositoryConfiguration(); + String binary = IOUtil.search("hg"); + if (Strings.isNullOrEmpty(binary)){ + throw new IOException("could not find mercurial binary (hg)"); + } + configuration.setHgBin(binary); + + File hgrc = null; + if (!Strings.isNullOrEmpty(username)) + { + hgrc = createHgrc(url, username, password); + configuration.setHgrcPath(hgrc.getAbsolutePath()); + } + + Repository repository = Repository.create(configuration, workingCopy); + PullCommand.on(repository).execute(url); + + return new HgRepositoryClientProvider(repository, hgrc, url); + } + + private File createHgrc(String url, String username, String password) throws IOException + { + URL repositoryUrl = new URL(url); + + INIConfiguration hgConfig = new INIConfiguration(); + + INISection pathSection = new INISection("paths"); + pathSection.setParameter(repositoryUrl.getHost(), url); + hgConfig.addSection(pathSection); + + String prefix = repositoryUrl.getHost() + "."; + + INISection authSection = new INISection("auth"); + authSection.setParameter( + prefix + "prefix", + repositoryUrl.getHost() + ":" + repositoryUrl.getPort() + repositoryUrl.getPath() + ); + authSection.setParameter(prefix + "schemes", repositoryUrl.getProtocol()); + authSection.setParameter(prefix + "username", username); + if (!Strings.isNullOrEmpty(password)) + { + authSection.setParameter(prefix + "password", password); + } + hgConfig.addSection(authSection); + + File hgrc = File.createTempFile("hgrc", ".temp"); + INIConfigurationWriter writer = new INIConfigurationWriter(); + writer.write(hgConfig, hgrc); + return hgrc; + } + + @Override + public String getType() + { + return TYPE; + } + +} diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgRepositoryClientProvider.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgRepositoryClientProvider.java new file mode 100644 index 0000000000..353e6a5b46 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgRepositoryClientProvider.java @@ -0,0 +1,114 @@ +/** + * 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.client.spi; + +import com.aragost.javahg.Repository; +import com.google.common.collect.ImmutableSet; +import java.io.File; +import java.io.IOException; +import java.util.Set; +import sonia.scm.repository.client.api.ClientCommand; + +/** + * Mercurial implementation of the {@link RepositoryClientProvider}. + * + * @author Sebastian Sdorra + */ +public class HgRepositoryClientProvider extends RepositoryClientProvider +{ + + private static final Set SUPPORTED_COMMANDS = ImmutableSet.of( + ClientCommand.ADD, ClientCommand.REMOVE, ClientCommand.COMMIT, + ClientCommand.TAG, ClientCommand.BANCH, ClientCommand.PUSH + ); + + private final Repository repository; + private final File hgrc; + private final String url; + + HgRepositoryClientProvider(Repository repository, File hgrc, String url) + { + this.repository = repository; + this.hgrc = hgrc; + this.url = url; + } + + @Override + public Set getSupportedClientCommands() + { + return SUPPORTED_COMMANDS; + } + + @Override + public AddCommand getAddCommand() + { + return new HgAddCommand(repository); + } + + @Override + public RemoveCommand getRemoveCommand() + { + return new HgRemoveCommand(repository); + } + + @Override + public CommitCommand getCommitCommand() + { + return new HgCommitCommand(repository); + } + + @Override + public TagCommand getTagCommand() + { + return new HgTagCommand(repository); + } + + @Override + public BranchCommand getBranchCommand() + { + return new HgBranchCommand(repository); + } + + @Override + public PushCommand getPushCommand() + { + return new HgPushCommand(repository, url); + } + + @Override + public void close() throws IOException + { + if ( hgrc != null && hgrc.exists() && ! hgrc.delete() ){ + throw new IOException("failed to remove hgrc file ".concat(hgrc.getPath())); + } + repository.close(); + } +} diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgTagCommand.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgTagCommand.java new file mode 100644 index 0000000000..3aa448bfca --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/client/spi/HgTagCommand.java @@ -0,0 +1,64 @@ +/** + * 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.client.spi; + +import com.aragost.javahg.Repository; +import com.google.common.base.Strings; +import java.io.IOException; +import sonia.scm.repository.Tag; + +/** + * Mercurial implementation of the {@link TagCommand}. + * + * @author Sebastian Sdorra + */ +public class HgTagCommand implements TagCommand +{ + + private final Repository repository; + + HgTagCommand(Repository repository) + { + this.repository = repository; + } + + @Override + public Tag tag(TagRequest request) throws IOException + { + String rev = request.getRevision(); + if ( Strings.isNullOrEmpty(rev) ){ + rev = repository.tip().getNode(); + } + com.aragost.javahg.commands.TagCommand.on(repository).rev(rev).execute(request.getName()); + return new Tag(request.getName(), rev); + } + +} diff --git a/scm-plugins/scm-hg-plugin/src/test/resources/META-INF/services/sonia.scm.repository.client.spi.RepositoryClientFactoryProvider b/scm-plugins/scm-hg-plugin/src/test/resources/META-INF/services/sonia.scm.repository.client.spi.RepositoryClientFactoryProvider new file mode 100644 index 0000000000..ebb411a4fe --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/test/resources/META-INF/services/sonia.scm.repository.client.spi.RepositoryClientFactoryProvider @@ -0,0 +1 @@ +sonia.scm.repository.client.spi.HgRepositoryClientFactoryProvider \ No newline at end of file diff --git a/scm-webapp/src/test/java/sonia/scm/it/RepositoryHookITCase.java b/scm-webapp/src/test/java/sonia/scm/it/RepositoryHookITCase.java index 60bf5d4aa9..4c5b3ec951 100644 --- a/scm-webapp/src/test/java/sonia/scm/it/RepositoryHookITCase.java +++ b/scm-webapp/src/test/java/sonia/scm/it/RepositoryHookITCase.java @@ -57,6 +57,7 @@ import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryTestData; import sonia.scm.repository.client.api.RepositoryClient; import sonia.scm.repository.client.api.RepositoryClientFactory; +import sonia.scm.util.IOUtil; /** * Integration tests for repository hooks. @@ -198,10 +199,10 @@ public class RepositoryHookITCase extends AbstractAdminITCaseBase Collection params = Lists.newArrayList(); params.add(new String[] { "git" }); // params.add(new String[] { "svn" }); - // if (IOUtil.search("hg") != null) - // { - // params.add(new String[] { "hg" }); - // } + if (IOUtil.search("hg") != null) + { + params.add(new String[] { "hg" }); + } return params; } From 3cd6a819b556ae8efd5653f03b4e938176022a34 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 26 Jul 2016 12:08:45 +0200 Subject: [PATCH 137/171] fix guice javadoc link --- pom.xml | 2 +- scm-core/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 15049ca050..b56511b3dd 100644 --- a/pom.xml +++ b/pom.xml @@ -215,7 +215,7 @@ http://download.oracle.com/javase/6/docs/api/ http://download.oracle.com/docs/cd/E17802_01/products/products/servlet/2.5/docs/servlet-2_5-mr2/ http://jersey.java.net/nonav/apidocs/${jersey.version}/jersey/ - http://google-guice.googlecode.com/svn/tags/3.0/javadoc/ + https://google.github.io/guice/api-docs/${guice.version}/javadoc http://www.slf4j.org/api/ http://shiro.apache.org/static/current/apidocs/ diff --git a/scm-core/pom.xml b/scm-core/pom.xml index 5ee1f110ab..7459278458 100644 --- a/scm-core/pom.xml +++ b/scm-core/pom.xml @@ -117,7 +117,7 @@ http://download.oracle.com/javase/6/docs/api/ http://download.oracle.com/docs/cd/E17802_01/products/products/servlet/2.5/docs/servlet-2_5-mr2/ http://jersey.java.net/nonav/apidocs/${jersey.version}/jersey/ - http://google-guice.googlecode.com/svn/tags/3.0/javadoc/ + https://google.github.io/guice/api-docs/${guice.version}/javadoc http://www.slf4j.org/api/ http://shiro.apache.org/static/current/apidocs/ From d2743ce5b05929d1e7c35a98112d05af13061226 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 26 Jul 2016 12:43:15 +0200 Subject: [PATCH 138/171] [maven-release-plugin] prepare release 1.49 --- maven/pom.xml | 4 ++-- maven/scm-maven-plugin/pom.xml | 4 ++-- maven/scm-plugin-archetype/pom.xml | 4 ++-- pom.xml | 4 ++-- scm-clients/pom.xml | 6 +++--- scm-clients/scm-cli-client/pom.xml | 6 +++--- scm-clients/scm-client-api/pom.xml | 4 ++-- scm-clients/scm-client-impl/pom.xml | 8 ++++---- scm-core/pom.xml | 4 ++-- scm-dao-orientdb/pom.xml | 8 ++++---- scm-dao-xml/pom.xml | 8 ++++---- scm-plugin-backend/pom.xml | 6 +++--- scm-plugins/pom.xml | 8 ++++---- scm-plugins/scm-git-plugin/pom.xml | 6 +++--- scm-plugins/scm-hg-plugin/pom.xml | 6 +++--- scm-plugins/scm-svn-plugin/pom.xml | 6 +++--- scm-samples/pom.xml | 4 ++-- scm-samples/scm-sample-auth/pom.xml | 6 +++--- scm-samples/scm-sample-hello/pom.xml | 6 +++--- scm-server/pom.xml | 4 ++-- scm-test/pom.xml | 6 +++--- scm-webapp/pom.xml | 24 ++++++++++++------------ support/pom.xml | 4 ++-- support/scm-support-btrace/pom.xml | 6 +++--- 24 files changed, 76 insertions(+), 76 deletions(-) diff --git a/maven/pom.xml b/maven/pom.xml index cf788a2471..33d10371e2 100644 --- a/maven/pom.xml +++ b/maven/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.49-SNAPSHOT + 1.49 sonia.scm.maven scm-maven-plugins pom - 1.49-SNAPSHOT + 1.49 scm-maven-plugins diff --git a/maven/scm-maven-plugin/pom.xml b/maven/scm-maven-plugin/pom.xml index 4f376e411c..7401b4e79d 100644 --- a/maven/scm-maven-plugin/pom.xml +++ b/maven/scm-maven-plugin/pom.xml @@ -6,12 +6,12 @@ scm-maven-plugins sonia.scm.maven - 1.49-SNAPSHOT + 1.49 sonia.scm.maven scm-maven-plugin - 1.49-SNAPSHOT + 1.49 maven-plugin scm-maven-plugin diff --git a/maven/scm-plugin-archetype/pom.xml b/maven/scm-plugin-archetype/pom.xml index 0a247d2cfb..4a6f4f1dee 100644 --- a/maven/scm-plugin-archetype/pom.xml +++ b/maven/scm-plugin-archetype/pom.xml @@ -6,12 +6,12 @@ scm-maven-plugins sonia.scm.maven - 1.49-SNAPSHOT + 1.49 sonia.scm.maven scm-plugin-archetype - 1.49-SNAPSHOT + 1.49 scm-plugin-archetype diff --git a/pom.xml b/pom.xml index b56511b3dd..92001d4b47 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ sonia.scm scm pom - 1.49-SNAPSHOT + 1.49 The easiest way to share your Git, Mercurial and Subversion repositories over http. @@ -36,7 +36,7 @@ scm:hg:http://bitbucket.org/sdorra/scm-manager scm:hg:https://bitbucket.org/sdorra/scm-manager http://bitbucket.org/sdorra/scm-manager - HEAD + 1.49 diff --git a/scm-clients/pom.xml b/scm-clients/pom.xml index e946a2fc8f..f827f87ca4 100644 --- a/scm-clients/pom.xml +++ b/scm-clients/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.49-SNAPSHOT + 1.49 sonia.scm.clients scm-clients pom - 1.49-SNAPSHOT + 1.49 scm-clients @@ -32,7 +32,7 @@ scm-core sonia.scm jar - 1.49-SNAPSHOT + 1.49 shiro-core diff --git a/scm-clients/scm-cli-client/pom.xml b/scm-clients/scm-cli-client/pom.xml index aa3d6a807b..68b74b4677 100644 --- a/scm-clients/scm-cli-client/pom.xml +++ b/scm-clients/scm-cli-client/pom.xml @@ -6,12 +6,12 @@ scm-clients sonia.scm.clients - 1.49-SNAPSHOT + 1.49 sonia.scm.clients scm-cli-client - 1.49-SNAPSHOT + 1.49 scm-cli-client @@ -34,7 +34,7 @@ sonia.scm.clients scm-client-impl - 1.49-SNAPSHOT + 1.49 diff --git a/scm-clients/scm-client-api/pom.xml b/scm-clients/scm-client-api/pom.xml index 3285997063..7c4ba8e036 100644 --- a/scm-clients/scm-client-api/pom.xml +++ b/scm-clients/scm-client-api/pom.xml @@ -6,13 +6,13 @@ sonia.scm.clients scm-clients - 1.49-SNAPSHOT + 1.49 sonia.scm.clients scm-client-api jar - 1.49-SNAPSHOT + 1.49 scm-client-api diff --git a/scm-clients/scm-client-impl/pom.xml b/scm-clients/scm-client-impl/pom.xml index 0173942ed2..44f5e10884 100644 --- a/scm-clients/scm-client-impl/pom.xml +++ b/scm-clients/scm-client-impl/pom.xml @@ -6,13 +6,13 @@ sonia.scm.clients scm-clients - 1.49-SNAPSHOT + 1.49 sonia.scm.clients scm-client-impl jar - 1.49-SNAPSHOT + 1.49 scm-client-impl @@ -36,7 +36,7 @@ sonia.scm.clients scm-client-api - 1.49-SNAPSHOT + 1.49 @@ -70,7 +70,7 @@ sonia.scm scm-test - 1.49-SNAPSHOT + 1.49 test diff --git a/scm-core/pom.xml b/scm-core/pom.xml index 7459278458..5b31fb0321 100644 --- a/scm-core/pom.xml +++ b/scm-core/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.49-SNAPSHOT + 1.49 sonia.scm scm-core - 1.49-SNAPSHOT + 1.49 scm-core diff --git a/scm-dao-orientdb/pom.xml b/scm-dao-orientdb/pom.xml index 5b54b2361e..c1fb2364f0 100644 --- a/scm-dao-orientdb/pom.xml +++ b/scm-dao-orientdb/pom.xml @@ -6,12 +6,12 @@ sonia.scm scm - 1.49-SNAPSHOT + 1.49 sonia.scm scm-dao-orientdb - 1.49-SNAPSHOT + 1.49 scm-dao-orientdb @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.49-SNAPSHOT + 1.49 @@ -52,7 +52,7 @@ sonia.scm scm-test - 1.49-SNAPSHOT + 1.49 test diff --git a/scm-dao-xml/pom.xml b/scm-dao-xml/pom.xml index 009b20f559..5ad193cc30 100644 --- a/scm-dao-xml/pom.xml +++ b/scm-dao-xml/pom.xml @@ -6,12 +6,12 @@ sonia.scm scm - 1.49-SNAPSHOT + 1.49 sonia.scm scm-dao-xml - 1.49-SNAPSHOT + 1.49 scm-dao-xml @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.49-SNAPSHOT + 1.49 @@ -34,7 +34,7 @@ sonia.scm scm-test - 1.49-SNAPSHOT + 1.49 test diff --git a/scm-plugin-backend/pom.xml b/scm-plugin-backend/pom.xml index 9f9024def5..e0e61c7c73 100644 --- a/scm-plugin-backend/pom.xml +++ b/scm-plugin-backend/pom.xml @@ -6,13 +6,13 @@ scm sonia.scm - 1.49-SNAPSHOT + 1.49 sonia.scm scm-plugin-backend war - 1.49-SNAPSHOT + 1.49 ${project.artifactId} @@ -62,7 +62,7 @@ sonia.scm scm-core - 1.49-SNAPSHOT + 1.49 diff --git a/scm-plugins/pom.xml b/scm-plugins/pom.xml index 35c64fb10c..cb3190b4b2 100644 --- a/scm-plugins/pom.xml +++ b/scm-plugins/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.49-SNAPSHOT + 1.49 sonia.scm.plugins scm-plugins pom - 1.49-SNAPSHOT + 1.49 scm-plugins @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.49-SNAPSHOT + 1.49 @@ -59,7 +59,7 @@ sonia.scm.maven scm-maven-plugin - 1.49-SNAPSHOT + 1.49 process-resources diff --git a/scm-plugins/scm-git-plugin/pom.xml b/scm-plugins/scm-git-plugin/pom.xml index 82515c13cd..d6ea1f7cff 100644 --- a/scm-plugins/scm-git-plugin/pom.xml +++ b/scm-plugins/scm-git-plugin/pom.xml @@ -6,12 +6,12 @@ scm-plugins sonia.scm.plugins - 1.49-SNAPSHOT + 1.49 sonia.scm.plugins scm-git-plugin - 1.49-SNAPSHOT + 1.49 scm-git-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Git @@ -48,7 +48,7 @@ sonia.scm scm-test - 1.49-SNAPSHOT + 1.49 test diff --git a/scm-plugins/scm-hg-plugin/pom.xml b/scm-plugins/scm-hg-plugin/pom.xml index 3a2611d6da..b74e0ddd4f 100644 --- a/scm-plugins/scm-hg-plugin/pom.xml +++ b/scm-plugins/scm-hg-plugin/pom.xml @@ -6,12 +6,12 @@ sonia.scm.plugins scm-plugins - 1.49-SNAPSHOT + 1.49 sonia.scm.plugins scm-hg-plugin - 1.49-SNAPSHOT + 1.49 scm-hg-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Mercurial @@ -42,7 +42,7 @@ sonia.scm scm-test - 1.49-SNAPSHOT + 1.49 test diff --git a/scm-plugins/scm-svn-plugin/pom.xml b/scm-plugins/scm-svn-plugin/pom.xml index 3de56a1a92..79021b3b6d 100644 --- a/scm-plugins/scm-svn-plugin/pom.xml +++ b/scm-plugins/scm-svn-plugin/pom.xml @@ -6,12 +6,12 @@ scm-plugins sonia.scm.plugins - 1.49-SNAPSHOT + 1.49 sonia.scm.plugins scm-svn-plugin - 1.49-SNAPSHOT + 1.49 scm-svn-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Subversion @@ -48,7 +48,7 @@ sonia.scm scm-test - 1.49-SNAPSHOT + 1.49 test diff --git a/scm-samples/pom.xml b/scm-samples/pom.xml index b647b307af..450399f181 100644 --- a/scm-samples/pom.xml +++ b/scm-samples/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.49-SNAPSHOT + 1.49 sonia.scm.samples scm-samples pom - 1.49-SNAPSHOT + 1.49 scm-samples diff --git a/scm-samples/scm-sample-auth/pom.xml b/scm-samples/scm-sample-auth/pom.xml index 53f30cc74d..6fcf0a630a 100644 --- a/scm-samples/scm-sample-auth/pom.xml +++ b/scm-samples/scm-sample-auth/pom.xml @@ -6,12 +6,12 @@ scm-samples sonia.scm.samples - 1.49-SNAPSHOT + 1.49 sonia.scm.sample scm-sample-auth - 1.49-SNAPSHOT + 1.49 scm-sample-auth Sample Authentication Plugin https://bitbucket.org/sdorra/scm-manager @@ -28,7 +28,7 @@ sonia.scm scm-core - 1.49-SNAPSHOT + 1.49 diff --git a/scm-samples/scm-sample-hello/pom.xml b/scm-samples/scm-sample-hello/pom.xml index 04ff972b50..e4968a1841 100644 --- a/scm-samples/scm-sample-hello/pom.xml +++ b/scm-samples/scm-sample-hello/pom.xml @@ -6,12 +6,12 @@ scm-samples sonia.scm.samples - 1.49-SNAPSHOT + 1.49 sonia.scm.sample scm-sample-hello - 1.49-SNAPSHOT + 1.49 scm-sample-hello A simple hello world plugin https://bitbucket.org/sdorra/scm-manager @@ -28,7 +28,7 @@ sonia.scm scm-core - 1.49-SNAPSHOT + 1.49 diff --git a/scm-server/pom.xml b/scm-server/pom.xml index df5ec69a0f..fca2d79e0a 100644 --- a/scm-server/pom.xml +++ b/scm-server/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.49-SNAPSHOT + 1.49 sonia.scm scm-server - 1.49-SNAPSHOT + 1.49 scm-server jar diff --git a/scm-test/pom.xml b/scm-test/pom.xml index 4d700407cf..784974c311 100644 --- a/scm-test/pom.xml +++ b/scm-test/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.49-SNAPSHOT + 1.49 sonia.scm scm-test - 1.49-SNAPSHOT + 1.49 scm-test @@ -25,7 +25,7 @@ sonia.scm scm-core - 1.49-SNAPSHOT + 1.49 diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index e4245649f1..a0a7b702b8 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.49-SNAPSHOT + 1.49 sonia.scm scm-webapp war - 1.49-SNAPSHOT + 1.49 scm-webapp @@ -38,31 +38,31 @@ sonia.scm scm-core - 1.49-SNAPSHOT + 1.49 sonia.scm scm-dao-xml - 1.49-SNAPSHOT + 1.49 sonia.scm.plugins scm-hg-plugin - 1.49-SNAPSHOT + 1.49 sonia.scm.plugins scm-svn-plugin - 1.49-SNAPSHOT + 1.49 sonia.scm.plugins scm-git-plugin - 1.49-SNAPSHOT + 1.49 @@ -286,7 +286,7 @@ sonia.scm scm-test - 1.49-SNAPSHOT + 1.49 test @@ -299,7 +299,7 @@ sonia.scm.plugins scm-git-plugin - 1.49-SNAPSHOT + 1.49 tests test @@ -307,7 +307,7 @@ sonia.scm.plugins scm-hg-plugin - 1.49-SNAPSHOT + 1.49 tests test @@ -315,7 +315,7 @@ sonia.scm.plugins scm-svn-plugin - 1.49-SNAPSHOT + 1.49 tests test @@ -547,7 +547,7 @@ sonia.scm scm-dao-orientdb - 1.49-SNAPSHOT + 1.49 diff --git a/support/pom.xml b/support/pom.xml index f8a2a61627..bd378580b3 100644 --- a/support/pom.xml +++ b/support/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.49-SNAPSHOT + 1.49 sonia.scm.support scm-support pom - 1.49-SNAPSHOT + 1.49 scm-support diff --git a/support/scm-support-btrace/pom.xml b/support/scm-support-btrace/pom.xml index aa4caaf32c..84ee3caf95 100644 --- a/support/scm-support-btrace/pom.xml +++ b/support/scm-support-btrace/pom.xml @@ -4,12 +4,12 @@ sonia.scm.support scm-support - 1.49-SNAPSHOT + 1.49 sonia.scm scm-support-btrace - 1.49-SNAPSHOT + 1.49 jar scm-support-btrace @@ -18,7 +18,7 @@ sonia.scm scm-core - 1.49-SNAPSHOT + 1.49 From b3cbebfdc005f35f5c9d425355580e46839b2b55 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 26 Jul 2016 12:43:15 +0200 Subject: [PATCH 139/171] [maven-release-plugin] copy for tag 1.49 From c4111ec73fd99d55fd45a40ead4a9789e785c2b5 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 26 Jul 2016 12:43:15 +0200 Subject: [PATCH 140/171] [maven-release-plugin] prepare for next development iteration --- maven/pom.xml | 4 ++-- maven/scm-maven-plugin/pom.xml | 4 ++-- maven/scm-plugin-archetype/pom.xml | 4 ++-- pom.xml | 4 ++-- scm-clients/pom.xml | 6 +++--- scm-clients/scm-cli-client/pom.xml | 6 +++--- scm-clients/scm-client-api/pom.xml | 4 ++-- scm-clients/scm-client-impl/pom.xml | 8 ++++---- scm-core/pom.xml | 4 ++-- scm-dao-orientdb/pom.xml | 8 ++++---- scm-dao-xml/pom.xml | 8 ++++---- scm-plugin-backend/pom.xml | 6 +++--- scm-plugins/pom.xml | 8 ++++---- scm-plugins/scm-git-plugin/pom.xml | 6 +++--- scm-plugins/scm-hg-plugin/pom.xml | 6 +++--- scm-plugins/scm-svn-plugin/pom.xml | 6 +++--- scm-samples/pom.xml | 4 ++-- scm-samples/scm-sample-auth/pom.xml | 6 +++--- scm-samples/scm-sample-hello/pom.xml | 6 +++--- scm-server/pom.xml | 4 ++-- scm-test/pom.xml | 6 +++--- scm-webapp/pom.xml | 24 ++++++++++++------------ support/pom.xml | 4 ++-- support/scm-support-btrace/pom.xml | 6 +++--- 24 files changed, 76 insertions(+), 76 deletions(-) diff --git a/maven/pom.xml b/maven/pom.xml index 33d10371e2..1a4ec67707 100644 --- a/maven/pom.xml +++ b/maven/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.49 + 1.50-SNAPSHOT sonia.scm.maven scm-maven-plugins pom - 1.49 + 1.50-SNAPSHOT scm-maven-plugins diff --git a/maven/scm-maven-plugin/pom.xml b/maven/scm-maven-plugin/pom.xml index 7401b4e79d..d1eefa8357 100644 --- a/maven/scm-maven-plugin/pom.xml +++ b/maven/scm-maven-plugin/pom.xml @@ -6,12 +6,12 @@ scm-maven-plugins sonia.scm.maven - 1.49 + 1.50-SNAPSHOT sonia.scm.maven scm-maven-plugin - 1.49 + 1.50-SNAPSHOT maven-plugin scm-maven-plugin diff --git a/maven/scm-plugin-archetype/pom.xml b/maven/scm-plugin-archetype/pom.xml index 4a6f4f1dee..b51bb677e4 100644 --- a/maven/scm-plugin-archetype/pom.xml +++ b/maven/scm-plugin-archetype/pom.xml @@ -6,12 +6,12 @@ scm-maven-plugins sonia.scm.maven - 1.49 + 1.50-SNAPSHOT sonia.scm.maven scm-plugin-archetype - 1.49 + 1.50-SNAPSHOT scm-plugin-archetype diff --git a/pom.xml b/pom.xml index 92001d4b47..9fd831b5e9 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ sonia.scm scm pom - 1.49 + 1.50-SNAPSHOT The easiest way to share your Git, Mercurial and Subversion repositories over http. @@ -36,7 +36,7 @@ scm:hg:http://bitbucket.org/sdorra/scm-manager scm:hg:https://bitbucket.org/sdorra/scm-manager http://bitbucket.org/sdorra/scm-manager - 1.49 + HEAD diff --git a/scm-clients/pom.xml b/scm-clients/pom.xml index f827f87ca4..7fca349a49 100644 --- a/scm-clients/pom.xml +++ b/scm-clients/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.49 + 1.50-SNAPSHOT sonia.scm.clients scm-clients pom - 1.49 + 1.50-SNAPSHOT scm-clients @@ -32,7 +32,7 @@ scm-core sonia.scm jar - 1.49 + 1.50-SNAPSHOT shiro-core diff --git a/scm-clients/scm-cli-client/pom.xml b/scm-clients/scm-cli-client/pom.xml index 68b74b4677..9806e37230 100644 --- a/scm-clients/scm-cli-client/pom.xml +++ b/scm-clients/scm-cli-client/pom.xml @@ -6,12 +6,12 @@ scm-clients sonia.scm.clients - 1.49 + 1.50-SNAPSHOT sonia.scm.clients scm-cli-client - 1.49 + 1.50-SNAPSHOT scm-cli-client @@ -34,7 +34,7 @@ sonia.scm.clients scm-client-impl - 1.49 + 1.50-SNAPSHOT diff --git a/scm-clients/scm-client-api/pom.xml b/scm-clients/scm-client-api/pom.xml index 7c4ba8e036..6f3cf0cf02 100644 --- a/scm-clients/scm-client-api/pom.xml +++ b/scm-clients/scm-client-api/pom.xml @@ -6,13 +6,13 @@ sonia.scm.clients scm-clients - 1.49 + 1.50-SNAPSHOT sonia.scm.clients scm-client-api jar - 1.49 + 1.50-SNAPSHOT scm-client-api diff --git a/scm-clients/scm-client-impl/pom.xml b/scm-clients/scm-client-impl/pom.xml index 44f5e10884..9a444bcbc8 100644 --- a/scm-clients/scm-client-impl/pom.xml +++ b/scm-clients/scm-client-impl/pom.xml @@ -6,13 +6,13 @@ sonia.scm.clients scm-clients - 1.49 + 1.50-SNAPSHOT sonia.scm.clients scm-client-impl jar - 1.49 + 1.50-SNAPSHOT scm-client-impl @@ -36,7 +36,7 @@ sonia.scm.clients scm-client-api - 1.49 + 1.50-SNAPSHOT @@ -70,7 +70,7 @@ sonia.scm scm-test - 1.49 + 1.50-SNAPSHOT test diff --git a/scm-core/pom.xml b/scm-core/pom.xml index 5b31fb0321..02236beae9 100644 --- a/scm-core/pom.xml +++ b/scm-core/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.49 + 1.50-SNAPSHOT sonia.scm scm-core - 1.49 + 1.50-SNAPSHOT scm-core diff --git a/scm-dao-orientdb/pom.xml b/scm-dao-orientdb/pom.xml index c1fb2364f0..edfb109d24 100644 --- a/scm-dao-orientdb/pom.xml +++ b/scm-dao-orientdb/pom.xml @@ -6,12 +6,12 @@ sonia.scm scm - 1.49 + 1.50-SNAPSHOT sonia.scm scm-dao-orientdb - 1.49 + 1.50-SNAPSHOT scm-dao-orientdb @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.49 + 1.50-SNAPSHOT @@ -52,7 +52,7 @@ sonia.scm scm-test - 1.49 + 1.50-SNAPSHOT test diff --git a/scm-dao-xml/pom.xml b/scm-dao-xml/pom.xml index 5ad193cc30..9331cd6231 100644 --- a/scm-dao-xml/pom.xml +++ b/scm-dao-xml/pom.xml @@ -6,12 +6,12 @@ sonia.scm scm - 1.49 + 1.50-SNAPSHOT sonia.scm scm-dao-xml - 1.49 + 1.50-SNAPSHOT scm-dao-xml @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.49 + 1.50-SNAPSHOT @@ -34,7 +34,7 @@ sonia.scm scm-test - 1.49 + 1.50-SNAPSHOT test diff --git a/scm-plugin-backend/pom.xml b/scm-plugin-backend/pom.xml index e0e61c7c73..bf29ddc57b 100644 --- a/scm-plugin-backend/pom.xml +++ b/scm-plugin-backend/pom.xml @@ -6,13 +6,13 @@ scm sonia.scm - 1.49 + 1.50-SNAPSHOT sonia.scm scm-plugin-backend war - 1.49 + 1.50-SNAPSHOT ${project.artifactId} @@ -62,7 +62,7 @@ sonia.scm scm-core - 1.49 + 1.50-SNAPSHOT diff --git a/scm-plugins/pom.xml b/scm-plugins/pom.xml index cb3190b4b2..16084bb91c 100644 --- a/scm-plugins/pom.xml +++ b/scm-plugins/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.49 + 1.50-SNAPSHOT sonia.scm.plugins scm-plugins pom - 1.49 + 1.50-SNAPSHOT scm-plugins @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.49 + 1.50-SNAPSHOT @@ -59,7 +59,7 @@ sonia.scm.maven scm-maven-plugin - 1.49 + 1.50-SNAPSHOT process-resources diff --git a/scm-plugins/scm-git-plugin/pom.xml b/scm-plugins/scm-git-plugin/pom.xml index d6ea1f7cff..6599004b02 100644 --- a/scm-plugins/scm-git-plugin/pom.xml +++ b/scm-plugins/scm-git-plugin/pom.xml @@ -6,12 +6,12 @@ scm-plugins sonia.scm.plugins - 1.49 + 1.50-SNAPSHOT sonia.scm.plugins scm-git-plugin - 1.49 + 1.50-SNAPSHOT scm-git-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Git @@ -48,7 +48,7 @@ sonia.scm scm-test - 1.49 + 1.50-SNAPSHOT test diff --git a/scm-plugins/scm-hg-plugin/pom.xml b/scm-plugins/scm-hg-plugin/pom.xml index b74e0ddd4f..29973aa774 100644 --- a/scm-plugins/scm-hg-plugin/pom.xml +++ b/scm-plugins/scm-hg-plugin/pom.xml @@ -6,12 +6,12 @@ sonia.scm.plugins scm-plugins - 1.49 + 1.50-SNAPSHOT sonia.scm.plugins scm-hg-plugin - 1.49 + 1.50-SNAPSHOT scm-hg-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Mercurial @@ -42,7 +42,7 @@ sonia.scm scm-test - 1.49 + 1.50-SNAPSHOT test diff --git a/scm-plugins/scm-svn-plugin/pom.xml b/scm-plugins/scm-svn-plugin/pom.xml index 79021b3b6d..d9a5d14228 100644 --- a/scm-plugins/scm-svn-plugin/pom.xml +++ b/scm-plugins/scm-svn-plugin/pom.xml @@ -6,12 +6,12 @@ scm-plugins sonia.scm.plugins - 1.49 + 1.50-SNAPSHOT sonia.scm.plugins scm-svn-plugin - 1.49 + 1.50-SNAPSHOT scm-svn-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Subversion @@ -48,7 +48,7 @@ sonia.scm scm-test - 1.49 + 1.50-SNAPSHOT test diff --git a/scm-samples/pom.xml b/scm-samples/pom.xml index 450399f181..7ec9c628b9 100644 --- a/scm-samples/pom.xml +++ b/scm-samples/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.49 + 1.50-SNAPSHOT sonia.scm.samples scm-samples pom - 1.49 + 1.50-SNAPSHOT scm-samples diff --git a/scm-samples/scm-sample-auth/pom.xml b/scm-samples/scm-sample-auth/pom.xml index 6fcf0a630a..e46745663e 100644 --- a/scm-samples/scm-sample-auth/pom.xml +++ b/scm-samples/scm-sample-auth/pom.xml @@ -6,12 +6,12 @@ scm-samples sonia.scm.samples - 1.49 + 1.50-SNAPSHOT sonia.scm.sample scm-sample-auth - 1.49 + 1.50-SNAPSHOT scm-sample-auth Sample Authentication Plugin https://bitbucket.org/sdorra/scm-manager @@ -28,7 +28,7 @@ sonia.scm scm-core - 1.49 + 1.50-SNAPSHOT diff --git a/scm-samples/scm-sample-hello/pom.xml b/scm-samples/scm-sample-hello/pom.xml index e4968a1841..3a37570356 100644 --- a/scm-samples/scm-sample-hello/pom.xml +++ b/scm-samples/scm-sample-hello/pom.xml @@ -6,12 +6,12 @@ scm-samples sonia.scm.samples - 1.49 + 1.50-SNAPSHOT sonia.scm.sample scm-sample-hello - 1.49 + 1.50-SNAPSHOT scm-sample-hello A simple hello world plugin https://bitbucket.org/sdorra/scm-manager @@ -28,7 +28,7 @@ sonia.scm scm-core - 1.49 + 1.50-SNAPSHOT diff --git a/scm-server/pom.xml b/scm-server/pom.xml index fca2d79e0a..1594795597 100644 --- a/scm-server/pom.xml +++ b/scm-server/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.49 + 1.50-SNAPSHOT sonia.scm scm-server - 1.49 + 1.50-SNAPSHOT scm-server jar diff --git a/scm-test/pom.xml b/scm-test/pom.xml index 784974c311..566c1543e8 100644 --- a/scm-test/pom.xml +++ b/scm-test/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.49 + 1.50-SNAPSHOT sonia.scm scm-test - 1.49 + 1.50-SNAPSHOT scm-test @@ -25,7 +25,7 @@ sonia.scm scm-core - 1.49 + 1.50-SNAPSHOT diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index a0a7b702b8..b1b971af3e 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.49 + 1.50-SNAPSHOT sonia.scm scm-webapp war - 1.49 + 1.50-SNAPSHOT scm-webapp @@ -38,31 +38,31 @@ sonia.scm scm-core - 1.49 + 1.50-SNAPSHOT sonia.scm scm-dao-xml - 1.49 + 1.50-SNAPSHOT sonia.scm.plugins scm-hg-plugin - 1.49 + 1.50-SNAPSHOT sonia.scm.plugins scm-svn-plugin - 1.49 + 1.50-SNAPSHOT sonia.scm.plugins scm-git-plugin - 1.49 + 1.50-SNAPSHOT @@ -286,7 +286,7 @@ sonia.scm scm-test - 1.49 + 1.50-SNAPSHOT test @@ -299,7 +299,7 @@ sonia.scm.plugins scm-git-plugin - 1.49 + 1.50-SNAPSHOT tests test @@ -307,7 +307,7 @@ sonia.scm.plugins scm-hg-plugin - 1.49 + 1.50-SNAPSHOT tests test @@ -315,7 +315,7 @@ sonia.scm.plugins scm-svn-plugin - 1.49 + 1.50-SNAPSHOT tests test @@ -547,7 +547,7 @@ sonia.scm scm-dao-orientdb - 1.49 + 1.50-SNAPSHOT diff --git a/support/pom.xml b/support/pom.xml index bd378580b3..1c7f87f7ce 100644 --- a/support/pom.xml +++ b/support/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.49 + 1.50-SNAPSHOT sonia.scm.support scm-support pom - 1.49 + 1.50-SNAPSHOT scm-support diff --git a/support/scm-support-btrace/pom.xml b/support/scm-support-btrace/pom.xml index 84ee3caf95..08a6d48665 100644 --- a/support/scm-support-btrace/pom.xml +++ b/support/scm-support-btrace/pom.xml @@ -4,12 +4,12 @@ sonia.scm.support scm-support - 1.49 + 1.50-SNAPSHOT sonia.scm scm-support-btrace - 1.49 + 1.50-SNAPSHOT jar scm-support-btrace @@ -18,7 +18,7 @@ sonia.scm scm-core - 1.49 + 1.50-SNAPSHOT From 5da8f5f052dc0b44245014ab6b82d46f222fa705 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 31 Jul 2016 20:21:11 +0200 Subject: [PATCH 141/171] fix selenium integration tests and introduce page object pattern --- scm-webapp/pom.xml | 29 +- .../scm/selenium/AuthenticationITCase.java | 24 +- ...RUDITCase.java => RepositoriesITCase.java} | 82 +++--- .../scm/selenium/SeleniumITCaseBase.java | 71 +++++ .../sonia/scm/selenium/SeleniumTestBase.java | 271 ------------------ .../sonia/scm/selenium/page/BasePage.java | 168 +++++++++++ .../sonia/scm/selenium/page/LoginPage.java | 89 ++++++ .../sonia/scm/selenium/page/MainPage.java | 95 ++++++ .../java/sonia/scm/selenium/page/Pages.java | 113 ++++++++ .../selenium/page/RepositoriesAddPage.java | 136 +++++++++ .../scm/selenium/page/RepositoriesPage.java | 103 +++++++ .../scm/selenium/page/RepositoryPage.java | 77 +++++ 12 files changed, 930 insertions(+), 328 deletions(-) rename scm-webapp/src/test/java/sonia/scm/selenium/{RepositoryCRUDITCase.java => RepositoriesITCase.java} (66%) create mode 100644 scm-webapp/src/test/java/sonia/scm/selenium/SeleniumITCaseBase.java delete mode 100644 scm-webapp/src/test/java/sonia/scm/selenium/SeleniumTestBase.java create mode 100644 scm-webapp/src/test/java/sonia/scm/selenium/page/BasePage.java create mode 100644 scm-webapp/src/test/java/sonia/scm/selenium/page/LoginPage.java create mode 100644 scm-webapp/src/test/java/sonia/scm/selenium/page/MainPage.java create mode 100644 scm-webapp/src/test/java/sonia/scm/selenium/page/Pages.java create mode 100644 scm-webapp/src/test/java/sonia/scm/selenium/page/RepositoriesAddPage.java create mode 100644 scm-webapp/src/test/java/sonia/scm/selenium/page/RepositoriesPage.java create mode 100644 scm-webapp/src/test/java/sonia/scm/selenium/page/RepositoryPage.java diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index b1b971af3e..1a629c71f9 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -326,13 +326,20 @@ ${selenium.version} test - + org.seleniumhq.selenium selenium-firefox-driver ${selenium.version} test + + + org.seleniumhq.selenium + htmlunit-driver + 2.21 + test + com.sun.jersey @@ -528,7 +535,7 @@ DEVELOPMENT target/scm-it default - 2.28.0 + 2.53.1 1.31 1.13.1 1.0 @@ -688,6 +695,24 @@ selenium + + + + org.apache.httpcomponents + httpclient + 4.3.2 + test + + + + xml-apis + xml-apis + 1.4.01 + test + + + + diff --git a/scm-webapp/src/test/java/sonia/scm/selenium/AuthenticationITCase.java b/scm-webapp/src/test/java/sonia/scm/selenium/AuthenticationITCase.java index a2b0d0fe6d..34013e7e76 100644 --- a/scm-webapp/src/test/java/sonia/scm/selenium/AuthenticationITCase.java +++ b/scm-webapp/src/test/java/sonia/scm/selenium/AuthenticationITCase.java @@ -34,25 +34,27 @@ package sonia.scm.selenium; //~--- non-JDK imports -------------------------------------------------------- +import sonia.scm.selenium.page.Pages; +import sonia.scm.selenium.page.MainPage; +import sonia.scm.selenium.page.LoginPage; +import static org.junit.Assert.*; import org.junit.Test; /** - * + * Authentication related selenium integration tests. + * * @author Sebastian Sdorra */ -public class AuthenticationITCase extends SeleniumTestBase -{ +public class AuthenticationITCase extends SeleniumITCaseBase { /** - * Method description - * - * - * @throws Exception + * Authenticates an user and call logout function. */ @Test - public void testAuthentication() throws Exception - { - login("scmadmin", "scmadmin"); - logout(); + public void testAuthentication() { + MainPage main = Pages.get(driver, LoginPage.class).login("scmadmin", "scmadmin"); + assertEquals("scmadmin", main.getUserInfo()); + main.logout(); } + } diff --git a/scm-webapp/src/test/java/sonia/scm/selenium/RepositoryCRUDITCase.java b/scm-webapp/src/test/java/sonia/scm/selenium/RepositoriesITCase.java similarity index 66% rename from scm-webapp/src/test/java/sonia/scm/selenium/RepositoryCRUDITCase.java rename to scm-webapp/src/test/java/sonia/scm/selenium/RepositoriesITCase.java index 64760ae774..05898f6529 100644 --- a/scm-webapp/src/test/java/sonia/scm/selenium/RepositoryCRUDITCase.java +++ b/scm-webapp/src/test/java/sonia/scm/selenium/RepositoriesITCase.java @@ -34,61 +34,55 @@ package sonia.scm.selenium; //~--- non-JDK imports -------------------------------------------------------- +import sonia.scm.selenium.page.Pages; +import sonia.scm.selenium.page.MainPage; +import sonia.scm.selenium.page.LoginPage; import org.junit.After; import org.junit.Before; import org.junit.Test; -import static org.junit.Assert.*; +import sonia.scm.repository.Repository; /** - * + * Repository related selenium integration tests. + * * @author Sebastian Sdorra */ -public class RepositoryCRUDITCase extends SeleniumTestBase -{ +public class RepositoriesITCase extends SeleniumITCaseBase { + private MainPage main; + /** - * Method description - * - */ - @After - public void after() - { - logout(); - } - - /** - * Method description - * - */ - @Test - public void createRepository() throws InterruptedException - { - waitAndClick("#repositoryAddButton"); - waitForPresence("input[name=name]").sendKeys("scm"); - select("#x-form-el-repositoryType img").click(); - waitAndClick("div.x-combo-list-item:nth-of-type(2)"); - type("input[name=contact]", "scmadmin@scm-manager.org"); - type("textarea[name=description]", "SCM-Manager"); - waitAndClick("div.x-panel-btns button:nth-of-type(1)"); - - String name = - waitForPresence( - "div.x-grid3-row-selected div.x-grid3-col-name").getText(); - - assertEquals("scm", name); - - waitAndClick("#repoRmButton button"); - waitAndClick("div.x-window button:nth-of-type(1)"); - } - - /** - * Method description - * + * Authenticates admin user, before each test. */ @Before - public void login() - { - login("scmadmin", "scmadmin"); + public void login() { + main = Pages.get(driver, LoginPage.class) + .login("scmadmin", "scmadmin"); + } + + /** + * Creates, select and removes a repository. + */ + @Test + public void createRepository() { + Repository repository = new Repository(); + repository.setName("scm"); + repository.setType("git"); + repository.setContact("scmadmin@scm-manager.org"); + repository.setDescription("SCM-Manager"); + + main.repositories() + .add(repository) + .select(repository.getName()) + .remove(); + } + + /** + * Logs the user out, after each test. + */ + @After + public void logout() { + main.logout(); } } diff --git a/scm-webapp/src/test/java/sonia/scm/selenium/SeleniumITCaseBase.java b/scm-webapp/src/test/java/sonia/scm/selenium/SeleniumITCaseBase.java new file mode 100644 index 0000000000..fde04d9cad --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/selenium/SeleniumITCaseBase.java @@ -0,0 +1,71 @@ +/** + * 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.selenium; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.firefox.FirefoxDriver; + +/** + * Base class for selenium integration tests. + * + * @author Sebastian Sdorra + */ +public class SeleniumITCaseBase { + + /** + * Selenium test driver. + */ + protected static WebDriver driver; + + /** + * Setup selenium test driver. + */ + @BeforeClass + public static void setUpDriver() { + // DesiredCapabilities capa = DesiredCapabilities.chrome(); + // capa.setBrowserName("firefox"); + // capa.setPlatform(Platform.ANY); + // RemoteWebDriver driver = new RemoteWebDriver(new URL("http://localhost:4444/wd/hub"), capa); + + driver = new FirefoxDriver(); + driver.get("http://localhost:8082/scm/index.html"); + } + + /** + * Closes the selenium test driver. + */ + @AfterClass + public static void tearDownDriver() { + driver.close(); + } +} diff --git a/scm-webapp/src/test/java/sonia/scm/selenium/SeleniumTestBase.java b/scm-webapp/src/test/java/sonia/scm/selenium/SeleniumTestBase.java deleted file mode 100644 index 1ae5e86b3d..0000000000 --- a/scm-webapp/src/test/java/sonia/scm/selenium/SeleniumTestBase.java +++ /dev/null @@ -1,271 +0,0 @@ -/** - * 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.selenium; - -//~--- non-JDK imports -------------------------------------------------------- - -import com.google.common.io.Files; - -import org.junit.After; -import org.junit.Before; - -import org.openqa.selenium.By; -import org.openqa.selenium.OutputType; -import org.openqa.selenium.TakesScreenshot; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.firefox.FirefoxDriver; -import org.openqa.selenium.support.ui.ExpectedConditions; -import org.openqa.selenium.support.ui.WebDriverWait; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import static org.junit.Assert.*; - -//~--- JDK imports ------------------------------------------------------------ - -import java.io.File; -import java.io.IOException; - -import java.util.concurrent.TimeUnit; - -/** - * - * @author Sebastian Sdorra - */ -public class SeleniumTestBase -{ - - /** - * the logger for SeleniumTestBase - */ - private static final Logger logger = - LoggerFactory.getLogger(SeleniumTestBase.class); - - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @throws Exception - */ - @After - public void tearDown() throws Exception - { - driver.quit(); - } - - //~--- set methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @throws Exception - */ - @Before - public void setUp() throws Exception - { - driver = new FirefoxDriver(); - baseUrl = "http://localhost:8082/scm/"; - open("index.html"); - } - - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @param username - * @param password - */ - protected void login(String username, String password) - { - type("input[name=username]", username); - type("input[name=password]", password); - waitAndClick("#loginButton button"); - - String ue = waitForPresence("#scm-userinfo-tip").getText(); - - assertEquals(username, ue); - } - - /** - * Method description - * - */ - protected void logout() - { - waitAndClick("#navLogout a"); - } - - /** - * Method description - * - * - * @param url - */ - protected void open(String url) - { - driver.get(baseUrl + url); - pause(500, TimeUnit.MILLISECONDS); - } - - /** - * Method description - * - * - * @param value - * @param unit - */ - protected void pause(int value, TimeUnit unit) - { - driver.manage().timeouts().implicitlyWait(value, unit); - } - - /** - * Method description - * - * - * @param target - */ - protected void screenshot(String target) - { - screenshot(new File(target)); - } - - /** - * Method description - * - * - * @param target - */ - protected void screenshot(File target) - { - try - { - File scrFile = - ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE); - - Files.copy(scrFile, target); - } - catch (IOException ex) - { - logger.error("could not create screenshot", ex); - } - } - - /** - * Method description - * - * - * @param cssSelector - * - * @return - */ - protected WebElement select(String cssSelector) - { - WebElement element = driver.findElement(By.cssSelector(cssSelector)); - - assertNotNull(element); - - return element; - } - - /** - * Method description - * - * - * @param cssLocator - * @param value - */ - protected void type(String cssLocator, String value) - { - select(cssLocator).clear(); - select(cssLocator).sendKeys(value); - } - - /** - * Method description - * - * - * @param query - */ - protected void waitAndClick(String query) - { - waitToBeClickable(query).click(); - } - - /** - * Method description - * - * - * @param query - * - * @return - */ - protected WebElement waitForPresence(String query) - { - WebDriverWait wait = new WebDriverWait(driver, 5); - - return wait.until( - ExpectedConditions.presenceOfElementLocated(By.cssSelector(query))); - } - - /** - * Method description - * - * - * @param query - * - * @return - */ - protected WebElement waitToBeClickable(String query) - { - WebDriverWait wait = new WebDriverWait(driver, 5); - - return wait.until( - ExpectedConditions.elementToBeClickable(By.cssSelector(query))); - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - protected WebDriver driver; - - /** Field description */ - private String baseUrl; -} diff --git a/scm-webapp/src/test/java/sonia/scm/selenium/page/BasePage.java b/scm-webapp/src/test/java/sonia/scm/selenium/page/BasePage.java new file mode 100644 index 0000000000..28c27457a8 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/selenium/page/BasePage.java @@ -0,0 +1,168 @@ +/** + * 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.selenium.page; + +import com.google.common.base.Throwables; +import com.google.common.io.Files; +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.concurrent.TimeUnit; +import org.openqa.selenium.By; +import org.openqa.selenium.OutputType; +import org.openqa.selenium.TakesScreenshot; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; + +/** + * Abstract selenium base page. + * + * @author Sebastian Sdorra + * + * @param

    concrete page implementation + */ +public abstract class BasePage

    { + + /** + * Selenium test driver. + */ + protected final WebDriver driver; + + /** + * Constructs a new base page. + * + * @param driver selenium test driver + */ + protected BasePage(WebDriver driver) { + this.driver = driver; + } + + /** + * Performs a {@link Thread#sleep(long)} for the given timeout. + * + * @param time timeout + * @param unit time unit of timeout + */ + protected void sleep(long time, TimeUnit unit) { + try { + unit.sleep(time); + } catch (InterruptedException ex) { + throw Throwables.propagate(ex); + } + } + + /** + * Wait for the element until it is clickable. + * + * @param by element selector + * + * @return web element + */ + protected WebElement waitToBeClickable(By by){ + return waitToBeClickable(driver.findElement(by)); + } + + /** + * Waits for the element until it is clickable. + * + * @param element web element + * + * @return web element + */ + protected WebElement waitToBeClickable(WebElement element) { + WebDriverWait wait = new WebDriverWait(driver, 5); + + return wait.until(ExpectedConditions.elementToBeClickable(element)); + } + + /** + * Waits until the element is present. + * + * @param by element locator + * + * @return web element + */ + protected WebElement waitFor(By by){ + WebDriverWait wait = new WebDriverWait(driver, 1); + return wait.until(ExpectedConditions.presenceOfElementLocated(by)); + } + + /** + * Waits until the elements are present. + * + * @param by element selector + * + * @return list of web elements + */ + protected List waitForAll(By by){ + WebDriverWait wait = new WebDriverWait(driver, 1); + return wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(by)); + } + + /** + * Creates a screenshot of the current browser content and stores it at the given path. + * + * @param target target file path + * + * @return {@code this} + */ + public P screenshot(String target) { + return screenshot(new File(target)); + } + + /** + * Creates a screenshot of the current browser content and stores it at the file. + * + * @param target target file + * + * @return {@code this} + */ + public P screenshot(File target) { + try { + File scrFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE); + + Files.copy(scrFile, target); + } catch (IOException ex) { + throw Throwables.propagate(ex); + } + return self(); + } + + /** + * Returns {@code this}. + * + * @return {@code this} + */ + protected abstract P self(); + +} diff --git a/scm-webapp/src/test/java/sonia/scm/selenium/page/LoginPage.java b/scm-webapp/src/test/java/sonia/scm/selenium/page/LoginPage.java new file mode 100644 index 0000000000..cf9d231510 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/selenium/page/LoginPage.java @@ -0,0 +1,89 @@ +/** + * 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.selenium.page; + +import java.util.concurrent.TimeUnit; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +/** + * Page object for the scm-manager login page. + * + * @author Sebastian Sdorra + */ +public class LoginPage extends BasePage { + + @FindBy(css = "input[name=username]") + private WebElement usernameInput; + + @FindBy(css = "input[name=password]") + private WebElement passwordInput; + + @FindBy(css = "#loginButton button") + private WebElement loginButton; + + /** + * Constructs a new page. This constructor should only be called from {@link Pages}. + * + * @param driver selenium test driver + */ + LoginPage(WebDriver driver) { + super(driver); + } + + @Override + protected LoginPage self() { + return this; + } + + /** + * Authenticates the user and returns the {@link MainPage}. + * + * @param username username + * @param password password + * + * @return {@link MainPage} after successful authentication + */ + public MainPage login(String username, String password) { + usernameInput.clear(); + usernameInput.sendKeys(username); + + passwordInput.clear(); + passwordInput.sendKeys(password); + + sleep(250, TimeUnit.MILLISECONDS); + + waitToBeClickable(loginButton).click(); + + return Pages.get(driver, MainPage.class); + } +} diff --git a/scm-webapp/src/test/java/sonia/scm/selenium/page/MainPage.java b/scm-webapp/src/test/java/sonia/scm/selenium/page/MainPage.java new file mode 100644 index 0000000000..42fc807e67 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/selenium/page/MainPage.java @@ -0,0 +1,95 @@ +/** + * 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.selenium.page; + +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +/** + * Page object for scm-manager's main page. + * + * @author Sebastian Sdorra + */ +public class MainPage extends BasePage { + + @FindBy(css = "#navLogout a") + private WebElement logoutLink; + + @FindBy(linkText = "Repositories") + private WebElement repositoriesLink; + + @FindBy(css = "#scm-userinfo-tip") + private WebElement userInfoTip; + + /** + * Constructs a new page. This constructor should only be called from {@link Pages}. + * + * @param driver selenium test driver + */ + MainPage(WebDriver driver) { + super(driver); + } + + @Override + protected MainPage self() { + return this; + } + + /** + * Returns the name of the current authenticated user from the user info tip. + * + * @return name of the current authenticated user + */ + public String getUserInfo(){ + return userInfoTip.getText(); + } + + /** + * Navigates to the repositories page and returns the page object for this page. + * + * @return page object for repositories page + */ + public RepositoriesPage repositories(){ + repositoriesLink.click(); + return Pages.get(driver, RepositoriesPage.class); + } + + /** + * Logs the current user out. + * + * @return page object for the login + */ + public LoginPage logout(){ + waitToBeClickable(logoutLink).click(); + return Pages.get(driver, LoginPage.class); + } +} diff --git a/scm-webapp/src/test/java/sonia/scm/selenium/page/Pages.java b/scm-webapp/src/test/java/sonia/scm/selenium/page/Pages.java new file mode 100644 index 0000000000..b8d85ea08b --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/selenium/page/Pages.java @@ -0,0 +1,113 @@ +/** + * 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.selenium.page; + +import com.google.common.base.Throwables; +import com.google.common.collect.Lists; +import java.lang.reflect.Constructor; +import java.util.Arrays; +import java.util.List; +import org.openqa.selenium.By; +import org.openqa.selenium.SearchContext; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.PageFactory; +import org.openqa.selenium.support.pagefactory.DefaultElementLocatorFactory; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; + +/** + * Helper class for selenium page objects. + * + * @author Sebastian Sdorra + */ +public final class Pages { + + private Pages() { + } + + /** + * Creates an instance of the given page object. + * + * @param page object type + * @param driver selenium driver + * @param clazz page object type + * @param otherArguments other constructor arguments + * + * @return instance of page object + */ + public static T get(WebDriver driver, Class clazz, Object... otherArguments) + { + T page = null; + try { + List> argumentTypes = Lists.newArrayList(); + argumentTypes.add(WebDriver.class); + for (Object argument : otherArguments) { + argumentTypes.add(argument.getClass()); + } + + List arguments = Lists.newArrayList(); + arguments.add(driver); + arguments.addAll(Arrays.asList(otherArguments)); + + Constructor constructor = clazz.getDeclaredConstructor( + argumentTypes.toArray(new Class[argumentTypes.size()]) + ); + page = constructor.newInstance(arguments.toArray(new Object[arguments.size()])); + + PageFactory.initElements(new DefaultElementLocatorFactory(new WaitingSearchContext(driver)), page); + } catch (Exception ex) { + throw Throwables.propagate(ex); + } + return page; + } + + private static class WaitingSearchContext implements SearchContext { + + private final WebDriver driver; + private final WebDriverWait wait; + + private WaitingSearchContext(WebDriver driver) { + this.driver = driver; + this.wait = new WebDriverWait(driver, 1); + } + + @Override + public List findElements(By by) { + return wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(by)); + } + + @Override + public WebElement findElement(By by) { + return wait.until(ExpectedConditions.presenceOfElementLocated(by)); + } + } +} diff --git a/scm-webapp/src/test/java/sonia/scm/selenium/page/RepositoriesAddPage.java b/scm-webapp/src/test/java/sonia/scm/selenium/page/RepositoriesAddPage.java new file mode 100644 index 0000000000..f9f00056ae --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/selenium/page/RepositoriesAddPage.java @@ -0,0 +1,136 @@ +/** + * 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.selenium.page; + +import com.google.common.base.Objects; +import java.util.List; +import org.openqa.selenium.By; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.NotFoundException; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.ui.WebDriverWait; +import sonia.scm.repository.Repository; + +/** + * Page object for scm-manager's repository creation page. + * + * @author Sebastian Sdorra + */ +public class RepositoriesAddPage extends BasePage { + + @FindBy(css = "input[name=name]") + private WebElement nameInput; + + @FindBy(css = "input[name=contact]") + private WebElement contactInput; + + @FindBy(css = "#x-form-el-repositoryType img") + private WebElement typeInput; + + @FindBy(css = "textarea[name=description]") + private WebElement descriptionInput; + + @FindBy(css = "div.x-panel-btns button:nth-of-type(1)") + private WebElement okButton; + + private final RepositoriesPage repositoriesPage; + + /** + * Constructs a new page. This constructor should only be called from {@link Pages}. + * + * @param driver selenium test driver + * @param repositoriesPage repositories page object + */ + RepositoriesAddPage(WebDriver driver, RepositoriesPage repositoriesPage) { + super(driver); + this.repositoriesPage = repositoriesPage; + } + + @Override + protected RepositoriesAddPage self() { + return this; + } + + /** + * Creates a new {@link Repository}. + * + * @param repository repository for creation + * + * @return repositories overview page + */ + public RepositoriesPage add(Repository repository) { + nameInput.sendKeys(repository.getName()); + + selectType(repository.getType()); + + contactInput.sendKeys(repository.getContact()); + descriptionInput.sendKeys(repository.getDescription()); + + waitToBeClickable(okButton).click(); + + return repositoriesPage; + } + + private void selectType(String type) { + typeInput.click(); + + String displayName = findDisplayName(type); + + WebDriverWait wait = new WebDriverWait(driver, 1); + List elements = waitForAll(By.className("x-combo-list-item")); + WebElement typeElement = null; + for (WebElement te : elements){ + if (te.getText().trim().equalsIgnoreCase(displayName)){ + typeElement = te; + break; + } + } + + if (typeElement == null){ + throw new NotFoundException("could not find type element with type " + displayName); + } + + typeElement.click(); + } + + private String findDisplayName(String type) { + String displayName = null; + if (driver instanceof JavascriptExecutor) { + // TODO seams not to work + String script = "Sonia.repository.getTypeByName('" + type + "').displayName;"; + displayName = (String) ((JavascriptExecutor)driver).executeScript(script); + } + return Objects.firstNonNull(displayName, type); + } + +} diff --git a/scm-webapp/src/test/java/sonia/scm/selenium/page/RepositoriesPage.java b/scm-webapp/src/test/java/sonia/scm/selenium/page/RepositoriesPage.java new file mode 100644 index 0000000000..fef75683ff --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/selenium/page/RepositoriesPage.java @@ -0,0 +1,103 @@ +/** + * 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.selenium.page; + +import java.util.List; +import java.util.Locale; +import org.openqa.selenium.By; +import org.openqa.selenium.NotFoundException; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import sonia.scm.repository.Repository; + +/** + * Page object for scm-manager's repositories overview page. + * + * @author Sebastian Sdorra + */ +public class RepositoriesPage extends BasePage { + + @FindBy(id = "repositoryAddButton") + private WebElement addButton; + + /** + * Constructs a new page. This constructor should only be called from {@link Pages}. + * + * @param driver selenium test driver + */ + RepositoriesPage(WebDriver driver) { + super(driver); + } + + @Override + protected RepositoriesPage self() { + return this; + } + + /** + * Creates a new {@link Repository}. + * + * @param repository repository for creation + * + * @return {@link this} + */ + public RepositoriesPage add(Repository repository){ + addButton.click(); + RepositoriesAddPage addPage = Pages.get(driver, RepositoriesAddPage.class, this); + return addPage.add(repository); + } + + /** + * Selects the repository with the given name and returns the detail page object for the selected repository. + * + * @param repositoryName name of the repository + * + * @return page object for selected repository + */ + public RepositoryPage select(String repositoryName){ + WebElement repositoryNameColumn = null; + + List elements = waitForAll(By.className("x-grid3-col-name")); + for (WebElement element : elements){ + if (element.getText().trim().toLowerCase(Locale.ENGLISH).equals(repositoryName)){ + repositoryNameColumn = element; + break; + } + } + + if ( repositoryNameColumn == null ) { + throw new NotFoundException("could not find repository " + repositoryName); + } + + return Pages.get(driver, RepositoryPage.class, this); + } +} diff --git a/scm-webapp/src/test/java/sonia/scm/selenium/page/RepositoryPage.java b/scm-webapp/src/test/java/sonia/scm/selenium/page/RepositoryPage.java new file mode 100644 index 0000000000..d09a0debb5 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/selenium/page/RepositoryPage.java @@ -0,0 +1,77 @@ +/** + * 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.selenium.page; + +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +/** + * Page object for scm-manager's repository detail page. + * + * @author Sebastian Sdorra + */ +public class RepositoryPage extends BasePage { + + @FindBy(css = "#repoRmButton button") + private WebElement removeButton; + + private final RepositoriesPage repositoriesPage; + + /** + * Constructs a new page. This constructor should only be called from {@link Pages}. + * + * @param driver selenium test driver + * @param repositoriesPage repositories page object + */ + RepositoryPage(WebDriver driver, RepositoriesPage repositoriesPage) { + super(driver); + this.repositoriesPage = repositoriesPage; + } + + @Override + protected RepositoryPage self() { + return this; + } + + /** + * Removes the selected repository. + * + * @return repositories overview page object + */ + public RepositoriesPage remove(){ + removeButton.click(); + waitToBeClickable(By.cssSelector("div.x-window button:nth-of-type(1)")).click(); + return repositoriesPage; + } + +} From da46a80b5f0b36d0a2248cd503b743e6f2690adf Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 28 Sep 2016 08:27:38 +0200 Subject: [PATCH 142/171] ignore rebel.xml --- .hgignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.hgignore b/.hgignore index 353d320a34..a49329d7a5 100644 --- a/.hgignore +++ b/.hgignore @@ -26,3 +26,5 @@ Desktop DF$ # idea files \.iml \.idea$ +# jrebel +rebel.xml From d38435c13a85a11cd08c50fab26a7f6959fd0872 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 28 Sep 2016 11:05:00 +0200 Subject: [PATCH 143/171] fix npe when GitHookBranchProvider tries to collect a tag as branch, see issue #865 --- .../repository/api/GitHookBranchProvider.java | 41 ++++--- .../api/GitHookBranchProviderTest.java | 115 ++++++++++++++++++ 2 files changed, 136 insertions(+), 20 deletions(-) create mode 100644 scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/api/GitHookBranchProviderTest.java diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/api/GitHookBranchProvider.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/api/GitHookBranchProvider.java index ecc373e2b8..23b3ce0196 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/api/GitHookBranchProvider.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/api/GitHookBranchProvider.java @@ -33,6 +33,7 @@ package sonia.scm.repository.api; //~--- non-JDK imports -------------------------------------------------------- +import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList.Builder; @@ -45,18 +46,24 @@ import sonia.scm.repository.GitUtil; import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** - * + * Collects created, modified and deleted git branches during a hook. + * * @author Sebastian Sdorra */ public class GitHookBranchProvider implements HookBranchProvider { + + private static final Logger logger = LoggerFactory.getLogger(GitHookBranchProvider.class); /** - * Constructs ... + * Constructs a new instance. * * - * @param commands + * @param commands received git commands */ public GitHookBranchProvider(List commands) { @@ -66,10 +73,14 @@ public class GitHookBranchProvider implements HookBranchProvider for (ReceiveCommand command : commands) { Type type = command.getType(); - String branch = GitUtil.getBranch(command.getRefName()); + String ref = command.getRefName(); + String branch = GitUtil.getBranch(ref); - if ((type == Type.CREATE) || (type == Type.UPDATE) - || (type == Type.UPDATE_NONFASTFORWARD)) + if (Strings.isNullOrEmpty(branch)) + { + logger.debug("ref {} is not a branch", ref); + } + else if (isCreateOrUpdate(type)) { createdOrModifiedBuilder.add(branch); } @@ -82,27 +93,19 @@ public class GitHookBranchProvider implements HookBranchProvider createdOrModified = createdOrModifiedBuilder.build(); deletedOrClosed = deletedOrClosedBuilder.build(); } + + private boolean isCreateOrUpdate(Type type){ + return type == Type.CREATE || type == Type.UPDATE || type == Type.UPDATE_NONFASTFORWARD; + } //~--- get methods ---------------------------------------------------------- - /** - * Method description - * - * - * @return - */ @Override public List getCreatedOrModified() { return createdOrModified; } - /** - * Method description - * - * - * @return - */ @Override public List getDeletedOrClosed() { @@ -111,9 +114,7 @@ public class GitHookBranchProvider implements HookBranchProvider //~--- fields --------------------------------------------------------------- - /** Field description */ private final List createdOrModified; - /** Field description */ private final List deletedOrClosed; } diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/api/GitHookBranchProviderTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/api/GitHookBranchProviderTest.java new file mode 100644 index 0000000000..9e5e0eef36 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/api/GitHookBranchProviderTest.java @@ -0,0 +1,115 @@ +/*** + * Copyright (c) 2015, 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. + * + * https://bitbucket.org/sdorra/scm-manager + * + */ + +package sonia.scm.repository.api; + +import com.google.common.collect.Lists; +import java.util.Arrays; +import java.util.List; +import org.eclipse.jgit.transport.ReceiveCommand; +import org.hamcrest.Matchers; +import org.junit.Test; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; +import static org.hamcrest.Matchers.*; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +/** + * Unit tests for {@link GitHookBranchProvider}. + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +public class GitHookBranchProviderTest { + + @Mock + private ReceiveCommand command; + + private List commands; + + /** + * Prepare mocks for upcoming test. + */ + @Before + public void setUpMocks(){ + commands = Lists.newArrayList(command); + } + + /** + * Tests {@link GitHookBranchProvider#getCreatedOrModified()}. + */ + @Test + public void testGetCreatedOrModified(){ + List types = Arrays.asList( + ReceiveCommand.Type.CREATE, ReceiveCommand.Type.UPDATE, ReceiveCommand.Type.UPDATE_NONFASTFORWARD + ); + for ( ReceiveCommand.Type type : types ){ + checkCreatedOrModified(type); + } + } + + private void checkCreatedOrModified(ReceiveCommand.Type type){ + GitHookBranchProvider provider = createGitHookBranchProvider(type, "refs/heads/hello"); + assertThat(provider.getCreatedOrModified(), Matchers.contains("hello")); + assertThat(provider.getDeletedOrClosed(), empty()); + } + + + /** + * Tests {@link GitHookBranchProvider#getDeletedOrClosed()}. + */ + @Test + public void testGetDeletedOrClosed(){ + GitHookBranchProvider provider = createGitHookBranchProvider(ReceiveCommand.Type.DELETE, "refs/heads/hello"); + assertThat(provider.getDeletedOrClosed(), Matchers.contains("hello")); + assertThat(provider.getCreatedOrModified(), empty()); + } + + /** + * Tests {@link GitHookBranchProvider} with a tag instead of a branch. + */ + @Test + public void testWithTag(){ + GitHookBranchProvider provider = createGitHookBranchProvider(ReceiveCommand.Type.CREATE, "refs/tags/1.0.0"); + assertThat(provider.getCreatedOrModified(), empty()); + assertThat(provider.getDeletedOrClosed(), empty()); + } + + private GitHookBranchProvider createGitHookBranchProvider(ReceiveCommand.Type type, String refName){ + when(command.getType()).thenReturn(type); + when(command.getRefName()).thenReturn(refName); + return new GitHookBranchProvider(commands); + } + +} \ No newline at end of file From 11c81a49942dfe6be091800cc58294e115538664 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 28 Sep 2016 13:33:44 +0200 Subject: [PATCH 144/171] added new hook context api for tags --- .../sonia/scm/repository/api/HookContext.java | 24 ++- .../sonia/scm/repository/api/HookFeature.java | 9 +- .../scm/repository/api/HookTagProvider.java | 59 ++++++++ .../repository/spi/HookContextProvider.java | 57 ++++--- .../scm/repository/api/HookContextTest.java | 141 ++++++++++++++++++ .../spi/HookContextProviderTest.java | 136 +++++++++++++++++ 6 files changed, 401 insertions(+), 25 deletions(-) create mode 100644 scm-core/src/main/java/sonia/scm/repository/api/HookTagProvider.java create mode 100644 scm-core/src/test/java/sonia/scm/repository/api/HookContextTest.java create mode 100644 scm-core/src/test/java/sonia/scm/repository/spi/HookContextProviderTest.java diff --git a/scm-core/src/main/java/sonia/scm/repository/api/HookContext.java b/scm-core/src/main/java/sonia/scm/repository/api/HookContext.java index 8e68c9af28..a7c7e8354e 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/HookContext.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/HookContext.java @@ -45,7 +45,7 @@ import sonia.scm.repository.spi.HookContextProvider; * The context for all repository hooks. With the {@link HookContext} class it * is able to send messages back to the client, retrieve {@link Changeset}s * which are added during this push/commit and gives informations about changed - * branches. + * branches and tags. * * @author Sebastian Sdorra * @since 1.33 @@ -100,6 +100,28 @@ public final class HookContext return provider.getBranchProvider(); } + + /** + * Returns a {@link HookTagProvider} which is able to return informations + * about changed tags during the current hook. + * + * @return {@link HookTagProvider} + * + * @throws HookFeatureIsNotSupportedException if the feature is not supported + * by the underlying provider + * + * @since 1.50 + */ + public HookTagProvider getTagProvider() + { + if (logger.isDebugEnabled()) + { + logger.debug("create tag provider for repository {}", + repository.getName()); + } + + return provider.getTagProvider(); + } /** * Returns a {@link HookChangesetBuilder} which is able to return all diff --git a/scm-core/src/main/java/sonia/scm/repository/api/HookFeature.java b/scm-core/src/main/java/sonia/scm/repository/api/HookFeature.java index 9594bbc168..15982022de 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/HookFeature.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/HookFeature.java @@ -55,5 +55,12 @@ public enum HookFeature * * @since 1.45 */ - BRANCH_PROVIDER; + BRANCH_PROVIDER, + + /** + * Hook tag provider + * + * @since 1.50 + */ + TAG_PROVIDER; } diff --git a/scm-core/src/main/java/sonia/scm/repository/api/HookTagProvider.java b/scm-core/src/main/java/sonia/scm/repository/api/HookTagProvider.java new file mode 100644 index 0000000000..695cba9d2d --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/repository/api/HookTagProvider.java @@ -0,0 +1,59 @@ +/*** + * Copyright (c) 2015, 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. + * + * https://bitbucket.org/sdorra/scm-manager + * + */ + +package sonia.scm.repository.api; + +import java.util.List; +import sonia.scm.repository.Tag; + +/** + * The HookTagProvider returns informations about tags during the + * current hook. + * + * @since 1.50 + * @author Sebastian Sdorra + */ +public interface HookTagProvider { + + /** + * Return all tags which are delivered during the current hook. + * + * @return all tags of current hook + */ + public List getCreatedTags(); + + /** + * Return all tags which are deleted during the current hook. + * + * @return all deleted tags of current hook + */ + public List getDeletedTags(); +} diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/HookContextProvider.java b/scm-core/src/main/java/sonia/scm/repository/spi/HookContextProvider.java index 0f1d039006..2419cd45ac 100644 --- a/scm-core/src/main/java/sonia/scm/repository/spi/HookContextProvider.java +++ b/scm-core/src/main/java/sonia/scm/repository/spi/HookContextProvider.java @@ -38,12 +38,15 @@ import sonia.scm.repository.api.HookException; import sonia.scm.repository.api.HookFeature; import sonia.scm.repository.api.HookFeatureIsNotSupportedException; import sonia.scm.repository.api.HookMessageProvider; +import sonia.scm.repository.api.HookContext; +import sonia.scm.repository.api.HookTagProvider; //~--- JDK imports ------------------------------------------------------------ import java.util.Set; /** + * Repository type specific provider for {@link HookContext}. * * @author Sebastian Sdorra * @since 1.33 @@ -52,10 +55,10 @@ public abstract class HookContextProvider { /** - * Method description - * - * - * @return + * Return the provider specific {@link HookMessageProvider} or throws a {@link HookFeatureIsNotSupportedException}. + * The method will throw a {@link HookException} if the client is already disconnected. + * + * @return provider specific {@link HookMessageProvider} */ public final HookMessageProvider getMessageProvider() { @@ -71,7 +74,7 @@ public abstract class HookContextProvider //~--- methods -------------------------------------------------------------- /** - * Method description + * Mark client connection as disconnected. * */ final void handleClientDisconnect() @@ -82,43 +85,52 @@ public abstract class HookContextProvider //~--- get methods ---------------------------------------------------------- /** - * Method description + * Returns a set of supported hook features of the client. * - * - * @return + * @return supported features */ public abstract Set getSupportedFeatures(); /** - * Method description - * - * - * @return + * Return the provider specific {@link HookBranchProvider} or throws a {@link HookFeatureIsNotSupportedException}. + * + * @return provider specific {@link HookBranchProvider} + * + * @since 1.45 */ public HookBranchProvider getBranchProvider() { throw new HookFeatureIsNotSupportedException(HookFeature.BRANCH_PROVIDER); } + + /** + * Return the provider specific {@link HookTagProvider} or throws a {@link HookFeatureIsNotSupportedException}. + * + * @return provider specific {@link HookTagProvider} + * + * @since 1.50 + */ + public HookTagProvider getTagProvider() + { + throw new HookFeatureIsNotSupportedException(HookFeature.TAG_PROVIDER); + } /** - * Method description - * - * - * @return + * Return the provider specific {@link HookChangesetProvider} or throws a {@link HookFeatureIsNotSupportedException}. + * + * @return provider specific {@link HookChangesetProvider} */ public HookChangesetProvider getChangesetProvider() { - throw new HookFeatureIsNotSupportedException( - HookFeature.CHANGESET_PROVIDER); + throw new HookFeatureIsNotSupportedException(HookFeature.CHANGESET_PROVIDER); } //~--- methods -------------------------------------------------------------- /** - * Method description - * - * - * @return + * Creates a new provider specific {@link HookMessageProvider} or throws a {@link HookFeatureIsNotSupportedException}. + * + * @return provider specific {@link HookChangesetProvider} */ protected HookMessageProvider createMessageProvider() { @@ -127,6 +139,5 @@ public abstract class HookContextProvider //~--- fields --------------------------------------------------------------- - /** Field description */ private boolean clientDisconnected = false; } diff --git a/scm-core/src/test/java/sonia/scm/repository/api/HookContextTest.java b/scm-core/src/test/java/sonia/scm/repository/api/HookContextTest.java new file mode 100644 index 0000000000..2f746ec5ad --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/repository/api/HookContextTest.java @@ -0,0 +1,141 @@ +/*** + * Copyright (c) 2015, 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. + * + * https://bitbucket.org/sdorra/scm-manager + * + */ + +package sonia.scm.repository.api; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import java.util.List; +import org.junit.Test; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import sonia.scm.repository.Changeset; +import sonia.scm.repository.Person; +import sonia.scm.repository.PreProcessorUtil; +import sonia.scm.repository.Repository; +import sonia.scm.repository.spi.HookChangesetProvider; +import sonia.scm.repository.spi.HookChangesetRequest; +import sonia.scm.repository.spi.HookChangesetResponse; +import sonia.scm.repository.spi.HookContextProvider; + +/** + * Unit tests for {@link HookContext}. + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +public class HookContextTest { + + @Mock + private HookContextProvider provider; + + @Mock + private Repository repository; + + @Mock + private PreProcessorUtil preProcessorUtil; + + @Mock + private HookChangesetProvider changesetProvider; + + @InjectMocks + private HookContext context; + + /** + * Set up mocks for upcoming test. + */ + @Before + public void setUpMocks(){ + when(repository.getName()).thenReturn("test"); + when(provider.getChangesetProvider()).thenReturn(changesetProvider); + when(provider.getSupportedFeatures()).thenReturn(Sets.newHashSet(HookFeature.CHANGESET_PROVIDER)); + + List changesets = Lists.newArrayList(new Changeset("1", Long.MIN_VALUE, new Person("Trillian"))); + HookChangesetResponse response = new HookChangesetResponse(changesets); + when(changesetProvider.handleRequest(any(HookChangesetRequest.class))).thenReturn(response); + } + + /** + * Tests {@link HookContext#getBranchProvider()}. + */ + @Test + public void testGetBranchProvider() { + context.getBranchProvider(); + + verify(provider).getBranchProvider(); + } + + /** + * Tests {@link HookContext#getTagProvider()}. + */ + @Test + public void testGetTagProvider() { + context.getTagProvider(); + + verify(provider).getTagProvider(); + } + + /** + * Tests {@link HookContext#getMessageProvider()}. + */ + @Test + public void testGetMessageProvider() { + context.getMessageProvider(); + + verify(provider).getMessageProvider(); + } + + /** + * Tests {@link HookContext#getChangesetProvider()}. + */ + @Test + public void testGetChangesetProvider() { + HookChangesetBuilder builder = context.getChangesetProvider(); + List changesets = builder.getChangesetList(); + assertNotNull(changesets); + assertEquals("1", changesets.get(0).getId()); + } + + /** + * Tests {@link HookContext#isFeatureSupported(sonia.scm.repository.api.HookFeature)}. + */ + @Test + public void testIsFeatureSupported(){ + assertTrue(context.isFeatureSupported(HookFeature.CHANGESET_PROVIDER)); + assertFalse(context.isFeatureSupported(HookFeature.BRANCH_PROVIDER)); + } + +} \ No newline at end of file diff --git a/scm-core/src/test/java/sonia/scm/repository/spi/HookContextProviderTest.java b/scm-core/src/test/java/sonia/scm/repository/spi/HookContextProviderTest.java new file mode 100644 index 0000000000..c6abae594e --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/repository/spi/HookContextProviderTest.java @@ -0,0 +1,136 @@ +/*** + * Copyright (c) 2015, 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. + * + * https://bitbucket.org/sdorra/scm-manager + * + */ + +package sonia.scm.repository.spi; + +import java.util.Collections; +import java.util.Set; +import org.junit.Test; +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; +import org.junit.Rule; +import org.junit.rules.ExpectedException; +import sonia.scm.repository.api.HookException; +import sonia.scm.repository.api.HookFeature; +import sonia.scm.repository.api.HookFeatureIsNotSupportedException; + +/** + * Unit tests for {@link HookContextProvider}. + * + * @author Sebastian Sdorra + */ +public class HookContextProviderTest { + + /** + * Expected exception rule. + */ + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private final HookContextProvider simpleHookContextProvider = new HookContextProvider() { + + @Override + public Set getSupportedFeatures() { + return Collections.emptySet(); + } + + }; + + /** + * Tests {@link HookContextProvider#getSupportedFeatures()}. + */ + @Test + public void testGetSupportedFeatures() { + assertThat(simpleHookContextProvider.getSupportedFeatures(), empty()); + } + + /** + * Tests {@link HookContextProvider#getBranchProvider()}. + */ + @Test + public void testGetBranchProvider(){ + expectNotSupported(HookFeature.BRANCH_PROVIDER); + simpleHookContextProvider.getBranchProvider(); + } + + /** + * Tests {@link HookContextProvider#getTagProvider()}. + */ + @Test + public void testGetTagProvider(){ + expectNotSupported(HookFeature.TAG_PROVIDER); + simpleHookContextProvider.getTagProvider(); + } + + /** + * Tests {@link HookContextProvider#getChangesetProvider()}. + */ + @Test + public void testGetChangesetProvider(){ + expectNotSupported(HookFeature.CHANGESET_PROVIDER); + simpleHookContextProvider.getChangesetProvider(); + } + + /** + * Tests {@link HookContextProvider#createMessageProvider()}. + */ + @Test + public void testCreateMessageProvider(){ + expectNotSupported(HookFeature.MESSAGE_PROVIDER); + simpleHookContextProvider.createMessageProvider(); + } + + /** + * Tests {@link HookContextProvider#getMessageProvider()}. + */ + @Test + public void testGetMessageProvider(){ + expectNotSupported(HookFeature.MESSAGE_PROVIDER); + simpleHookContextProvider.getMessageProvider(); + } + + /** + * Tests {@link HookContextProvider#getMessageProvider()} with disconnected client. + */ + @Test + public void testGetMessageProviderDisconnected(){ + expectedException.expect(HookException.class); + expectedException.expectMessage(containsString("message provider")); + simpleHookContextProvider.handleClientDisconnect(); + simpleHookContextProvider.getMessageProvider(); + } + + private void expectNotSupported(HookFeature feature){ + expectedException.expect(HookFeatureIsNotSupportedException.class); + expectedException.expectMessage(containsString(feature.toString())); + } + +} \ No newline at end of file From 8591ced68dfd4e9508c87b0ebb3a32c9b19f67f2 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 28 Sep 2016 14:28:38 +0200 Subject: [PATCH 145/171] implemented new HookTagProvider api for git --- .../java/sonia/scm/repository/GitUtil.java | 20 +++ .../repository/api/GitHookTagProvider.java | 92 +++++++++++++ .../spi/GitHookContextProvider.java | 40 ++---- .../sonia/scm/repository/GitUtilTest.java | 27 ++-- .../api/GitHookTagProviderTest.java | 130 ++++++++++++++++++ 5 files changed, 268 insertions(+), 41 deletions(-) create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/api/GitHookTagProvider.java create mode 100644 scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/api/GitHookTagProviderTest.java diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java index 16a6f05541..daa016b00e 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java @@ -633,6 +633,26 @@ public final class GitUtil return String.format(REMOTE_REF, repository.getId(), branch); } + /** + * Returns the name of the tag or {@code null} if the the ref is not a tag. + * + * @param refName ref name + * + * @return name of tag or {@link null} + * + * @since 1.50 + */ + public static String getTagName(String refName) + { + String tagName = null; + if (refName.startsWith(PREFIX_TAG)) + { + tagName = refName.substring(PREFIX_TAG.length()); + } + + return tagName; + } + /** * Method description * diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/api/GitHookTagProvider.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/api/GitHookTagProvider.java new file mode 100644 index 0000000000..e7a75a0ff4 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/api/GitHookTagProvider.java @@ -0,0 +1,92 @@ +/*** + * Copyright (c) 2015, 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. + * + * https://bitbucket.org/sdorra/scm-manager + * + */ + +package sonia.scm.repository.api; + +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; +import java.util.List; +import org.eclipse.jgit.transport.ReceiveCommand; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.repository.GitUtil; +import sonia.scm.repository.Tag; + +/** + * Git provider implementation of {@link HookTagProvider}. + * + * @since 1.50 + * @author Sebastian Sdorra + */ +public class GitHookTagProvider implements HookTagProvider { + + private static final Logger logger = LoggerFactory.getLogger(GitHookTagProvider.class); + + private final List createdTags; + private final List deletedTags; + + /** + * Constructs new instance. + * + * @param commands received commands + */ + public GitHookTagProvider(List commands) { + ImmutableList.Builder createdTagBuilder = ImmutableList.builder(); + ImmutableList.Builder deletedTagBuilder = ImmutableList.builder(); + + for ( ReceiveCommand rc : commands ){ + String refName = rc.getRefName(); + String tag = GitUtil.getTagName(refName); + + if (Strings.isNullOrEmpty(tag)){ + logger.debug("received ref name {} is not a tag", refName); + } else if (rc.getType() == ReceiveCommand.Type.CREATE) { + createdTagBuilder.add(new Tag(tag, GitUtil.getId(rc.getNewId()))); + } else if (rc.getType() == ReceiveCommand.Type.DELETE){ + deletedTagBuilder.add(new Tag(tag, GitUtil.getId(rc.getOldId()))); + } + } + + createdTags = createdTagBuilder.build(); + deletedTags = deletedTagBuilder.build(); + } + + @Override + public List getCreatedTags() { + return createdTags; + } + + @Override + public List getDeletedTags() { + return deletedTags; + } + +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitHookContextProvider.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitHookContextProvider.java index 62b88db1d3..0d3a30ed52 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitHookContextProvider.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitHookContextProvider.java @@ -47,6 +47,8 @@ import sonia.scm.repository.api.HookMessageProvider; import java.util.EnumSet; import java.util.List; import java.util.Set; +import sonia.scm.repository.api.GitHookTagProvider; +import sonia.scm.repository.api.HookTagProvider; /** * @@ -58,16 +60,15 @@ public class GitHookContextProvider extends HookContextProvider /** Field description */ private static final Set SUPPORTED_FEATURES = EnumSet.of(HookFeature.MESSAGE_PROVIDER, HookFeature.CHANGESET_PROVIDER, - HookFeature.BRANCH_PROVIDER); + HookFeature.BRANCH_PROVIDER, HookFeature.TAG_PROVIDER); //~--- constructors --------------------------------------------------------- /** - * Constructs ... + * Constructs a new instance * - * - * @param receivePack - * @param receiveCommands + * @param receivePack git receive pack + * @param receiveCommands received commands */ public GitHookContextProvider(ReceivePack receivePack, List receiveCommands) @@ -80,12 +81,6 @@ public class GitHookContextProvider extends HookContextProvider //~--- methods -------------------------------------------------------------- - /** - * Method description - * - * - * @return - */ @Override public HookMessageProvider createMessageProvider() { @@ -94,36 +89,23 @@ public class GitHookContextProvider extends HookContextProvider //~--- get methods ---------------------------------------------------------- - /** - * Method description - * - * - * @return - */ @Override public HookBranchProvider getBranchProvider() { return new GitHookBranchProvider(receiveCommands); } - /** - * Method description - * - * - * @return - */ + @Override + public HookTagProvider getTagProvider() { + return new GitHookTagProvider(receiveCommands); + } + @Override public HookChangesetProvider getChangesetProvider() { return changesetProvider; } - /** - * Method description - * - * - * @return - */ @Override public Set getSupportedFeatures() { diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitUtilTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitUtilTest.java index 9eed5602f0..e5c33fc9d8 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitUtilTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitUtilTest.java @@ -43,16 +43,18 @@ import static org.mockito.Mockito.*; import java.io.File; import java.io.IOException; +import static org.junit.Assert.*; + /** - * + * Unit tests for {@link GitUtil}. + * * @author Sebastian Sdorra */ public class GitUtilTest { /** - * Method description - * + * Tests {@link GitUtil#checkBranchName(org.eclipse.jgit.lib.Repository, java.lang.String)} with invalid name. * * @throws IOException */ @@ -66,8 +68,7 @@ public class GitUtilTest } /** - * Method description - * + * Tests {@link GitUtil#checkBranchName(org.eclipse.jgit.lib.Repository, java.lang.String)}. * * @throws IOException */ @@ -80,15 +81,17 @@ public class GitUtilTest GitUtil.checkBranchName(repo, GitUtil.REF_HEAD_PREFIX.concat("dev")); GitUtil.checkBranchName(repo, GitUtil.REF_HEAD_PREFIX.concat("develop")); } - + /** - * Method description - * - * - * @param directory - * - * @return + * Tests {@link GitUtil#getTagName(java.lang.String)}. */ + @Test + public void testGetTagName(){ + assertNull(GitUtil.getTagName("refs/head/master")); + assertEquals("1.0.0", GitUtil.getTagName("refs/tags/1.0.0")); + assertEquals("super/1.0.0", GitUtil.getTagName("refs/tags/super/1.0.0")); + } + private org.eclipse.jgit.lib.Repository mockRepo(File directory) { org.eclipse.jgit.lib.Repository repo = diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/api/GitHookTagProviderTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/api/GitHookTagProviderTest.java new file mode 100644 index 0000000000..87e277b633 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/api/GitHookTagProviderTest.java @@ -0,0 +1,130 @@ +/*** + * Copyright (c) 2015, 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. + * + * https://bitbucket.org/sdorra/scm-manager + * + */ + +package sonia.scm.repository.api; + +import com.google.common.collect.Lists; +import java.util.List; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.transport.ReceiveCommand; +import org.junit.Test; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; +import static org.hamcrest.Matchers.*; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.stubbing.OngoingStubbing; +import sonia.scm.repository.Tag; + +/** + * Unit tests for {@link GitHookTagProvider}. + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +public class GitHookTagProviderTest { + + @Mock + private ReceiveCommand command; + + private List commands; + + /** + * Set up mocks for upcoming tests. + */ + @Before + public void setUpMocks(){ + commands = Lists.newArrayList(command); + } + + /** + * Tests {@link GitHookTagProvider#getCreatedTags()}. + */ + @Test + public void testGetCreatedTags() { + String revision = "b2002b64013e54b78eac251df0672bd5d6a83aa7"; + GitHookTagProvider provider = createProvider(ReceiveCommand.Type.CREATE, "refs/tags/1.0.0", revision); + + assertTag("1.0.0", revision, provider.getCreatedTags()); + assertThat(provider.getDeletedTags(), empty()); + } + + /** + * Tests {@link GitHookTagProvider#getDeletedTags()}. + */ + @Test + public void testGetDeletedTags() { + String revision = "b2002b64013e54b78eac251df0672bd5d6a83aa7"; + GitHookTagProvider provider = createProvider(ReceiveCommand.Type.DELETE, "refs/tags/1.0.0", revision); + + assertThat(provider.getCreatedTags(), empty()); + assertTag("1.0.0", revision, provider.getDeletedTags()); + } + + /** + * Tests {@link GitHookTagProvider} with a branch ref instead of a tag. + */ + @Test + public void testWithBranch(){ + String revision = "b2002b64013e54b78eac251df0672bd5d6a83aa7"; + GitHookTagProvider provider = createProvider(ReceiveCommand.Type.CREATE, "refs/heads/1.0.0", revision); + + assertThat(provider.getCreatedTags(), empty()); + assertThat(provider.getDeletedTags(), empty()); + } + + private void assertTag(String name, String revision, List tags){ + assertNotNull(tags); + assertFalse(tags.isEmpty()); + assertEquals(1, tags.size()); + Tag tag = tags.get(0); + assertEquals(name, tag.getName()); + assertEquals(revision, tag.getRevision()); + } + + private GitHookTagProvider createProvider(ReceiveCommand.Type type, String ref, String id){ + OngoingStubbing ongoing; + if (type == ReceiveCommand.Type.CREATE){ + ongoing = when(command.getNewId()); + } else { + ongoing = when(command.getOldId()); + } + ongoing.thenReturn(ObjectId.fromString(id)); + + when(command.getType()).thenReturn(type); + when(command.getRefName()).thenReturn(ref); + + return new GitHookTagProvider(commands); + } + +} \ No newline at end of file From 9fad94403a0bcc00fc5596e357b2f8c9ce718812 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 28 Sep 2016 14:52:18 +0200 Subject: [PATCH 146/171] do not collect the changesets for the whole branch, if only a git tag was pushed --- .../repository/GitHookChangesetCollector.java | 50 +++++++------------ .../java/sonia/scm/repository/GitUtil.java | 15 ++++++ .../sonia/scm/repository/GitUtilTest.java | 13 +++++ 3 files changed, 45 insertions(+), 33 deletions(-) diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitHookChangesetCollector.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitHookChangesetCollector.java index a13a08997c..0acd7c1ad2 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitHookChangesetCollector.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitHookChangesetCollector.java @@ -75,7 +75,7 @@ public class GitHookChangesetCollector //~--- constructors --------------------------------------------------------- /** - * Constructs ... + * Constructs a new instance * * * @param rpack @@ -92,10 +92,9 @@ public class GitHookChangesetCollector //~--- methods -------------------------------------------------------------- /** - * Method description + * Collect all new changesets from the received hook. * - * - * @return + * @return new changesets */ public List collectChangesets() { @@ -114,17 +113,19 @@ 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+ - - if (rc.getType() != ReceiveCommand.Type.DELETE) + String ref = rc.getRefName(); + + logger.trace("handle receive command, type={}, ref={}, result={}", rc.getType(), ref, rc.getResult()); + + if (rc.getType() == ReceiveCommand.Type.DELETE) + { + logger.debug("skip delete of ref {}", ref); + } + else if (! GitUtil.isBranch(ref)) + { + logger.debug("skip ref {}, because it is not a branch", ref); + } + else { try { @@ -138,13 +139,10 @@ public class GitHookChangesetCollector builder.append(rc.getType()).append(", ref="); builder.append(rc.getRefName()).append(", result="); builder.append(rc.getResult()); + logger.error(builder.toString(), ex); } } - else - { - logger.debug("skip delete of branch {}", rc.getRefName()); - } } } @@ -161,18 +159,6 @@ public class GitHookChangesetCollector return Lists.newArrayList(changesets.values()); } - /** - * Method description - * - * - * @param changesets - * @param converter - * @param walk - * @param rc - * - * @throws IOException - * @throws IncorrectObjectTypeException - */ private void collectChangesets(Map changesets, GitChangesetConverter converter, RevWalk walk, ReceiveCommand rc) throws IncorrectObjectTypeException, IOException @@ -243,9 +229,7 @@ public class GitHookChangesetCollector /** listener to track new objects */ private final CollectingPackParserListener listener; - /** Field description */ private final List receiveCommands; - /** Field description */ private final ReceivePack rpack; } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java index daa016b00e..ef0f12c27d 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java @@ -36,6 +36,7 @@ package sonia.scm.repository; //~--- non-JDK imports -------------------------------------------------------- import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Strings; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; @@ -337,6 +338,20 @@ public final class GitUtil return branch; } + + /** + * Returns {@code true} if the provided reference name is a branch name. + * + * @param refName reference name + * + * @return {@code true} if the name is a branch name + * + * @since 1.50 + */ + public static boolean isBranch(String refName) + { + return Strings.nullToEmpty(refName).startsWith(PREFIX_HEADS); + } /** * Method description diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitUtilTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitUtilTest.java index e5c33fc9d8..fdaa825a3f 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitUtilTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitUtilTest.java @@ -91,6 +91,19 @@ public class GitUtilTest assertEquals("1.0.0", GitUtil.getTagName("refs/tags/1.0.0")); assertEquals("super/1.0.0", GitUtil.getTagName("refs/tags/super/1.0.0")); } + + /** + * Tests {@link GitUtil#isBranch(java.lang.String)}. + */ + @Test + public void testIsBranchName(){ + assertTrue(GitUtil.isBranch("refs/heads/master")); + assertTrue(GitUtil.isBranch("refs/heads/feature/super")); + assertFalse(GitUtil.isBranch("")); + assertFalse(GitUtil.isBranch(null)); + assertFalse(GitUtil.isBranch("refs/tags/1.0.0")); + assertFalse(GitUtil.isBranch("refs/heads")); + } private org.eclipse.jgit.lib.Repository mockRepo(File directory) { From 617ed81b533cbf51d0ed69984923ccc22c7754fa Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 29 Sep 2016 09:11:53 +0200 Subject: [PATCH 147/171] implemented HookTagProvider api for mercurial --- .../repository/api/HgHookBranchProvider.java | 55 +++------ .../scm/repository/api/HgHookTagProvider.java | 107 ++++++++++++++++++ .../scm/repository/api/HgHookTagTest.java | 57 ++++++++++ .../repository/spi/HgHookContextProvider.java | 69 ++++------- .../repository/api/HgHookTagProviderTest.java | 104 +++++++++++++++++ 5 files changed, 307 insertions(+), 85 deletions(-) create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/api/HgHookTagProvider.java create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/api/HgHookTagTest.java create mode 100644 scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/api/HgHookTagProviderTest.java diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/api/HgHookBranchProvider.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/api/HgHookBranchProvider.java index 05f05202e7..0d134a9c8f 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/api/HgHookBranchProvider.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/api/HgHookBranchProvider.java @@ -37,7 +37,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList.Builder; import sonia.scm.repository.Changeset; -import sonia.scm.repository.spi.HgHookChangesetProvider; +import sonia.scm.repository.spi.HookChangesetProvider; import sonia.scm.repository.spi.HookChangesetRequest; import sonia.scm.repository.spi.javahg.AbstractChangesetCommand; import sonia.scm.util.Util; @@ -46,38 +46,37 @@ import sonia.scm.util.Util; import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** - * + * Mercurial hook branch provider implementation. + * * @author Sebastian Sdorra */ public class HgHookBranchProvider implements HookBranchProvider { + + private static final Logger logger = LoggerFactory.getLogger(HgHookBranchProvider.class); - /** Field description */ private static final HookChangesetRequest REQUEST = new HookChangesetRequest(); //~--- constructors --------------------------------------------------------- /** - * Constructs ... + * Constructs a new instance. * * - * @param changesetProvider + * @param changesetProvider changeset provider */ - public HgHookBranchProvider(HgHookChangesetProvider changesetProvider) + public HgHookBranchProvider(HookChangesetProvider changesetProvider) { this.changesetProvider = changesetProvider; } //~--- get methods ---------------------------------------------------------- - /** - * Method description - * - * - * @return - */ @Override public List getCreatedOrModified() { @@ -89,12 +88,6 @@ public class HgHookBranchProvider implements HookBranchProvider return createdOrModified; } - /** - * Method description - * - * - * @return - */ @Override public List getDeletedOrClosed() { @@ -108,15 +101,6 @@ public class HgHookBranchProvider implements HookBranchProvider //~--- methods -------------------------------------------------------------- - /** - * Method description - * - * - * @param builder - * @param c - * - * @return - */ private List appendBranches(Builder builder, Changeset c) { List branches = c.getBranches(); @@ -133,26 +117,18 @@ public class HgHookBranchProvider implements HookBranchProvider return branches; } - /** - * Method description - * - * - * @return - */ private Iterable changesets() { return changesetProvider.handleRequest(REQUEST).getChangesets(); } - /** - * Method description - * - */ private void collect() { Builder createdOrModifiedBuilder = ImmutableList.builder(); Builder deletedOrClosedBuilder = ImmutableList.builder(); + logger.trace("collecting branches from hook changesets"); + for (Changeset c : changesets()) { if (c.getProperty(AbstractChangesetCommand.PROPERTY_CLOSE) != null) @@ -171,12 +147,9 @@ public class HgHookBranchProvider implements HookBranchProvider //~--- fields --------------------------------------------------------------- - /** Field description */ - private final HgHookChangesetProvider changesetProvider; + private final HookChangesetProvider changesetProvider; - /** Field description */ private List createdOrModified; - /** Field description */ private List deletedOrClosed; } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/api/HgHookTagProvider.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/api/HgHookTagProvider.java new file mode 100644 index 0000000000..be89ebb415 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/api/HgHookTagProvider.java @@ -0,0 +1,107 @@ +/** + * * + * Copyright (c) 2015, 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. + * + * https://bitbucket.org/sdorra/scm-manager + * + */ +package sonia.scm.repository.api; + +import com.google.common.collect.ImmutableList; +import java.util.Collections; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.repository.Changeset; +import sonia.scm.repository.Tag; +import sonia.scm.repository.spi.HookChangesetProvider; +import sonia.scm.repository.spi.HookChangesetRequest; +import sonia.scm.repository.spi.HookChangesetResponse; + +/** + * Mercurial tag provider implementation. + * + * @author Sebastian Sdorra + * @since 1.50 + */ +public class HgHookTagProvider implements HookTagProvider { + + private static final Logger logger = LoggerFactory.getLogger(HgHookTagProvider.class); + + private static final HookChangesetRequest REQUEST = new HookChangesetRequest(); + + private final HookChangesetProvider changesetProvider; + + private List createdTags; + private final List deletedTags = Collections.emptyList(); + + /** + * Constructs a new instance. + * + * @param changesetProvider changeset provider + */ + public HgHookTagProvider(HookChangesetProvider changesetProvider) { + this.changesetProvider = changesetProvider; + } + + @Override + public List getCreatedTags() { + if (createdTags == null) { + collect(); + } + return createdTags; + } + + @Override + public List getDeletedTags() { + logger.warn("detecting deleted tags with mercurial is currently not supported"); + return deletedTags; + } + + private void collect() { + ImmutableList.Builder createdTagsBuilder = ImmutableList.builder(); + + logger.trace("collecting tags from hook changesets"); + HookChangesetResponse response = changesetProvider.handleRequest(REQUEST); + for ( Changeset c : response.getChangesets() ){ + appendTags(createdTagsBuilder, c); + } + + createdTags = createdTagsBuilder.build(); + } + + private void appendTags(ImmutableList.Builder tags, Changeset c){ + List tagNames = c.getTags(); + if (tagNames != null){ + for ( String tagName : tagNames ){ + logger.trace("found tag {} at changeset {}", tagName, c.getId()); + tags.add(new Tag(tagName, c.getId())); + } + } + } + +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/api/HgHookTagTest.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/api/HgHookTagTest.java new file mode 100644 index 0000000000..363705d774 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/api/HgHookTagTest.java @@ -0,0 +1,57 @@ +/*** + * Copyright (c) 2015, 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. + * + * https://bitbucket.org/sdorra/scm-manager + * + */ + +package sonia.scm.repository.api; + +import com.google.common.eventbus.Subscribe; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.EagerSingleton; +import sonia.scm.plugin.ext.Extension; +import sonia.scm.repository.PostReceiveRepositoryHookEvent; + +/** + * + * @author Sebastian Sdorra + */ +@Extension +@EagerSingleton +public class HgHookTagTest { + + private static final Logger logger = LoggerFactory.getLogger(HgHookTagTest.class); + + @Subscribe + public void handle(PostReceiveRepositoryHookEvent event){ + HookTagProvider tagProvider = event.getContext().getTagProvider(); + logger.error("!!!! received mercurial tags {}", tagProvider.getCreatedTags()); + } + +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgHookContextProvider.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgHookContextProvider.java index 4087d08751..dede59796c 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgHookContextProvider.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgHookContextProvider.java @@ -46,30 +46,31 @@ import sonia.scm.repository.api.HookMessageProvider; import java.util.EnumSet; import java.util.Set; +import sonia.scm.repository.api.HgHookTagProvider; +import sonia.scm.repository.api.HookTagProvider; /** - * + * Mercurial implementation of {@link HookContextProvider}. + * * @author Sebastian Sdorra */ public class HgHookContextProvider extends HookContextProvider { - /** Field description */ private static final Set SUPPORTED_FEATURES = EnumSet.of(HookFeature.CHANGESET_PROVIDER, HookFeature.MESSAGE_PROVIDER, - HookFeature.BRANCH_PROVIDER); + HookFeature.BRANCH_PROVIDER, HookFeature.TAG_PROVIDER); //~--- constructors --------------------------------------------------------- /** - * Constructs ... + * Constructs a new instance. * - * - * @param handler - * @param repositoryName - * @param hookManager - * @param startRev - * @param type + * @param handler mercurial repository handler + * @param repositoryName name of changed repository + * @param hookManager mercurial hook manager + * @param startRev start revision + * @param type type of hook */ public HgHookContextProvider(HgRepositoryHandler handler, String repositoryName, HgHookManager hookManager, String startRev, @@ -81,12 +82,6 @@ public class HgHookContextProvider extends HookContextProvider //~--- get methods ---------------------------------------------------------- - /** - * Method description - * - * - * @return - */ @Override public HookBranchProvider getBranchProvider() { @@ -98,24 +93,23 @@ public class HgHookContextProvider extends HookContextProvider return hookBranchProvider; } - /** - * Method description - * - * - * @return - */ + @Override + public HookTagProvider getTagProvider() + { + if (hookTagProvider == null) + { + hookTagProvider = new HgHookTagProvider(hookChangesetProvider); + } + + return hookTagProvider; + } + @Override public HookChangesetProvider getChangesetProvider() { return hookChangesetProvider; } - - /** - * Method description - * - * - * @return - */ + public HgHookMessageProvider getHgMessageProvider() { if (hgMessageProvider == null) @@ -126,12 +120,6 @@ public class HgHookContextProvider extends HookContextProvider return hgMessageProvider; } - /** - * Method description - * - * - * @return - */ @Override public Set getSupportedFeatures() { @@ -140,12 +128,6 @@ public class HgHookContextProvider extends HookContextProvider //~--- methods -------------------------------------------------------------- - /** - * Method description - * - * - * @return - */ @Override protected HookMessageProvider createMessageProvider() { @@ -154,12 +136,11 @@ public class HgHookContextProvider extends HookContextProvider //~--- fields --------------------------------------------------------------- - /** Field description */ private final HgHookChangesetProvider hookChangesetProvider; - /** Field description */ private HgHookMessageProvider hgMessageProvider; - /** Field description */ private HgHookBranchProvider hookBranchProvider; + + private HgHookTagProvider hookTagProvider; } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/api/HgHookTagProviderTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/api/HgHookTagProviderTest.java new file mode 100644 index 0000000000..6d78d0eb3c --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/api/HgHookTagProviderTest.java @@ -0,0 +1,104 @@ +/*** + * Copyright (c) 2015, 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. + * + * https://bitbucket.org/sdorra/scm-manager + * + */ + +package sonia.scm.repository.api; + +import com.google.common.collect.Lists; +import java.util.List; +import org.junit.Test; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; +import static org.hamcrest.Matchers.*; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; +import sonia.scm.repository.Changeset; +import sonia.scm.repository.Tag; +import sonia.scm.repository.spi.HookChangesetProvider; +import sonia.scm.repository.spi.HookChangesetRequest; +import sonia.scm.repository.spi.HookChangesetResponse; + +/** + * Unit tests for {@link HgHookTagProvider}. + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +public class HgHookTagProviderTest { + + @Mock + private HookChangesetProvider changesetProvider; + + @InjectMocks + private HgHookTagProvider tagProvider; + + /** + * Tests {@link HgHookTagProvider#getDeletedTags()}. + */ + @Test + public void testGetDeletedTags() { + prepareChangesets(new Changeset("1", Long.MIN_VALUE, null)); + assertThat(tagProvider.getDeletedTags(), empty()); + } + + /** + * Tests {@link HgHookTagProvider#getCreatedTags()}. + */ + @Test + public void testGetCreatedTags(){ + Changeset c1 = new Changeset("1", Long.MIN_VALUE, null); + c1.getTags().add("1.0.0"); + Changeset c2 = new Changeset("2", Long.MIN_VALUE, null); + c2.getTags().add("2.0.0"); + Changeset c3 = new Changeset("3", Long.MIN_VALUE, null); + prepareChangesets(c1, c2, c3); + + List tags = tagProvider.getCreatedTags(); + assertNotNull(tags); + assertEquals(2, tags.size()); + + Tag t1 = tags.get(0); + assertEquals("1", t1.getRevision()); + assertEquals("1.0.0", t1.getName()); + + Tag t2 = tags.get(1); + assertEquals("2", t2.getRevision()); + assertEquals("2.0.0", t2.getName()); + } + + private void prepareChangesets(Changeset... changesets){ + List list = Lists.newArrayList(changesets); + HookChangesetResponse response = new HookChangesetResponse(list); + when(changesetProvider.handleRequest(Mockito.any(HookChangesetRequest.class))).thenReturn(response); + } +} \ No newline at end of file From c988b01ab73c13e490c68ea52365611f9d21273e Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 29 Sep 2016 09:23:13 +0200 Subject: [PATCH 148/171] remove test hook --- .../scm/repository/api/HgHookTagTest.java | 57 ------------------- 1 file changed, 57 deletions(-) delete mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/api/HgHookTagTest.java diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/api/HgHookTagTest.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/api/HgHookTagTest.java deleted file mode 100644 index 363705d774..0000000000 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/api/HgHookTagTest.java +++ /dev/null @@ -1,57 +0,0 @@ -/*** - * Copyright (c) 2015, 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. - * - * https://bitbucket.org/sdorra/scm-manager - * - */ - -package sonia.scm.repository.api; - -import com.google.common.eventbus.Subscribe; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sonia.scm.EagerSingleton; -import sonia.scm.plugin.ext.Extension; -import sonia.scm.repository.PostReceiveRepositoryHookEvent; - -/** - * - * @author Sebastian Sdorra - */ -@Extension -@EagerSingleton -public class HgHookTagTest { - - private static final Logger logger = LoggerFactory.getLogger(HgHookTagTest.class); - - @Subscribe - public void handle(PostReceiveRepositoryHookEvent event){ - HookTagProvider tagProvider = event.getContext().getTagProvider(); - logger.error("!!!! received mercurial tags {}", tagProvider.getCreatedTags()); - } - -} From 6a917f03a602210c094cb436a1c88c14d16e5266 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Fri, 30 Sep 2016 19:45:21 +0200 Subject: [PATCH 149/171] close branch issue-865 From d5fda27c33583076f0a48a9659e7700152d796d5 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Fri, 30 Sep 2016 20:02:55 +0200 Subject: [PATCH 150/171] fix java.lang.NoClassDefFoundError org/w3c/dom/ElementTraversal --- scm-webapp/pom.xml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index 1a629c71f9..5b440d07d6 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -159,6 +159,16 @@ ${ehcache.version} + + + + xml-apis + xml-apis + 1.4.01 + + @@ -704,13 +714,6 @@ test - - xml-apis - xml-apis - 1.4.01 - test - - From b69d1b3ed3d8397e64890dd8c63aecf2248a3ea2 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Fri, 30 Sep 2016 21:07:23 +0200 Subject: [PATCH 151/171] send http status code 401 unauthorized on failed git authentication, see issue #870 Revert use of GitSmartHttpTools to send unauthoried errors back to the git client, because the password is stored in the git credentials store event if the username or password was invalid. Switch back to default method, which send http status code 401 back to the client. This method does not send the customized client message, but the default one from git is good enough. --- .../scm/web/GitBasicAuthenticationFilter.java | 47 +++---------------- 1 file changed, 6 insertions(+), 41 deletions(-) diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitBasicAuthenticationFilter.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitBasicAuthenticationFilter.java index b15f75f297..5a0b14bdce 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitBasicAuthenticationFilter.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitBasicAuthenticationFilter.java @@ -35,26 +35,19 @@ package sonia.scm.web; import com.google.inject.Inject; import com.google.inject.Singleton; - -import org.eclipse.jgit.http.server.GitSmartHttpTools; - -import sonia.scm.ClientMessages; import sonia.scm.config.ScmConfiguration; -import sonia.scm.repository.GitUtil; import sonia.scm.web.filter.AutoLoginModule; import sonia.scm.web.filter.BasicAuthenticationFilter; //~--- JDK imports ------------------------------------------------------------ -import java.io.IOException; import java.util.Set; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; /** - * + * Handles git specific basic authentication. + * * @author Sebastian Sdorra */ @Singleton @@ -62,12 +55,12 @@ public class GitBasicAuthenticationFilter extends BasicAuthenticationFilter { /** - * Constructs ... + * Constructs a new instance. * * - * @param configuration - * @param autoLoginModules - * @param userAgentParser + * @param configuration scm-manager main configuration + * @param autoLoginModules auto login modules + * @param userAgentParser user agent parser */ @Inject public GitBasicAuthenticationFilter(ScmConfiguration configuration, @@ -75,32 +68,4 @@ public class GitBasicAuthenticationFilter extends BasicAuthenticationFilter { super(configuration, autoLoginModules, userAgentParser); } - - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @param request - * @param response - * - * @throws IOException - */ - @Override - protected void sendFailedAuthenticationError(HttpServletRequest request, - HttpServletResponse response) - throws IOException - { - if (GitUtil.isGitClient(request)) - { - GitSmartHttpTools.sendError(request, response, - HttpServletResponse.SC_FORBIDDEN, - ClientMessages.get(request).failedAuthentication()); - } - else - { - super.sendFailedAuthenticationError(request, response); - } - } } From 264a1af6342596d0134092c7aee965ff821b0f7b Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Fri, 30 Sep 2016 22:23:14 +0200 Subject: [PATCH 152/171] treat HEAD, OPTIONS and TRACE as mercurial read requests not only GET, see issue #859 --- .../sonia/scm/web/HgPermissionFilter.java | 24 +++-- .../sonia/scm/web/HgPermissionFilterTest.java | 88 +++++++++++++++++++ 2 files changed, 99 insertions(+), 13 deletions(-) create mode 100644 scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgPermissionFilterTest.java diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgPermissionFilter.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgPermissionFilter.java index 0a9d3e43b9..6700ae3b8d 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgPermissionFilter.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgPermissionFilter.java @@ -35,6 +35,7 @@ package sonia.scm.web; //~--- non-JDK imports -------------------------------------------------------- +import com.google.common.collect.ImmutableSet; import com.google.inject.Inject; import com.google.inject.Singleton; @@ -44,21 +45,26 @@ import sonia.scm.web.filter.ProviderPermissionFilter; //~--- JDK imports ------------------------------------------------------------ +import java.util.Set; + import javax.servlet.http.HttpServletRequest; /** - * + * Permission filter for mercurial repositories. + * * @author Sebastian Sdorra */ @Singleton public class HgPermissionFilter extends ProviderPermissionFilter { + + private static final Set READ_METHODS = ImmutableSet.of("GET", "HEAD", "OPTIONS", "TRACE"); /** - * Constructs ... + * Constructs a new instance. * - * @param configuration - * @param repositoryProvider + * @param configuration scm configuration + * @param repositoryProvider repository provider */ @Inject public HgPermissionFilter(ScmConfiguration configuration, @@ -69,17 +75,9 @@ public class HgPermissionFilter extends ProviderPermissionFilter //~--- get methods ---------------------------------------------------------- - /** - * Method description - * - * - * @param request - * - * @return - */ @Override protected boolean isWriteRequest(HttpServletRequest request) { - return !request.getMethod().equalsIgnoreCase("GET"); + return !READ_METHODS.contains(request.getMethod()); } } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgPermissionFilterTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgPermissionFilterTest.java new file mode 100644 index 0000000000..01e01cf302 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgPermissionFilterTest.java @@ -0,0 +1,88 @@ +/** + * 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.web; + +import javax.servlet.http.HttpServletRequest; +import org.junit.Test; +import static org.junit.Assert.*; + +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import static org.mockito.Mockito.*; +import org.mockito.runners.MockitoJUnitRunner; +import sonia.scm.config.ScmConfiguration; +import sonia.scm.repository.RepositoryProvider; + +/** + * Unit tests for {@link HgPermissionFilter}. + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +public class HgPermissionFilterTest { + + @Mock + private HttpServletRequest request; + + @Mock + private ScmConfiguration configuration; + + @Mock + private RepositoryProvider repositoryProvider; + + @InjectMocks + private HgPermissionFilter filter; + + /** + * Tests {@link HgPermissionFilter#isWriteRequest(HttpServletRequest)}. + */ + @Test + public void testIsWriteRequest() { + // read methods + assertFalse(isWriteRequest("GET")); + assertFalse(isWriteRequest("HEAD")); + assertFalse(isWriteRequest("TRACE")); + assertFalse(isWriteRequest("OPTIONS")); + + // write methods + assertTrue(isWriteRequest("POST")); + assertTrue(isWriteRequest("PUT")); + assertTrue(isWriteRequest("DELETE")); + assertTrue(isWriteRequest("KA")); + } + + private boolean isWriteRequest(String method) { + when(request.getMethod()).thenReturn(method); + return filter.isWriteRequest(request); + } +} \ No newline at end of file From 51e1fe6ecbc94bf7d776476635edcc717e92f9cf Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Mon, 3 Oct 2016 10:52:47 +0200 Subject: [PATCH 153/171] close branch issue-859 From bde4dd48b208a5059b55d625b7d5b5e1e13f132c Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 18 Oct 2016 20:51:54 +0200 Subject: [PATCH 154/171] close branch issue-870 From da8c997f5949744541312792a32609dd4554e3ab Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Mon, 31 Oct 2016 22:16:21 +0100 Subject: [PATCH 155/171] added primary principal as request attribute, see issue #877 --- .../main/java/sonia/scm/filter/SecurityFilter.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/scm-webapp/src/main/java/sonia/scm/filter/SecurityFilter.java b/scm-webapp/src/main/java/sonia/scm/filter/SecurityFilter.java index b469a6b89a..61dd73ea81 100644 --- a/scm-webapp/src/main/java/sonia/scm/filter/SecurityFilter.java +++ b/scm-webapp/src/main/java/sonia/scm/filter/SecurityFilter.java @@ -64,6 +64,9 @@ import javax.servlet.http.HttpServletResponse; public class SecurityFilter extends HttpFilter { + /** name of request attribute for the primary principal */ + private static final String ATTRIBUTE_REMOTE_USER = "principal"; + /** Field description */ public static final String URL_AUTHENTICATION = "/api/rest/authentication"; @@ -108,8 +111,14 @@ public class SecurityFilter extends HttpFilter { if (hasPermission(subject)) { + // add primary principal as request attribute + // see https://goo.gl/JRjNmf + User user = getUser(subject); + request.setAttribute(ATTRIBUTE_REMOTE_USER, user.getId()); + + // wrap servlet request to provide authentication informations chain.doFilter(new SecurityHttpServletRequestWrapper(request, - getUser(subject)), response); + user), response); } else if (subject.isAuthenticated() || subject.isRemembered()) { From 70a2b52e97c46f2aaddbba14533ab44d3a5b5424 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Mon, 31 Oct 2016 22:16:44 +0100 Subject: [PATCH 156/171] close branch issue-877 From 807eccf459613461dff5889dd8ab356884305372 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sat, 5 Nov 2016 19:46:32 +0100 Subject: [PATCH 157/171] added unit tests for security filters --- .../sonia/scm/filter/AdminSecurityFilter.java | 18 +- .../java/sonia/scm/filter/SecurityFilter.java | 11 +- .../scm/filter/AdminSecurityFilterTest.java | 85 ++++++ .../sonia/scm/filter/SecurityFilterTest.java | 242 ++++++++++++++++++ .../test/resources/sonia/scm/shiro-001.ini | 1 + 5 files changed, 342 insertions(+), 15 deletions(-) create mode 100644 scm-webapp/src/test/java/sonia/scm/filter/AdminSecurityFilterTest.java create mode 100644 scm-webapp/src/test/java/sonia/scm/filter/SecurityFilterTest.java diff --git a/scm-webapp/src/main/java/sonia/scm/filter/AdminSecurityFilter.java b/scm-webapp/src/main/java/sonia/scm/filter/AdminSecurityFilter.java index a458138910..a85f3a54f9 100644 --- a/scm-webapp/src/main/java/sonia/scm/filter/AdminSecurityFilter.java +++ b/scm-webapp/src/main/java/sonia/scm/filter/AdminSecurityFilter.java @@ -44,7 +44,9 @@ import sonia.scm.config.ScmConfiguration; import sonia.scm.security.Role; /** - * + * Security filter which allow only administrators to access the underlying + * resources. + * * @author Sebastian Sdorra */ @Singleton @@ -52,10 +54,9 @@ public class AdminSecurityFilter extends SecurityFilter { /** - * Constructs ... + * Constructs a new instance. * - * - * @param configuration + * @param configuration scm-manager main configuration */ @Inject public AdminSecurityFilter(ScmConfiguration configuration) @@ -66,14 +67,11 @@ public class AdminSecurityFilter extends SecurityFilter //~--- get methods ---------------------------------------------------------- /** - * Method description + * Returns {@code true} if the subject has the admin role. * + * @param subject subject * - * @param securityContext - * - * @param subject - * - * @return + * @return {@code true} if the subject has the admin role */ @Override protected boolean hasPermission(Subject subject) diff --git a/scm-webapp/src/main/java/sonia/scm/filter/SecurityFilter.java b/scm-webapp/src/main/java/sonia/scm/filter/SecurityFilter.java index 61dd73ea81..45f0a3ac8a 100644 --- a/scm-webapp/src/main/java/sonia/scm/filter/SecurityFilter.java +++ b/scm-webapp/src/main/java/sonia/scm/filter/SecurityFilter.java @@ -35,6 +35,7 @@ package sonia.scm.filter; //~--- non-JDK imports -------------------------------------------------------- +import com.google.common.annotations.VisibleForTesting; import com.google.inject.Inject; import com.google.inject.Singleton; @@ -65,7 +66,8 @@ public class SecurityFilter extends HttpFilter { /** name of request attribute for the primary principal */ - private static final String ATTRIBUTE_REMOTE_USER = "principal"; + @VisibleForTesting + static final String ATTRIBUTE_REMOTE_USER = "principal"; /** Field description */ public static final String URL_AUTHENTICATION = "/api/rest/authentication"; @@ -102,13 +104,12 @@ public class SecurityFilter extends HttpFilter HttpServletResponse response, FilterChain chain) throws IOException, ServletException { - Subject subject = SecurityUtils.getSubject(); - String uri = request.getRequestURI().substring(request.getContextPath().length()); if (!uri.startsWith(URL_AUTHENTICATION)) { + Subject subject = SecurityUtils.getSubject(); if (hasPermission(subject)) { // add primary principal as request attribute @@ -164,7 +165,7 @@ public class SecurityFilter extends HttpFilter */ private User getUser(Subject subject) { - User user = null; + User user; if (subject.isAuthenticated() || subject.isRemembered()) { @@ -181,5 +182,5 @@ public class SecurityFilter extends HttpFilter //~--- fields --------------------------------------------------------------- /** Field description */ - private ScmConfiguration configuration; + private final ScmConfiguration configuration; } diff --git a/scm-webapp/src/test/java/sonia/scm/filter/AdminSecurityFilterTest.java b/scm-webapp/src/test/java/sonia/scm/filter/AdminSecurityFilterTest.java new file mode 100644 index 0000000000..b6a9c02a8d --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/filter/AdminSecurityFilterTest.java @@ -0,0 +1,85 @@ +/** + * 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.filter; + +import com.github.sdorra.shiro.ShiroRule; +import com.github.sdorra.shiro.SubjectAware; +import org.apache.shiro.SecurityUtils; +import org.junit.Test; +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.Rule; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; +import sonia.scm.config.ScmConfiguration; + +/** + * Unit tests for {@link AdminSecurityFilter}. + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +@SubjectAware(configuration = "classpath:sonia/scm/shiro-001.ini") +public class AdminSecurityFilterTest { + + private AdminSecurityFilter securityFilter; + + @Rule + public ShiroRule shiro = new ShiroRule(); + + /** + * Prepare object under test and mocks. + */ + @Before + public void setUp(){ + this.securityFilter = new AdminSecurityFilter(new ScmConfiguration()); + } + + /** + * Tests {@link AdminSecurityFilter#hasPermission(org.apache.shiro.subject.Subject)} as administrator. + */ + @Test + @SubjectAware(username = "dent", password = "secret") + public void testHasPermissionAsAdministrator() { + assertTrue(securityFilter.hasPermission(SecurityUtils.getSubject())); + } + + /** + * Tests {@link AdminSecurityFilter#hasPermission(org.apache.shiro.subject.Subject)} as user. + */ + @Test + @SubjectAware(username = "trillian", password = "secret") + public void testHasPermissionAsUser() { + assertFalse(securityFilter.hasPermission(SecurityUtils.getSubject())); + } + +} \ No newline at end of file diff --git a/scm-webapp/src/test/java/sonia/scm/filter/SecurityFilterTest.java b/scm-webapp/src/test/java/sonia/scm/filter/SecurityFilterTest.java new file mode 100644 index 0000000000..9a87d63151 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/filter/SecurityFilterTest.java @@ -0,0 +1,242 @@ +/** + * 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.filter; + +import com.github.sdorra.shiro.ShiroRule; +import com.github.sdorra.shiro.SubjectAware; +import java.io.IOException; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.apache.shiro.subject.SimplePrincipalCollection; +import org.apache.shiro.subject.Subject; +import org.junit.Test; +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; +import org.junit.Before; +import org.junit.Rule; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.Mockito; +import static org.mockito.Mockito.*; +import org.mockito.runners.MockitoJUnitRunner; +import sonia.scm.SCMContext; +import sonia.scm.config.ScmConfiguration; +import sonia.scm.user.User; +import sonia.scm.user.UserTestData; + +/** + * Unit tests for {@link SecurityFilter}. + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +@SubjectAware(configuration = "classpath:sonia/scm/shiro-001.ini") +public class SecurityFilterTest { + + @Mock + private HttpServletRequest request; + + @Captor + private ArgumentCaptor requestCaptor; + + @Mock + private HttpServletResponse response; + + @Captor + private ArgumentCaptor responseCaptor; + + @Mock + private FilterChain chain; + + private ScmConfiguration configuration; + + private SecurityFilter securityFilter; + + @Rule + public ShiroRule shiro = new ShiroRule(); + + /** + * Prepare object under test and mocks. + */ + @Before + public void setUp(){ + this.configuration = new ScmConfiguration(); + this.securityFilter = new SecurityFilter(configuration); + + when(request.getContextPath()).thenReturn("/scm"); + } + + /** + * Tests filter on authentication endpoint. + * + * @throws IOException + * @throws ServletException + */ + @Test + public void testDoOnAuthenticationUrl() throws IOException, ServletException { + when(request.getRequestURI()).thenReturn("/scm/api/rest/authentication"); + securityFilter.doFilter(request, response, chain); + verify(request, never()).setAttribute(Mockito.anyString(), Mockito.any()); + verify(chain).doFilter(request, response); + } + + /** + * Tests filter without prior authentication. + * + * @throws IOException + * @throws ServletException + */ + @Test + public void testAnonymous() throws IOException, ServletException { + when(request.getRequestURI()).thenReturn("/scm/api"); + securityFilter.doFilter(request, response, chain); + response.sendError(HttpServletResponse.SC_FORBIDDEN); + } + + /** + * Tests filter without prior authentication and enabled anonymous access. + * + * @throws IOException + * @throws ServletException + */ + @Test + public void testAnonymousWithAccessEnabled() throws IOException, ServletException { + when(request.getRequestURI()).thenReturn("/scm/api"); + configuration.setAnonymousAccessEnabled(true); + + // execute + securityFilter.doFilter(request, response, chain); + + // verify and capture + verify(request).setAttribute(SecurityFilter.ATTRIBUTE_REMOTE_USER, SCMContext.USER_ANONYMOUS); + verify(chain).doFilter(requestCaptor.capture(), responseCaptor.capture()); + + // assert + HttpServletRequest captured = requestCaptor.getValue(); + assertEquals(SCMContext.USER_ANONYMOUS, captured.getRemoteUser()); + assertEquals(SCMContext.ANONYMOUS, captured.getUserPrincipal()); + } + + /** + * Tests filter with prior authentication. + * + * @throws IOException + * @throws ServletException + */ + @Test + public void testAuthenticated() throws IOException, ServletException { + authenticateUser(UserTestData.createTrillian()); + when(request.getRequestURI()).thenReturn("/scm/api"); + + // execute + securityFilter.doFilter(request, response, chain); + + // verify and capture + verify(request).setAttribute(SecurityFilter.ATTRIBUTE_REMOTE_USER, "trillian"); + verify(chain).doFilter(requestCaptor.capture(), responseCaptor.capture()); + + // assert + HttpServletRequest captured = requestCaptor.getValue(); + assertEquals(HttpServletRequest.BASIC_AUTH, captured.getAuthType()); + assertEquals("trillian", captured.getRemoteUser()); + assertThat(captured.getUserPrincipal(), instanceOf(User.class)); + assertEquals("trillian", captured.getUserPrincipal().getName()); + } + + /** + * Tests filter without permissions. + * + * @throws IOException + * @throws ServletException + */ + @Test + public void testForbidden() throws IOException, ServletException { + authenticateUser(UserTestData.createTrillian()); + when(request.getRequestURI()).thenReturn("/scm/api"); + + // execute + securityFilter = new AccessForbiddenSecurityFilter(configuration); + securityFilter.doFilter(request, response, chain); + + // assert + verify(response).sendError(HttpServletResponse.SC_FORBIDDEN); + } + + /** + * Tests filter unauthenticated and without permissions. + * + * @throws IOException + * @throws ServletException + */ + @Test + public void testUnauthorized() throws IOException, ServletException { + when(request.getRequestURI()).thenReturn("/scm/api"); + + // execute + securityFilter = new AccessForbiddenSecurityFilter(configuration); + securityFilter.doFilter(request, response, chain); + + // assert + verify(response).sendError(HttpServletResponse.SC_UNAUTHORIZED); + } + + private void authenticateUser(User user) { + SimplePrincipalCollection spc = new SimplePrincipalCollection(); + spc.add(user.getName(), "unit-test"); + spc.add(user, "unit-test"); + + Subject subject = new Subject.Builder() + .authenticated(true) + .principals(spc) + .buildSubject(); + + shiro.setSubject(subject); + } + + private static class AccessForbiddenSecurityFilter extends SecurityFilter { + + private AccessForbiddenSecurityFilter(ScmConfiguration configuration) { + super(configuration); + } + + @Override + protected boolean hasPermission(Subject subject) { + return false; + } + + } + +} \ No newline at end of file diff --git a/scm-webapp/src/test/resources/sonia/scm/shiro-001.ini b/scm-webapp/src/test/resources/sonia/scm/shiro-001.ini index 0202162d00..54741bcf4d 100644 --- a/scm-webapp/src/test/resources/sonia/scm/shiro-001.ini +++ b/scm-webapp/src/test/resources/sonia/scm/shiro-001.ini @@ -1,5 +1,6 @@ [users] trillian = secret, user +dent = secret, admin [roles] admin = * From df7b554b80ca656acd04ce4887dbd92b57c88881 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 10 Nov 2016 19:28:46 +0100 Subject: [PATCH 158/171] #873 implemented default branch repository property for git --- .../repository/spi/AbstractGitCommand.java | 47 ++++++++++++++++++- .../AbstractGitIncomingOutgoingCommand.java | 2 +- .../scm/repository/spi/GitBlameCommand.java | 2 +- .../scm/repository/spi/GitBrowseCommand.java | 16 ++----- .../scm/repository/spi/GitCatCommand.java | 16 ++----- .../scm/repository/spi/GitDiffCommand.java | 1 - .../scm/repository/spi/GitLogCommand.java | 13 +---- .../repository/spi/GitBlameCommandTest.java | 29 +++++++++++- .../repository/spi/GitBrowseCommandTest.java | 39 ++++++++++++++- .../scm/repository/spi/GitCatCommandTest.java | 21 +++++++++ .../scm/repository/spi/GitLogCommandTest.java | 35 +++++++++++++- .../spi/GitOutgoingCommandTest.java | 3 +- 12 files changed, 182 insertions(+), 42 deletions(-) diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitCommand.java index dc26777f65..9680a9136a 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitCommand.java @@ -34,11 +34,17 @@ package sonia.scm.repository.spi; //~--- non-JDK imports -------------------------------------------------------- +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Strings; import org.eclipse.jgit.lib.Repository; //~--- JDK imports ------------------------------------------------------------ import java.io.IOException; +import org.eclipse.jgit.lib.ObjectId; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.repository.GitUtil; /** * @@ -46,6 +52,14 @@ import java.io.IOException; */ public class AbstractGitCommand { + + /** + * the logger for AbstractGitCommand + */ + private static final Logger logger = LoggerFactory.getLogger(AbstractGitCommand.class); + + @VisibleForTesting + static final String PROPERTY_DEFAULT_BRANCH = "git.default-branch"; /** * Constructs ... @@ -54,7 +68,6 @@ public class AbstractGitCommand * * @param context * @param repository - * @param repositoryDirectory */ protected AbstractGitCommand(GitContext context, sonia.scm.repository.Repository repository) @@ -77,6 +90,38 @@ public class AbstractGitCommand { return context.open(); } + + protected ObjectId getCommitOrDefault(Repository gitRepository, String requestedCommit) throws IOException { + ObjectId commit; + if ( Strings.isNullOrEmpty(requestedCommit) ) { + commit = getDefaultBranch(gitRepository); + } else { + commit = gitRepository.resolve(requestedCommit); + } + return commit; + } + + protected ObjectId getBranchOrDefault(Repository gitRepository, String requestedBranch) throws IOException { + ObjectId head; + if ( Strings.isNullOrEmpty(requestedBranch) ) { + head = getDefaultBranch(gitRepository); + } else { + head = GitUtil.getBranchId(gitRepository, requestedBranch); + } + return head; + } + + protected ObjectId getDefaultBranch(Repository gitRepository) throws IOException { + ObjectId head; + String defaultBranchName = repository.getProperty(PROPERTY_DEFAULT_BRANCH); + if (!Strings.isNullOrEmpty(defaultBranchName)) { + head = GitUtil.getBranchId(gitRepository, defaultBranchName); + } else { + logger.trace("no default branch configured, use repository head as default"); + head = GitUtil.getRepositoryHead(gitRepository); + } + return head; + } //~--- fields --------------------------------------------------------------- diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitIncomingOutgoingCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitIncomingOutgoingCommand.java index d510cd3b51..e3fec1c8e8 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitIncomingOutgoingCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitIncomingOutgoingCommand.java @@ -138,7 +138,7 @@ public abstract class AbstractGitIncomingOutgoingCommand GitUtil.fetch(git, handler.getDirectory(remoteRepository), remoteRepository); - ObjectId localId = GitUtil.getRepositoryHead(git.getRepository()); + ObjectId localId = getDefaultBranch(git.getRepository()); ObjectId remoteId = null; Ref remoteBranch = getRemoteBranch(git.getRepository(), localId, diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBlameCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBlameCommand.java index a48b4b2b1e..c797fd70eb 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBlameCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBlameCommand.java @@ -124,7 +124,7 @@ public class GitBlameCommand extends AbstractGitCommand implements BlameCommand blame.setFilePath(request.getPath()); - ObjectId revId = GitUtil.getRevisionId(gr, request.getRevision()); + ObjectId revId = getCommitOrDefault(gr, request.getRevision()); blame.setStartCommit(revId); diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBrowseCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBrowseCommand.java index a2962f43c4..1af5a35d29 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBrowseCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBrowseCommand.java @@ -45,7 +45,6 @@ import org.eclipse.jgit.lib.ObjectLoader; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevTree; import org.eclipse.jgit.revwalk.RevWalk; -import org.eclipse.jgit.submodule.SubmoduleWalk; import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.treewalk.filter.AndTreeFilter; import org.eclipse.jgit.treewalk.filter.PathFilter; @@ -72,7 +71,6 @@ import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Map.Entry; /** * @@ -96,11 +94,8 @@ public class GitBrowseCommand extends AbstractGitCommand /** * Constructs ... * - * - * * @param context * @param repository - * @param repositoryDirectory */ public GitBrowseCommand(GitContext context, Repository repository) { @@ -124,18 +119,15 @@ public class GitBrowseCommand extends AbstractGitCommand public BrowserResult getBrowserResult(BrowseCommandRequest request) throws IOException, RepositoryException { - if (logger.isDebugEnabled()) - { - logger.debug("try to create browse result for {}", request); - } + logger.debug("try to create browse result for {}", request); - BrowserResult result = null; + BrowserResult result; org.eclipse.jgit.lib.Repository repo = open(); - ObjectId revId = null; + ObjectId revId; if (Util.isEmpty(request.getRevision())) { - revId = GitUtil.getRepositoryHead(repo); + revId = getDefaultBranch(repo); } else { diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitCatCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitCatCommand.java index 17dad775b1..4420b9a22a 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitCatCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitCatCommand.java @@ -34,6 +34,7 @@ package sonia.scm.repository.spi; //~--- non-JDK imports -------------------------------------------------------- +import com.google.common.base.Strings; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectLoader; @@ -78,7 +79,6 @@ public class GitCatCommand extends AbstractGitCommand implements CatCommand * * @param context * @param repository - * @param repositoryDirectory */ public GitCatCommand(GitContext context, sonia.scm.repository.Repository repository) @@ -102,17 +102,11 @@ public class GitCatCommand extends AbstractGitCommand implements CatCommand public void getCatResult(CatCommandRequest request, OutputStream output) throws IOException, RepositoryException { - if (logger.isDebugEnabled()) - { - logger.debug("try to read content for {}", request); - } - - org.eclipse.jgit.lib.Repository repo = null; - - repo = open(); - - ObjectId revId = GitUtil.getRevisionId(repo, request.getRevision()); + logger.debug("try to read content for {}", request); + org.eclipse.jgit.lib.Repository repo = open(); + + ObjectId revId = getCommitOrDefault(repo, request.getRevision()); getContent(repo, revId, request.getPath(), output); } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitDiffCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitDiffCommand.java index a8edfa8848..83977ef290 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitDiffCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitDiffCommand.java @@ -79,7 +79,6 @@ public class GitDiffCommand extends AbstractGitCommand implements DiffCommand * * @param context * @param repository - * @param repositoryDirectory */ public GitDiffCommand(GitContext context, Repository repository) { diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogCommand.java index d47dea9440..9e4e3949f1 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogCommand.java @@ -220,17 +220,8 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand AndTreeFilter.create( PathFilter.create(request.getPath()), TreeFilter.ANY_DIFF)); } - - ObjectId head = null; - - if (!Strings.isNullOrEmpty(request.getBranch())) - { - head = GitUtil.getBranchId(gr, request.getBranch()); - } - else - { - head = GitUtil.getRepositoryHead(gr); - } + + ObjectId head = getBranchOrDefault(gr, request.getBranch()); if (head != null) { diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBlameCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBlameCommandTest.java index 953372020b..88c9235373 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBlameCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBlameCommandTest.java @@ -47,12 +47,39 @@ import static org.junit.Assert.*; import java.io.IOException; /** - * + * Unit tests for {@link GitBlameCommand}. + * * @author Sebastian Sdorra */ public class GitBlameCommandTest extends AbstractGitCommandTestBase { + /** + * Tests blame command with default branch. + * + * @throws IOException + * @throws RepositoryException + */ + @Test + public void testDefaultBranch() throws IOException, RepositoryException { + // without default branch, the repository head should be used + BlameCommandRequest request = new BlameCommandRequest(); + request.setPath("a.txt"); + + BlameResult result = createCommand().getBlameResult(request); + assertNotNull(result); + assertEquals(2, result.getTotal()); + assertEquals("435df2f061add3589cb326cc64be9b9c3897ceca", result.getLine(0).getRevision()); + assertEquals("fcd0ef1831e4002ac43ea539f4094334c79ea9ec", result.getLine(1).getRevision()); + + // set default branch and test again + repository.setProperty(AbstractGitCommand.PROPERTY_DEFAULT_BRANCH, "test-branch"); + result = createCommand().getBlameResult(request); + assertNotNull(result); + assertEquals(1, result.getTotal()); + assertEquals("3f76a12f08a6ba0dc988c68b7f0b2cd190efc3c4", result.getLine(0).getRevision()); + } + /** * Method description * diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java index 3999e26f85..d1c2f6abc9 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java @@ -50,11 +50,48 @@ import java.io.IOException; import java.util.List; /** - * + * Unit tests for {@link GitBrowseCommand}. + * * @author Sebastian Sdorra */ public class GitBrowseCommandTest extends AbstractGitCommandTestBase { + + /** + * Test browse command with default branch. + * + * @throws IOException + * @throws RepositoryException + */ + @Test + public void testDefaultBranch() throws IOException, RepositoryException { + // without default branch, the repository head should be used + BrowserResult result = createCommand().getBrowserResult(new BrowseCommandRequest()); + assertNotNull(result); + + List foList = result.getFiles(); + assertNotNull(foList); + assertFalse(foList.isEmpty()); + assertEquals(4, foList.size()); + + assertEquals("a.txt", foList.get(0).getName()); + assertEquals("b.txt", foList.get(1).getName()); + assertEquals("c", foList.get(2).getName()); + assertEquals("f.txt", foList.get(3).getName()); + + // set default branch and fetch again + repository.setProperty(AbstractGitCommand.PROPERTY_DEFAULT_BRANCH, "test-branch"); + result = createCommand().getBrowserResult(new BrowseCommandRequest()); + assertNotNull(result); + + foList = result.getFiles(); + assertNotNull(foList); + assertFalse(foList.isEmpty()); + assertEquals(2, foList.size()); + + assertEquals("a.txt", foList.get(0).getName()); + assertEquals("c", foList.get(1).getName()); + } /** * Method description diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitCatCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitCatCommandTest.java index 1a44a0076c..0d7d743e5a 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitCatCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitCatCommandTest.java @@ -46,12 +46,33 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; /** + * Unit tests for {@link GitCatCommand}. + * * TODO add not found test * * @author Sebastian Sdorra */ public class GitCatCommandTest extends AbstractGitCommandTestBase { + + /** + * Tests cat command with default branch. + * + * @throws IOException + * @throws RepositoryException + */ + @Test + public void testDefaultBranch() throws IOException, RepositoryException { + // without default branch, the repository head should be used + CatCommandRequest request = new CatCommandRequest(); + request.setPath("a.txt"); + + assertEquals("a\nline for blame", execute(request)); + + // set default branch for repository and check again + repository.setProperty(AbstractGitCommand.PROPERTY_DEFAULT_BRANCH, "test-branch"); + assertEquals("a and b", execute(request)); + } /** * Method description diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java index 62c6069fba..640a28a598 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java @@ -49,14 +49,47 @@ import static org.junit.Assert.*; //~--- JDK imports ------------------------------------------------------------ import java.io.IOException; +import org.eclipse.jgit.api.errors.GitAPIException; /** - * + * Unit tests for {@link GitLogCommand}. + * * @author Sebastian Sdorra */ public class GitLogCommandTest extends AbstractGitCommandTestBase { + /** + * Tests log command with the usage of a default branch. + * + * @throws IOException + * @throws GitAPIException + * @throws RepositoryException + */ + @Test + public void testGetDefaultBranch() throws IOException, GitAPIException, RepositoryException { + // without default branch, the repository head should be used + ChangesetPagingResult result = createCommand().getChangesets(new LogCommandRequest()); + + assertNotNull(result); + assertEquals(4, result.getTotal()); + assertEquals("fcd0ef1831e4002ac43ea539f4094334c79ea9ec", result.getChangesets().get(0).getId()); + assertEquals("86a6645eceefe8b9a247db5eb16e3d89a7e6e6d1", result.getChangesets().get(1).getId()); + assertEquals("592d797cd36432e591416e8b2b98154f4f163411", result.getChangesets().get(2).getId()); + assertEquals("435df2f061add3589cb326cc64be9b9c3897ceca", result.getChangesets().get(3).getId()); + + // set default branch and fetch again + repository.setProperty(AbstractGitCommand.PROPERTY_DEFAULT_BRANCH, "test-branch"); + + result = createCommand().getChangesets(new LogCommandRequest()); + + assertNotNull(result); + assertEquals(3, result.getTotal()); + assertEquals("3f76a12f08a6ba0dc988c68b7f0b2cd190efc3c4", result.getChangesets().get(0).getId()); + assertEquals("592d797cd36432e591416e8b2b98154f4f163411", result.getChangesets().get(1).getId()); + assertEquals("435df2f061add3589cb326cc64be9b9c3897ceca", result.getChangesets().get(2).getId()); + } + /** * Method description * diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitOutgoingCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitOutgoingCommandTest.java index dc47c80291..28be6f7df0 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitOutgoingCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitOutgoingCommandTest.java @@ -51,7 +51,8 @@ import static org.junit.Assert.assertNotNull; import java.io.IOException; /** - * + * Unit tests for {@link OutgoingCommand}. + * * @author Sebastian Sdorra */ public class GitOutgoingCommandTest extends AbstractRemoteCommandTestBase From f2137bd761e0554554b925f836a946710d7a3005 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 10 Nov 2016 19:48:13 +0100 Subject: [PATCH 159/171] #873 added reusable components for branch and tag combo boxes --- scm-webapp/src/main/webapp/index.mustache | 2 + .../sonia.repository.branchcombobox.js | 65 +++++++++++++++++++ .../sonia.repository.changesetviewerpanel.js | 23 +------ .../sonia.repository.repositorybrowser.js | 40 ++---------- .../sonia.repository.tagcombobox.js | 65 +++++++++++++++++++ 5 files changed, 138 insertions(+), 57 deletions(-) create mode 100644 scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.branchcombobox.js create mode 100644 scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.tagcombobox.js diff --git a/scm-webapp/src/main/webapp/index.mustache b/scm-webapp/src/main/webapp/index.mustache index 4c2c3c913c..fec01e96ca 100644 --- a/scm-webapp/src/main/webapp/index.mustache +++ b/scm-webapp/src/main/webapp/index.mustache @@ -115,6 +115,8 @@ + + diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.branchcombobox.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.branchcombobox.js new file mode 100644 index 0000000000..9ba2575c11 --- /dev/null +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.branchcombobox.js @@ -0,0 +1,65 @@ +/* * + * 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 + * + */ + + +Sonia.repository.BranchComboBox = Ext.extend(Ext.form.ComboBox, { + + repositoryId: null, + + initComponent: function(){ + var branchStore = new Sonia.rest.JsonStore({ + proxy: new Ext.data.HttpProxy({ + url: restUrl + 'repositories/' + this.repositoryId + '/branches.json', + method: 'GET', + disableCaching: false + }), + root: 'branch', + idProperty: 'name', + fields: ['name', 'revision'] + }); + + var config = { + valueField: 'revision', + displayField: 'name', + typeAhead: false, + editable: false, + triggerAction: 'all', + store: branchStore + }; + + Ext.apply(this, Ext.apply(this.initialConfig, config)); + Sonia.repository.BranchComboBox.superclass.initComponent.apply(this, arguments); + } + +}); + +// register xtype +Ext.reg('repositoryBranchComboBox', Sonia.repository.BranchComboBox); \ No newline at end of file diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.changesetviewerpanel.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.changesetviewerpanel.js index 3e276d2c27..aa18d29e0d 100644 --- a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.changesetviewerpanel.js +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.changesetviewerpanel.js @@ -120,33 +120,14 @@ Sonia.repository.ChangesetViewerPanel = Ext.extend(Ext.Panel, { }, createTopToolbar: function(){ - var branchStore = new Sonia.rest.JsonStore({ - proxy: new Ext.data.HttpProxy({ - url: restUrl + 'repositories/' + this.repository.id + '/branches.json', - method: 'GET', - disableCaching: false - }), - root: 'branch', - idProperty: 'name', - fields: [ 'name' ], - sortInfo: { - field: 'name' - } - }); - return { xtype: 'toolbar', items: [ this.repository.name, '->', 'Branches:', ' ',{ - xtype: 'combo', - valueField: 'name', - displayField: 'name', - typeAhead: false, - editable: false, - triggerAction: 'all', - store: branchStore, + xtype: 'repositoryBranchComboBox', + repositoryId: this.repository.id, listeners: { select: { fn: this.selectBranch, diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.repositorybrowser.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.repositorybrowser.js index 7ec444222c..489dd3c06c 100644 --- a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.repositorybrowser.js +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.repositorybrowser.js @@ -159,26 +159,10 @@ Sonia.repository.RepositoryBrowser = Ext.extend(Ext.grid.GridPanel, { branches = true; - var branchStore = new Sonia.rest.JsonStore({ - proxy: new Ext.data.HttpProxy({ - url: restUrl + 'repositories/' + this.repository.id + '/branches.json', - method: 'GET', - disableCaching: false - }), - root: 'branch', - idProperty: 'name', - fields: [ 'name', 'revision' ] - }); - items.push('->','Branches:', ' ',{ id: 'branchComboBox', - xtype: 'combo', - valueField: 'revision', - displayField: 'name', - typeAhead: false, - editable: false, - triggerAction: 'all', - store: branchStore, + xtype: 'repositoryBranchComboBox', + repositoryId: this.repository.id, listeners: { select: { fn: this.selectBranch, @@ -191,17 +175,6 @@ Sonia.repository.RepositoryBrowser = Ext.extend(Ext.grid.GridPanel, { if ( type && type.supportedCommands && type.supportedCommands.indexOf('TAGS') >= 0){ - var tagStore = new Sonia.rest.JsonStore({ - proxy: new Ext.data.HttpProxy({ - url: restUrl + 'repositories/' + this.repository.id + '/tags.json', - method: 'GET', - disableCaching: false - }), - root: 'tag', - idProperty: 'name', - fields: [ 'name', 'revision' ] - }); - if (branches){ items.push(' '); } else { @@ -210,13 +183,8 @@ Sonia.repository.RepositoryBrowser = Ext.extend(Ext.grid.GridPanel, { items.push('Tags:', ' ',{ id: 'tagComboBox', - xtype: 'combo', - valueField: 'revision', - displayField: 'name', - typeAhead: false, - editable: false, - triggerAction: 'all', - store: tagStore, + xtype: 'repositoryTagComboBox', + repositoryId: this.repository.id, listeners: { select: { fn: this.selectTag, diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.tagcombobox.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.tagcombobox.js new file mode 100644 index 0000000000..0db635f8cd --- /dev/null +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.tagcombobox.js @@ -0,0 +1,65 @@ +/* * + * 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 + * + */ + + +Sonia.repository.TagComboBox = Ext.extend(Ext.form.ComboBox, { + + repositoryId: null, + + initComponent: function(){ + var tagStore = new Sonia.rest.JsonStore({ + proxy: new Ext.data.HttpProxy({ + url: restUrl + 'repositories/' + this.repositoryId + '/tags.json', + method: 'GET', + disableCaching: false + }), + root: 'tag', + idProperty: 'name', + fields: [ 'name', 'revision' ] + }); + + var config = { + valueField: 'revision', + displayField: 'name', + typeAhead: false, + editable: false, + triggerAction: 'all', + store: tagStore, + }; + + Ext.apply(this, Ext.apply(this.initialConfig, config)); + Sonia.repository.TagComboBox.superclass.initComponent.apply(this, arguments); + } + +}); + +// register xtype +Ext.reg('repositoryTagComboBox', Sonia.repository.TagComboBox); \ No newline at end of file From 0c0bdfa37618f51de908570cc7a92e3aac365a48 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 10 Nov 2016 20:54:20 +0100 Subject: [PATCH 160/171] #873 added default branch chooser to git settings --- .../main/resources/sonia/scm/git.config.js | 102 ++++++++++++++++++ .../sonia.repository.branchcombobox.js | 3 +- .../repository/sonia.repository.formpanel.js | 7 ++ .../js/repository/sonia.repository.grid.js | 25 ++--- .../sonia.repository.settingsformpanel.js | 6 ++ .../src/main/webapp/resources/js/sonia.scm.js | 18 ++++ 6 files changed, 148 insertions(+), 13 deletions(-) diff --git a/scm-plugins/scm-git-plugin/src/main/resources/sonia/scm/git.config.js b/scm-plugins/scm-git-plugin/src/main/resources/sonia/scm/git.config.js index 4a5c75caae..9e038e4dee 100644 --- a/scm-plugins/scm-git-plugin/src/main/resources/sonia/scm/git.config.js +++ b/scm-plugins/scm-git-plugin/src/main/resources/sonia/scm/git.config.js @@ -90,6 +90,93 @@ Sonia.git.ConfigPanel = Ext.extend(Sonia.config.SimpleConfigForm, { Ext.reg("gitConfigPanel", Sonia.git.ConfigPanel); +// add default branch chooser to settings panel +Sonia.git.GitSettingsFormPanel = Ext.extend(Sonia.repository.SettingsFormPanel, { + + defaultBranchText: 'Default Branch', + defaultBranchHelpText: 'The default branch which is show first on source or commit view.', + + modifyDefaultConfig: function(config){ + if (this.item) { + var position = -1; + for ( var i=0; i= 0) { + config.items.splice(position, 0, defaultBranchComboxBox); + } else { + config.items.push(defaultBranchComboxBox); + } + } + }, + + getDefaultBranch: function(item){ + if (item.properties) { + for ( var i=0; i{0}', xtype: 'repositoryExtendedInfoPanel' }); + main.registerSettingsForm('git', { + xtype: 'gitSettingsForm' + }); }); // register panel diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.branchcombobox.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.branchcombobox.js index 9ba2575c11..cddcad4552 100644 --- a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.branchcombobox.js +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.branchcombobox.js @@ -33,6 +33,7 @@ Sonia.repository.BranchComboBox = Ext.extend(Ext.form.ComboBox, { repositoryId: null, + useNameAsValue: false, initComponent: function(){ var branchStore = new Sonia.rest.JsonStore({ @@ -47,8 +48,8 @@ Sonia.repository.BranchComboBox = Ext.extend(Ext.form.ComboBox, { }); var config = { - valueField: 'revision', displayField: 'name', + valueField: this.useNameAsValue ? 'name' : 'revision', typeAhead: false, editable: false, triggerAction: 'all', diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.formpanel.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.formpanel.js index d717a77daf..66482399ac 100644 --- a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.formpanel.js +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.formpanel.js @@ -61,8 +61,15 @@ Sonia.repository.FormPanel = Ext.extend(Sonia.rest.FormPanel,{ Sonia.repository.FormPanel.superclass.initComponent.apply(this, arguments); }, + prepareUpdate: function(item) { + + }, + update: function(item){ item = Ext.apply( this.item, item ); + // allow plugins to modify item + this.prepareUpdate(item); + if ( debug ){ console.debug( 'update repository: ' + item.name ); } diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.grid.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.grid.js index 19f8fc311d..fb66b91a5a 100644 --- a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.grid.js +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.grid.js @@ -432,21 +432,22 @@ Sonia.repository.Grid = Ext.extend(Sonia.rest.Grid, { var infoPanel = main.getInfoPanel(item.type); infoPanel.item = item; + var settingsForm = main.getSettingsForm(item.type); + settingsForm.item = item; + settingsForm.onUpdate = { + fn: this.reload, + scope: this + }; + settingsForm.onCreate = { + fn: this.reload, + scope: this + }; + var panels = [infoPanel]; if ( owner ){ - panels.push({ - item: item, - xtype: 'repositorySettingsForm', - onUpdate: { - fn: this.reload, - scope: this - }, - onCreate: { - fn: this.reload, - scope: this - } - },{ + panels.push( + settingsForm, { item: item, xtype: 'repositoryPermissionsForm', listeners: { diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.settingsformpanel.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.settingsformpanel.js index 5c6b902756..2b182a5de2 100644 --- a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.settingsformpanel.js +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.settingsformpanel.js @@ -78,9 +78,15 @@ Sonia.repository.SettingsFormPanel = Ext.extend(Sonia.repository.FormPanel, { helpText: this.publicHelpText }] }; + + this.modifyDefaultConfig(config); Ext.apply(this, Ext.apply(this.initialConfig, config)); Sonia.repository.SettingsFormPanel.superclass.initComponent.apply(this, arguments); + }, + + modifyDefaultConfig: function(config){ + } }); diff --git a/scm-webapp/src/main/webapp/resources/js/sonia.scm.js b/scm-webapp/src/main/webapp/resources/js/sonia.scm.js index 4e54c3af09..6d7e6a3257 100644 --- a/scm-webapp/src/main/webapp/resources/js/sonia.scm.js +++ b/scm-webapp/src/main/webapp/resources/js/sonia.scm.js @@ -78,6 +78,7 @@ Sonia.scm.Main = Ext.extend(Ext.util.Observable, { mainTabPanel: null, infoPanels: [], + settingsForm: [], scripts: [], stylesheets: [], @@ -96,6 +97,23 @@ Sonia.scm.Main = Ext.extend(Ext.util.Observable, { this.infoPanels[type] = panel; }, + registerSettingsForm: function(type, form){ + this.settingsForm[type] = form; + }, + + getSettingsForm: function(type){ + var rp = null; + var panel = this.settingsForm[type]; + if ( ! panel ){ + rp = { + xtype: 'repositorySettingsForm' + }; + } else { + rp = Sonia.util.clone( panel ); + } + return rp; + }, + getInfoPanel: function(type){ var rp = null; var panel = this.infoPanels[type]; From 5cb32b268f979acb1b214cb249af6129572b517c Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 10 Nov 2016 21:56:27 +0100 Subject: [PATCH 161/171] #873 clear repository caches, if the git default branch has changed --- .../repository/ClearRepositoryCacheEvent.java | 64 +++++++ .../api/RepositoryServiceFactory.java | 11 ++ .../sonia/scm/repository/GitConstants.java | 49 ++++++ .../GitRepositoryModifyListener.java | 97 +++++++++++ .../repository/spi/AbstractGitCommand.java | 6 +- .../GitRepositoryModifyListenerTest.java | 163 ++++++++++++++++++ .../repository/spi/GitBlameCommandTest.java | 3 +- .../repository/spi/GitBrowseCommandTest.java | 3 +- .../scm/repository/spi/GitCatCommandTest.java | 3 +- .../scm/repository/spi/GitLogCommandTest.java | 3 +- 10 files changed, 394 insertions(+), 8 deletions(-) create mode 100644 scm-core/src/main/java/sonia/scm/repository/ClearRepositoryCacheEvent.java create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitConstants.java create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryModifyListener.java create mode 100644 scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryModifyListenerTest.java diff --git a/scm-core/src/main/java/sonia/scm/repository/ClearRepositoryCacheEvent.java b/scm-core/src/main/java/sonia/scm/repository/ClearRepositoryCacheEvent.java new file mode 100644 index 0000000000..13c228bd34 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/repository/ClearRepositoryCacheEvent.java @@ -0,0 +1,64 @@ +/** + * 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; + +import sonia.scm.event.Event; + +/** + * Event which causes clearing of repository cache. + * + * @author Sebastian Sdorra + * @since 1.50 + */ +@Event +public class ClearRepositoryCacheEvent { + + private final Repository repository; + + /** + * Constructs a new instance. + * + * @param repository repository + */ + public ClearRepositoryCacheEvent(Repository repository) { + this.repository = repository; + } + + /** + * Returns repository. + * + * @return repository + */ + public Repository getRepository() { + return repository; + } + +} diff --git a/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java b/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java index 68e7f30ba4..6af1eedc0c 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java @@ -37,6 +37,7 @@ package sonia.scm.repository.api; import com.google.common.base.Preconditions; import com.google.common.base.Strings; +import com.google.common.eventbus.Subscribe; import com.google.inject.Inject; import com.google.inject.Singleton; @@ -69,6 +70,8 @@ import sonia.scm.security.ScmSecurityException; //~--- JDK imports ------------------------------------------------------------ import java.util.Set; +import sonia.scm.event.ScmEventBus; +import sonia.scm.repository.ClearRepositoryCacheEvent; /** * The {@link RepositoryServiceFactory} is the entrypoint of the repository api. @@ -172,6 +175,9 @@ public final class RepositoryServiceFactory repositoryManager.addHook(cch); repositoryManager.addListener(cch); + + // register cache clear hook for incoming events + ScmEventBus.getInstance().register(cch); } //~--- methods -------------------------------------------------------------- @@ -345,6 +351,11 @@ public final class RepositoryServiceFactory //~--- methods ------------------------------------------------------------ + @Subscribe + public void onEvent(ClearRepositoryCacheEvent event) { + clearCaches(event.getRepository().getId()); + } + /** * Method description * diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitConstants.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitConstants.java new file mode 100644 index 0000000000..6d833577e1 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitConstants.java @@ -0,0 +1,49 @@ +/** + * 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; + +/** + * Constants for Git. + * + * @author Sebastian Sdorra + * @since 1.50 + */ +public final class GitConstants { + + /** + * Default branch repository property. + */ + public static final String PROPERTY_DEFAULT_BRANCH = "git.default-branch"; + + private GitConstants() { + } + +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryModifyListener.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryModifyListener.java new file mode 100644 index 0000000000..4309257350 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryModifyListener.java @@ -0,0 +1,97 @@ +/** + * 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; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Objects; +import com.google.common.eventbus.Subscribe; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.EagerSingleton; +import sonia.scm.HandlerEvent; +import sonia.scm.event.ScmEventBus; +import sonia.scm.plugin.ext.Extension; + +/** + * Repository listener which handles git related repository events. + * + * @author Sebastian Sdorra + * @since 1.50 + */ +@Extension +@EagerSingleton +public class GitRepositoryModifyListener { + + /** + * the logger for GitRepositoryModifyListener + */ + private static final Logger logger = LoggerFactory.getLogger(GitRepositoryModifyListener.class); + + /** + * Receives {@link RepositoryModificationEvent} and fires a {@link ClearRepositoryCacheEvent} if + * the default branch of a git repository was modified. + * + * @param event repository modification event + */ + @Subscribe + public void handleEvent(RepositoryModificationEvent event){ + Repository repository = event.getItem(); + + if ( isModifyEvent(event) && + isGitRepository(event.getItem()) && + hasDefaultBranchChanged(event.getItemBeforeModification(), repository)) + { + logger.info("git default branch of repository {} has changed, sending clear cache event", repository.getId()); + sendClearRepositoryCacheEvent(repository); + } + } + + @VisibleForTesting + protected void sendClearRepositoryCacheEvent(Repository repository) { + ScmEventBus.getInstance().post(new ClearRepositoryCacheEvent(repository)); + } + + private boolean isModifyEvent(RepositoryEvent event) { + return event.getEventType() == HandlerEvent.MODIFY; + } + + private boolean isGitRepository(Repository repository) { + return GitRepositoryHandler.TYPE_NAME.equals(repository.getType()); + } + + private boolean hasDefaultBranchChanged(Repository old, Repository current) { + return !Objects.equal( + old.getProperty(GitConstants.PROPERTY_DEFAULT_BRANCH), + current.getProperty(GitConstants.PROPERTY_DEFAULT_BRANCH) + ); + } + +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitCommand.java index 9680a9136a..d865f77e48 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitCommand.java @@ -44,6 +44,7 @@ import java.io.IOException; import org.eclipse.jgit.lib.ObjectId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sonia.scm.repository.GitConstants; import sonia.scm.repository.GitUtil; /** @@ -57,9 +58,6 @@ public class AbstractGitCommand * the logger for AbstractGitCommand */ private static final Logger logger = LoggerFactory.getLogger(AbstractGitCommand.class); - - @VisibleForTesting - static final String PROPERTY_DEFAULT_BRANCH = "git.default-branch"; /** * Constructs ... @@ -113,7 +111,7 @@ public class AbstractGitCommand protected ObjectId getDefaultBranch(Repository gitRepository) throws IOException { ObjectId head; - String defaultBranchName = repository.getProperty(PROPERTY_DEFAULT_BRANCH); + String defaultBranchName = repository.getProperty(GitConstants.PROPERTY_DEFAULT_BRANCH); if (!Strings.isNullOrEmpty(defaultBranchName)) { head = GitUtil.getBranchId(gitRepository, defaultBranchName); } else { diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryModifyListenerTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryModifyListenerTest.java new file mode 100644 index 0000000000..9f6768aeac --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryModifyListenerTest.java @@ -0,0 +1,163 @@ +/** + * 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; + +import org.junit.Test; +import static org.junit.Assert.*; +import org.junit.Before; +import sonia.scm.HandlerEvent; + +/** + * Unit tests for {@link GitRepositoryModifyListener}. + * + * @author Sebastian Sdorra + */ +public class GitRepositoryModifyListenerTest { + + private GitRepositoryModifyTestListener repositoryModifyListener; + + /** + * Set up test object. + */ + @Before + public void setUpObjectUnderTest(){ + repositoryModifyListener = new GitRepositoryModifyTestListener(); + } + + /** + * Tests happy path. + */ + @Test + public void testHandleEvent() { + Repository old = RepositoryTestData.createHeartOfGold("git"); + old.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "master"); + Repository current = RepositoryTestData.createHeartOfGold("git"); + current.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "develop"); + + RepositoryModificationEvent event = new RepositoryModificationEvent(current, old, HandlerEvent.MODIFY); + repositoryModifyListener.handleEvent(event); + + assertNotNull(repositoryModifyListener.repository); + assertSame(current, repositoryModifyListener.repository); + } + + /** + * Tests with new default branch. + */ + @Test + public void testWithNewDefaultBranch() { + Repository old = RepositoryTestData.createHeartOfGold("git"); + Repository current = RepositoryTestData.createHeartOfGold("git"); + current.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "develop"); + + RepositoryModificationEvent event = new RepositoryModificationEvent(current, old, HandlerEvent.MODIFY); + repositoryModifyListener.handleEvent(event); + + assertNotNull(repositoryModifyListener.repository); + assertSame(current, repositoryModifyListener.repository); + } + + /** + * Tests with non git repositories. + */ + @Test + public void testNonGitRepository(){ + Repository old = RepositoryTestData.createHeartOfGold("hg"); + old.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "master"); + Repository current = RepositoryTestData.createHeartOfGold("hg"); + current.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "develop"); + + RepositoryModificationEvent event = new RepositoryModificationEvent(current, old, HandlerEvent.MODIFY); + repositoryModifyListener.handleEvent(event); + + assertNull(repositoryModifyListener.repository); + } + + /** + * Tests without default branch. + */ + @Test + public void testWithoutDefaultBranch(){ + Repository old = RepositoryTestData.createHeartOfGold("git"); + Repository current = RepositoryTestData.createHeartOfGold("git"); + + RepositoryModificationEvent event = new RepositoryModificationEvent(current, old, HandlerEvent.MODIFY); + repositoryModifyListener.handleEvent(event); + + assertNull(repositoryModifyListener.repository); + } + + /** + * Tests with non modify event. + */ + @Test + public void testNonModifyEvent(){ + Repository old = RepositoryTestData.createHeartOfGold("git"); + old.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "master"); + Repository current = RepositoryTestData.createHeartOfGold("git"); + current.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "develop"); + + RepositoryModificationEvent event = new RepositoryModificationEvent(current, old, HandlerEvent.CREATE); + repositoryModifyListener.handleEvent(event); + + assertNull(repositoryModifyListener.repository); + } + + /** + * Tests with non git repositories. + */ + @Test + public void testNoModification(){ + Repository old = RepositoryTestData.createHeartOfGold("git"); + old.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "master"); + Repository current = RepositoryTestData.createHeartOfGold("git"); + current.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "master"); + + RepositoryModificationEvent event = new RepositoryModificationEvent(current, old, HandlerEvent.MODIFY); + repositoryModifyListener.handleEvent(event); + + assertNull(repositoryModifyListener.repository); + } + + private static class GitRepositoryModifyTestListener extends GitRepositoryModifyListener { + + private Repository repository; + + @Override + protected void sendClearRepositoryCacheEvent(Repository repository) { + this.repository = repository; + } + + } + + +} \ No newline at end of file diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBlameCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBlameCommandTest.java index 88c9235373..d049447d7f 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBlameCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBlameCommandTest.java @@ -45,6 +45,7 @@ import static org.junit.Assert.*; //~--- JDK imports ------------------------------------------------------------ import java.io.IOException; +import sonia.scm.repository.GitConstants; /** * Unit tests for {@link GitBlameCommand}. @@ -73,7 +74,7 @@ public class GitBlameCommandTest extends AbstractGitCommandTestBase assertEquals("fcd0ef1831e4002ac43ea539f4094334c79ea9ec", result.getLine(1).getRevision()); // set default branch and test again - repository.setProperty(AbstractGitCommand.PROPERTY_DEFAULT_BRANCH, "test-branch"); + repository.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "test-branch"); result = createCommand().getBlameResult(request); assertNotNull(result); assertEquals(1, result.getTotal()); diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java index d1c2f6abc9..727034b9ca 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java @@ -48,6 +48,7 @@ import static org.junit.Assert.*; import java.io.IOException; import java.util.List; +import sonia.scm.repository.GitConstants; /** * Unit tests for {@link GitBrowseCommand}. @@ -80,7 +81,7 @@ public class GitBrowseCommandTest extends AbstractGitCommandTestBase assertEquals("f.txt", foList.get(3).getName()); // set default branch and fetch again - repository.setProperty(AbstractGitCommand.PROPERTY_DEFAULT_BRANCH, "test-branch"); + repository.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "test-branch"); result = createCommand().getBrowserResult(new BrowseCommandRequest()); assertNotNull(result); diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitCatCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitCatCommandTest.java index 0d7d743e5a..ede6a53429 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitCatCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitCatCommandTest.java @@ -44,6 +44,7 @@ import static org.junit.Assert.*; import java.io.ByteArrayOutputStream; import java.io.IOException; +import sonia.scm.repository.GitConstants; /** * Unit tests for {@link GitCatCommand}. @@ -70,7 +71,7 @@ public class GitCatCommandTest extends AbstractGitCommandTestBase assertEquals("a\nline for blame", execute(request)); // set default branch for repository and check again - repository.setProperty(AbstractGitCommand.PROPERTY_DEFAULT_BRANCH, "test-branch"); + repository.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "test-branch"); assertEquals("a and b", execute(request)); } diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java index 640a28a598..c5af70e3a9 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java @@ -50,6 +50,7 @@ import static org.junit.Assert.*; import java.io.IOException; import org.eclipse.jgit.api.errors.GitAPIException; +import sonia.scm.repository.GitConstants; /** * Unit tests for {@link GitLogCommand}. @@ -79,7 +80,7 @@ public class GitLogCommandTest extends AbstractGitCommandTestBase assertEquals("435df2f061add3589cb326cc64be9b9c3897ceca", result.getChangesets().get(3).getId()); // set default branch and fetch again - repository.setProperty(AbstractGitCommand.PROPERTY_DEFAULT_BRANCH, "test-branch"); + repository.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "test-branch"); result = createCommand().getChangesets(new LogCommandRequest()); From 8628fd9e11df2f46b64e5c88c218fbb88eebac71 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 10 Nov 2016 22:05:24 +0100 Subject: [PATCH 162/171] refactor CacheClearHook of RepositoryServiceFactory --- .../api/RepositoryServiceFactory.java | 102 ++++++++---------- 1 file changed, 42 insertions(+), 60 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java b/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java index 6af1eedc0c..0a532b652a 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java @@ -37,6 +37,7 @@ package sonia.scm.repository.api; import com.google.common.base.Preconditions; import com.google.common.base.Strings; +import com.google.common.collect.Sets; import com.google.common.eventbus.Subscribe; import com.google.inject.Inject; import com.google.inject.Singleton; @@ -316,51 +317,60 @@ public final class RepositoryServiceFactory //~--- inner classes -------------------------------------------------------- /** - * TODO find a more elegant way - * - * - * @version Enter version here..., 12/06/16 - * @author Enter your name here... + * Hook and listener to clear all relevant repository caches. */ private static class CacheClearHook extends PostReceiveRepositoryHook implements RepositoryListener { + + private final Set> caches = Sets.newHashSet(); /** - * Constructs ... + * Constructs a new instance and collect all repository relevant + * caches from the {@link CacheManager}. * - * - * @param cacheManager + * @param cacheManager cache manager */ public CacheClearHook(CacheManager cacheManager) { - this.blameCache = - cacheManager.getCache(BlameCommandBuilder.CacheKey.class, - BlameResult.class, BlameCommandBuilder.CACHE_NAME); - this.browseCache = - cacheManager.getCache(BrowseCommandBuilder.CacheKey.class, - BrowserResult.class, BrowseCommandBuilder.CACHE_NAME); - this.logCache = cacheManager.getCache(LogCommandBuilder.CacheKey.class, - ChangesetPagingResult.class, LogCommandBuilder.CACHE_NAME); - this.tagsCache = cacheManager.getCache(TagsCommandBuilder.CacheKey.class, - Tags.class, TagsCommandBuilder.CACHE_NAME); - this.branchesCache = - cacheManager.getCache(BranchesCommandBuilder.CacheKey.class, - Branches.class, BranchesCommandBuilder.CACHE_NAME); + this.caches.add(cacheManager.getCache( + BlameCommandBuilder.CacheKey.class, + BlameResult.class, BlameCommandBuilder.CACHE_NAME) + ); + this.caches.add(cacheManager.getCache( + BrowseCommandBuilder.CacheKey.class, + BrowserResult.class, BrowseCommandBuilder.CACHE_NAME) + ); + this.caches.add(cacheManager.getCache( + LogCommandBuilder.CacheKey.class, + ChangesetPagingResult.class, LogCommandBuilder.CACHE_NAME) + ); + this.caches.add(cacheManager.getCache( + TagsCommandBuilder.CacheKey.class, + Tags.class, TagsCommandBuilder.CACHE_NAME) + ); + this.caches.add(cacheManager.getCache( + BranchesCommandBuilder.CacheKey.class, + Branches.class, BranchesCommandBuilder.CACHE_NAME) + ); } //~--- methods ------------------------------------------------------------ + /** + * Clear caches on explicit repository cache clear event. + * + * @param event clear event + */ @Subscribe public void onEvent(ClearRepositoryCacheEvent event) { clearCaches(event.getRepository().getId()); } /** - * Method description + * Clear caches on repository push. * - * - * @param event + * @param event hook event */ @Override public void onEvent(RepositoryHookEvent event) @@ -376,11 +386,10 @@ public final class RepositoryServiceFactory } /** - * Method description + * Clear caches on repository delete event. * - * - * @param repository - * @param event + * @param repository changed repository + * @param event repository event */ @Override public void onEvent(Repository repository, HandlerEvent event) @@ -390,13 +399,7 @@ public final class RepositoryServiceFactory clearCaches(repository.getId()); } } - - /** - * Method description - * - * - * @param repositoryId - */ + @SuppressWarnings("unchecked") private void clearCaches(final String repositoryId) { @@ -405,32 +408,11 @@ public final class RepositoryServiceFactory logger.debug("clear caches for repository id {}", repositoryId); } - RepositoryCacheKeyFilter filter = - new RepositoryCacheKeyFilter(repositoryId); - - blameCache.removeAll(filter); - browseCache.removeAll(filter); - logCache.removeAll(filter); - tagsCache.removeAll(filter); - branchesCache.removeAll(filter); + RepositoryCacheKeyFilter filter = new RepositoryCacheKeyFilter(repositoryId); + for (Cache cache : caches) { + cache.removeAll(filter); + } } - - //~--- fields ------------------------------------------------------------- - - /** Field description */ - private Cache blameCache; - - /** Field description */ - private Cache branchesCache; - - /** Field description */ - private Cache browseCache; - - /** Field description */ - private Cache logCache; - - /** Field description */ - private Cache tagsCache; } From d940c2e9b93f050b1343cb1e70a42973da1c6389 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 10 Nov 2016 22:14:29 +0100 Subject: [PATCH 163/171] fix release build --- .../resources/js/repository/sonia.repository.tagcombobox.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.tagcombobox.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.tagcombobox.js index 0db635f8cd..3f045a8cf6 100644 --- a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.tagcombobox.js +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.tagcombobox.js @@ -52,7 +52,7 @@ Sonia.repository.TagComboBox = Ext.extend(Ext.form.ComboBox, { typeAhead: false, editable: false, triggerAction: 'all', - store: tagStore, + store: tagStore }; Ext.apply(this, Ext.apply(this.initialConfig, config)); From 5678520b595955edb23b0ab87a245fcced07bcfd Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 24 Nov 2016 14:03:14 +0100 Subject: [PATCH 164/171] fix hidden help text on default branch configuration --- .../src/main/webapp/resources/js/override/ext.form.field.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scm-webapp/src/main/webapp/resources/js/override/ext.form.field.js b/scm-webapp/src/main/webapp/resources/js/override/ext.form.field.js index 61abb54b88..2a66bfe8b0 100644 --- a/scm-webapp/src/main/webapp/resources/js/override/ext.form.field.js +++ b/scm-webapp/src/main/webapp/resources/js/override/ext.form.field.js @@ -71,6 +71,8 @@ Ext.override(Ext.form.Field, { switch ( this.getXType() ){ case 'combo': + case 'repositoryBranchComboBox': + case 'repositoryTagComboBox': if ( this.readOnly ){ cls = 'scm-form-help-button'; } else { From 0bbea7a47a6f698c3bfed0ed98c6e2dfc528a72b Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 24 Nov 2016 14:11:36 +0100 Subject: [PATCH 165/171] [maven-release-plugin] prepare release 1.50 --- maven/pom.xml | 4 ++-- maven/scm-maven-plugin/pom.xml | 4 ++-- maven/scm-plugin-archetype/pom.xml | 4 ++-- pom.xml | 4 ++-- scm-clients/pom.xml | 6 +++--- scm-clients/scm-cli-client/pom.xml | 6 +++--- scm-clients/scm-client-api/pom.xml | 4 ++-- scm-clients/scm-client-impl/pom.xml | 8 ++++---- scm-core/pom.xml | 4 ++-- scm-dao-orientdb/pom.xml | 8 ++++---- scm-dao-xml/pom.xml | 8 ++++---- scm-plugin-backend/pom.xml | 6 +++--- scm-plugins/pom.xml | 8 ++++---- scm-plugins/scm-git-plugin/pom.xml | 6 +++--- scm-plugins/scm-hg-plugin/pom.xml | 6 +++--- scm-plugins/scm-svn-plugin/pom.xml | 6 +++--- scm-samples/pom.xml | 4 ++-- scm-samples/scm-sample-auth/pom.xml | 6 +++--- scm-samples/scm-sample-hello/pom.xml | 6 +++--- scm-server/pom.xml | 4 ++-- scm-test/pom.xml | 6 +++--- scm-webapp/pom.xml | 24 ++++++++++++------------ support/pom.xml | 4 ++-- support/scm-support-btrace/pom.xml | 6 +++--- 24 files changed, 76 insertions(+), 76 deletions(-) diff --git a/maven/pom.xml b/maven/pom.xml index 1a4ec67707..db58b6fdf2 100644 --- a/maven/pom.xml +++ b/maven/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.50-SNAPSHOT + 1.50 sonia.scm.maven scm-maven-plugins pom - 1.50-SNAPSHOT + 1.50 scm-maven-plugins diff --git a/maven/scm-maven-plugin/pom.xml b/maven/scm-maven-plugin/pom.xml index d1eefa8357..5e8f0c1746 100644 --- a/maven/scm-maven-plugin/pom.xml +++ b/maven/scm-maven-plugin/pom.xml @@ -6,12 +6,12 @@ scm-maven-plugins sonia.scm.maven - 1.50-SNAPSHOT + 1.50 sonia.scm.maven scm-maven-plugin - 1.50-SNAPSHOT + 1.50 maven-plugin scm-maven-plugin diff --git a/maven/scm-plugin-archetype/pom.xml b/maven/scm-plugin-archetype/pom.xml index b51bb677e4..9325c738e6 100644 --- a/maven/scm-plugin-archetype/pom.xml +++ b/maven/scm-plugin-archetype/pom.xml @@ -6,12 +6,12 @@ scm-maven-plugins sonia.scm.maven - 1.50-SNAPSHOT + 1.50 sonia.scm.maven scm-plugin-archetype - 1.50-SNAPSHOT + 1.50 scm-plugin-archetype diff --git a/pom.xml b/pom.xml index 9fd831b5e9..701f6087ea 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ sonia.scm scm pom - 1.50-SNAPSHOT + 1.50 The easiest way to share your Git, Mercurial and Subversion repositories over http. @@ -36,7 +36,7 @@ scm:hg:http://bitbucket.org/sdorra/scm-manager scm:hg:https://bitbucket.org/sdorra/scm-manager http://bitbucket.org/sdorra/scm-manager - HEAD + 1.50 diff --git a/scm-clients/pom.xml b/scm-clients/pom.xml index 7fca349a49..310828cdca 100644 --- a/scm-clients/pom.xml +++ b/scm-clients/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.50-SNAPSHOT + 1.50 sonia.scm.clients scm-clients pom - 1.50-SNAPSHOT + 1.50 scm-clients @@ -32,7 +32,7 @@ scm-core sonia.scm jar - 1.50-SNAPSHOT + 1.50 shiro-core diff --git a/scm-clients/scm-cli-client/pom.xml b/scm-clients/scm-cli-client/pom.xml index 9806e37230..a1da9bd220 100644 --- a/scm-clients/scm-cli-client/pom.xml +++ b/scm-clients/scm-cli-client/pom.xml @@ -6,12 +6,12 @@ scm-clients sonia.scm.clients - 1.50-SNAPSHOT + 1.50 sonia.scm.clients scm-cli-client - 1.50-SNAPSHOT + 1.50 scm-cli-client @@ -34,7 +34,7 @@ sonia.scm.clients scm-client-impl - 1.50-SNAPSHOT + 1.50 diff --git a/scm-clients/scm-client-api/pom.xml b/scm-clients/scm-client-api/pom.xml index 6f3cf0cf02..ebe35f13b8 100644 --- a/scm-clients/scm-client-api/pom.xml +++ b/scm-clients/scm-client-api/pom.xml @@ -6,13 +6,13 @@ sonia.scm.clients scm-clients - 1.50-SNAPSHOT + 1.50 sonia.scm.clients scm-client-api jar - 1.50-SNAPSHOT + 1.50 scm-client-api diff --git a/scm-clients/scm-client-impl/pom.xml b/scm-clients/scm-client-impl/pom.xml index 9a444bcbc8..50d5eb6126 100644 --- a/scm-clients/scm-client-impl/pom.xml +++ b/scm-clients/scm-client-impl/pom.xml @@ -6,13 +6,13 @@ sonia.scm.clients scm-clients - 1.50-SNAPSHOT + 1.50 sonia.scm.clients scm-client-impl jar - 1.50-SNAPSHOT + 1.50 scm-client-impl @@ -36,7 +36,7 @@ sonia.scm.clients scm-client-api - 1.50-SNAPSHOT + 1.50 @@ -70,7 +70,7 @@ sonia.scm scm-test - 1.50-SNAPSHOT + 1.50 test diff --git a/scm-core/pom.xml b/scm-core/pom.xml index 02236beae9..607702e3d7 100644 --- a/scm-core/pom.xml +++ b/scm-core/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.50-SNAPSHOT + 1.50 sonia.scm scm-core - 1.50-SNAPSHOT + 1.50 scm-core diff --git a/scm-dao-orientdb/pom.xml b/scm-dao-orientdb/pom.xml index edfb109d24..9e6c1d30d6 100644 --- a/scm-dao-orientdb/pom.xml +++ b/scm-dao-orientdb/pom.xml @@ -6,12 +6,12 @@ sonia.scm scm - 1.50-SNAPSHOT + 1.50 sonia.scm scm-dao-orientdb - 1.50-SNAPSHOT + 1.50 scm-dao-orientdb @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.50-SNAPSHOT + 1.50 @@ -52,7 +52,7 @@ sonia.scm scm-test - 1.50-SNAPSHOT + 1.50 test diff --git a/scm-dao-xml/pom.xml b/scm-dao-xml/pom.xml index 9331cd6231..e3ea3ce6ca 100644 --- a/scm-dao-xml/pom.xml +++ b/scm-dao-xml/pom.xml @@ -6,12 +6,12 @@ sonia.scm scm - 1.50-SNAPSHOT + 1.50 sonia.scm scm-dao-xml - 1.50-SNAPSHOT + 1.50 scm-dao-xml @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.50-SNAPSHOT + 1.50 @@ -34,7 +34,7 @@ sonia.scm scm-test - 1.50-SNAPSHOT + 1.50 test diff --git a/scm-plugin-backend/pom.xml b/scm-plugin-backend/pom.xml index bf29ddc57b..52044be9b2 100644 --- a/scm-plugin-backend/pom.xml +++ b/scm-plugin-backend/pom.xml @@ -6,13 +6,13 @@ scm sonia.scm - 1.50-SNAPSHOT + 1.50 sonia.scm scm-plugin-backend war - 1.50-SNAPSHOT + 1.50 ${project.artifactId} @@ -62,7 +62,7 @@ sonia.scm scm-core - 1.50-SNAPSHOT + 1.50 diff --git a/scm-plugins/pom.xml b/scm-plugins/pom.xml index 16084bb91c..2fb669187f 100644 --- a/scm-plugins/pom.xml +++ b/scm-plugins/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.50-SNAPSHOT + 1.50 sonia.scm.plugins scm-plugins pom - 1.50-SNAPSHOT + 1.50 scm-plugins @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.50-SNAPSHOT + 1.50 @@ -59,7 +59,7 @@ sonia.scm.maven scm-maven-plugin - 1.50-SNAPSHOT + 1.50 process-resources diff --git a/scm-plugins/scm-git-plugin/pom.xml b/scm-plugins/scm-git-plugin/pom.xml index 6599004b02..22052093d6 100644 --- a/scm-plugins/scm-git-plugin/pom.xml +++ b/scm-plugins/scm-git-plugin/pom.xml @@ -6,12 +6,12 @@ scm-plugins sonia.scm.plugins - 1.50-SNAPSHOT + 1.50 sonia.scm.plugins scm-git-plugin - 1.50-SNAPSHOT + 1.50 scm-git-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Git @@ -48,7 +48,7 @@ sonia.scm scm-test - 1.50-SNAPSHOT + 1.50 test diff --git a/scm-plugins/scm-hg-plugin/pom.xml b/scm-plugins/scm-hg-plugin/pom.xml index 29973aa774..b8c670f758 100644 --- a/scm-plugins/scm-hg-plugin/pom.xml +++ b/scm-plugins/scm-hg-plugin/pom.xml @@ -6,12 +6,12 @@ sonia.scm.plugins scm-plugins - 1.50-SNAPSHOT + 1.50 sonia.scm.plugins scm-hg-plugin - 1.50-SNAPSHOT + 1.50 scm-hg-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Mercurial @@ -42,7 +42,7 @@ sonia.scm scm-test - 1.50-SNAPSHOT + 1.50 test diff --git a/scm-plugins/scm-svn-plugin/pom.xml b/scm-plugins/scm-svn-plugin/pom.xml index d9a5d14228..1d3847043e 100644 --- a/scm-plugins/scm-svn-plugin/pom.xml +++ b/scm-plugins/scm-svn-plugin/pom.xml @@ -6,12 +6,12 @@ scm-plugins sonia.scm.plugins - 1.50-SNAPSHOT + 1.50 sonia.scm.plugins scm-svn-plugin - 1.50-SNAPSHOT + 1.50 scm-svn-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Subversion @@ -48,7 +48,7 @@ sonia.scm scm-test - 1.50-SNAPSHOT + 1.50 test diff --git a/scm-samples/pom.xml b/scm-samples/pom.xml index 7ec9c628b9..0ec3fa1ab4 100644 --- a/scm-samples/pom.xml +++ b/scm-samples/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.50-SNAPSHOT + 1.50 sonia.scm.samples scm-samples pom - 1.50-SNAPSHOT + 1.50 scm-samples diff --git a/scm-samples/scm-sample-auth/pom.xml b/scm-samples/scm-sample-auth/pom.xml index e46745663e..5a3b9d0661 100644 --- a/scm-samples/scm-sample-auth/pom.xml +++ b/scm-samples/scm-sample-auth/pom.xml @@ -6,12 +6,12 @@ scm-samples sonia.scm.samples - 1.50-SNAPSHOT + 1.50 sonia.scm.sample scm-sample-auth - 1.50-SNAPSHOT + 1.50 scm-sample-auth Sample Authentication Plugin https://bitbucket.org/sdorra/scm-manager @@ -28,7 +28,7 @@ sonia.scm scm-core - 1.50-SNAPSHOT + 1.50 diff --git a/scm-samples/scm-sample-hello/pom.xml b/scm-samples/scm-sample-hello/pom.xml index 3a37570356..215ac59d19 100644 --- a/scm-samples/scm-sample-hello/pom.xml +++ b/scm-samples/scm-sample-hello/pom.xml @@ -6,12 +6,12 @@ scm-samples sonia.scm.samples - 1.50-SNAPSHOT + 1.50 sonia.scm.sample scm-sample-hello - 1.50-SNAPSHOT + 1.50 scm-sample-hello A simple hello world plugin https://bitbucket.org/sdorra/scm-manager @@ -28,7 +28,7 @@ sonia.scm scm-core - 1.50-SNAPSHOT + 1.50 diff --git a/scm-server/pom.xml b/scm-server/pom.xml index 1594795597..2dfe0f6dde 100644 --- a/scm-server/pom.xml +++ b/scm-server/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.50-SNAPSHOT + 1.50 sonia.scm scm-server - 1.50-SNAPSHOT + 1.50 scm-server jar diff --git a/scm-test/pom.xml b/scm-test/pom.xml index 566c1543e8..cef0f9b14a 100644 --- a/scm-test/pom.xml +++ b/scm-test/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.50-SNAPSHOT + 1.50 sonia.scm scm-test - 1.50-SNAPSHOT + 1.50 scm-test @@ -25,7 +25,7 @@ sonia.scm scm-core - 1.50-SNAPSHOT + 1.50 diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index 5b440d07d6..e4f39a7c7a 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.50-SNAPSHOT + 1.50 sonia.scm scm-webapp war - 1.50-SNAPSHOT + 1.50 scm-webapp @@ -38,31 +38,31 @@ sonia.scm scm-core - 1.50-SNAPSHOT + 1.50 sonia.scm scm-dao-xml - 1.50-SNAPSHOT + 1.50 sonia.scm.plugins scm-hg-plugin - 1.50-SNAPSHOT + 1.50 sonia.scm.plugins scm-svn-plugin - 1.50-SNAPSHOT + 1.50 sonia.scm.plugins scm-git-plugin - 1.50-SNAPSHOT + 1.50 @@ -296,7 +296,7 @@ sonia.scm scm-test - 1.50-SNAPSHOT + 1.50 test @@ -309,7 +309,7 @@ sonia.scm.plugins scm-git-plugin - 1.50-SNAPSHOT + 1.50 tests test @@ -317,7 +317,7 @@ sonia.scm.plugins scm-hg-plugin - 1.50-SNAPSHOT + 1.50 tests test @@ -325,7 +325,7 @@ sonia.scm.plugins scm-svn-plugin - 1.50-SNAPSHOT + 1.50 tests test @@ -564,7 +564,7 @@ sonia.scm scm-dao-orientdb - 1.50-SNAPSHOT + 1.50 diff --git a/support/pom.xml b/support/pom.xml index 1c7f87f7ce..344af58431 100644 --- a/support/pom.xml +++ b/support/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.50-SNAPSHOT + 1.50 sonia.scm.support scm-support pom - 1.50-SNAPSHOT + 1.50 scm-support diff --git a/support/scm-support-btrace/pom.xml b/support/scm-support-btrace/pom.xml index 08a6d48665..175b171553 100644 --- a/support/scm-support-btrace/pom.xml +++ b/support/scm-support-btrace/pom.xml @@ -4,12 +4,12 @@ sonia.scm.support scm-support - 1.50-SNAPSHOT + 1.50 sonia.scm scm-support-btrace - 1.50-SNAPSHOT + 1.50 jar scm-support-btrace @@ -18,7 +18,7 @@ sonia.scm scm-core - 1.50-SNAPSHOT + 1.50 From c358bce1fd06dc247325315a51967c0e4eb615a4 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 24 Nov 2016 14:11:36 +0100 Subject: [PATCH 166/171] [maven-release-plugin] copy for tag 1.50 From 526d79b96d5f83c29fb5c95cb910f3fc28171a4e Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 24 Nov 2016 14:11:36 +0100 Subject: [PATCH 167/171] [maven-release-plugin] prepare for next development iteration --- maven/pom.xml | 4 ++-- maven/scm-maven-plugin/pom.xml | 4 ++-- maven/scm-plugin-archetype/pom.xml | 4 ++-- pom.xml | 4 ++-- scm-clients/pom.xml | 6 +++--- scm-clients/scm-cli-client/pom.xml | 6 +++--- scm-clients/scm-client-api/pom.xml | 4 ++-- scm-clients/scm-client-impl/pom.xml | 8 ++++---- scm-core/pom.xml | 4 ++-- scm-dao-orientdb/pom.xml | 8 ++++---- scm-dao-xml/pom.xml | 8 ++++---- scm-plugin-backend/pom.xml | 6 +++--- scm-plugins/pom.xml | 8 ++++---- scm-plugins/scm-git-plugin/pom.xml | 6 +++--- scm-plugins/scm-hg-plugin/pom.xml | 6 +++--- scm-plugins/scm-svn-plugin/pom.xml | 6 +++--- scm-samples/pom.xml | 4 ++-- scm-samples/scm-sample-auth/pom.xml | 6 +++--- scm-samples/scm-sample-hello/pom.xml | 6 +++--- scm-server/pom.xml | 4 ++-- scm-test/pom.xml | 6 +++--- scm-webapp/pom.xml | 24 ++++++++++++------------ support/pom.xml | 4 ++-- support/scm-support-btrace/pom.xml | 6 +++--- 24 files changed, 76 insertions(+), 76 deletions(-) diff --git a/maven/pom.xml b/maven/pom.xml index db58b6fdf2..2a2d159589 100644 --- a/maven/pom.xml +++ b/maven/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.50 + 1.51-SNAPSHOT sonia.scm.maven scm-maven-plugins pom - 1.50 + 1.51-SNAPSHOT scm-maven-plugins diff --git a/maven/scm-maven-plugin/pom.xml b/maven/scm-maven-plugin/pom.xml index 5e8f0c1746..9cdce7ddc2 100644 --- a/maven/scm-maven-plugin/pom.xml +++ b/maven/scm-maven-plugin/pom.xml @@ -6,12 +6,12 @@ scm-maven-plugins sonia.scm.maven - 1.50 + 1.51-SNAPSHOT sonia.scm.maven scm-maven-plugin - 1.50 + 1.51-SNAPSHOT maven-plugin scm-maven-plugin diff --git a/maven/scm-plugin-archetype/pom.xml b/maven/scm-plugin-archetype/pom.xml index 9325c738e6..ecd05d7b8d 100644 --- a/maven/scm-plugin-archetype/pom.xml +++ b/maven/scm-plugin-archetype/pom.xml @@ -6,12 +6,12 @@ scm-maven-plugins sonia.scm.maven - 1.50 + 1.51-SNAPSHOT sonia.scm.maven scm-plugin-archetype - 1.50 + 1.51-SNAPSHOT scm-plugin-archetype diff --git a/pom.xml b/pom.xml index 701f6087ea..2175f10340 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ sonia.scm scm pom - 1.50 + 1.51-SNAPSHOT The easiest way to share your Git, Mercurial and Subversion repositories over http. @@ -36,7 +36,7 @@ scm:hg:http://bitbucket.org/sdorra/scm-manager scm:hg:https://bitbucket.org/sdorra/scm-manager http://bitbucket.org/sdorra/scm-manager - 1.50 + HEAD diff --git a/scm-clients/pom.xml b/scm-clients/pom.xml index 310828cdca..8d171d3c6a 100644 --- a/scm-clients/pom.xml +++ b/scm-clients/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.50 + 1.51-SNAPSHOT sonia.scm.clients scm-clients pom - 1.50 + 1.51-SNAPSHOT scm-clients @@ -32,7 +32,7 @@ scm-core sonia.scm jar - 1.50 + 1.51-SNAPSHOT shiro-core diff --git a/scm-clients/scm-cli-client/pom.xml b/scm-clients/scm-cli-client/pom.xml index a1da9bd220..915bc119f1 100644 --- a/scm-clients/scm-cli-client/pom.xml +++ b/scm-clients/scm-cli-client/pom.xml @@ -6,12 +6,12 @@ scm-clients sonia.scm.clients - 1.50 + 1.51-SNAPSHOT sonia.scm.clients scm-cli-client - 1.50 + 1.51-SNAPSHOT scm-cli-client @@ -34,7 +34,7 @@ sonia.scm.clients scm-client-impl - 1.50 + 1.51-SNAPSHOT diff --git a/scm-clients/scm-client-api/pom.xml b/scm-clients/scm-client-api/pom.xml index ebe35f13b8..9ab3dc0561 100644 --- a/scm-clients/scm-client-api/pom.xml +++ b/scm-clients/scm-client-api/pom.xml @@ -6,13 +6,13 @@ sonia.scm.clients scm-clients - 1.50 + 1.51-SNAPSHOT sonia.scm.clients scm-client-api jar - 1.50 + 1.51-SNAPSHOT scm-client-api diff --git a/scm-clients/scm-client-impl/pom.xml b/scm-clients/scm-client-impl/pom.xml index 50d5eb6126..2d75e2ca2c 100644 --- a/scm-clients/scm-client-impl/pom.xml +++ b/scm-clients/scm-client-impl/pom.xml @@ -6,13 +6,13 @@ sonia.scm.clients scm-clients - 1.50 + 1.51-SNAPSHOT sonia.scm.clients scm-client-impl jar - 1.50 + 1.51-SNAPSHOT scm-client-impl @@ -36,7 +36,7 @@ sonia.scm.clients scm-client-api - 1.50 + 1.51-SNAPSHOT @@ -70,7 +70,7 @@ sonia.scm scm-test - 1.50 + 1.51-SNAPSHOT test diff --git a/scm-core/pom.xml b/scm-core/pom.xml index 607702e3d7..5818e3fadf 100644 --- a/scm-core/pom.xml +++ b/scm-core/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.50 + 1.51-SNAPSHOT sonia.scm scm-core - 1.50 + 1.51-SNAPSHOT scm-core diff --git a/scm-dao-orientdb/pom.xml b/scm-dao-orientdb/pom.xml index 9e6c1d30d6..afe1126321 100644 --- a/scm-dao-orientdb/pom.xml +++ b/scm-dao-orientdb/pom.xml @@ -6,12 +6,12 @@ sonia.scm scm - 1.50 + 1.51-SNAPSHOT sonia.scm scm-dao-orientdb - 1.50 + 1.51-SNAPSHOT scm-dao-orientdb @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.50 + 1.51-SNAPSHOT @@ -52,7 +52,7 @@ sonia.scm scm-test - 1.50 + 1.51-SNAPSHOT test diff --git a/scm-dao-xml/pom.xml b/scm-dao-xml/pom.xml index e3ea3ce6ca..91be016395 100644 --- a/scm-dao-xml/pom.xml +++ b/scm-dao-xml/pom.xml @@ -6,12 +6,12 @@ sonia.scm scm - 1.50 + 1.51-SNAPSHOT sonia.scm scm-dao-xml - 1.50 + 1.51-SNAPSHOT scm-dao-xml @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.50 + 1.51-SNAPSHOT @@ -34,7 +34,7 @@ sonia.scm scm-test - 1.50 + 1.51-SNAPSHOT test diff --git a/scm-plugin-backend/pom.xml b/scm-plugin-backend/pom.xml index 52044be9b2..dbb9e8abf9 100644 --- a/scm-plugin-backend/pom.xml +++ b/scm-plugin-backend/pom.xml @@ -6,13 +6,13 @@ scm sonia.scm - 1.50 + 1.51-SNAPSHOT sonia.scm scm-plugin-backend war - 1.50 + 1.51-SNAPSHOT ${project.artifactId} @@ -62,7 +62,7 @@ sonia.scm scm-core - 1.50 + 1.51-SNAPSHOT diff --git a/scm-plugins/pom.xml b/scm-plugins/pom.xml index 2fb669187f..21ef2ff9ad 100644 --- a/scm-plugins/pom.xml +++ b/scm-plugins/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.50 + 1.51-SNAPSHOT sonia.scm.plugins scm-plugins pom - 1.50 + 1.51-SNAPSHOT scm-plugins @@ -26,7 +26,7 @@ sonia.scm scm-core - 1.50 + 1.51-SNAPSHOT @@ -59,7 +59,7 @@ sonia.scm.maven scm-maven-plugin - 1.50 + 1.51-SNAPSHOT process-resources diff --git a/scm-plugins/scm-git-plugin/pom.xml b/scm-plugins/scm-git-plugin/pom.xml index 22052093d6..9828e73fbb 100644 --- a/scm-plugins/scm-git-plugin/pom.xml +++ b/scm-plugins/scm-git-plugin/pom.xml @@ -6,12 +6,12 @@ scm-plugins sonia.scm.plugins - 1.50 + 1.51-SNAPSHOT sonia.scm.plugins scm-git-plugin - 1.50 + 1.51-SNAPSHOT scm-git-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Git @@ -48,7 +48,7 @@ sonia.scm scm-test - 1.50 + 1.51-SNAPSHOT test diff --git a/scm-plugins/scm-hg-plugin/pom.xml b/scm-plugins/scm-hg-plugin/pom.xml index b8c670f758..aba8b7e9f1 100644 --- a/scm-plugins/scm-hg-plugin/pom.xml +++ b/scm-plugins/scm-hg-plugin/pom.xml @@ -6,12 +6,12 @@ sonia.scm.plugins scm-plugins - 1.50 + 1.51-SNAPSHOT sonia.scm.plugins scm-hg-plugin - 1.50 + 1.51-SNAPSHOT scm-hg-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Mercurial @@ -42,7 +42,7 @@ sonia.scm scm-test - 1.50 + 1.51-SNAPSHOT test diff --git a/scm-plugins/scm-svn-plugin/pom.xml b/scm-plugins/scm-svn-plugin/pom.xml index 1d3847043e..a3df44cf88 100644 --- a/scm-plugins/scm-svn-plugin/pom.xml +++ b/scm-plugins/scm-svn-plugin/pom.xml @@ -6,12 +6,12 @@ scm-plugins sonia.scm.plugins - 1.50 + 1.51-SNAPSHOT sonia.scm.plugins scm-svn-plugin - 1.50 + 1.51-SNAPSHOT scm-svn-plugin https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Subversion @@ -48,7 +48,7 @@ sonia.scm scm-test - 1.50 + 1.51-SNAPSHOT test diff --git a/scm-samples/pom.xml b/scm-samples/pom.xml index 0ec3fa1ab4..7cab4076df 100644 --- a/scm-samples/pom.xml +++ b/scm-samples/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.50 + 1.51-SNAPSHOT sonia.scm.samples scm-samples pom - 1.50 + 1.51-SNAPSHOT scm-samples diff --git a/scm-samples/scm-sample-auth/pom.xml b/scm-samples/scm-sample-auth/pom.xml index 5a3b9d0661..88735a12b3 100644 --- a/scm-samples/scm-sample-auth/pom.xml +++ b/scm-samples/scm-sample-auth/pom.xml @@ -6,12 +6,12 @@ scm-samples sonia.scm.samples - 1.50 + 1.51-SNAPSHOT sonia.scm.sample scm-sample-auth - 1.50 + 1.51-SNAPSHOT scm-sample-auth Sample Authentication Plugin https://bitbucket.org/sdorra/scm-manager @@ -28,7 +28,7 @@ sonia.scm scm-core - 1.50 + 1.51-SNAPSHOT diff --git a/scm-samples/scm-sample-hello/pom.xml b/scm-samples/scm-sample-hello/pom.xml index 215ac59d19..aa56099793 100644 --- a/scm-samples/scm-sample-hello/pom.xml +++ b/scm-samples/scm-sample-hello/pom.xml @@ -6,12 +6,12 @@ scm-samples sonia.scm.samples - 1.50 + 1.51-SNAPSHOT sonia.scm.sample scm-sample-hello - 1.50 + 1.51-SNAPSHOT scm-sample-hello A simple hello world plugin https://bitbucket.org/sdorra/scm-manager @@ -28,7 +28,7 @@ sonia.scm scm-core - 1.50 + 1.51-SNAPSHOT diff --git a/scm-server/pom.xml b/scm-server/pom.xml index 2dfe0f6dde..70c39dc31c 100644 --- a/scm-server/pom.xml +++ b/scm-server/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.50 + 1.51-SNAPSHOT sonia.scm scm-server - 1.50 + 1.51-SNAPSHOT scm-server jar diff --git a/scm-test/pom.xml b/scm-test/pom.xml index cef0f9b14a..e710891aed 100644 --- a/scm-test/pom.xml +++ b/scm-test/pom.xml @@ -6,12 +6,12 @@ scm sonia.scm - 1.50 + 1.51-SNAPSHOT sonia.scm scm-test - 1.50 + 1.51-SNAPSHOT scm-test @@ -25,7 +25,7 @@ sonia.scm scm-core - 1.50 + 1.51-SNAPSHOT diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index e4f39a7c7a..a8106de7eb 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.50 + 1.51-SNAPSHOT sonia.scm scm-webapp war - 1.50 + 1.51-SNAPSHOT scm-webapp @@ -38,31 +38,31 @@ sonia.scm scm-core - 1.50 + 1.51-SNAPSHOT sonia.scm scm-dao-xml - 1.50 + 1.51-SNAPSHOT sonia.scm.plugins scm-hg-plugin - 1.50 + 1.51-SNAPSHOT sonia.scm.plugins scm-svn-plugin - 1.50 + 1.51-SNAPSHOT sonia.scm.plugins scm-git-plugin - 1.50 + 1.51-SNAPSHOT @@ -296,7 +296,7 @@ sonia.scm scm-test - 1.50 + 1.51-SNAPSHOT test @@ -309,7 +309,7 @@ sonia.scm.plugins scm-git-plugin - 1.50 + 1.51-SNAPSHOT tests test @@ -317,7 +317,7 @@ sonia.scm.plugins scm-hg-plugin - 1.50 + 1.51-SNAPSHOT tests test @@ -325,7 +325,7 @@ sonia.scm.plugins scm-svn-plugin - 1.50 + 1.51-SNAPSHOT tests test @@ -564,7 +564,7 @@ sonia.scm scm-dao-orientdb - 1.50 + 1.51-SNAPSHOT diff --git a/support/pom.xml b/support/pom.xml index 344af58431..c74f0eb9b0 100644 --- a/support/pom.xml +++ b/support/pom.xml @@ -6,13 +6,13 @@ sonia.scm scm - 1.50 + 1.51-SNAPSHOT sonia.scm.support scm-support pom - 1.50 + 1.51-SNAPSHOT scm-support diff --git a/support/scm-support-btrace/pom.xml b/support/scm-support-btrace/pom.xml index 175b171553..0f0b170631 100644 --- a/support/scm-support-btrace/pom.xml +++ b/support/scm-support-btrace/pom.xml @@ -4,12 +4,12 @@ sonia.scm.support scm-support - 1.50 + 1.51-SNAPSHOT sonia.scm scm-support-btrace - 1.50 + 1.51-SNAPSHOT jar scm-support-btrace @@ -18,7 +18,7 @@ sonia.scm scm-core - 1.50 + 1.51-SNAPSHOT From a546246fc11e560fb0cce648e07919bc3b858e79 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 29 Nov 2016 20:11:03 +0100 Subject: [PATCH 168/171] fix wrong key usage during encoding in DefaultCipherHandler, see issue #887 --- .../scm/security/DefaultCipherHandler.java | 2 +- .../security/DefaultCipherHandlerTest.java | 63 +++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 scm-core/src/test/java/sonia/scm/security/DefaultCipherHandlerTest.java diff --git a/scm-core/src/main/java/sonia/scm/security/DefaultCipherHandler.java b/scm-core/src/main/java/sonia/scm/security/DefaultCipherHandler.java index 4b82563687..0edc77009c 100644 --- a/scm-core/src/main/java/sonia/scm/security/DefaultCipherHandler.java +++ b/scm-core/src/main/java/sonia/scm/security/DefaultCipherHandler.java @@ -251,7 +251,7 @@ public class DefaultCipherHandler implements CipherHandler random.nextBytes(salt); IvParameterSpec iv = new IvParameterSpec(salt); - SecretKey secretKey = buildSecretKey(key); + SecretKey secretKey = buildSecretKey(plainKey); javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(CIPHER_TYPE); cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, secretKey, iv); diff --git a/scm-core/src/test/java/sonia/scm/security/DefaultCipherHandlerTest.java b/scm-core/src/test/java/sonia/scm/security/DefaultCipherHandlerTest.java new file mode 100644 index 0000000000..95414d5f71 --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/security/DefaultCipherHandlerTest.java @@ -0,0 +1,63 @@ +/** + * 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.security; + +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * Unit tests for {@link DefaultCipherHandler}. + * + * @author Sebastian Sdorra + */ +public class DefaultCipherHandlerTest { + + /** + * Test encode and decode method with a separate key. + */ + @Test + public void testEncodeDecodeWithSeparateKey(){ + char[] key = "testkey".toCharArray(); + DefaultCipherHandler cipher = new DefaultCipherHandler("somekey"); + assertEquals("hallo123", cipher.decode(key, cipher.encode(key, "hallo123"))); + } + + /** + * Test encode and decode method with the default key. + */ + @Test + public void testEncodeDecodeWithDefaultKey() { + DefaultCipherHandler cipher = new DefaultCipherHandler("testkey"); + assertEquals("hallo123", cipher.decode(cipher.encode("hallo123"))); + } + +} \ No newline at end of file From 634061a91db5376aeb290dd83bc52340af3fb383 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 29 Nov 2016 20:36:11 +0100 Subject: [PATCH 169/171] refactoring DefaultCipherHandler and added javadoc to CipherHandler --- .../sonia/scm/security/CipherHandler.java | 17 +- .../scm/security/DefaultCipherHandler.java | 212 +++++------------- 2 files changed, 64 insertions(+), 165 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/security/CipherHandler.java b/scm-core/src/main/java/sonia/scm/security/CipherHandler.java index 8463f4a4e3..a27c4b4e28 100644 --- a/scm-core/src/main/java/sonia/scm/security/CipherHandler.java +++ b/scm-core/src/main/java/sonia/scm/security/CipherHandler.java @@ -35,7 +35,8 @@ package sonia.scm.security; /** - * + * Encrypts and decrypts string values. + * * @author Sebastian Sdorra * @since 1.7 */ @@ -43,22 +44,20 @@ public interface CipherHandler { /** - * Method description + * Decrypts the given value. * + * @param value encrypted value * - * @param value - * - * @return + * @return decrypted value */ public String decode(String value); /** - * Method description + * Encrypts the given value. * + * @param value plain text value to encrypt. * - * @param value - * - * @return + * @return encrypted value */ public String encode(String value); } diff --git a/scm-core/src/main/java/sonia/scm/security/DefaultCipherHandler.java b/scm-core/src/main/java/sonia/scm/security/DefaultCipherHandler.java index 0edc77009c..ddb8a699a7 100644 --- a/scm-core/src/main/java/sonia/scm/security/DefaultCipherHandler.java +++ b/scm-core/src/main/java/sonia/scm/security/DefaultCipherHandler.java @@ -53,7 +53,7 @@ import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; -import java.io.UnsupportedEncodingException; +import java.security.GeneralSecurityException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -66,32 +66,32 @@ import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; /** - * + * Default implementation of the {@link CipherHandler}, which uses AES for + * encryption and decryption. + * * @author Sebastian Sdorra * @since 1.7 */ -public class DefaultCipherHandler implements CipherHandler -{ +public class DefaultCipherHandler implements CipherHandler { - /** Field description */ + /** used cipher type */ public static final String CIPHER_TYPE = "AES/CTR/PKCS5PADDING"; - /** Field description */ + /** digest type for key generation */ public static final String DIGEST_TYPE = "SHA-512"; - /** Field description */ + /** string encoding */ public static final String ENCODING = "UTF-8"; - /** Field description */ + /** default key length */ public static final int KEY_LENGTH = 16; - /** Field description */ + /** default salt length */ public static final int SALT_LENGTH = 16; - /** Field description */ - private static final String CIPHERKEY_FILENAME = ".cipherkey"; + @VisibleForTesting + static final String CIPHERKEY_FILENAME = ".cipherkey"; - /** Field description */ private static final char[] KEY_BASE = new char[] { '1', '4', '7', '3', 'F', '2', '1', 'E', '-', 'C', '4', 'C', '4', '-', '4', @@ -99,96 +99,72 @@ public class DefaultCipherHandler implements CipherHandler 'E', 'C', '7', '7', '2', 'E' }; - /** Field description */ private static final String KEY_TYPE = "AES"; /** the logger for DefaultCipherHandler */ - private static final Logger logger = - LoggerFactory.getLogger(DefaultCipherHandler.class); - - //~--- constructors --------------------------------------------------------- - + private static final Logger logger = LoggerFactory.getLogger(DefaultCipherHandler.class); + + private final SecureRandom random = new SecureRandom(); + + private final char[] key; + /** * Constructs a new DefaultCipherHandler. Note this constructor is only for * unit tests. * - * - * @param key + * @param key default encryption key * * @since 1.38 */ @VisibleForTesting - protected DefaultCipherHandler(String key) - { + protected DefaultCipherHandler(String key) { this.key = key.toCharArray(); } /** - * Constructs ... - * - * - * @param context - * @param keyGenerator - * + * Constructs a new instance and reads the default key from the scm home directory, + * if the key file does not exists it will be generated with the {@link KeyGenerator}. * + * @param context SCM-Manager context provider + * @param keyGenerator key generator for default key generation */ - public DefaultCipherHandler(SCMContextProvider context, - KeyGenerator keyGenerator) - { + public DefaultCipherHandler(SCMContextProvider context, KeyGenerator keyGenerator) { File configDirectory = new File(context.getBaseDirectory(), "config"); IOUtil.mkdirs(configDirectory); - cipherKeyFile = new File(configDirectory, CIPHERKEY_FILENAME); + File cipherKeyFile = new File(configDirectory, CIPHERKEY_FILENAME); - try - { - if (cipherKeyFile.exists()) - { - loadKey(); - } - else - { + try { + if (cipherKeyFile.exists()) { + key = loadKey(cipherKeyFile); + } else { key = keyGenerator.createKey().toCharArray(); - storeKey(); + storeKey(cipherKeyFile); } - } - catch (IOException ex) - { + } catch (IOException ex) { throw new CipherException("could not create CipherHandler", ex); } } //~--- methods -------------------------------------------------------------- - /** - * Method description - * - * - * @param value - * - * @return - */ @Override - public String decode(String value) - { + public String decode(String value) { return decode(key, value); } /** - * Method description + * Decodes the given value with the provided key. * + * @param plainKey key which is used for decoding + * @param value encrypted value * - * @param plainKey - * @param value - * - * @return + * @return decrypted value */ - public String decode(char[] plainKey, String value) - { + public String decode(char[] plainKey, String value) { String result = null; - try - { + try { byte[] encodedInput = Base64.decode(value); byte[] salt = new byte[SALT_LENGTH]; byte[] encoded = new byte[encodedInput.length - SALT_LENGTH]; @@ -206,46 +182,29 @@ public class DefaultCipherHandler implements CipherHandler byte[] decoded = cipher.doFinal(encoded); result = new String(decoded, ENCODING); - } - catch (Exception ex) - { - logger.error("could not decode string", ex); - - throw new CipherException(ex); + } catch (IOException | GeneralSecurityException ex) { + throw new CipherException("could not decode string", ex); } return result; } - /** - * Method description - * - * - * @param value - * - * @return - */ @Override - public String encode(String value) - { + public String encode(String value) { return encode(key, value); } /** - * Method description + * Encrypts the given value with the provided key. * + * @param plainKey key which is used for encoding + * @param value plain text value to encrypt * - * @param plainKey - * @param value - * - * @return + * @return encrypted value */ - public String encode(char[] plainKey, String value) - { + public String encode(char[] plainKey, String value) { String res = null; - - try - { + try { byte[] salt = new byte[SALT_LENGTH]; random.nextBytes(salt); @@ -264,31 +223,14 @@ public class DefaultCipherHandler implements CipherHandler System.arraycopy(encodedInput, 0, result, SALT_LENGTH, result.length - SALT_LENGTH); res = new String(Base64.encode(result), ENCODING); - } - catch (Exception ex) - { - logger.error("could not encode string", ex); - - throw new CipherException(ex); + } catch (IOException | GeneralSecurityException ex) { + throw new CipherException("could not encode string", ex); } return res; } - /** - * Method description - * - * - * @param plainKey - * - * @return - * - * @throws NoSuchAlgorithmException - * @throws UnsupportedEncodingException - */ - private SecretKey buildSecretKey(char[] plainKey) - throws UnsupportedEncodingException, NoSuchAlgorithmException - { + private SecretKey buildSecretKey(char[] plainKey) throws IOException, NoSuchAlgorithmException { byte[] raw = new String(plainKey).getBytes(ENCODING); MessageDigest digest = MessageDigest.getInstance(DIGEST_TYPE); @@ -298,60 +240,18 @@ public class DefaultCipherHandler implements CipherHandler return new SecretKeySpec(raw, KEY_TYPE); } - /** - * Method description - * - * - * @throws IOException - */ - private void loadKey() throws IOException - { - BufferedReader reader = null; - - try - { - reader = new BufferedReader(new FileReader(cipherKeyFile)); - + private char[] loadKey(File cipherKeyFile) throws IOException { + try (BufferedReader reader = new BufferedReader(new FileReader(cipherKeyFile))) { String line = reader.readLine(); - key = decode(KEY_BASE, line).toCharArray(); - } - finally - { - IOUtil.close(reader); + return decode(KEY_BASE, line).toCharArray(); } } - /** - * Method description - * - * - * @throws FileNotFoundException - */ - private void storeKey() throws FileNotFoundException - { + private void storeKey(File cipherKeyFile) throws FileNotFoundException { String storeKey = encode(KEY_BASE, new String(key)); - PrintWriter output = null; - - try - { - output = new PrintWriter(cipherKeyFile); + try (PrintWriter output = new PrintWriter(cipherKeyFile)) { output.write(storeKey); } - finally - { - IOUtil.close(output); - } } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private File cipherKeyFile; - - /** Field description */ - private char[] key = null; - - /** Field description */ - private SecureRandom random = new SecureRandom(); } From 0a47d5946a4bc72b6536d56f062ccf580e1b39a6 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 29 Nov 2016 20:46:30 +0100 Subject: [PATCH 170/171] added unit test for storing and loading of default cipher key --- .../security/DefaultCipherHandlerTest.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/scm-core/src/test/java/sonia/scm/security/DefaultCipherHandlerTest.java b/scm-core/src/test/java/sonia/scm/security/DefaultCipherHandlerTest.java index 95414d5f71..1fe78191de 100644 --- a/scm-core/src/test/java/sonia/scm/security/DefaultCipherHandlerTest.java +++ b/scm-core/src/test/java/sonia/scm/security/DefaultCipherHandlerTest.java @@ -31,15 +31,62 @@ package sonia.scm.security; +import java.io.File; +import java.io.IOException; import org.junit.Test; import static org.junit.Assert.*; +import org.junit.Rule; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import static org.mockito.Mockito.*; +import org.mockito.runners.MockitoJUnitRunner; +import sonia.scm.SCMContextProvider; /** * Unit tests for {@link DefaultCipherHandler}. * * @author Sebastian Sdorra */ +@RunWith(MockitoJUnitRunner.class) public class DefaultCipherHandlerTest { + + @Mock + private SCMContextProvider context; + + @Mock + private KeyGenerator keyGenerator; + + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder(); + + /** + * Tests loading and storing default key. + * + * @throws IOException + */ + @Test + public void testLoadingAndStoringDefaultKey() throws IOException { + File baseDirectory = tempFolder.newFolder(); + when(context.getBaseDirectory()).thenReturn(baseDirectory); + when(keyGenerator.createKey()).thenReturn("secret"); + + DefaultCipherHandler cipher = new DefaultCipherHandler(context, keyGenerator); + File configDirectory = new File(baseDirectory, "config"); + assertTrue(new File(configDirectory, DefaultCipherHandler.CIPHERKEY_FILENAME).exists()); + + // plain text for assertion + String plain = "hallo123"; + + // encrypt value with new generated key + String encrypted = cipher.encode(plain); + + // load key from disk + cipher = new DefaultCipherHandler(context, keyGenerator); + + // decrypt with loaded key + assertEquals(plain, cipher.decode(encrypted)); + } /** * Test encode and decode method with a separate key. From 70bd1ea2272bd1f43117e05cf87135fe299e9e69 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 6 Dec 2016 19:24:47 +0100 Subject: [PATCH 171/171] close branch issue-887