From 5fe7df81eb38dc66f2cfc4bf1973863a19f55cf2 Mon Sep 17 00:00:00 2001 From: James Moger Date: Mon, 4 Apr 2011 09:10:51 -0400 Subject: Initial import of Git:Blit. Change-Id: Ifce000c85c8947c3a768e782c841e41a8953d314 --- src/com/gitblit/GitBlitServer.java | 393 +++++++++++++++++++++++++++++++++++++ 1 file changed, 393 insertions(+) create mode 100644 src/com/gitblit/GitBlitServer.java (limited to 'src/com/gitblit/GitBlitServer.java') diff --git a/src/com/gitblit/GitBlitServer.java b/src/com/gitblit/GitBlitServer.java new file mode 100644 index 00000000..ae542e8d --- /dev/null +++ b/src/com/gitblit/GitBlitServer.java @@ -0,0 +1,393 @@ +package com.gitblit; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URL; +import java.net.UnknownHostException; +import java.security.ProtectionDomain; +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.ConsoleAppender; +import org.apache.log4j.PatternLayout; +import org.apache.wicket.protocol.http.ContextParamWebApplicationFactory; +import org.apache.wicket.protocol.http.WicketFilter; +import org.eclipse.jetty.http.security.Constraint; +import org.eclipse.jetty.security.ConstraintMapping; +import org.eclipse.jetty.security.ConstraintSecurityHandler; +import org.eclipse.jetty.security.HashLoginService; +import org.eclipse.jetty.security.authentication.BasicAuthenticator; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.bio.SocketConnector; +import org.eclipse.jetty.server.nio.SelectChannelConnector; +import org.eclipse.jetty.server.ssl.SslConnector; +import org.eclipse.jetty.server.ssl.SslSelectChannelConnector; +import org.eclipse.jetty.server.ssl.SslSocketConnector; +import org.eclipse.jetty.servlet.FilterHolder; +import org.eclipse.jetty.servlet.FilterMapping; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jetty.webapp.WebAppContext; +import org.eclipse.jgit.http.server.GitServlet; + + +import com.beust.jcommander.JCommander; +import com.beust.jcommander.Parameter; +import com.beust.jcommander.ParameterException; +import com.beust.jcommander.Parameters; +import com.gitblit.wicket.GitBlitWebApp; + +public class GitBlitServer { + + private final static Logger logger = Log.getLogger(GitBlitServer.class.getSimpleName()); + private final static String border_star = "***********************************************************"; + private static boolean debugMode = false; + + public static boolean isDebugMode() { + return debugMode; + } + + public static void main(String[] args) { + Params params = new Params(); + JCommander jc = new JCommander(params); + try { + jc.parse(args); + if (params.help) + usage(jc, null); + } catch (ParameterException t) { + usage(jc, t); + } + + if (params.stop) + stop(params); + else + start(params); + } + + private static void usage(JCommander jc, ParameterException t) { + System.out.println(border_star); + System.out.println(Constants.getRunningVersion()); + System.out.println(border_star); + System.out.println(); + if (t != null) { + System.out.println(t.getMessage()); + System.out.println(); + } + if (jc != null) { + jc.usage(); + System.out.println("\nExample:\n java -server -Xmx1024M -jar go-git-go.jar --repos c:\\git --port 80 --securePort 443"); + } + System.exit(0); + } + + /** + * Stop Server. + */ + public static void stop(Params params) { + try { + Socket s = new Socket(InetAddress.getByName("127.0.0.1"), params.shutdownPort); + OutputStream out = s.getOutputStream(); + System.out.println("Sending Shutdown Request to " + Constants.NAME); + out.write(("\r\n").getBytes()); + out.flush(); + s.close(); + } catch (UnknownHostException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Start Server. + */ + private static void start(Params params) { + PatternLayout layout = new PatternLayout(StoredSettings.getString("log4jPattern", "%-5p %d{MM-dd HH:mm:ss.SSS} %-20.20c{1} %m%n")); + org.apache.log4j.Logger rootLogger = org.apache.log4j.Logger.getRootLogger(); + rootLogger.addAppender(new ConsoleAppender(layout)); + + logger.info(border_star); + logger.info(Constants.getRunningVersion()); + logger.info(border_star); + + String osname = System.getProperty("os.name"); + String osversion = System.getProperty("os.version"); + logger.info("Running on " + osname + " (" + osversion + ")"); + + if (params.debug) { + logger.warn("DEBUG Mode"); + } + + // Determine port connectors + List connectors = new ArrayList(); + if (params.port > 0) { + Connector httpConnector = createConnector(params.useNIO, params.port); + connectors.add(httpConnector); + } + + if (params.securePort > 0) { + if (new File("keystore").exists()) { + Connector secureConnector = createSSLConnector(params.useNIO, params.securePort, params.storePassword); + connectors.add(secureConnector); + } else { + logger.warn("Failed to find Keystore? Did you run \"makekeystore\"?"); + logger.warn("SSL connector DISABLED."); + } + } + + // tempDir = Directory where... + // * WebApp is expanded + // + File tempDir = new File(params.temp); + if (tempDir.exists()) + deleteRecursively(tempDir); + tempDir.mkdirs(); + + Server server = new Server(); + server.setStopAtShutdown(true); + server.setConnectors(connectors.toArray(new Connector[connectors.size()])); + + // Get the execution path of this class + // We use this to set the WAR path. + ProtectionDomain protectionDomain = GitBlitServer.class.getProtectionDomain(); + URL location = protectionDomain.getCodeSource().getLocation(); + + // Root WebApp Context + WebAppContext rootContext = new WebAppContext(); + rootContext.setContextPath("/"); + rootContext.setServer(server); + rootContext.setWar(location.toExternalForm()); + rootContext.setTempDirectory(tempDir); + + // Wicket Filter + String wicketPathSpec = "/*"; + FilterHolder wicketFilter = new FilterHolder(WicketFilter.class); + wicketFilter.setInitParameter(ContextParamWebApplicationFactory.APP_CLASS_PARAM, GitBlitWebApp.class.getName()); + wicketFilter.setInitParameter(WicketFilter.FILTER_MAPPING_PARAM, wicketPathSpec); + rootContext.addFilter(wicketFilter, wicketPathSpec, FilterMapping.DEFAULT); + + // GIT Servlet + String gitServletPathSpec = "/git/*"; + ServletHolder gitServlet = rootContext.addServlet(GitServlet.class, gitServletPathSpec); + gitServlet.setInitParameter("base-path", params.repositoriesFolder); + gitServlet.setInitParameter("export-all", params.exportAll ? "1" : "0"); + + String realmUsers = params.realmFile; + + // Authentication Realm + Handler handler; + if (realmUsers != null && new File(realmUsers).exists() && params.authenticateAccess) { + List list = StoredSettings.getStrings("gitRoles"); + String[] roles; + if (list.size() == 0) { + roles = new String[] { "*" }; + } else { + roles = list.toArray(new String[list.size()]); + } + logger.info("Authentication required for GIT access"); + logger.info("Setting up realm from " + realmUsers); + HashLoginService loginService = new HashLoginService(Constants.NAME, realmUsers); + + Constraint constraint = new Constraint(); + constraint.setName("auth"); + constraint.setAuthenticate(true); + constraint.setRoles(roles); + + ConstraintMapping mapping = new ConstraintMapping(); + mapping.setPathSpec(gitServletPathSpec); + mapping.setConstraint(constraint); + + ConstraintSecurityHandler security = new ConstraintSecurityHandler(); + security.addConstraintMapping(mapping); + for (String role : roles) { + security.addRole(role); + } + security.setAuthenticator(new BasicAuthenticator()); + security.setLoginService(loginService); + security.setStrict(false); + + security.setHandler(rootContext); + + handler = security; + } else { + logger.info("Setting up anonymous access"); + handler = rootContext; + } + + // Set the server's contexts + server.setHandler(handler); + + // Start the Server + try { + if (params.shutdownPort > 0) { + Thread shutdownMonitor = new ShutdownMonitorThread(server, params); + shutdownMonitor.start(); + } + server.start(); + server.join(); + } catch (Exception e) { + e.printStackTrace(); + System.exit(100); + } + } + + private static Connector createConnector(boolean useNIO, int port) { + Connector connector; + if (useNIO) { + logger.info("Setting up NIO SelectChannelConnector on port " + port); + SelectChannelConnector nioconn = new SelectChannelConnector(); + nioconn.setSoLingerTime(-1); + nioconn.setThreadPool(new QueuedThreadPool(20)); + connector = nioconn; + } else { + logger.info("Setting up SocketConnector on port " + port); + SocketConnector sockconn = new SocketConnector(); + connector = sockconn; + } + + connector.setPort(port); + connector.setMaxIdleTime(30000); + return connector; + } + + private static Connector createSSLConnector(boolean useNIO, int port, String password) { + SslConnector connector; + if (useNIO) { + logger.info("Setting up NIO SslSelectChannelConnector on port " + port); + SslSelectChannelConnector ssl = new SslSelectChannelConnector(); + ssl.setSoLingerTime(-1); + ssl.setThreadPool(new QueuedThreadPool(20)); + connector = ssl; + } else { + logger.info("Setting up NIO SslSocketConnector on port " + port); + SslSocketConnector ssl = new SslSocketConnector(); + connector = ssl; + } + connector.setKeystore("keystore"); + connector.setPassword(password); + connector.setPort(port); + connector.setMaxIdleTime(30000); + return connector; + } + + /** + * Recursively delete a folder and its contents. + * + * @param folder + */ + private static void deleteRecursively(File folder) { + for (File file : folder.listFiles()) { + if (file.isDirectory()) + deleteRecursively(file); + else + file.delete(); + } + folder.delete(); + } + + private static class ShutdownMonitorThread extends Thread { + + private final ServerSocket socket; + + private final Server server; + + public ShutdownMonitorThread(Server server, Params params) { + this.server = server; + setDaemon(true); + setName(Constants.NAME + " Shutdown Monitor"); + ServerSocket skt = null; + try { + skt = new ServerSocket(params.shutdownPort, 1, InetAddress.getByName("127.0.0.1")); + } catch (Exception e) { + logger.warn(e); + } + socket = skt; + } + + @Override + public void run() { + logger.info("Shutdown Monitor listening on port " + socket.getLocalPort()); + Socket accept; + try { + accept = socket.accept(); + BufferedReader reader = new BufferedReader(new InputStreamReader(accept.getInputStream())); + reader.readLine(); + logger.info(border_star); + logger.info("Stopping " + Constants.NAME); + logger.info(border_star); + server.stop(); + server.setStopAtShutdown(false); + accept.close(); + socket.close(); + } catch (Exception e) { + logger.warn("Failed to shutdown Jetty", e); + } + } + } + + @Parameters(separators = " ") + private static class Params { + + /* + * Server parameters + */ + @Parameter(names = { "-h", "--help" }, description = "Show this help") + public Boolean help = false; + + @Parameter(names = { "--stop" }, description = "Stop Server") + public Boolean stop = false; + + @Parameter(names = { "--temp" }, description = "Server temp folder") + public String temp = StoredSettings.getString("tempFolder", "temp"); + + @Parameter(names = { "--debug" }, description = "Run server in DEBUG mode") + public Boolean debug = StoredSettings.getBoolean("debug", false); + + /* + * GIT Servlet Parameters + */ + @Parameter(names = { "--repos" }, description = "GIT Repositories Folder") + public String repositoriesFolder = StoredSettings.getString("repositoriesFolder", "repos"); + + @Parameter(names = { "--exportAll" }, description = "Export All Found Repositories") + public Boolean exportAll = StoredSettings.getBoolean("exportAll", true); + + /* + * Authentication Parameters + */ + @Parameter(names = { "--authenticateAccess" }, description = "Authenticate GIT access") + public Boolean authenticateAccess = StoredSettings.getBoolean("authenticateAccess", true); + + @Parameter(names = { "--realm" }, description = "Users Realm Hash File") + public String realmFile = StoredSettings.getString("realmFile", "users.properties"); + + /* + * JETTY Parameters + */ + @Parameter(names = { "--nio" }, description = "Use NIO Connector else use Socket Connector.") + public Boolean useNIO = StoredSettings.getBoolean("useNio", true); + + @Parameter(names = "--port", description = "HTTP port for to serve. (port <= 0 will disable this connector)") + public Integer port = StoredSettings.getInteger("httpPort", 80); + + @Parameter(names = "--securePort", description = "HTTPS port to serve. (port <= 0 will disable this connector)") + public Integer securePort = StoredSettings.getInteger("httpsPort", 443); + + @Parameter(names = "--storePassword", description = "Password for SSL (https) keystore.") + public String storePassword = StoredSettings.getString("storePassword", ""); + + @Parameter(names = "--shutdownPort", description = "Port for Shutdown Monitor to listen on. (port <= 0 will disable this monitor)") + public Integer shutdownPort = StoredSettings.getInteger("shutdownPort", 8081); + + } +} \ No newline at end of file -- cgit v1.2.3