mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-02-28 01:10:49 +01:00
merge with branch 1.x
This commit is contained in:
17
.editorconfig
Normal file
17
.editorconfig
Normal file
@@ -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
|
||||
@@ -1,6 +1,7 @@
|
||||
# netbeans temp & private files
|
||||
/?target/
|
||||
nbactions.*\.xml
|
||||
/?nbproject/
|
||||
nb-configuration\.xml
|
||||
# MacOS X Files
|
||||
\.DS_Store$
|
||||
@@ -25,3 +26,5 @@ Desktop DF$
|
||||
# idea files
|
||||
\.iml
|
||||
\.idea$
|
||||
# jrebel
|
||||
rebel.xml
|
||||
|
||||
35
pom.xml
35
pom.xml
@@ -262,7 +262,7 @@
|
||||
<link>http://download.oracle.com/javase/6/docs/api/</link>
|
||||
<link>http://download.oracle.com/docs/cd/E17802_01/products/products/servlet/2.5/docs/servlet-2_5-mr2/</link>
|
||||
<link>http://jersey.java.net/nonav/apidocs/${jersey.version}/jersey/</link>
|
||||
<link>http://google-guice.googlecode.com/svn/tags/3.0/javadoc/</link>
|
||||
<link>https://google.github.io/guice/api-docs/${guice.version}/javadoc</link>
|
||||
<link>http://www.slf4j.org/api/</link>
|
||||
<link>http://shiro.apache.org/static/current/apidocs/</link>
|
||||
</links>
|
||||
@@ -274,7 +274,7 @@
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>2.2.1</version>
|
||||
</plugin>
|
||||
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-clean-plugin</artifactId>
|
||||
@@ -419,16 +419,6 @@
|
||||
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>JDK7</id>
|
||||
<activation>
|
||||
<jdk>1.7</jdk>
|
||||
</activation>
|
||||
<properties>
|
||||
<jdk.classifier>-jdk7</jdk.classifier>
|
||||
</properties>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>APIviz</id>
|
||||
<activation>
|
||||
@@ -449,8 +439,8 @@
|
||||
<doclet>org.jboss.apiviz.APIviz</doclet>
|
||||
<docletArtifact>
|
||||
<groupId>org.jboss.apiviz</groupId>
|
||||
<artifactId>apiviz${jdk.classifier}</artifactId>
|
||||
<version>1.3.1.GA</version>
|
||||
<artifactId>apiviz</artifactId>
|
||||
<version>1.3.2.GA</version>
|
||||
</docletArtifact>
|
||||
<additionalparam>
|
||||
-sourceclasspath ${project.build.outputDirectory}
|
||||
@@ -484,40 +474,41 @@
|
||||
|
||||
<properties>
|
||||
<!-- test libraries -->
|
||||
<mokito.version>1.10.8</mokito.version>
|
||||
<mokito.version>1.10.19</mokito.version>
|
||||
<hamcrest.version>1.3</hamcrest.version>
|
||||
<junit.version>4.11</junit.version>
|
||||
<junit.version>4.12</junit.version>
|
||||
|
||||
<!-- logging libraries -->
|
||||
<slf4j.version>1.7.7</slf4j.version>
|
||||
<logback.version>1.1.2</logback.version>
|
||||
<slf4j.version>1.7.21</slf4j.version>
|
||||
<logback.version>1.1.7</logback.version>
|
||||
<servlet.version>3.0.1</servlet.version>
|
||||
<guice.version>4.0</guice.version>
|
||||
<jersey.version>1.18.2</jersey.version>
|
||||
<jersey.version>1.19.1</jersey.version>
|
||||
|
||||
<!-- event bus -->
|
||||
<legman.version>1.2.0</legman.version>
|
||||
|
||||
<!-- webserver -->
|
||||
<jetty.version>9.2.10.v20150310</jetty.version>
|
||||
<jetty.maven.version>9.2.10.v20150310</jetty.maven.version>
|
||||
|
||||
<!-- security libraries -->
|
||||
<ssp.version>1.0.0-SNAPSHOT</ssp.version>
|
||||
<shiro.version>1.4.0-RC2</shiro.version>
|
||||
|
||||
<!-- repostitory libraries -->
|
||||
<jgit.version>3.4.1.201406201815-r</jgit.version>
|
||||
<jgit.version>4.4.0.201606070830-r-scm1</jgit.version>
|
||||
<svnkit.version>1.8.5-scm3</svnkit.version>
|
||||
|
||||
<!-- util libraries -->
|
||||
<guava.version>16.0.1</guava.version>
|
||||
<quartz.version>2.2.3</quartz.version>
|
||||
|
||||
<!-- build properties -->
|
||||
<project.build.javaLevel>1.8</project.build.javaLevel>
|
||||
<project.test.javaLevel>1.8</project.test.javaLevel>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<netbeans.hint.license>SCM-BSD</netbeans.hint.license>
|
||||
<jdk.classifier />
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
|
||||
@@ -168,7 +168,7 @@
|
||||
<plugin>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-maven-plugin</artifactId>
|
||||
<version>${jetty.version}</version>
|
||||
<version>${jetty.maven.version}</version>
|
||||
<configuration>
|
||||
<stopPort>8085</stopPort>
|
||||
<stopKey>STOP</stopKey>
|
||||
|
||||
@@ -35,4 +35,4 @@
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
|
||||
@@ -66,6 +66,7 @@ public class FileObjectWrapper
|
||||
String revision, FileObject file)
|
||||
{
|
||||
this.repositoryBrowser = repositoryBrowser;
|
||||
this.revision = revision;
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@
|
||||
<plugin>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-maven-plugin</artifactId>
|
||||
<version>${jetty.version}</version>
|
||||
<version>${jetty.maven.version}</version>
|
||||
<configuration>
|
||||
<stopPort>8085</stopPort>
|
||||
<stopKey>STOP</stopKey>
|
||||
@@ -224,4 +224,4 @@
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
|
||||
@@ -164,7 +164,7 @@
|
||||
<link>http://download.oracle.com/javase/6/docs/api/</link>
|
||||
<link>http://download.oracle.com/docs/cd/E17802_01/products/products/servlet/2.5/docs/servlet-2_5-mr2/</link>
|
||||
<link>http://jersey.java.net/nonav/apidocs/${jersey.version}/jersey/</link>
|
||||
<link>http://google-guice.googlecode.com/svn/tags/${guice.version}/javadoc/</link>
|
||||
<link>https://google.github.io/guice/api-docs/${guice.version}/javadoc</link>
|
||||
<link>http://www.slf4j.org/api/</link>
|
||||
<link>http://shiro.apache.org/static/${shiro.version}/apidocs/</link>
|
||||
</links>
|
||||
|
||||
@@ -96,7 +96,7 @@ public class BasicContextProvider implements SCMContextProvider
|
||||
version = loadVersion();
|
||||
stage = loadProjectStage();
|
||||
}
|
||||
catch (Throwable ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.startupError = ex;
|
||||
|
||||
|
||||
@@ -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.HandlerEvent;
|
||||
|
||||
/**
|
||||
* Extension to the {@link ModificationHandlerEvent}.
|
||||
*
|
||||
* @param <T> type of changed item
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.48
|
||||
*/
|
||||
public interface ModificationHandlerEvent<T> extends HandlerEvent<T>
|
||||
{
|
||||
/**
|
||||
* Returns item, before it was modified.
|
||||
*
|
||||
* @return item before modification
|
||||
*/
|
||||
public T getItemBeforeModification();
|
||||
}
|
||||
@@ -134,6 +134,7 @@ public class ScmConfiguration
|
||||
this.skipFailedAuthenticators = other.skipFailedAuthenticators;
|
||||
this.loginAttemptLimit = other.loginAttemptLimit;
|
||||
this.loginAttemptLimitTimeout = other.loginAttemptLimitTimeout;
|
||||
this.enabledXsrfProtection = other.enabledXsrfProtection;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
@@ -325,6 +326,19 @@ public class ScmConfiguration
|
||||
return disableGroupingGrid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the cookie xsrf protection is enabled.
|
||||
*
|
||||
* @see <a href="https://goo.gl/s67xO3">Issue 793</a>
|
||||
* @return {@code true} if the cookie xsrf protection is enabled
|
||||
*
|
||||
* @since 1.47
|
||||
*/
|
||||
public boolean isEnabledXsrfProtection()
|
||||
{
|
||||
return enabledXsrfProtection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if proxy is enabled.
|
||||
*
|
||||
@@ -611,6 +625,19 @@ public class ScmConfiguration
|
||||
this.skipFailedAuthenticators = skipFailedAuthenticators;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set {@code true} to enable xsrf cookie protection.
|
||||
*
|
||||
* @param enabledXsrfProtection {@code true} to enable xsrf protection
|
||||
* @see <a href="https://goo.gl/s67xO3">Issue 793</a>
|
||||
*
|
||||
* @since 1.47
|
||||
*/
|
||||
public void setEnabledXsrfProtection(boolean enabledXsrfProtection)
|
||||
{
|
||||
this.enabledXsrfProtection = enabledXsrfProtection;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
@@ -700,4 +727,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;
|
||||
}
|
||||
|
||||
@@ -48,18 +48,18 @@ public abstract class AbstractGroupManager implements GroupManager
|
||||
{
|
||||
|
||||
/**
|
||||
* Send a {@link GroupEvent} to the {@link ScmEventBus}.
|
||||
* Creates a {@link GroupEvent} and send it to the {@link ScmEventBus}.
|
||||
*
|
||||
* @param event type of change event
|
||||
* @param group group that has changed
|
||||
*/
|
||||
protected void fireEvent(HandlerEventType event, Group group)
|
||||
{
|
||||
ScmEventBus.getInstance().post(new GroupEvent(event, group));
|
||||
fireEvent(new GroupEvent(event, group));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a {@link GroupEvent} to the {@link ScmEventBus}.
|
||||
* Creates a {@link GroupModificationEvent} and send it to the {@link ScmEventBus}.
|
||||
*
|
||||
* @param event type of change event
|
||||
* @param group group that has changed
|
||||
@@ -67,6 +67,16 @@ public abstract class AbstractGroupManager implements GroupManager
|
||||
*/
|
||||
protected void fireEvent(HandlerEventType event, Group group, Group oldGroup)
|
||||
{
|
||||
ScmEventBus.getInstance().post(new GroupEvent(event, group, oldGroup));
|
||||
fireEvent(new GroupModificationEvent(event, group, oldGroup));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a {@link GroupEvent} to the {@link ScmEventBus}.
|
||||
*
|
||||
* @param event group event
|
||||
*/
|
||||
protected void fireEvent(GroupEvent event)
|
||||
{
|
||||
ScmEventBus.getInstance().post(event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ import sonia.scm.event.Event;
|
||||
* @since 1.23
|
||||
*/
|
||||
@Event
|
||||
public final class GroupEvent extends AbstractHandlerEvent<Group>
|
||||
public class GroupEvent extends AbstractHandlerEvent<Group>
|
||||
{
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.HandlerEventType;
|
||||
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<Group>
|
||||
{
|
||||
|
||||
private final Group itemBeforeModification;
|
||||
|
||||
/**
|
||||
* Constructs a new {@link GroupModificationEvent}.
|
||||
*
|
||||
* @param eventType type of event
|
||||
* @param item changed group
|
||||
* @param itemBeforeModification changed group before it was modified
|
||||
*/
|
||||
public GroupModificationEvent(HandlerEventType eventType, Group item, Group itemBeforeModification)
|
||||
{
|
||||
super(eventType, item);
|
||||
this.itemBeforeModification = itemBeforeModification;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group getItemBeforeModification()
|
||||
{
|
||||
return itemBeforeModification;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
|
||||
202
scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpClient.java
Normal file
202
scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpClient.java
Normal file
@@ -0,0 +1,202 @@
|
||||
/**
|
||||
* 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.
|
||||
* <p> </p>
|
||||
* <b>Http GET example:</b>
|
||||
*
|
||||
* <pre><code>
|
||||
* AdvancedHttpResponse response = client.get("https://www.scm-manager.org")
|
||||
* .decodeGZip(true)
|
||||
* .request();
|
||||
*
|
||||
* System.out.println(response.contentAsString());
|
||||
* </code></pre>
|
||||
*
|
||||
* <p> </p>
|
||||
* <b>Http POST example:</b>
|
||||
*
|
||||
* <pre><code>
|
||||
* 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");
|
||||
* }
|
||||
* </code></pre>
|
||||
*
|
||||
* @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
|
||||
* {@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. <strong>Note:</strong> 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);
|
||||
}
|
||||
}
|
||||
@@ -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<AdvancedHttpRequest>
|
||||
{
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
/**
|
||||
* 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<AdvancedHttpRequestWithBody>
|
||||
{
|
||||
|
||||
/**
|
||||
* 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. <strong>Note:</strong> 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
/**
|
||||
* 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.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
|
||||
{
|
||||
|
||||
/**
|
||||
* 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<String, String> 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.
|
||||
*
|
||||
*
|
||||
* @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 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the response content from json to the given type.
|
||||
*
|
||||
* @param <T> 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> T contentFromJson(Class<T> type) throws IOException
|
||||
{
|
||||
return contentTransformed(type, ContentType.JSON);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the response content from xml to the given type.
|
||||
*
|
||||
* @param <T> 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> T contentFromXml(Class<T> 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 <T> 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> T contentTransformed(Class<T> 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 <T> 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> T contentTransformed(Class<T> 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 ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 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 {@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);
|
||||
}
|
||||
}
|
||||
409
scm-core/src/main/java/sonia/scm/net/ahc/BaseHttpRequest.java
Normal file
409
scm-core/src/main/java/sonia/scm/net/ahc/BaseHttpRequest.java
Normal file
@@ -0,0 +1,409 @@
|
||||
/**
|
||||
* 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.LinkedHashMultimap;
|
||||
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 <T> request implementation
|
||||
*
|
||||
* @since 1.46
|
||||
*/
|
||||
public abstract class BaseHttpRequest<T extends 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<? extends Object> 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<? extends Object> 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<String, String> 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<String, String> headers = LinkedHashMultimap.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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
69
scm-core/src/main/java/sonia/scm/net/ahc/Content.java
Normal file
69
scm-core/src/main/java/sonia/scm/net/ahc/Content.java
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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 <T> type of result object
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public <T> T unmarshall(Class<T> type, ByteSource content);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
52
scm-core/src/main/java/sonia/scm/net/ahc/ContentType.java
Normal file
52
scm-core/src/main/java/sonia/scm/net/ahc/ContentType.java
Normal file
@@ -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() {}
|
||||
}
|
||||
109
scm-core/src/main/java/sonia/scm/net/ahc/FileContent.java
Normal file
109
scm-core/src/main/java/sonia/scm/net/ahc/FileContent.java
Normal file
@@ -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;
|
||||
}
|
||||
139
scm-core/src/main/java/sonia/scm/net/ahc/FormContentBuilder.java
Normal file
139
scm-core/src/main/java/sonia/scm/net/ahc/FormContentBuilder.java
Normal file
@@ -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<? extends Object> 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;
|
||||
}
|
||||
64
scm-core/src/main/java/sonia/scm/net/ahc/HttpMethod.java
Normal file
64
scm-core/src/main/java/sonia/scm/net/ahc/HttpMethod.java
Normal file
@@ -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() {}
|
||||
}
|
||||
109
scm-core/src/main/java/sonia/scm/net/ahc/RawContent.java
Normal file
109
scm-core/src/main/java/sonia/scm/net/ahc/RawContent.java
Normal file
@@ -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;
|
||||
}
|
||||
58
scm-core/src/main/java/sonia/scm/net/ahc/StringContent.java
Normal file
58
scm-core/src/main/java/sonia/scm/net/ahc/StringContent.java
Normal file
@@ -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));
|
||||
}
|
||||
}
|
||||
36
scm-core/src/main/java/sonia/scm/net/ahc/package-info.java
Normal file
36
scm-core/src/main/java/sonia/scm/net/ahc/package-info.java
Normal file
@@ -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;
|
||||
@@ -80,7 +80,7 @@ public abstract class AbstractRepositoryManager implements RepositoryManager
|
||||
protected void fireEvent(HandlerEventType event, Repository repository,
|
||||
Repository oldRepository)
|
||||
{
|
||||
ScmEventBus.getInstance().post(new RepositoryEvent(event, repository,
|
||||
ScmEventBus.getInstance().post(new RepositoryModificationEvent(event, repository,
|
||||
oldRepository));
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,12 +35,10 @@ package sonia.scm.repository;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
import sonia.scm.HandlerEventType;
|
||||
import sonia.scm.event.AbstractHandlerEvent;
|
||||
import sonia.scm.event.Event;
|
||||
import sonia.scm.event.HandlerEvent;
|
||||
|
||||
/**
|
||||
* The RepositoryEvent is fired if a {@link Repository} object changes.
|
||||
@@ -49,7 +47,7 @@ import sonia.scm.event.HandlerEvent;
|
||||
* @since 1.23
|
||||
*/
|
||||
@Event
|
||||
public final class RepositoryEvent extends AbstractHandlerEvent<Repository>
|
||||
public class RepositoryEvent extends AbstractHandlerEvent<Repository>
|
||||
{
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.HandlerEventType;
|
||||
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<Repository>
|
||||
{
|
||||
|
||||
private final Repository itemBeforeModification;
|
||||
|
||||
/**
|
||||
* Constructs a new {@link RepositoryModificationEvent}.
|
||||
*
|
||||
* @param eventType event type
|
||||
* @param item changed repository
|
||||
* @param itemBeforeModification repository before it was modified
|
||||
*/
|
||||
public RepositoryModificationEvent(HandlerEventType eventType, Repository item, Repository itemBeforeModification)
|
||||
{
|
||||
super(eventType, item);
|
||||
this.itemBeforeModification = itemBeforeModification;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Repository getItemBeforeModification()
|
||||
{
|
||||
return itemBeforeModification;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<String> getCreatedOrModified();
|
||||
|
||||
/**
|
||||
* Returns the list deleted or closed branch names.
|
||||
*
|
||||
* @return list of deleted or closed branches
|
||||
*/
|
||||
public List<String> getDeletedOrClosed();
|
||||
}
|
||||
@@ -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 and tags.
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.33
|
||||
@@ -78,12 +79,59 @@ 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 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
|
||||
* {@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 +159,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 +189,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;
|
||||
}
|
||||
|
||||
@@ -37,4 +37,30 @@ 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,
|
||||
|
||||
/**
|
||||
* Hook tag provider
|
||||
*
|
||||
* @since 1.50
|
||||
*/
|
||||
TAG_PROVIDER;
|
||||
}
|
||||
|
||||
@@ -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<Tag> getCreatedTags();
|
||||
|
||||
/**
|
||||
* Return all tags which are deleted during the current hook.
|
||||
*
|
||||
* @return all deleted tags of current hook
|
||||
*/
|
||||
public List<Tag> getDeletedTags();
|
||||
}
|
||||
@@ -40,6 +40,7 @@ import com.github.legman.Subscribe;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
@@ -50,11 +51,6 @@ import sonia.scm.HandlerEventType;
|
||||
import sonia.scm.cache.Cache;
|
||||
import sonia.scm.cache.CacheManager;
|
||||
import sonia.scm.config.ScmConfiguration;
|
||||
import sonia.scm.event.ScmEventBus;
|
||||
import sonia.scm.repository.BlameResult;
|
||||
import sonia.scm.repository.Branches;
|
||||
import sonia.scm.repository.BrowserResult;
|
||||
import sonia.scm.repository.ChangesetPagingResult;
|
||||
import sonia.scm.repository.PostReceiveRepositoryHookEvent;
|
||||
import sonia.scm.repository.PreProcessorUtil;
|
||||
import sonia.scm.repository.Repository;
|
||||
@@ -63,7 +59,6 @@ import sonia.scm.repository.RepositoryEvent;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.repository.RepositoryNotFoundException;
|
||||
import sonia.scm.repository.RepositoryPermissions;
|
||||
import sonia.scm.repository.Tags;
|
||||
import sonia.scm.repository.spi.RepositoryServiceProvider;
|
||||
import sonia.scm.repository.spi.RepositoryServiceResolver;
|
||||
import sonia.scm.security.ScmSecurityException;
|
||||
@@ -71,6 +66,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.
|
||||
@@ -285,38 +282,44 @@ 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
|
||||
{
|
||||
|
||||
private final Set<Cache<?, ?>> 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.CACHE_NAME);
|
||||
this.browseCache = cacheManager.getCache(BrowseCommandBuilder.CACHE_NAME);
|
||||
this.logCache = cacheManager.getCache(LogCommandBuilder.CACHE_NAME);
|
||||
this.tagsCache = cacheManager.getCache(TagsCommandBuilder.CACHE_NAME);
|
||||
this.branchesCache =
|
||||
cacheManager.getCache(BranchesCommandBuilder.CACHE_NAME);
|
||||
this.caches.add(cacheManager.getCache(BlameCommandBuilder.CACHE_NAME));
|
||||
this.caches.add(cacheManager.getCache(BrowseCommandBuilder.CACHE_NAME));
|
||||
this.caches.add(cacheManager.getCache(LogCommandBuilder.CACHE_NAME));
|
||||
this.caches.add(cacheManager.getCache(TagsCommandBuilder.CACHE_NAME));
|
||||
this.caches.add(cacheManager.getCache(BranchesCommandBuilder.CACHE_NAME));
|
||||
}
|
||||
|
||||
//~--- methods ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
* Clear caches on explicit repository cache clear event.
|
||||
*
|
||||
* @param event clear event
|
||||
*/
|
||||
@Subscribe
|
||||
public void onEvent(ClearRepositoryCacheEvent event) {
|
||||
clearCaches(event.getRepository().getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear caches on repository push.
|
||||
*
|
||||
*
|
||||
* @param event
|
||||
* @param event hook event
|
||||
*/
|
||||
@Subscribe(referenceType = ReferenceType.STRONG)
|
||||
public void onEvent(PostReceiveRepositoryHookEvent event)
|
||||
@@ -332,11 +335,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
|
||||
*/
|
||||
@Subscribe(referenceType = ReferenceType.STRONG)
|
||||
public void onEvent(RepositoryEvent event)
|
||||
@@ -346,13 +348,7 @@ public final class RepositoryServiceFactory
|
||||
clearCaches(event.getItem().getId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param repositoryId
|
||||
*/
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void clearCaches(final String repositoryId)
|
||||
{
|
||||
@@ -361,32 +357,11 @@ public final class RepositoryServiceFactory
|
||||
logger.debug("clear caches for repository id {}", repositoryId);
|
||||
}
|
||||
|
||||
RepositoryCacheKeyPredicate filter =
|
||||
new RepositoryCacheKeyPredicate(repositoryId);
|
||||
|
||||
blameCache.removeAll(filter);
|
||||
browseCache.removeAll(filter);
|
||||
logCache.removeAll(filter);
|
||||
tagsCache.removeAll(filter);
|
||||
branchesCache.removeAll(filter);
|
||||
RepositoryCacheKeyPredicate predicate = new RepositoryCacheKeyPredicate(repositoryId);
|
||||
caches.forEach((cache) -> {
|
||||
cache.removeAll(predicate);
|
||||
});
|
||||
}
|
||||
|
||||
//~--- fields -------------------------------------------------------------
|
||||
|
||||
/** blame cache */
|
||||
private final Cache<BlameCommandBuilder.CacheKey, BlameResult> blameCache;
|
||||
|
||||
/** branches cache */
|
||||
private final Cache<BranchesCommandBuilder.CacheKey, Branches> branchesCache;
|
||||
|
||||
/** browse cache */
|
||||
private final Cache<BrowseCommandBuilder.CacheKey, BrowserResult> browseCache;
|
||||
|
||||
/** log cache */
|
||||
private final Cache<LogCommandBuilder.CacheKey, ChangesetPagingResult> logCache;
|
||||
|
||||
/** tags cache */
|
||||
private final Cache<TagsCommandBuilder.CacheKey, Tags> tagsCache;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -33,16 +33,20 @@ 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;
|
||||
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
|
||||
@@ -51,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()
|
||||
{
|
||||
@@ -70,7 +74,7 @@ public abstract class HookContextProvider
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
* Mark client connection as disconnected.
|
||||
*
|
||||
*/
|
||||
final void handleClientDisconnect()
|
||||
@@ -81,32 +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<HookFeature> 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
@@ -115,6 +139,5 @@ public abstract class HookContextProvider
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private boolean clientDisconnected = false;
|
||||
}
|
||||
|
||||
65
scm-core/src/main/java/sonia/scm/schedule/Scheduler.java
Normal file
65
scm-core/src/main/java/sonia/scm/schedule/Scheduler.java
Normal file
@@ -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.
|
||||
* <strong>Note:</strong> 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<? extends Runnable> runnable);
|
||||
|
||||
}
|
||||
47
scm-core/src/main/java/sonia/scm/schedule/Task.java
Normal file
47
scm-core/src/main/java/sonia/scm/schedule/Task.java
Normal file
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,52 +182,35 @@ 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);
|
||||
|
||||
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);
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -56,17 +56,28 @@ public abstract class AbstractUserManager implements UserManager
|
||||
*/
|
||||
protected void fireEvent(HandlerEventType event, User user, User oldUser)
|
||||
{
|
||||
ScmEventBus.getInstance().post(new UserEvent(event, user, oldUser));
|
||||
fireEvent(new UserModificationEvent(event, user, oldUser));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(HandlerEventType event, User user)
|
||||
{
|
||||
ScmEventBus.getInstance().post(new UserEvent(event, user));
|
||||
fireEvent(new UserEvent(event, user));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a {@link UserEvent} to the {@link ScmEventBus}.
|
||||
*
|
||||
* @param event user event
|
||||
* @since 1.48
|
||||
*/
|
||||
protected void fireEvent(UserEvent event)
|
||||
{
|
||||
ScmEventBus.getInstance().post(event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ import sonia.scm.event.Event;
|
||||
* @since 1.23
|
||||
*/
|
||||
@Event
|
||||
public final class UserEvent extends AbstractHandlerEvent<User>
|
||||
public class UserEvent extends AbstractHandlerEvent<User>
|
||||
{
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.HandlerEventType;
|
||||
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<User>
|
||||
{
|
||||
|
||||
private final User itemBeforeModification;
|
||||
|
||||
/**
|
||||
* Constructs a new {@link UserModificationEvent}.
|
||||
*
|
||||
* @param eventType type of event
|
||||
* @param item changed user
|
||||
* @param itemBeforeModification changed user before it was modified
|
||||
*/
|
||||
public UserModificationEvent(HandlerEventType eventType, User item, User itemBeforeModification)
|
||||
{
|
||||
super(eventType, item);
|
||||
this.itemBeforeModification = itemBeforeModification;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getItemBeforeModification()
|
||||
{
|
||||
return itemBeforeModification;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
@@ -96,6 +98,12 @@ public final class HttpUtil
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public static final String HEADER_AUTHORIZATION = "Authorization";
|
||||
|
||||
/**
|
||||
* content-length header
|
||||
* @since 1.46
|
||||
*/
|
||||
public static final String HEADER_CONTENT_LENGTH = "Content-Length";
|
||||
|
||||
/**
|
||||
* location header
|
||||
@@ -115,6 +123,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
|
||||
@@ -315,8 +341,26 @@ public final class HttpUtil
|
||||
}
|
||||
|
||||
/**
|
||||
* Url decode.
|
||||
* Creates the value for the content-disposition attachment header. The method
|
||||
* creates the filename as specified in rfc6266.
|
||||
*
|
||||
* @param name attachment name
|
||||
* @see <a href="http://tools.ietf.org/html/rfc6266#section-5">rfc6266 section 5</a>
|
||||
* @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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Url decode.
|
||||
*
|
||||
* @param value value to decode
|
||||
*
|
||||
@@ -578,21 +622,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 <a href="https://goo.gl/PvGQyH">Issue 748</a>
|
||||
* @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))
|
||||
{
|
||||
@@ -622,6 +676,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.
|
||||
*
|
||||
@@ -776,6 +848,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.
|
||||
*
|
||||
@@ -790,4 +877,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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
257
scm-core/src/main/java/sonia/scm/web/UserAgent.java
Normal file
257
scm-core/src/main/java/sonia/scm/web/UserAgent.java
Normal file
@@ -0,0 +1,257 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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 <s.sdorra@gmail.com>
|
||||
* @since 1.45
|
||||
*/
|
||||
public final class UserAgent
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs a new user agent
|
||||
*
|
||||
*
|
||||
* @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 --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the {@link Builder} for the UserAgent.
|
||||
*
|
||||
*
|
||||
* @param name name of the UserAgent
|
||||
*
|
||||
* @return builder for UserAgent
|
||||
*/
|
||||
public static Builder builder(String name)
|
||||
{
|
||||
return new Builder(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@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);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hashCode(name, browser, basicAuthenticationCharset);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
//J-
|
||||
return Objects.toStringHelper(this)
|
||||
.add("name", name)
|
||||
.add("browser", browser)
|
||||
.add("basicAuthenticationCharset", basicAuthenticationCharset)
|
||||
.toString();
|
||||
//J+
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the {@link Charset}, which is used to decode the basic
|
||||
* authentication header.
|
||||
*
|
||||
* @return {@link Charset} for basic authentication
|
||||
*/
|
||||
public Charset getBasicAuthenticationCharset()
|
||||
{
|
||||
return basicAuthenticationCharset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of UserAgent.
|
||||
*
|
||||
*
|
||||
* @return name of UserAgent
|
||||
*/
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if UserAgent is a browser.
|
||||
*
|
||||
*
|
||||
* @return {@code true} if UserAgent is a browser
|
||||
*/
|
||||
public boolean isBrowser()
|
||||
{
|
||||
return browser;
|
||||
}
|
||||
|
||||
//~--- inner classes --------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Builder class for {@link UserAgent}.
|
||||
*/
|
||||
public static class Builder
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs a new UserAgent builder.
|
||||
*
|
||||
*
|
||||
* @param name name of the UserAgent
|
||||
*/
|
||||
public Builder(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
//~--- methods ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Sets {@link Charset} which is used to decode the basic authentication.
|
||||
*
|
||||
*
|
||||
* @param basicAuthenticationCharset charset for basic authentication
|
||||
*
|
||||
* @return {@code this}
|
||||
*/
|
||||
public Builder basicAuthenticationCharset(
|
||||
Charset basicAuthenticationCharset)
|
||||
{
|
||||
this.basicAuthenticationCharset =
|
||||
checkNotNull(basicAuthenticationCharset);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to {@code true} if the {@link UserAgent} is a browser.
|
||||
*
|
||||
*
|
||||
* @param browser {@code true} for a browser
|
||||
*
|
||||
* @return {@code this}
|
||||
*/
|
||||
public Builder browser(boolean browser)
|
||||
{
|
||||
this.browser = browser;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the {@link UserAgent}.
|
||||
*
|
||||
*
|
||||
* @return new {@link UserAgent}
|
||||
*/
|
||||
public UserAgent build()
|
||||
{
|
||||
return new UserAgent(name, browser, basicAuthenticationCharset);
|
||||
}
|
||||
|
||||
//~--- fields -------------------------------------------------------------
|
||||
|
||||
/** name of UserAgent */
|
||||
private final String name;
|
||||
|
||||
/** indicator for browsers */
|
||||
private boolean browser = true;
|
||||
|
||||
/** basic authentication charset */
|
||||
private Charset basicAuthenticationCharset = Charsets.ISO_8859_1;
|
||||
}
|
||||
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** basic authentication charset */
|
||||
private final Charset basicAuthenticationCharset;
|
||||
|
||||
/** indicator for browsers */
|
||||
private final boolean browser;
|
||||
|
||||
/** name of UserAgent */
|
||||
private final String name;
|
||||
}
|
||||
157
scm-core/src/main/java/sonia/scm/web/UserAgentParser.java
Normal file
157
scm-core/src/main/java/sonia/scm/web/UserAgentParser.java
Normal file
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Parser for User-Agent header. The UserAgentParser parses the User-Agent
|
||||
* header and returns a {@link UserAgent} object.
|
||||
*
|
||||
* @author Sebastian Sdorra <s.sdorra@gmail.com>
|
||||
* @since 1.45
|
||||
*/
|
||||
@Singleton
|
||||
public final class UserAgentParser
|
||||
{
|
||||
|
||||
/** name of the cache */
|
||||
@VisibleForTesting
|
||||
static final String CACHE_NAME = "sonia.scm.user-agent";
|
||||
|
||||
/** unknown UserAgent */
|
||||
@VisibleForTesting
|
||||
static final UserAgent UNKNOWN = UserAgent.builder("UNKNOWN").build();
|
||||
|
||||
/** logger */
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(UserAgentParser.class);
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs a new UserAgentParser.
|
||||
*
|
||||
*
|
||||
* @param providers set of providers
|
||||
* @param cacheManager cache manager
|
||||
*/
|
||||
@Inject
|
||||
public UserAgentParser(Set<UserAgentProvider> providers,
|
||||
CacheManager cacheManager)
|
||||
{
|
||||
this.providers = providers;
|
||||
this.cache = cacheManager.getCache(CACHE_NAME);
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Extracts the User-Agent header and returns an {@link UserAgent} object.
|
||||
*
|
||||
*
|
||||
* @param request http request
|
||||
*
|
||||
* @return {@link UserAgent} object
|
||||
*/
|
||||
public UserAgent parse(HttpServletRequest request)
|
||||
{
|
||||
return parse(request.getHeader(HttpUtil.HEADER_USERAGENT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the User-Agent header and returns a {@link UserAgent} object.
|
||||
*
|
||||
*
|
||||
* @param userAgent User-Agent header
|
||||
*
|
||||
* @return {@link UserAgent} object
|
||||
*/
|
||||
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 ---------------------------------------------------------------
|
||||
|
||||
/** cache for parsed UserAgents */
|
||||
private final Cache<String, UserAgent> cache;
|
||||
|
||||
/** set of providers */
|
||||
private final Set<UserAgentProvider> providers;
|
||||
}
|
||||
58
scm-core/src/main/java/sonia/scm/web/UserAgentProvider.java
Normal file
58
scm-core/src/main/java/sonia/scm/web/UserAgentProvider.java
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Provider to parse User-Agent header and returns an {@link UserAgent} object.
|
||||
* The {@link UserAgentProvider} is used by the {@link UserAgentParser}.
|
||||
*
|
||||
* @author Sebastian Sdorra <s.sdorra@gmail.com>
|
||||
* @since 1.45
|
||||
*/
|
||||
@ExtensionPoint(multi = true)
|
||||
public interface UserAgentProvider
|
||||
{
|
||||
|
||||
/**
|
||||
* Parses the User-Agent header and returns a {@link UserAgent} object.
|
||||
*
|
||||
*
|
||||
* @param userAgentString User-Agent header
|
||||
*
|
||||
* @return {@link UserAgent} object
|
||||
*/
|
||||
public UserAgent parseUserAgent(String userAgentString);
|
||||
}
|
||||
@@ -93,9 +93,7 @@ public class AuthenticationFilter extends HttpFilter
|
||||
* @param tokenGenerators web token generators
|
||||
*/
|
||||
@Inject
|
||||
public AuthenticationFilter(ScmConfiguration configuration,
|
||||
Set<WebTokenGenerator> tokenGenerators)
|
||||
{
|
||||
public AuthenticationFilter(ScmConfiguration configuration, Set<WebTokenGenerator> tokenGenerators) {
|
||||
this.configuration = configuration;
|
||||
this.tokenGenerators = tokenGenerators;
|
||||
}
|
||||
@@ -106,7 +104,6 @@ public class AuthenticationFilter extends HttpFilter
|
||||
* Handles authentication, if a one of the {@link WebTokenGenerator} returns
|
||||
* an {@link AuthenticationToken}.
|
||||
*
|
||||
*
|
||||
* @param request servlet request
|
||||
* @param response servlet response
|
||||
* @param chain filter chain
|
||||
@@ -222,7 +219,6 @@ public class AuthenticationFilter extends HttpFilter
|
||||
private AuthenticationToken createToken(HttpServletRequest request)
|
||||
{
|
||||
AuthenticationToken token = null;
|
||||
|
||||
for (WebTokenGenerator generator : tokenGenerators)
|
||||
{
|
||||
token = generator.createToken(request);
|
||||
|
||||
@@ -144,33 +144,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);
|
||||
}
|
||||
@@ -193,12 +184,7 @@ public abstract class PermissionFilter extends HttpFilter
|
||||
}
|
||||
catch (ScmSecurityException | AuthorizationException 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ import javax.servlet.http.HttpServletRequestWrapper;
|
||||
public class SecurityHttpServletRequestWrapper extends HttpServletRequestWrapper
|
||||
{
|
||||
|
||||
/**
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
@@ -61,9 +61,6 @@ public class SecurityHttpServletRequestWrapper extends HttpServletRequestWrapper
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String getRemoteUser()
|
||||
{
|
||||
|
||||
@@ -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.net.ahc;
|
||||
|
||||
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 {
|
||||
|
||||
@Mock(answer = Answers.CALLS_REAL_METHODS)
|
||||
private AdvancedHttpClient client;
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMethod()
|
||||
{
|
||||
AdvancedHttpRequestWithBody request = client.method("PROPFIND", URL);
|
||||
assertEquals(URL, request.getUrl());
|
||||
assertEquals("PROPFIND", request.getMethod());
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* 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.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;
|
||||
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;
|
||||
|
||||
@Mock
|
||||
private ContentTransformer transformer;
|
||||
|
||||
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 testXmlContent() throws IOException{
|
||||
when(ahc.createTransformer(String.class, ContentType.XML)).thenReturn(transformer);
|
||||
when(transformer.marshall("<root />")).thenReturn(ByteSource.wrap("<root></root>".getBytes(Charsets.UTF_8)));
|
||||
Content content = request.xmlContent("<root />").getContent();
|
||||
assertThat(content, instanceOf(ByteSourceContent.class));
|
||||
ByteSourceContent bsc = (ByteSourceContent) content;
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
bsc.process(baos);
|
||||
assertEquals("<root></root>", 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()
|
||||
{
|
||||
assertEquals(AdvancedHttpRequestWithBody.class, request.self().getClass());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
/**
|
||||
* 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.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;
|
||||
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;
|
||||
|
||||
@Mock
|
||||
private ContentTransformer transformer;
|
||||
|
||||
@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 testContentWithoutByteSource() throws IOException
|
||||
{
|
||||
assertNull(response.content());
|
||||
}
|
||||
|
||||
@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 testContentAsStingWithoutByteSource() throws IOException
|
||||
{
|
||||
assertNull(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 testContentAsReaderWithoutByteSource() throws IOException
|
||||
{
|
||||
assertNull(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));
|
||||
}
|
||||
|
||||
@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("<root />".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("<root></root>");
|
||||
String c = response.contentFromXml(String.class);
|
||||
assertEquals("<root></root>", c);
|
||||
}
|
||||
|
||||
@Test(expected = ContentTransformerException.class)
|
||||
public void testContentTransformedWithoutHeader() throws IOException{
|
||||
Multimap<String,String> map = LinkedHashMultimap.create();
|
||||
when(response.getHeaders()).thenReturn(map);
|
||||
response.contentTransformed(String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContentTransformedFromHeader() throws IOException{
|
||||
Multimap<String,String> 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
|
||||
{
|
||||
Multimap<String,String> 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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<AdvancedHttpRequest> 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<String,String> 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<? extends Object> i1 = Lists.newArrayList("b");
|
||||
Iterable<? extends Object> 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<String> values = request.getHeaders().get("a");
|
||||
assertThat(values, contains("b", "c", "d"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequest() throws IOException{
|
||||
request.request();
|
||||
verify(ahc).request(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuilderMethods(){
|
||||
Iterable<? extends Object> 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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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"));
|
||||
}
|
||||
|
||||
}
|
||||
116
scm-core/src/test/java/sonia/scm/net/ahc/FileContentTest.java
Normal file
116
scm-core/src/test/java/sonia/scm/net/ahc/FileContentTest.java
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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<? extends Object> 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<String> captor = ArgumentCaptor.forClass(String.class);
|
||||
verify(request).stringContent(captor.capture());
|
||||
assertEquals(content, captor.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
72
scm-core/src/test/java/sonia/scm/net/ahc/RawContentTest.java
Normal file
72
scm-core/src/test/java/sonia/scm/net/ahc/RawContentTest.java
Normal file
@@ -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"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<Changeset> 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<Changeset> 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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<HookFeature> 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()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* 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.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.
|
||||
*/
|
||||
@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")));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
187
scm-core/src/test/java/sonia/scm/web/UserAgentParserTest.java
Normal file
187
scm-core/src/test/java/sonia/scm/web/UserAgentParserTest.java
Normal file
@@ -0,0 +1,187 @@
|
||||
/**
|
||||
* 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.junit.Assert.*;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra <sebastian.sdorra@triology.de>
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@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<UserAgentProvider> providers = Sets.newHashSet(provider1, provider2);
|
||||
when(cacheManager.getCache(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;
|
||||
}
|
||||
@@ -142,4 +142,4 @@
|
||||
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
|
||||
@@ -37,12 +37,6 @@
|
||||
<artifactId>org.eclipse.jgit.http.server</artifactId>
|
||||
<version>${jgit.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>sonia.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit.java7</artifactId>
|
||||
<version>${jgit.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-lang</groupId>
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/***
|
||||
* 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.annotations.VisibleForTesting;
|
||||
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<Object,Object> 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 = 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
@@ -49,12 +50,14 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.util.IOUtil;
|
||||
import sonia.scm.web.CollectingPackParserListener;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -72,7 +75,7 @@ public class GitHookChangesetCollector
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
* Constructs a new instance
|
||||
*
|
||||
*
|
||||
* @param rpack
|
||||
@@ -83,19 +86,19 @@ public class GitHookChangesetCollector
|
||||
{
|
||||
this.rpack = rpack;
|
||||
this.receiveCommands = receiveCommands;
|
||||
this.listener = CollectingPackParserListener.get(rpack);
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
* Collect all new changesets from the received hook.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
* @return new changesets
|
||||
*/
|
||||
public List<Changeset> collectChangesets()
|
||||
{
|
||||
List<Changeset> changesets = Lists.newArrayList();
|
||||
Map<String, Changeset> changesets = Maps.newLinkedHashMap();
|
||||
|
||||
org.eclipse.jgit.lib.Repository repository = rpack.getRepository();
|
||||
|
||||
@@ -110,7 +113,19 @@ public class GitHookChangesetCollector
|
||||
|
||||
for (ReceiveCommand rc : receiveCommands)
|
||||
{
|
||||
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
|
||||
{
|
||||
@@ -124,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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -144,35 +156,13 @@ public class GitHookChangesetCollector
|
||||
GitUtil.release(walk);
|
||||
}
|
||||
|
||||
return changesets;
|
||||
return Lists.newArrayList(changesets.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param changesets
|
||||
* @param converter
|
||||
* @param walk
|
||||
* @param rc
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws IncorrectObjectTypeException
|
||||
*/
|
||||
private void collectChangesets(List<Changeset> changesets,
|
||||
private void collectChangesets(Map<String, Changeset> changesets,
|
||||
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());
|
||||
@@ -187,7 +177,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());
|
||||
|
||||
@@ -196,19 +186,39 @@ public class GitHookChangesetCollector
|
||||
|
||||
RevCommit commit = walk.next();
|
||||
|
||||
List<String> branches = Lists.newArrayList(branch);
|
||||
|
||||
while (commit != null)
|
||||
{
|
||||
String id = commit.getId().name();
|
||||
Changeset changeset = changesets.get(id);
|
||||
|
||||
// parse commit body to avoid npe
|
||||
walk.parseBody(commit);
|
||||
if (changeset != null)
|
||||
{
|
||||
logger.trace(
|
||||
"commit {} already received durring this push, add branch {} to the commit",
|
||||
commit, branch);
|
||||
changeset.getBranches().add(branch);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Changeset changeset = converter.createChangeset(commit, branches);
|
||||
// only append new commits
|
||||
if (listener.isNew(commit))
|
||||
{
|
||||
|
||||
logger.trace("retrive commit {} for hook", changeset.getId());
|
||||
// parse commit body to avoid npe
|
||||
walk.parseBody(commit);
|
||||
|
||||
changesets.add(changeset);
|
||||
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();
|
||||
}
|
||||
@@ -216,9 +226,10 @@ public class GitHookChangesetCollector
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
/** listener to track new objects */
|
||||
private final CollectingPackParserListener listener;
|
||||
|
||||
private final List<ReceiveCommand> receiveCommands;
|
||||
|
||||
/** Field description */
|
||||
private final ReceivePack rpack;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -50,6 +51,11 @@ import sonia.scm.repository.spi.GitRepositoryServiceProvider;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import sonia.scm.store.ConfigurationStoreFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.SCMContextProvider;
|
||||
import sonia.scm.schedule.Scheduler;
|
||||
import sonia.scm.schedule.Task;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -66,19 +72,27 @@ 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";
|
||||
|
||||
/** 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 final Scheduler scheduler;
|
||||
|
||||
private Task task;
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
@@ -87,15 +101,48 @@ public class GitRepositoryHandler
|
||||
*
|
||||
* @param storeFactory
|
||||
* @param fileSystem
|
||||
* @param scheduler
|
||||
*/
|
||||
@Inject
|
||||
public GitRepositoryHandler(ConfigurationStoreFactory storeFactory, FileSystem fileSystem)
|
||||
public GitRepositoryHandler(ConfigurationStoreFactory 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
|
||||
*
|
||||
|
||||
@@ -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.HandlerEventType;
|
||||
import sonia.scm.event.ScmEventBus;
|
||||
import sonia.scm.plugin.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() == HandlerEventType.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)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -262,7 +263,7 @@ public final class GitUtil
|
||||
{
|
||||
if (formatter != null)
|
||||
{
|
||||
formatter.release();
|
||||
formatter.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,7 +277,7 @@ public final class GitUtil
|
||||
{
|
||||
if (walk != null)
|
||||
{
|
||||
walk.release();
|
||||
walk.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,7 +291,7 @@ public final class GitUtil
|
||||
{
|
||||
if (walk != null)
|
||||
{
|
||||
walk.release();
|
||||
walk.close();;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,6 +336,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
|
||||
@@ -631,6 +646,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
|
||||
*
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Sebastian Sdorra All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer. 2. Redistributions in
|
||||
* binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution. 3. Neither the name of SCM-Manager;
|
||||
* nor the names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* http://bitbucket.org/sdorra/scm-manager
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
package sonia.scm.repository.api;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
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;
|
||||
|
||||
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 a new instance.
|
||||
*
|
||||
*
|
||||
* @param commands received git commands
|
||||
*/
|
||||
public GitHookBranchProvider(List<ReceiveCommand> commands)
|
||||
{
|
||||
Builder<String> createdOrModifiedBuilder = ImmutableList.builder();
|
||||
Builder<String> deletedOrClosedBuilder = ImmutableList.builder();
|
||||
|
||||
for (ReceiveCommand command : commands)
|
||||
{
|
||||
Type type = command.getType();
|
||||
String ref = command.getRefName();
|
||||
String branch = GitUtil.getBranch(ref);
|
||||
|
||||
if (Strings.isNullOrEmpty(branch))
|
||||
{
|
||||
logger.debug("ref {} is not a branch", ref);
|
||||
}
|
||||
else if (isCreateOrUpdate(type))
|
||||
{
|
||||
createdOrModifiedBuilder.add(branch);
|
||||
}
|
||||
else if (command.getType() == Type.DELETE)
|
||||
{
|
||||
deletedOrClosedBuilder.add(branch);
|
||||
}
|
||||
}
|
||||
|
||||
createdOrModified = createdOrModifiedBuilder.build();
|
||||
deletedOrClosed = deletedOrClosedBuilder.build();
|
||||
}
|
||||
|
||||
private boolean isCreateOrUpdate(Type type){
|
||||
return type == Type.CREATE || type == Type.UPDATE || type == Type.UPDATE_NONFASTFORWARD;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public List<String> getCreatedOrModified()
|
||||
{
|
||||
return createdOrModified;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getDeletedOrClosed()
|
||||
{
|
||||
return deletedOrClosed;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
private final List<String> createdOrModified;
|
||||
|
||||
private final List<String> deletedOrClosed;
|
||||
}
|
||||
@@ -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<Tag> createdTags;
|
||||
private final List<Tag> deletedTags;
|
||||
|
||||
/**
|
||||
* Constructs new instance.
|
||||
*
|
||||
* @param commands received commands
|
||||
*/
|
||||
public GitHookTagProvider(List<ReceiveCommand> commands) {
|
||||
ImmutableList.Builder<Tag> createdTagBuilder = ImmutableList.builder();
|
||||
ImmutableList.Builder<Tag> 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<Tag> getCreatedTags() {
|
||||
return createdTags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Tag> getDeletedTags() {
|
||||
return deletedTags;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -34,11 +34,18 @@ 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.GitConstants;
|
||||
import sonia.scm.repository.GitUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -46,6 +53,11 @@ import java.io.IOException;
|
||||
*/
|
||||
public class AbstractGitCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* the logger for AbstractGitCommand
|
||||
*/
|
||||
private static final Logger logger = LoggerFactory.getLogger(AbstractGitCommand.class);
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
@@ -75,6 +87,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(GitConstants.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 ---------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -71,7 +71,6 @@ import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import sonia.scm.util.IOUtil;
|
||||
|
||||
/**
|
||||
@@ -96,8 +95,6 @@ public class GitBrowseCommand extends AbstractGitCommand
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param context
|
||||
* @param repository
|
||||
*/
|
||||
@@ -124,18 +121,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
|
||||
{
|
||||
@@ -169,70 +163,6 @@ public class GitBrowseCommand extends AbstractGitCommand
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param files
|
||||
* @param repo
|
||||
* @param revId
|
||||
* @param path
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
private void appendSubModules(List<FileObject> files,
|
||||
org.eclipse.jgit.lib.Repository repo, ObjectId revId, String path)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
path = Util.nonNull(path);
|
||||
|
||||
Map<String, SubRepository> subRepositories = subrepositoryCache.get(revId);
|
||||
|
||||
if (subRepositories == null)
|
||||
{
|
||||
subRepositories = getSubRepositories(repo, revId);
|
||||
subrepositoryCache.put(revId, subRepositories);
|
||||
}
|
||||
|
||||
if (subRepositories != null)
|
||||
{
|
||||
for (Entry<String, SubRepository> 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
|
||||
@@ -244,9 +174,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
|
||||
{
|
||||
@@ -257,26 +187,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -386,11 +333,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())
|
||||
@@ -496,6 +438,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<String, SubRepository> 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 */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,6 @@ public class GitDiffCommand extends AbstractGitCommand implements DiffCommand
|
||||
*
|
||||
* @param context
|
||||
* @param repository
|
||||
* @param repositoryDirectory
|
||||
*/
|
||||
public GitDiffCommand(GitContext context, Repository repository)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -45,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;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -55,33 +59,28 @@ public class GitHookContextProvider extends HookContextProvider
|
||||
|
||||
/** Field description */
|
||||
private static final Set<HookFeature> SUPPORTED_FEATURES =
|
||||
EnumSet.of(HookFeature.MESSAGE_PROVIDER, HookFeature.CHANGESET_PROVIDER);
|
||||
EnumSet.of(HookFeature.MESSAGE_PROVIDER, HookFeature.CHANGESET_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<ReceiveCommand> receiveCommands)
|
||||
{
|
||||
this.receivePack = receivePack;
|
||||
this.receiveCommands = receiveCommands;
|
||||
this.changesetProvider = new GitHookChangesetProvider(receivePack,
|
||||
receiveCommands);
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public HookMessageProvider createMessageProvider()
|
||||
{
|
||||
@@ -90,24 +89,23 @@ public class GitHookContextProvider extends HookContextProvider
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public HookBranchProvider getBranchProvider()
|
||||
{
|
||||
return new GitHookBranchProvider(receiveCommands);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HookTagProvider getTagProvider() {
|
||||
return new GitHookTagProvider(receiveCommands);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HookChangesetProvider getChangesetProvider()
|
||||
{
|
||||
return changesetProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Set<HookFeature> getSupportedFeatures()
|
||||
{
|
||||
@@ -117,8 +115,11 @@ public class GitHookContextProvider extends HookContextProvider
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private GitHookChangesetProvider changesetProvider;
|
||||
private final GitHookChangesetProvider changesetProvider;
|
||||
|
||||
/** Field description */
|
||||
private ReceivePack receivePack;
|
||||
private final List<ReceiveCommand> receiveCommands;
|
||||
|
||||
/** Field description */
|
||||
private final ReceivePack receivePack;
|
||||
}
|
||||
|
||||
@@ -221,17 +221,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)
|
||||
{
|
||||
|
||||
@@ -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<ObjectId> 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<ObjectId> newObjectIds;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user