1 /* 2 * Copyright (C) 2015 Obeo. 3 * and other copyright owners as documented in the project's IP log. 4 * 5 * This program and the accompanying materials are made available 6 * under the terms of the Eclipse Distribution License v1.0 which 7 * accompanies this distribution, is reproduced below, and is 8 * available at http://www.eclipse.org/org/documents/edl-v10.php 9 * 10 * All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or 13 * without modification, are permitted provided that the following 14 * conditions are met: 15 * 16 * - Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 19 * - Redistributions in binary form must reproduce the above 20 * copyright notice, this list of conditions and the following 21 * disclaimer in the documentation and/or other materials provided 22 * with the distribution. 23 * 24 * - Neither the name of the Eclipse Foundation, Inc. nor the 25 * names of its contributors may be used to endorse or promote 26 * products derived from this software without specific prior 27 * written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 30 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 31 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 32 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 34 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 35 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 36 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 37 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 38 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 40 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 41 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 42 */ 43 package org.eclipse.jgit.hooks; 44 45 import static java.nio.charset.StandardCharsets.UTF_8; 46 47 import java.io.ByteArrayOutputStream; 48 import java.io.IOException; 49 import java.io.PrintStream; 50 import java.io.UnsupportedEncodingException; 51 import java.util.concurrent.Callable; 52 53 import org.eclipse.jgit.api.errors.AbortedByHookException; 54 import org.eclipse.jgit.lib.Repository; 55 import org.eclipse.jgit.util.FS; 56 import org.eclipse.jgit.util.ProcessResult; 57 58 /** 59 * Git can fire off custom scripts when certain important actions occur. These 60 * custom scripts are called "hooks". There are two groups of hooks: client-side 61 * (that run on local operations such as committing and merging), and 62 * server-side (that run on network operations such as receiving pushed 63 * commits). This is the abstract super-class of the different hook 64 * implementations in JGit. 65 * 66 * @param <T> 67 * the return type which is expected from {@link #call()} 68 * @see <a href="http://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks">Git 69 * Hooks on the git-scm official site</a> 70 * @since 4.0 71 */ 72 abstract class GitHook<T> implements Callable<T> { 73 74 private final Repository repo; 75 76 /** 77 * The output stream to be used by the hook. 78 */ 79 protected final PrintStream outputStream; 80 81 /** 82 * @param repo 83 * @param outputStream 84 * The output stream the hook must use. {@code null} is allowed, 85 * in which case the hook will use {@code System.out}. 86 */ 87 protected GitHook(Repository repo, PrintStream outputStream) { 88 this.repo = repo; 89 this.outputStream = outputStream; 90 } 91 92 /** 93 * Run the hook. 94 * 95 * @throws IOException 96 * if IO goes wrong. 97 * @throws AbortedByHookException 98 * If the hook has been run and a returned an exit code 99 * different from zero. 100 */ 101 @Override 102 public abstract T call() throws IOException, AbortedByHookException; 103 104 /** 105 * @return The name of the hook, which must not be {@code null}. 106 */ 107 public abstract String getHookName(); 108 109 /** 110 * @return The repository. 111 */ 112 protected Repository getRepository() { 113 return repo; 114 } 115 116 /** 117 * Override this method when needed to provide relevant parameters to the 118 * underlying hook script. The default implementation returns an empty 119 * array. 120 * 121 * @return The parameters the hook receives. 122 */ 123 protected String[] getParameters() { 124 return new String[0]; 125 } 126 127 /** 128 * Override to provide relevant arguments via stdin to the underlying hook 129 * script. The default implementation returns {@code null}. 130 * 131 * @return The parameters the hook receives. 132 */ 133 protected String getStdinArgs() { 134 return null; 135 } 136 137 /** 138 * @return The output stream the hook must use. Never {@code null}, 139 * {@code System.out} is returned by default. 140 */ 141 protected PrintStream getOutputStream() { 142 return outputStream == null ? System.out : outputStream; 143 } 144 145 /** 146 * Runs the hook, without performing any validity checks. 147 * 148 * @throws AbortedByHookException 149 * If the underlying hook script exited with non-zero. 150 */ 151 protected void doRun() throws AbortedByHookException { 152 final ByteArrayOutputStream errorByteArray = new ByteArrayOutputStream(); 153 PrintStream hookErrRedirect = null; 154 try { 155 hookErrRedirect = new PrintStream(errorByteArray, false, 156 UTF_8.name()); 157 } catch (UnsupportedEncodingException e) { 158 // UTF-8 is guaranteed to be available 159 } 160 ProcessResult result = FS.DETECTED.runHookIfPresent(getRepository(), 161 getHookName(), getParameters(), getOutputStream(), 162 hookErrRedirect, getStdinArgs()); 163 if (result.isExecutedWithError()) { 164 throw new AbortedByHookException( 165 new String(errorByteArray.toByteArray(), UTF_8), 166 getHookName(), result.getExitCode()); 167 } 168 } 169 170 }