diff --git a/pom.xml b/pom.xml index f569dd96ec..39cbaef7e5 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,8 @@ scm-core scm-cli - scm-server-util + scm-server-api + scm-server-jetty plugins scm-agent scm-webapp @@ -26,12 +27,11 @@ http://download.java.net/maven/2 - - guice-maven - guice maven - http://guiceyfruit.googlecode.com/svn/repo/releases - - + + guice-maven + guice maven + http://guiceyfruit.googlecode.com/svn/repo/releases + diff --git a/scm-core/src/main/java/sonia/scm/util/ChecksumUtil.java b/scm-core/src/main/java/sonia/scm/util/ChecksumUtil.java new file mode 100644 index 0000000000..b56a83f9ba --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/util/ChecksumUtil.java @@ -0,0 +1,136 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + + + +package sonia.scm.util; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * + * @author Sebastian Sdorra + */ +public class ChecksumUtil +{ + + /** Field description */ + private static final String DIGEST_TYPE = "SHA-1"; + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param in + * + * @return + * + * @throws IOException + */ + public static String createChecksum(InputStream in) throws IOException + { + MessageDigest digest = null; + + try + { + byte[] buffer = new byte[1024]; + + digest = getDigest(); + + int numRead = 0; + + do + { + numRead = in.read(buffer); + + if (numRead > 0) + { + digest.update(buffer, 0, numRead); + } + } + while (numRead != -1); + } + finally + { + if (in != null) + { + in.close(); + } + } + + byte[] b = digest.digest(); + + return toHexString(b); + } + + /** + * Method description + * + * + * @param file + * + * @return + * + * @throws IOException + */ + public static String createChecksum(File file) throws IOException + { + return createChecksum(new FileInputStream(file)); + } + + /** + * Method description + * + * + * @param byteArray + * + * @return + */ + private static String toHexString(byte[] byteArray) + { + StringBuilder buffer = new StringBuilder(); + + for (byte b : byteArray) + { + buffer.append(Integer.toHexString(b)); + } + + return buffer.toString(); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + private static MessageDigest getDigest() + { + MessageDigest digest = null; + + try + { + digest = MessageDigest.getInstance(DIGEST_TYPE); + } + catch (NoSuchAlgorithmException ex) + { + throw new RuntimeException("no such digest"); + } + + return digest; + } +} diff --git a/scm-core/src/main/java/sonia/scm/util/Util.java b/scm-core/src/main/java/sonia/scm/util/Util.java index b8f7344285..e29e7e08d9 100644 --- a/scm-core/src/main/java/sonia/scm/util/Util.java +++ b/scm-core/src/main/java/sonia/scm/util/Util.java @@ -12,6 +12,8 @@ package sonia.scm.util; import java.io.Closeable; import java.io.File; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.Collection; import java.util.logging.Level; @@ -50,6 +52,25 @@ public class Util } } + /** + * Method description + * + * + * @param in + * @param out + * + * @throws IOException + */ + public static void copy(InputStream in, OutputStream out) throws IOException + { + byte[] buffer = new byte[0xFFFF]; + + for (int len; (len = in.read(buffer)) != -1; ) + { + out.write(buffer, 0, len); + } + } + /** * Method description * diff --git a/scm-server-util/pom.xml b/scm-server-api/pom.xml similarity index 71% rename from scm-server-util/pom.xml rename to scm-server-api/pom.xml index 1b3b64087a..1dfaa8e5cb 100644 --- a/scm-server-util/pom.xml +++ b/scm-server-api/pom.xml @@ -10,19 +10,25 @@ 1.0-SNAPSHOT - sonia.scm.server - scm-server-util + sonia.scm + scm-server-api 1.0-SNAPSHOT - scm-server-util + scm-server-api + + sonia.scm + scm-core + 1.0-SNAPSHOT + + sonia.scm scm-cli 1.0-SNAPSHOT - + diff --git a/scm-server-api/src/main/java/sonia/scm/server/ApplicationInformation.java b/scm-server-api/src/main/java/sonia/scm/server/ApplicationInformation.java new file mode 100644 index 0000000000..b38e829805 --- /dev/null +++ b/scm-server-api/src/main/java/sonia/scm/server/ApplicationInformation.java @@ -0,0 +1,100 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + + + +package sonia.scm.server; + +//~--- JDK imports ------------------------------------------------------------ + +import javax.xml.bind.annotation.XmlRootElement; + +/** + * + * @author Sebastian Sdorra + */ +@XmlRootElement(name = "app-info") +public class ApplicationInformation +{ + + /** + * Method description + * + * + * @return + */ + public String getAppName() + { + return appName; + } + + /** + * Method description + * + * + * @return + */ + public String getName() + { + return name; + } + + /** + * Method description + * + * + * @return + */ + public String getVersion() + { + return version; + } + + //~--- set methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param appName + */ + public void setAppName(String appName) + { + this.appName = appName; + } + + /** + * Method description + * + * + * @param name + */ + public void setName(String name) + { + this.name = name; + } + + /** + * Method description + * + * + * @param version + */ + public void setVersion(String version) + { + this.version = version; + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private String appName; + + /** Field description */ + private String name; + + /** Field description */ + private String version; +} diff --git a/scm-server-api/src/main/java/sonia/scm/server/Server.java b/scm-server-api/src/main/java/sonia/scm/server/Server.java new file mode 100644 index 0000000000..62bef052de --- /dev/null +++ b/scm-server-api/src/main/java/sonia/scm/server/Server.java @@ -0,0 +1,69 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + + + +package sonia.scm.server; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.File; +import java.io.IOException; + +/** + * + * @author Sebastian Sdorra + */ +public interface Server +{ + + /** + * Method description + * + * + * @param listener + */ + public void addListener(ServerListener listener); + + /** + * Method description + * + * + * @param listener + */ + public void removeListener(ServerListener listener); + + /** + * Method description + * + * + * @param config + * @param webapp + * + * @throws IOException + * @throws ServerException + */ + public void start(ServerConfig config, File webapp) + throws ServerException, IOException; + + /** + * Method description + * + * + * @throws IOException + * @throws ServerException + */ + public void stop() throws ServerException, IOException; + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + public boolean isRunning(); +} diff --git a/scm-server-api/src/main/java/sonia/scm/server/ServerAllreadyRunningException.java b/scm-server-api/src/main/java/sonia/scm/server/ServerAllreadyRunningException.java new file mode 100644 index 0000000000..d908708981 --- /dev/null +++ b/scm-server-api/src/main/java/sonia/scm/server/ServerAllreadyRunningException.java @@ -0,0 +1,19 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + + + +package sonia.scm.server; + +/** + * + * @author Sebastian Sdorra + */ +public class ServerAllreadyRunningException extends ServerException +{ + + /** Field description */ + private static final long serialVersionUID = 3816513762034851638L; +} diff --git a/scm-server-api/src/main/java/sonia/scm/server/ServerApplication.java b/scm-server-api/src/main/java/sonia/scm/server/ServerApplication.java new file mode 100644 index 0000000000..5af381fd00 --- /dev/null +++ b/scm-server-api/src/main/java/sonia/scm/server/ServerApplication.java @@ -0,0 +1,145 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + + + +package sonia.scm.server; + +//~--- non-JDK imports -------------------------------------------------------- + +import sonia.scm.cli.CliException; +import sonia.scm.cli.CliParser; +import sonia.scm.cli.DefaultCliHelpBuilder; +import sonia.scm.util.ServiceUtil; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.xml.bind.JAXB; + +/** + * + * @author Sebastian Sdorra + */ +public class ServerApplication +{ + + /** Field description */ + public static final String APPINFO = "/app-info.xml"; + + /** Field description */ + public static final int RETURNCODE_CLI_ERROR = 2; + + /** Field description */ + public static final int RETURNCODE_MISSING_APPINFO = 1; + + /** Field description */ + public static final int RETURNCODE_MISSING_SERVER_IMPLEMENTATION = 3; + + /** Field description */ + private static final Logger logger = + Logger.getLogger(ServerApplication.class.getName()); + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param args + * + * @throws CliException + * @throws IOException + * @throws ServerException + */ + public static void main(String[] args) + throws CliException, ServerException, IOException + { + InputStream input = ServerApplication.class.getResourceAsStream(APPINFO); + + if (input == null) + { + System.err.println("could not find /app-info.xml in classpath"); + System.exit(RETURNCODE_MISSING_APPINFO); + } + + ApplicationInformation appInfo = JAXB.unmarshal(input, + ApplicationInformation.class); + ServerConfig config = new ServerConfig(); + CliParser parser = new CliParser(); + + parser.parse(parser, args); + + if (config.getShowHelp()) + { + printHelp(appInfo, parser, config); + } + else + { + final Server server = ServiceUtil.getService(Server.class); + + if (server == null) + { + System.err.println("could not find an server implementation"); + System.exit(RETURNCODE_MISSING_SERVER_IMPLEMENTATION); + } + + File webapp = new File("webapp", appInfo.getAppName()); + + server.start(config, webapp); + Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() + { + @Override + public void run() + { + if (server.isRunning()) + { + try + { + server.stop(); + } + catch (ServerException ex) + { + logger.log(Level.SEVERE, null, ex); + } + catch (IOException ex) + { + logger.log(Level.SEVERE, null, ex); + } + } + } + })); + } + } + + /** + * Method description + * + * + * @param parser + * @param config + */ + private static void printHelp(ApplicationInformation appInfo, CliParser parser, ServerConfig config) + { + String s = System.getProperty("line.separator"); + StringBuilder prefix = new StringBuilder(appInfo.getName()); + prefix.append(" ").append( appInfo.getVersion() ); + + prefix.append(s).append("usage: "); + prefix.append(s); + + DefaultCliHelpBuilder helpBuilder = + new DefaultCliHelpBuilder(prefix.toString(), null); + + System.err.println(parser.createHelp(helpBuilder, config)); + System.exit(RETURNCODE_CLI_ERROR); + } +} diff --git a/scm-server-api/src/main/java/sonia/scm/server/ServerConfig.java b/scm-server-api/src/main/java/sonia/scm/server/ServerConfig.java new file mode 100644 index 0000000000..feb8f37c95 --- /dev/null +++ b/scm-server-api/src/main/java/sonia/scm/server/ServerConfig.java @@ -0,0 +1,152 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + + + +package sonia.scm.server; + +//~--- non-JDK imports -------------------------------------------------------- + +import sonia.scm.cli.Argument; + +/** + * + * @author Sebastian Sdorra + */ +public class ServerConfig +{ + + /** + * Constructs ... + * + */ + public ServerConfig() {} + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + public String getContextPath() + { + return contextPath; + } + + /** + * Method description + * + * + * @return + */ + public Integer getPort() + { + return port; + } + + /** + * Method description + * + * + * @return + */ + public String getResourcePath() + { + return resourcePath; + } + + /** + * Method description + * + * + * @return + */ + public Boolean getShowHelp() + { + return showHelp; + } + + //~--- set methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param contextPath + */ + public void setContextPath(String contextPath) + { + this.contextPath = contextPath; + } + + /** + * Method description + * + * + * @param port + */ + public void setPort(Integer port) + { + this.port = port; + } + + /** + * Method description + * + * + * @param resourcePath + */ + public void setResourcePath(String resourcePath) + { + this.resourcePath = resourcePath; + } + + /** + * Method description + * + * + * @param showHelp + */ + public void setShowHelp(Boolean showHelp) + { + this.showHelp = showHelp; + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + @Argument( + value = "c", + longName = "context-path", + description = "The Context-Path of the Jab-WebApp" + ) + private String contextPath = "/"; + + /** Field description */ + @Argument( + value = "p", + longName = "port", + description = "The port for the listener" + ) + private Integer port = Integer.valueOf(8080); + + /** Field description */ + @Argument( + value = "r", + longName = "resource-path", + description = "Path to the server resource directory" + ) + private String resourcePath; + + /** Field description */ + @Argument( + value = "h", + longName = "help", + description = "Shows this help" + ) + private Boolean showHelp = Boolean.FALSE; +} diff --git a/scm-server-api/src/main/java/sonia/scm/server/ServerException.java b/scm-server-api/src/main/java/sonia/scm/server/ServerException.java new file mode 100644 index 0000000000..eb30bcc808 --- /dev/null +++ b/scm-server-api/src/main/java/sonia/scm/server/ServerException.java @@ -0,0 +1,61 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + + + +package sonia.scm.server; + +/** + * + * @author Sebastian Sdorra + */ +public class ServerException extends Exception +{ + + /** Field description */ + private static final long serialVersionUID = 2936673332739265774L; + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + */ + public ServerException() {} + + /** + * Constructs ... + * + * + * @param message + */ + public ServerException(String message) + { + super(message); + } + + /** + * Constructs ... + * + * + * @param cause + */ + public ServerException(Throwable cause) + { + super(cause); + } + + /** + * Constructs ... + * + * + * @param message + * @param cause + */ + public ServerException(String message, Throwable cause) + { + super(message, cause); + } +} diff --git a/scm-server-api/src/main/java/sonia/scm/server/ServerListener.java b/scm-server-api/src/main/java/sonia/scm/server/ServerListener.java new file mode 100644 index 0000000000..03a2ef2dbd --- /dev/null +++ b/scm-server-api/src/main/java/sonia/scm/server/ServerListener.java @@ -0,0 +1,36 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + + + +package sonia.scm.server; + +/** + * + * @author Sebastian Sdorra + */ +public interface ServerListener +{ + + /** + * Method description + * + * + * @param throwable + */ + public void failed(Throwable throwable); + + /** + * Method description + * + */ + public void started(); + + /** + * Method description + * + */ + public void stopped(); +} diff --git a/scm-server-jetty/pom.xml b/scm-server-jetty/pom.xml new file mode 100644 index 0000000000..e8393e036a --- /dev/null +++ b/scm-server-jetty/pom.xml @@ -0,0 +1,44 @@ + + + + 4.0.0 + + + scm + sonia.scm + 1.0-SNAPSHOT + + + sonia.scm + scm-server-jetty + 1.0-SNAPSHOT + scm-server-jetty + + + + + sonia.scm + scm-server-api + 1.0-SNAPSHOT + + + + org.eclipse.jetty.aggregate + jetty-server + ${jetty.version} + + + + org.eclipse.jetty.aggregate + jetty-webapp + ${jetty.version} + + + + + + 7.1.6.v20100715 + + + diff --git a/scm-server-jetty/src/main/java/sonia/scm/server/jetty/JettyServer.java b/scm-server-jetty/src/main/java/sonia/scm/server/jetty/JettyServer.java new file mode 100644 index 0000000000..8ac31adc4b --- /dev/null +++ b/scm-server-jetty/src/main/java/sonia/scm/server/jetty/JettyServer.java @@ -0,0 +1,155 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + + + +package sonia.scm.server.jetty; + +//~--- non-JDK imports -------------------------------------------------------- + +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.nio.SelectChannelConnector; +import org.eclipse.jetty.webapp.WebAppContext; + +import sonia.scm.server.Server; +import sonia.scm.server.ServerAllreadyRunningException; +import sonia.scm.server.ServerConfig; +import sonia.scm.server.ServerException; +import sonia.scm.server.ServerListener; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.File; +import java.io.IOException; + +import java.util.HashSet; +import java.util.Set; + +/** + * + * @author Sebastian Sdorra + */ +public class JettyServer implements Server +{ + + /** + * Method description + * + * + * @param listener + */ + @Override + public void addListener(ServerListener listener) + { + listeners.add(listener); + } + + /** + * Method description + * + * + * @param listener + */ + @Override + public void removeListener(ServerListener listener) + { + listeners.remove(listener); + } + + /** + * Method description + * + * + * @param config + * @param webapp + * + * @throws IOException + * @throws ServerException + */ + @Override + public void start(ServerConfig config, File webapp) + throws ServerException, IOException + { + if (isRunning()) + { + throw new ServerAllreadyRunningException(); + } + + server = new org.eclipse.jetty.server.Server(); + + Connector connector = new SelectChannelConnector(); + + for (ServerListener listener : listeners) + { + connector.addLifeCycleListener(new JettyServerListenerAdapter(listener)); + } + + connector.setPort(config.getPort()); + server.addConnector(connector); + + WebAppContext wac = new WebAppContext(); + + wac.setContextPath(config.getContextPath()); + wac.setWar(webapp.getAbsolutePath()); + wac.setExtractWAR(false); + server.setHandler(server); + server.setStopAtShutdown(true); + + try + { + server.start(); + server.join(); + } + catch (Exception ex) + { + throw new ServerException(ex); + } + } + + /** + * Method description + * + * + * @throws IOException + * @throws ServerException + */ + @Override + public void stop() throws ServerException, IOException + { + if (isRunning()) + { + try + { + server.stop(); + } + catch (Exception ex) + { + throw new ServerException(ex); + } + } + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + @Override + public boolean isRunning() + { + return server != null; + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private Set listeners = new HashSet(); + + /** Field description */ + private org.eclipse.jetty.server.Server server; +} diff --git a/scm-server-jetty/src/main/java/sonia/scm/server/jetty/JettyServerListenerAdapter.java b/scm-server-jetty/src/main/java/sonia/scm/server/jetty/JettyServerListenerAdapter.java new file mode 100644 index 0000000000..d81257fdf6 --- /dev/null +++ b/scm-server-jetty/src/main/java/sonia/scm/server/jetty/JettyServerListenerAdapter.java @@ -0,0 +1,103 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + + + +package sonia.scm.server.jetty; + +//~--- non-JDK imports -------------------------------------------------------- + +import org.eclipse.jetty.util.component.LifeCycle; + +import sonia.scm.server.ServerListener; + +/** + * + * @author Sebastian Sdorra + */ +public class JettyServerListenerAdapter implements LifeCycle.Listener +{ + + /** + * Constructs ... + * + * + * @param listener + */ + public JettyServerListenerAdapter(ServerListener listener) + { + this.listener = listener; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param lc + * @param throwable + */ + @Override + public void lifeCycleFailure(LifeCycle lc, Throwable throwable) + { + listener.failed(throwable); + } + + /** + * Method description + * + * + * @param lc + */ + @Override + public void lifeCycleStarted(LifeCycle lc) + { + listener.started(); + } + + /** + * Method description + * + * + * @param lc + */ + @Override + public void lifeCycleStarting(LifeCycle lc) + { + + // do nothing + } + + /** + * Method description + * + * + * @param lc + */ + @Override + public void lifeCycleStopped(LifeCycle lc) + { + listener.stopped(); + } + + /** + * Method description + * + * + * @param lc + */ + @Override + public void lifeCycleStopping(LifeCycle lc) + { + + // do nothing + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private ServerListener listener; +} diff --git a/scm-server-jetty/src/main/resources/META-INF/services/sonia.scm.server.Server b/scm-server-jetty/src/main/resources/META-INF/services/sonia.scm.server.Server new file mode 100644 index 0000000000..bfef293326 --- /dev/null +++ b/scm-server-jetty/src/main/resources/META-INF/services/sonia.scm.server.Server @@ -0,0 +1 @@ +sonia.scm.server.jetty.JettyServer \ No newline at end of file