summaryrefslogtreecommitdiffstats
path: root/src/com/gitblit/GitBlitServer.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/gitblit/GitBlitServer.java')
-rw-r--r--src/com/gitblit/GitBlitServer.java393
1 files changed, 393 insertions, 0 deletions
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<Connector> connectors = new ArrayList<Connector>();
+ 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<String> 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