James Moger
2013-02-25 64b6f382d35e1fea0172b222277dae0312f274e4
src/com/gitblit/GitBlit.java
@@ -85,6 +85,9 @@
import com.gitblit.Constants.FederationToken;
import com.gitblit.Constants.PermissionType;
import com.gitblit.Constants.RegistrantType;
import com.gitblit.fanout.FanoutNioService;
import com.gitblit.fanout.FanoutService;
import com.gitblit.fanout.FanoutSocketService;
import com.gitblit.models.FederationModel;
import com.gitblit.models.FederationProposal;
import com.gitblit.models.FederationSet;
@@ -109,7 +112,6 @@
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.JsonUtils;
import com.gitblit.utils.MetricUtils;
import com.gitblit.utils.MultiConfigUtil;
import com.gitblit.utils.ObjectCache;
import com.gitblit.utils.StringUtils;
import com.gitblit.utils.TimeUtils;
@@ -161,6 +163,8 @@
   private final ObjectCache<String> projectRepositoriesMarkdownCache = new ObjectCache<String>();
   private ServletContext servletContext;
   private File baseFolder;
   private File repositoriesFolder;
@@ -182,7 +186,7 @@
   
   private FileBasedConfig projectConfigs;
   
   private MultiConfigUtil multiConfigUtil = new MultiConfigUtil();
   private FanoutService fanoutService;
   public GitBlit() {
      if (gitblit == null) {
@@ -392,12 +396,8 @@
    * @return the file
    */
   public static File getFileOrFolder(String fileOrFolder) {
      String openShift = System.getenv("OPENSHIFT_DATA_DIR");
      if (!StringUtils.isEmpty(openShift)) {
         // running on RedHat OpenShift
         return new File(openShift, fileOrFolder);
      }
      return new File(fileOrFolder);
      return com.gitblit.utils.FileUtils.resolveParameter(Constants.baseFolder$,
            self().baseFolder, fileOrFolder);
   }
   /**
@@ -407,7 +407,7 @@
    * @return the repositories folder path
    */
   public static File getRepositoriesFolder() {
      return getFileOrFolder(Keys.git.repositoriesFolder, "git");
      return getFileOrFolder(Keys.git.repositoriesFolder, "${baseFolder}/git");
   }
   /**
@@ -417,7 +417,7 @@
    * @return the proposals folder path
    */
   public static File getProposalsFolder() {
      return getFileOrFolder(Keys.federation.proposalsFolder, "proposals");
      return getFileOrFolder(Keys.federation.proposalsFolder, "${baseFolder}/proposals");
   }
   /**
@@ -427,9 +427,9 @@
    * @return the Groovy scripts folder path
    */
   public static File getGroovyScriptsFolder() {
      return getFileOrFolder(Keys.groovy.scriptsFolder, "groovy");
      return getFileOrFolder(Keys.groovy.scriptsFolder, "${baseFolder}/groovy");
   }
   /**
    * Updates the list of server settings.
    * 
@@ -825,7 +825,7 @@
      // TODO reconsider ownership as a user property
      // manually specify personal repository ownerships
      for (RepositoryModel rm : repositoryListCache.values()) {
         if (rm.isUsersPersonalRepository(user.username) || rm.isRepoAdministrator(user.username)) {
         if (rm.isUsersPersonalRepository(user.username) || rm.isOwner(user.username)) {
            RegistrantAccessPermission rp = new RegistrantAccessPermission(rm.name, AccessPermission.REWIND,
                  PermissionType.OWNER, RegistrantType.REPOSITORY, null, false);
            // user may be owner of a repository to which they've inherited
@@ -939,14 +939,14 @@
         for (RepositoryModel model : getRepositoryModels(user)) {
            if (model.isUsersPersonalRepository(username)) {
               // personal repository
               model.addRepoAdministrator(user.username);
               model.addOwner(user.username);
               String oldRepositoryName = model.name;
               model.name = "~" + user.username + model.name.substring(model.projectPath.length());
               model.projectPath = "~" + user.username;
               updateRepositoryModel(oldRepositoryName, model, false);
            } else if (model.isRepoAdministrator(username)) {
            } else if (model.isOwner(username)) {
               // common/shared repo
               model.addRepoAdministrator(user.username);
               model.addOwner(user.username);
               updateRepositoryModel(model.name, model, false);
            }
         }
@@ -1650,7 +1650,7 @@
      }
      RepositoryModel model = new RepositoryModel();
      model.isBare = r.isBare();
      File basePath = getFileOrFolder(Keys.git.repositoriesFolder, "git");
      File basePath = getFileOrFolder(Keys.git.repositoriesFolder, "${baseFolder}/git");
      if (model.isBare) {
         model.name = com.gitblit.utils.FileUtils.getRelativePath(basePath, r.getDirectory());
      } else {
@@ -1665,7 +1665,7 @@
      
      if (config != null) {
         model.description = getConfig(config, "description", "");
         model.addRepoAdministrators(multiConfigUtil.convertStringToSet(getConfig(config, "owner", "")));
         model.addOwners(ArrayUtils.fromString(getConfig(config, "owner", "")));
         model.useTickets = getConfig(config, "useTickets", false);
         model.useDocs = getConfig(config, "useDocs", false);
         model.allowForks = getConfig(config, "allowForks", true);
@@ -1713,6 +1713,7 @@
      }
      model.HEAD = JGitUtils.getHEADRef(r);
      model.availableRefs = JGitUtils.getAvailableHeadTargets(r);
      model.sparkleshareId = JGitUtils.getSparkleshareId(r);
      r.close();
      
      if (model.origin != null && model.origin.startsWith("file://")) {
@@ -1869,11 +1870,16 @@
   
   private ForkModel getForkModelFromCache(String repository) {
      RepositoryModel model = repositoryListCache.get(repository.toLowerCase());
      if (model == null) {
         return null;
      }
      ForkModel fork = new ForkModel(model);
      if (!ArrayUtils.isEmpty(model.forks)) {
         for (String aFork : model.forks) {
            ForkModel fm = getForkModelFromCache(aFork);
            fork.forks.add(fm);
            if (fm != null) {
               fork.forks.add(fm);
            }
         }
      }
      return fork;
@@ -1881,11 +1887,16 @@
   
   private ForkModel getForkModel(String repository) {
      RepositoryModel model = getRepositoryModel(repository.toLowerCase());
      if (model == null) {
         return null;
      }
      ForkModel fork = new ForkModel(model);
      if (!ArrayUtils.isEmpty(model.forks)) {
         for (String aFork : model.forks) {
            ForkModel fm = getForkModel(aFork);
            fork.forks.add(fm);
            if (fm != null) {
               fork.forks.add(fm);
            }
         }
      }
      return fork;
@@ -2172,7 +2183,7 @@
   public void updateConfiguration(Repository r, RepositoryModel repository) {
      StoredConfig config = r.getConfig();
      config.setString(Constants.CONFIG_GITBLIT, null, "description", repository.description);
      config.setString(Constants.CONFIG_GITBLIT, null, "owner", multiConfigUtil.convertCollectionToSingleLineString(repository.getRepoAdministrators()));
      config.setString(Constants.CONFIG_GITBLIT, null, "owner", ArrayUtils.toString(repository.owners));
      config.setBoolean(Constants.CONFIG_GITBLIT, null, "useTickets", repository.useTickets);
      config.setBoolean(Constants.CONFIG_GITBLIT, null, "useDocs", repository.useDocs);
      config.setBoolean(Constants.CONFIG_GITBLIT, null, "allowForks", repository.allowForks);
@@ -3030,12 +3041,15 @@
    * 
    * @param settings
    */
   public void configureContext(IStoredSettings settings, boolean startFederation) {
      logger.info("Reading configuration from " + settings.toString());
   public void configureContext(IStoredSettings settings, File folder, boolean startFederation) {
      this.settings = settings;
      this.baseFolder = folder;
      repositoriesFolder = getRepositoriesFolder();
      logger.info("Git repositories folder " + repositoriesFolder.getAbsolutePath());
      logger.info("Gitblit base folder     = " + folder.getAbsolutePath());
      logger.info("Git repositories folder = " + repositoriesFolder.getAbsolutePath());
      logger.info("Gitblit settings        = " + settings.toString());
      // prepare service executors
      mailExecutor = new MailExecutor(settings);
@@ -3057,7 +3071,7 @@
      serverStatus = new ServerStatus(isGO());
      if (this.userService == null) {
         String realm = settings.getString(Keys.realm.userService, "users.properties");
         String realm = settings.getString(Keys.realm.userService, "${baseFolder}/users.properties");
         IUserService loginService = null;
         try {
            // check to see if this "file" is a login service class
@@ -3070,7 +3084,7 @@
      }
      
      // load and cache the project metadata
      projectConfigs = new FileBasedConfig(getFileOrFolder(Keys.web.projectsFile, "projects.conf"), FS.detect());
      projectConfigs = new FileBasedConfig(getFileOrFolder(Keys.web.projectsFile, "${baseFolder}/projects.conf"), FS.detect());
      getProjectConfigs();
      
      // schedule mail engine
@@ -3082,15 +3096,9 @@
      }
      
      // schedule lucene engine
      boolean branchIndexingActivated = settings.getBoolean(
            Keys.git.branchIndexingActivated, true);
      logger.info("Branch indexing is "
            + (branchIndexingActivated ? "" : "not") + " activated");
      if (branchIndexingActivated) {
         logger.info("Lucene executor is scheduled to process indexed branches every 2 minutes.");
         scheduledExecutor.scheduleAtFixedRate(luceneExecutor, 1, 2,
               TimeUnit.MINUTES);
      }
      enableLuceneIndexing();
      // schedule gc engine
      if (gcExecutor.isReady()) {
         logger.info("GC executor is scheduled to scan repositories every 24 hours.");
@@ -3142,6 +3150,49 @@
      }
      ContainerUtils.CVE_2007_0450.test();
      // startup Fanout PubSub service
      if (settings.getInteger(Keys.fanout.port, 0) > 0) {
         String bindInterface = settings.getString(Keys.fanout.bindInterface, null);
         int port = settings.getInteger(Keys.fanout.port, FanoutService.DEFAULT_PORT);
         boolean useNio = settings.getBoolean(Keys.fanout.useNio, true);
         int limit = settings.getInteger(Keys.fanout.connectionLimit, 0);
         if (useNio) {
            if (StringUtils.isEmpty(bindInterface)) {
               fanoutService = new FanoutNioService(port);
            } else {
               fanoutService = new FanoutNioService(bindInterface, port);
            }
         } else {
            if (StringUtils.isEmpty(bindInterface)) {
               fanoutService = new FanoutSocketService(port);
            } else {
               fanoutService = new FanoutSocketService(bindInterface, port);
            }
         }
         fanoutService.setConcurrentConnectionLimit(limit);
         fanoutService.setAllowAllChannelAnnouncements(false);
         fanoutService.start();
      }
   }
   protected void enableLuceneIndexing() {
      scheduledExecutor.scheduleAtFixedRate(luceneExecutor, 1, 2,  TimeUnit.MINUTES);
      logger.info("Lucene executor is scheduled to process indexed branches every 2 minutes.");
   }
   protected final Logger getLogger() {
      return logger;
   }
   protected final ScheduledExecutorService getScheduledExecutor() {
      return scheduledExecutor;
   }
   protected final LuceneExecutor getLuceneExecutor() {
      return luceneExecutor;
   }
   
   private void logTimezone(String type, TimeZone zone) {
@@ -3165,39 +3216,63 @@
   public void contextInitialized(ServletContextEvent contextEvent, InputStream referencePropertiesInputStream) {
      servletContext = contextEvent.getServletContext();
      if (settings == null) {
         // Gitblit WAR is running in a servlet container
         // Gitblit is running in a servlet container
         ServletContext context = contextEvent.getServletContext();
         WebXmlSettings webxmlSettings = new WebXmlSettings(context);
         // gitblit.properties file located within the webapp
         String webProps = context.getRealPath("/WEB-INF/gitblit.properties");
         if (!StringUtils.isEmpty(webProps)) {
            File overrideFile = new File(webProps);
            webxmlSettings.applyOverrides(overrideFile);
         }
         File contextFolder = new File(context.getRealPath("/"));
         String openShift = System.getenv("OPENSHIFT_DATA_DIR");
         
         // gitblit.properties file located outside the deployed war
         // folder lie, for example, on RedHat OpenShift.
         File overrideFile = getFileOrFolder("gitblit.properties");
         if (!overrideFile.getPath().equals("gitblit.properties")) {
            webxmlSettings.applyOverrides(overrideFile);
         }
         configureContext(webxmlSettings, true);
         if (!StringUtils.isEmpty(openShift)) {
            // Gitblit is running in OpenShift/JBoss
            File base = new File(openShift);
         // Copy the included scripts to the configured groovy folder
         File localScripts = getFileOrFolder(Keys.groovy.scriptsFolder, "groovy");
         if (!localScripts.exists()) {
            File includedScripts = new File(context.getRealPath("/WEB-INF/groovy"));
            if (!includedScripts.equals(localScripts)) {
               try {
                  com.gitblit.utils.FileUtils.copy(localScripts, includedScripts.listFiles());
               } catch (IOException e) {
                  logger.error(MessageFormat.format(
                        "Failed to copy included Groovy scripts from {0} to {1}",
                        includedScripts, localScripts));
            // gitblit.properties setting overrides
            File overrideFile = new File(base, "gitblit.properties");
            webxmlSettings.applyOverrides(overrideFile);
            // Copy the included scripts to the configured groovy folder
            File localScripts = new File(base, webxmlSettings.getString(Keys.groovy.scriptsFolder, "groovy"));
            if (!localScripts.exists()) {
               File warScripts = new File(contextFolder, "/WEB-INF/data/groovy");
               if (!warScripts.equals(localScripts)) {
                  try {
                     com.gitblit.utils.FileUtils.copy(localScripts, warScripts.listFiles());
                  } catch (IOException e) {
                     logger.error(MessageFormat.format(
                           "Failed to copy included Groovy scripts from {0} to {1}",
                           warScripts, localScripts));
                  }
               }
            }
            // configure context using the web.xml
            configureContext(webxmlSettings, base, true);
         } else {
            // Gitblit is running in a standard servlet container
            logger.info("WAR contextFolder is " + contextFolder.getAbsolutePath());
            String path = webxmlSettings.getString(Constants.baseFolder, Constants.contextFolder$ + "/WEB-INF/data");
            File base = com.gitblit.utils.FileUtils.resolveParameter(Constants.contextFolder$, contextFolder, path);
            base.mkdirs();
            // try to copy the data folder contents to the baseFolder
            File localSettings = new File(base, "gitblit.properties");
            if (!localSettings.exists()) {
               File contextData = new File(contextFolder, "/WEB-INF/data");
               if (!base.equals(contextData)) {
                  try {
                     com.gitblit.utils.FileUtils.copy(base, contextData.listFiles());
                  } catch (IOException e) {
                     logger.error(MessageFormat.format(
                           "Failed to copy included data from {0} to {1}",
                        contextData, base));
                  }
               }
            }
            // delegate all config to baseFolder/gitblit.properties file
            FileSettings settings = new FileSettings(localSettings.getAbsolutePath());
            configureContext(settings, base, true);
         }
      }
      
@@ -3215,6 +3290,9 @@
      scheduledExecutor.shutdownNow();
      luceneExecutor.close();
      gcExecutor.close();
      if (fanoutService != null) {
         fanoutService.stop();
      }
   }
   
   /**
@@ -3258,23 +3336,20 @@
      // create a Gitblit repository model for the clone
      RepositoryModel cloneModel = repository.cloneAs(cloneName);
      // owner has REWIND/RW+ permissions
      cloneModel.addRepoAdministrator(user.username);
      // owner has REWIND/RW+ permissions
      cloneModel.addOwner(user.username);
      updateRepositoryModel(cloneName, cloneModel, false);
      // add the owner of the source repository to the clone's access list
      Set<String> repoAdministrators = repository.getRepoAdministrators();
      if (repoAdministrators != null) {
         for (String repoAdministrator : repoAdministrators) {
            if (!StringUtils.isEmpty(repoAdministrator)) {
               UserModel originOwner = getUserModel(repoAdministrator);
               if (originOwner != null) {
                  originOwner.setRepositoryPermission(cloneName, AccessPermission.CLONE);
                  updateUserModel(originOwner.username, originOwner, false);
               }
      if (!ArrayUtils.isEmpty(repository.owners)) {
         for (String owner : repository.owners) {
            UserModel originOwner = getUserModel(owner);
            if (originOwner != null) {
               originOwner.setRepositoryPermission(cloneName, AccessPermission.CLONE);
               updateUserModel(originOwner.username, originOwner, false);
            }
         }
      }
      }
      // grant origin's user list clone permission to fork
      List<String> users = getRepositoryUsers(repository);