]> source.dussan.org Git - gitblit.git/commitdiff
Extract Federation, Gitblit and Services manager from GitBlit singleton
authorJames Moger <james.moger@gitblit.com>
Thu, 21 Nov 2013 22:26:04 +0000 (17:26 -0500)
committerJames Moger <james.moger@gitblit.com>
Fri, 29 Nov 2013 16:05:51 +0000 (11:05 -0500)
Change-Id: I2b2f361a868c8eedf4b6df5939e7dfac2d5f92a9

19 files changed:
src/main/java/com/gitblit/DaggerModule.java
src/main/java/com/gitblit/FederationClient.java
src/main/java/com/gitblit/FederationPullExecutor.java
src/main/java/com/gitblit/GitBlit.java
src/main/java/com/gitblit/Gitblit.java
src/main/java/com/gitblit/manager/FederationManager.java [new file with mode: 0644]
src/main/java/com/gitblit/manager/GitblitManager.java [new file with mode: 0644]
src/main/java/com/gitblit/manager/IFederationManager.java
src/main/java/com/gitblit/manager/IGitblitManager.java
src/main/java/com/gitblit/manager/IManager.java
src/main/java/com/gitblit/manager/IServicesManager.java [new file with mode: 0644]
src/main/java/com/gitblit/manager/NotificationManager.java
src/main/java/com/gitblit/manager/ProjectManager.java
src/main/java/com/gitblit/manager/RepositoryManager.java
src/main/java/com/gitblit/manager/RuntimeManager.java
src/main/java/com/gitblit/manager/ServicesManager.java [new file with mode: 0644]
src/main/java/com/gitblit/manager/SessionManager.java
src/main/java/com/gitblit/manager/UserManager.java
src/test/java/com/gitblit/tests/mock/MockRuntimeManager.java

index 8cd9c8b52c03428f191b7654e55ece3fc4b49dc4..0cbb739aa433fed665abdfcb7fb1b4d1f02eb6b1 100644 (file)
@@ -20,18 +20,22 @@ import javax.inject.Singleton;
 import org.apache.wicket.protocol.http.WebApplication;
 
 import com.gitblit.git.GitServlet;
+import com.gitblit.manager.FederationManager;
+import com.gitblit.manager.GitblitManager;
 import com.gitblit.manager.IFederationManager;
 import com.gitblit.manager.IGitblitManager;
 import com.gitblit.manager.INotificationManager;
 import com.gitblit.manager.IProjectManager;
 import com.gitblit.manager.IRepositoryManager;
 import com.gitblit.manager.IRuntimeManager;
+import com.gitblit.manager.IServicesManager;
 import com.gitblit.manager.ISessionManager;
 import com.gitblit.manager.IUserManager;
 import com.gitblit.manager.NotificationManager;
 import com.gitblit.manager.ProjectManager;
 import com.gitblit.manager.RepositoryManager;
 import com.gitblit.manager.RuntimeManager;
+import com.gitblit.manager.ServicesManager;
 import com.gitblit.manager.SessionManager;
 import com.gitblit.manager.UserManager;
 import com.gitblit.wicket.GitBlitWebApp;
@@ -47,6 +51,7 @@ import dagger.Provides;
  *
  */
 @Module(
+       library = true,
        injects = {
                        IStoredSettings.class,
 
@@ -59,6 +64,7 @@ import dagger.Provides;
                        IProjectManager.class,
                        IGitblitManager.class,
                        IFederationManager.class,
+                       IServicesManager.class,
 
                        // the monolithic manager
                        Gitblit.class,
@@ -85,13 +91,6 @@ import dagger.Provides;
 )
 public class DaggerModule {
 
-       final GitBlit gitblit;
-
-       // HACK but necessary for now
-       public DaggerModule(GitBlit gitblit) {
-               this.gitblit = gitblit;
-       }
-
        @Provides @Singleton IStoredSettings provideSettings() {
                return new FileSettings();
        }
@@ -137,12 +136,28 @@ public class DaggerModule {
                                repositoryManager);
        }
 
-       @Provides @Singleton IGitblitManager provideGitblitManager() {
-               return gitblit;
+       @Provides @Singleton IFederationManager provideFederationManager(
+                       IRuntimeManager runtimeManager,
+                       INotificationManager notificationManager,
+                       IUserManager userManager,
+                       IRepositoryManager repositoryManager) {
+
+               return new FederationManager(
+                               runtimeManager,
+                               notificationManager,
+                               userManager,
+                               repositoryManager);
        }
 
-       @Provides @Singleton IFederationManager provideFederationManager() {
-               return gitblit;
+       @Provides @Singleton IGitblitManager provideGitblitManager(
+                       IRuntimeManager runtimeManager,
+                       IUserManager userManager,
+                       IRepositoryManager repositoryManager) {
+
+               return new GitblitManager(
+                               runtimeManager,
+                               userManager,
+                               repositoryManager);
        }
 
        @Provides @Singleton Gitblit provideGitblit(
@@ -162,8 +177,12 @@ public class DaggerModule {
                                sessionManager,
                                repositoryManager,
                                projectManager,
-                               federationManager,
-                               gitblitManager);
+                               gitblitManager,
+                               federationManager);
+       }
+
+       @Provides @Singleton IServicesManager provideServicesManager(Gitblit gitblit) {
+               return new ServicesManager(gitblit);
        }
 
        @Provides @Singleton WebApplication provideWebApplication(
index 862b64c194dca33f599615ff8c180b475c5c5312..f32fa0f239e1875e1c0453dfabf6a1afc798719b 100644 (file)
@@ -23,6 +23,11 @@ import com.beust.jcommander.JCommander;
 import com.beust.jcommander.Parameter;\r
 import com.beust.jcommander.ParameterException;\r
 import com.beust.jcommander.Parameters;\r
+import com.gitblit.manager.FederationManager;\r
+import com.gitblit.manager.NotificationManager;\r
+import com.gitblit.manager.RepositoryManager;\r
+import com.gitblit.manager.RuntimeManager;\r
+import com.gitblit.manager.UserManager;\r
 import com.gitblit.models.FederationModel;\r
 import com.gitblit.utils.FederationUtils;\r
 import com.gitblit.utils.StringUtils;\r
@@ -53,7 +58,7 @@ public class FederationClient {
                }\r
 \r
                File regFile = com.gitblit.utils.FileUtils.resolveParameter(Constants.baseFolder$, baseFolder, params.registrationsFile);\r
-               IStoredSettings settings = new FileSettings(regFile.getAbsolutePath());\r
+               FileSettings settings = new FileSettings(regFile.getAbsolutePath());\r
                List<FederationModel> registrations = new ArrayList<FederationModel>();\r
                if (StringUtils.isEmpty(params.url)) {\r
                        registrations.addAll(FederationUtils.getFederationRegistrations(settings));\r
@@ -83,14 +88,23 @@ public class FederationClient {
                }\r
 \r
                // configure the Gitblit singleton for minimal, non-server operation\r
-               GitBlit gitblit = new GitBlit(settings, baseFolder);\r
-               gitblit.beforeServletInjection(null); // XXX broken\r
-               FederationPullExecutor executor = new FederationPullExecutor(registrations, params.isDaemon);\r
-               executor.run();\r
-               if (!params.isDaemon) {\r
-                       System.out.println("Finished.");\r
-                       System.exit(0);\r
-               }\r
+               RuntimeManager runtime = new RuntimeManager(settings);\r
+               runtime.setBaseFolder(baseFolder);\r
+               NotificationManager notifications = new NotificationManager(settings).start();\r
+               UserManager users = new UserManager(runtime).start();\r
+               RepositoryManager repositories = new RepositoryManager(runtime, users).start();\r
+               FederationManager federation = new FederationManager(runtime, notifications, users, repositories).start();\r
+\r
+               FederationPullExecutor puller = new FederationPullExecutor(federation.getFederationRegistrations()) {\r
+                       @Override\r
+                       public void reschedule(FederationModel registration) {\r
+                               // NOOP\r
+                       }\r
+               };\r
+               puller.run();\r
+\r
+               System.out.println("Finished.");\r
+               System.exit(0);\r
        }\r
 \r
        private static void usage(JCommander jc, ParameterException t) {\r
@@ -116,9 +130,6 @@ public class FederationClient {
                @Parameter(names = { "--registrations" }, description = "Gitblit Federation Registrations File", required = false)\r
                public String registrationsFile = "${baseFolder}/federation.properties";\r
 \r
-               @Parameter(names = { "--daemon" }, description = "Runs in daemon mode to schedule and pull repositories", required = false)\r
-               public boolean isDaemon;\r
-\r
                @Parameter(names = { "--url" }, description = "URL of Gitblit instance to mirror from", required = false)\r
                public String url;\r
 \r
index e9a604da7f719e1a154553870305bd0d062beb61..bbe73cfa696b062ee68eb05a9278d45d09cfad60 100644 (file)
-/*\r
- * Copyright 2011 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- *     http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import static org.eclipse.jgit.lib.Constants.DOT_GIT_EXT;\r
-\r
-import java.io.File;\r
-import java.io.FileOutputStream;\r
-import java.io.IOException;\r
-import java.net.InetAddress;\r
-import java.text.MessageFormat;\r
-import java.util.ArrayList;\r
-import java.util.Arrays;\r
-import java.util.Collection;\r
-import java.util.Date;\r
-import java.util.HashMap;\r
-import java.util.HashSet;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Properties;\r
-import java.util.Set;\r
-import java.util.concurrent.TimeUnit;\r
-\r
-import org.eclipse.jgit.lib.Repository;\r
-import org.eclipse.jgit.lib.StoredConfig;\r
-import org.eclipse.jgit.revwalk.RevCommit;\r
-import org.eclipse.jgit.transport.CredentialsProvider;\r
-import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-import com.gitblit.Constants.AccessPermission;\r
-import com.gitblit.Constants.FederationPullStatus;\r
-import com.gitblit.Constants.FederationStrategy;\r
-import com.gitblit.GitBlitException.ForbiddenException;\r
-import com.gitblit.manager.IGitblitManager;\r
-import com.gitblit.manager.INotificationManager;\r
-import com.gitblit.manager.IRepositoryManager;\r
-import com.gitblit.manager.IRuntimeManager;\r
-import com.gitblit.manager.IUserManager;\r
-import com.gitblit.models.FederationModel;\r
-import com.gitblit.models.RefModel;\r
-import com.gitblit.models.RepositoryModel;\r
-import com.gitblit.models.TeamModel;\r
-import com.gitblit.models.UserModel;\r
-import com.gitblit.utils.ArrayUtils;\r
-import com.gitblit.utils.FederationUtils;\r
-import com.gitblit.utils.FileUtils;\r
-import com.gitblit.utils.JGitUtils;\r
-import com.gitblit.utils.JGitUtils.CloneResult;\r
-import com.gitblit.utils.StringUtils;\r
-import com.gitblit.utils.TimeUtils;\r
-\r
-/**\r
- * FederationPullExecutor pulls repository updates and, optionally, user\r
- * accounts and server settings from registered Gitblit instances.\r
- */\r
-public class FederationPullExecutor implements Runnable {\r
-\r
-       private final Logger logger = LoggerFactory.getLogger(FederationPullExecutor.class);\r
-\r
-       private final List<FederationModel> registrations;\r
-\r
-       private final boolean isDaemon;\r
-\r
-       /**\r
-        * Constructor for specifying a single federation registration. This\r
-        * constructor is used to schedule the next pull execution.\r
-        *\r
-        * @param registration\r
-        */\r
-       private FederationPullExecutor(FederationModel registration) {\r
-               this(Arrays.asList(registration), true);\r
-       }\r
-\r
-       /**\r
-        * Constructor to specify a group of federation registrations. This is\r
-        * normally used at startup to pull and then schedule the next update based\r
-        * on each registrations frequency setting.\r
-        *\r
-        * @param registrations\r
-        * @param isDaemon\r
-        *            if true, registrations are rescheduled in perpetuity. if\r
-        *            false, the federation pull operation is executed once.\r
-        */\r
-       public FederationPullExecutor(List<FederationModel> registrations, boolean isDaemon) {\r
-               this.registrations = registrations;\r
-               this.isDaemon = isDaemon;\r
-       }\r
-\r
-       /**\r
-        * Run method for this pull executor.\r
-        */\r
-       @Override\r
-       public void run() {\r
-               for (FederationModel registration : registrations) {\r
-                       FederationPullStatus was = registration.getLowestStatus();\r
-                       try {\r
-                               Date now = new Date(System.currentTimeMillis());\r
-                               pull(registration);\r
-                               sendStatusAcknowledgment(registration);\r
-                               registration.lastPull = now;\r
-                               FederationPullStatus is = registration.getLowestStatus();\r
-                               if (is.ordinal() < was.ordinal()) {\r
-                                       // the status for this registration has downgraded\r
-                                       logger.warn("Federation pull status of {0} is now {1}", registration.name,\r
-                                                       is.name());\r
-                                       if (registration.notifyOnError) {\r
-                                               String message = "Federation pull of " + registration.name + " @ "\r
-                                                               + registration.url + " is now at " + is.name();\r
-                                               INotificationManager mailManager = GitBlit.getManager(INotificationManager.class);\r
-                                               mailManager\r
-                                                               .sendMailToAdministrators(\r
-                                                                               "Pull Status of " + registration.name + " is " + is.name(),\r
-                                                                               message);\r
-                                       }\r
-                               }\r
-                       } catch (Throwable t) {\r
-                               logger.error(MessageFormat.format(\r
-                                               "Failed to pull from federated gitblit ({0} @ {1})", registration.name,\r
-                                               registration.url), t);\r
-                       } finally {\r
-                               if (isDaemon) {\r
-                                       schedule(registration);\r
-                               }\r
-                       }\r
-               }\r
-       }\r
-\r
-       /**\r
-        * Mirrors a repository and, optionally, the server's users, and/or\r
-        * configuration settings from a origin Gitblit instance.\r
-        *\r
-        * @param registration\r
-        * @throws Exception\r
-        */\r
-       private void pull(FederationModel registration) throws Exception {\r
-               Map<String, RepositoryModel> repositories = FederationUtils.getRepositories(registration,\r
-                               true);\r
-               String registrationFolder = registration.folder.toLowerCase().trim();\r
-               // confirm valid characters in server alias\r
-               Character c = StringUtils.findInvalidCharacter(registrationFolder);\r
-               if (c != null) {\r
-                       logger.error(MessageFormat\r
-                                       .format("Illegal character ''{0}'' in folder name ''{1}'' of federation registration {2}!",\r
-                                                       c, registrationFolder, registration.name));\r
-                       return;\r
-               }\r
-               IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);\r
-               File repositoriesFolder = repositoryManager.getRepositoriesFolder();\r
-               File registrationFolderFile = new File(repositoriesFolder, registrationFolder);\r
-               registrationFolderFile.mkdirs();\r
-\r
-               // Clone/Pull the repository\r
-               for (Map.Entry<String, RepositoryModel> entry : repositories.entrySet()) {\r
-                       String cloneUrl = entry.getKey();\r
-                       RepositoryModel repository = entry.getValue();\r
-                       if (!repository.hasCommits) {\r
-                               logger.warn(MessageFormat.format(\r
-                                               "Skipping federated repository {0} from {1} @ {2}. Repository is EMPTY.",\r
-                                               repository.name, registration.name, registration.url));\r
-                               registration.updateStatus(repository, FederationPullStatus.SKIPPED);\r
-                               continue;\r
-                       }\r
-\r
-                       // Determine local repository name\r
-                       String repositoryName;\r
-                       if (StringUtils.isEmpty(registrationFolder)) {\r
-                               repositoryName = repository.name;\r
-                       } else {\r
-                               repositoryName = registrationFolder + "/" + repository.name;\r
-                       }\r
-\r
-                       if (registration.bare) {\r
-                               // bare repository, ensure .git suffix\r
-                               if (!repositoryName.toLowerCase().endsWith(DOT_GIT_EXT)) {\r
-                                       repositoryName += DOT_GIT_EXT;\r
-                               }\r
-                       } else {\r
-                               // normal repository, strip .git suffix\r
-                               if (repositoryName.toLowerCase().endsWith(DOT_GIT_EXT)) {\r
-                                       repositoryName = repositoryName.substring(0,\r
-                                                       repositoryName.indexOf(DOT_GIT_EXT));\r
-                               }\r
-                       }\r
-\r
-                       // confirm that the origin of any pre-existing repository matches\r
-                       // the clone url\r
-                       String fetchHead = null;\r
-                       Repository existingRepository = repositoryManager.getRepository(repositoryName);\r
-\r
-                       if (existingRepository == null && repositoryManager.isCollectingGarbage(repositoryName)) {\r
-                               logger.warn(MessageFormat.format("Skipping local repository {0}, busy collecting garbage", repositoryName));\r
-                               continue;\r
-                       }\r
-\r
-                       if (existingRepository != null) {\r
-                               StoredConfig config = existingRepository.getConfig();\r
-                               config.load();\r
-                               String origin = config.getString("remote", "origin", "url");\r
-                               RevCommit commit = JGitUtils.getCommit(existingRepository,\r
-                                               org.eclipse.jgit.lib.Constants.FETCH_HEAD);\r
-                               if (commit != null) {\r
-                                       fetchHead = commit.getName();\r
-                               }\r
-                               existingRepository.close();\r
-                               if (!origin.startsWith(registration.url)) {\r
-                                       logger.warn(MessageFormat\r
-                                                       .format("Skipping federated repository {0} from {1} @ {2}. Origin does not match, consider EXCLUDING.",\r
-                                                                       repository.name, registration.name, registration.url));\r
-                                       registration.updateStatus(repository, FederationPullStatus.SKIPPED);\r
-                                       continue;\r
-                               }\r
-                       }\r
-\r
-                       // clone/pull this repository\r
-                       CredentialsProvider credentials = new UsernamePasswordCredentialsProvider(\r
-                                       Constants.FEDERATION_USER, registration.token);\r
-                       logger.info(MessageFormat.format("Pulling federated repository {0} from {1} @ {2}",\r
-                                       repository.name, registration.name, registration.url));\r
-\r
-                       CloneResult result = JGitUtils.cloneRepository(registrationFolderFile, repository.name,\r
-                                       cloneUrl, registration.bare, credentials);\r
-                       Repository r = repositoryManager.getRepository(repositoryName);\r
-                       RepositoryModel rm = repositoryManager.getRepositoryModel(repositoryName);\r
-                       repository.isFrozen = registration.mirror;\r
-                       if (result.createdRepository) {\r
-                               // default local settings\r
-                               repository.federationStrategy = FederationStrategy.EXCLUDE;\r
-                               repository.isFrozen = registration.mirror;\r
-                               repository.showRemoteBranches = !registration.mirror;\r
-                               logger.info(MessageFormat.format("     cloning {0}", repository.name));\r
-                               registration.updateStatus(repository, FederationPullStatus.MIRRORED);\r
-                       } else {\r
-                               // fetch and update\r
-                               boolean fetched = false;\r
-                               RevCommit commit = JGitUtils.getCommit(r, org.eclipse.jgit.lib.Constants.FETCH_HEAD);\r
-                               String newFetchHead = commit.getName();\r
-                               fetched = fetchHead == null || !fetchHead.equals(newFetchHead);\r
-\r
-                               if (registration.mirror) {\r
-                                       // mirror\r
-                                       if (fetched) {\r
-                                               // update local branches to match the remote tracking branches\r
-                                               for (RefModel ref : JGitUtils.getRemoteBranches(r, false, -1)) {\r
-                                                       if (ref.displayName.startsWith("origin/")) {\r
-                                                               String branch = org.eclipse.jgit.lib.Constants.R_HEADS\r
-                                                                               + ref.displayName.substring(ref.displayName.indexOf('/') + 1);\r
-                                                               String hash = ref.getReferencedObjectId().getName();\r
-\r
-                                                               JGitUtils.setBranchRef(r, branch, hash);\r
-                                                               logger.info(MessageFormat.format("     resetting {0} of {1} to {2}", branch,\r
-                                                                               repository.name, hash));\r
-                                                       }\r
-                                               }\r
-\r
-                                               String newHead;\r
-                                               if (StringUtils.isEmpty(repository.HEAD)) {\r
-                                                       newHead = newFetchHead;\r
-                                               } else {\r
-                                                       newHead = repository.HEAD;\r
-                                               }\r
-                                               JGitUtils.setHEADtoRef(r, newHead);\r
-                                               logger.info(MessageFormat.format("     resetting HEAD of {0} to {1}",\r
-                                                               repository.name, newHead));\r
-                                               registration.updateStatus(repository, FederationPullStatus.MIRRORED);\r
-                                       } else {\r
-                                               // indicate no commits pulled\r
-                                               registration.updateStatus(repository, FederationPullStatus.NOCHANGE);\r
-                                       }\r
-                               } else {\r
-                                       // non-mirror\r
-                                       if (fetched) {\r
-                                               // indicate commits pulled to origin/master\r
-                                               registration.updateStatus(repository, FederationPullStatus.PULLED);\r
-                                       } else {\r
-                                               // indicate no commits pulled\r
-                                               registration.updateStatus(repository, FederationPullStatus.NOCHANGE);\r
-                                       }\r
-                               }\r
-\r
-                               // preserve local settings\r
-                               repository.isFrozen = rm.isFrozen;\r
-                               repository.federationStrategy = rm.federationStrategy;\r
-\r
-                               // merge federation sets\r
-                               Set<String> federationSets = new HashSet<String>();\r
-                               if (rm.federationSets != null) {\r
-                                       federationSets.addAll(rm.federationSets);\r
-                               }\r
-                               if (repository.federationSets != null) {\r
-                                       federationSets.addAll(repository.federationSets);\r
-                               }\r
-                               repository.federationSets = new ArrayList<String>(federationSets);\r
-\r
-                               // merge indexed branches\r
-                               Set<String> indexedBranches = new HashSet<String>();\r
-                               if (rm.indexedBranches != null) {\r
-                                       indexedBranches.addAll(rm.indexedBranches);\r
-                               }\r
-                               if (repository.indexedBranches != null) {\r
-                                       indexedBranches.addAll(repository.indexedBranches);\r
-                               }\r
-                               repository.indexedBranches = new ArrayList<String>(indexedBranches);\r
-\r
-                       }\r
-                       // only repositories that are actually _cloned_ from the origin\r
-                       // Gitblit repository are marked as federated. If the origin\r
-                       // is from somewhere else, these repositories are not considered\r
-                       // "federated" repositories.\r
-                       repository.isFederated = cloneUrl.startsWith(registration.url);\r
-\r
-                       repositoryManager.updateConfiguration(r, repository);\r
-                       r.close();\r
-               }\r
-\r
-               IUserManager userManager = GitBlit.getManager(IUserManager.class);\r
-               IGitblitManager gitblitManager = GitBlit.getManager(IGitblitManager.class);\r
-               IUserService userService = null;\r
-\r
-               try {\r
-                       // Pull USERS\r
-                       // TeamModels are automatically pulled because they are contained\r
-                       // within the UserModel. The UserService creates unknown teams\r
-                       // and updates existing teams.\r
-                       Collection<UserModel> users = FederationUtils.getUsers(registration);\r
-                       if (users != null && users.size() > 0) {\r
-                               File realmFile = new File(registrationFolderFile, registration.name + "_users.conf");\r
-                               realmFile.delete();\r
-                               userService = new ConfigUserService(realmFile);\r
-                               for (UserModel user : users) {\r
-                                       userService.updateUserModel(user.username, user);\r
-\r
-                                       // merge the origin permissions and origin accounts into\r
-                                       // the user accounts of this Gitblit instance\r
-                                       if (registration.mergeAccounts) {\r
-                                               // reparent all repository permissions if the local\r
-                                               // repositories are stored within subfolders\r
-                                               if (!StringUtils.isEmpty(registrationFolder)) {\r
-                                                       if (user.permissions != null) {\r
-                                                               // pulling from >= 1.2 version\r
-                                                               Map<String, AccessPermission> copy = new HashMap<String, AccessPermission>(user.permissions);\r
-                                                               user.permissions.clear();\r
-                                                               for (Map.Entry<String, AccessPermission> entry : copy.entrySet()) {\r
-                                                                       user.setRepositoryPermission(registrationFolder + "/" + entry.getKey(), entry.getValue());\r
-                                                               }\r
-                                                       } else {\r
-                                                               // pulling from <= 1.1 version\r
-                                                               List<String> permissions = new ArrayList<String>(user.repositories);\r
-                                                               user.repositories.clear();\r
-                                                               for (String permission : permissions) {\r
-                                                                       user.addRepositoryPermission(registrationFolder + "/" + permission);\r
-                                                               }\r
-                                                       }\r
-                                               }\r
-\r
-                                               // insert new user or update local user\r
-                                               UserModel localUser = userManager.getUserModel(user.username);\r
-                                               if (localUser == null) {\r
-                                                       // create new local user\r
-                                                       gitblitManager.updateUserModel(user.username, user, true);\r
-                                               } else {\r
-                                                       // update repository permissions of local user\r
-                                                       if (user.permissions != null) {\r
-                                                               // pulling from >= 1.2 version\r
-                                                               Map<String, AccessPermission> copy = new HashMap<String, AccessPermission>(user.permissions);\r
-                                                               for (Map.Entry<String, AccessPermission> entry : copy.entrySet()) {\r
-                                                                       localUser.setRepositoryPermission(entry.getKey(), entry.getValue());\r
-                                                               }\r
-                                                       } else {\r
-                                                               // pulling from <= 1.1 version\r
-                                                               for (String repository : user.repositories) {\r
-                                                                       localUser.addRepositoryPermission(repository);\r
-                                                               }\r
-                                                       }\r
-                                                       localUser.password = user.password;\r
-                                                       localUser.canAdmin = user.canAdmin;\r
-                                                       gitblitManager.updateUserModel(localUser.username, localUser, false);\r
-                                               }\r
-\r
-                                               for (String teamname : userManager.getAllTeamNames()) {\r
-                                                       TeamModel team = userManager.getTeamModel(teamname);\r
-                                                       if (user.isTeamMember(teamname) && !team.hasUser(user.username)) {\r
-                                                               // new team member\r
-                                                               team.addUser(user.username);\r
-                                                               userManager.updateTeamModel(teamname, team);\r
-                                                       } else if (!user.isTeamMember(teamname) && team.hasUser(user.username)) {\r
-                                                               // remove team member\r
-                                                               team.removeUser(user.username);\r
-                                                               userManager.updateTeamModel(teamname, team);\r
-                                                       }\r
-\r
-                                                       // update team repositories\r
-                                                       TeamModel remoteTeam = user.getTeam(teamname);\r
-                                                       if (remoteTeam != null) {\r
-                                                               if (remoteTeam.permissions != null) {\r
-                                                                       // pulling from >= 1.2\r
-                                                                       for (Map.Entry<String, AccessPermission> entry : remoteTeam.permissions.entrySet()){\r
-                                                                               team.setRepositoryPermission(entry.getKey(), entry.getValue());\r
-                                                                       }\r
-                                                                       userManager.updateTeamModel(teamname, team);\r
-                                                               } else if(!ArrayUtils.isEmpty(remoteTeam.repositories)) {\r
-                                                                       // pulling from <= 1.1\r
-                                                                       team.addRepositoryPermissions(remoteTeam.repositories);\r
-                                                                       userManager.updateTeamModel(teamname, team);\r
-                                                               }\r
-                                                       }\r
-                                               }\r
-                                       }\r
-                               }\r
-                       }\r
-               } catch (ForbiddenException e) {\r
-                       // ignore forbidden exceptions\r
-               } catch (IOException e) {\r
-                       logger.warn(MessageFormat.format(\r
-                                       "Failed to retrieve USERS from federated gitblit ({0} @ {1})",\r
-                                       registration.name, registration.url), e);\r
-               }\r
-\r
-               try {\r
-                       // Pull TEAMS\r
-                       // We explicitly pull these even though they are embedded in\r
-                       // UserModels because it is possible to use teams to specify\r
-                       // mailing lists or push scripts without specifying users.\r
-                       if (userService != null) {\r
-                               Collection<TeamModel> teams = FederationUtils.getTeams(registration);\r
-                               if (teams != null && teams.size() > 0) {\r
-                                       for (TeamModel team : teams) {\r
-                                               userService.updateTeamModel(team);\r
-                                       }\r
-                               }\r
-                       }\r
-               } catch (ForbiddenException e) {\r
-                       // ignore forbidden exceptions\r
-               } catch (IOException e) {\r
-                       logger.warn(MessageFormat.format(\r
-                                       "Failed to retrieve TEAMS from federated gitblit ({0} @ {1})",\r
-                                       registration.name, registration.url), e);\r
-               }\r
-\r
-               try {\r
-                       // Pull SETTINGS\r
-                       Map<String, String> settings = FederationUtils.getSettings(registration);\r
-                       if (settings != null && settings.size() > 0) {\r
-                               Properties properties = new Properties();\r
-                               properties.putAll(settings);\r
-                               FileOutputStream os = new FileOutputStream(new File(registrationFolderFile,\r
-                                               registration.name + "_" + Constants.PROPERTIES_FILE));\r
-                               properties.store(os, null);\r
-                               os.close();\r
-                       }\r
-               } catch (ForbiddenException e) {\r
-                       // ignore forbidden exceptions\r
-               } catch (IOException e) {\r
-                       logger.warn(MessageFormat.format(\r
-                                       "Failed to retrieve SETTINGS from federated gitblit ({0} @ {1})",\r
-                                       registration.name, registration.url), e);\r
-               }\r
-\r
-               try {\r
-                       // Pull SCRIPTS\r
-                       Map<String, String> scripts = FederationUtils.getScripts(registration);\r
-                       if (scripts != null && scripts.size() > 0) {\r
-                               for (Map.Entry<String, String> script : scripts.entrySet()) {\r
-                                       String scriptName = script.getKey();\r
-                                       if (scriptName.endsWith(".groovy")) {\r
-                                               scriptName = scriptName.substring(0, scriptName.indexOf(".groovy"));\r
-                                       }\r
-                                       File file = new File(registrationFolderFile, registration.name + "_"\r
-                                                       + scriptName + ".groovy");\r
-                                       FileUtils.writeContent(file, script.getValue());\r
-                               }\r
-                       }\r
-               } catch (ForbiddenException e) {\r
-                       // ignore forbidden exceptions\r
-               } catch (IOException e) {\r
-                       logger.warn(MessageFormat.format(\r
-                                       "Failed to retrieve SCRIPTS from federated gitblit ({0} @ {1})",\r
-                                       registration.name, registration.url), e);\r
-               }\r
-       }\r
-\r
-       /**\r
-        * Sends a status acknowledgment to the origin Gitblit instance. This\r
-        * includes the results of the federated pull.\r
-        *\r
-        * @param registration\r
-        * @throws Exception\r
-        */\r
-       private void sendStatusAcknowledgment(FederationModel registration) throws Exception {\r
-               if (!registration.sendStatus) {\r
-                       // skip status acknowledgment\r
-                       return;\r
-               }\r
-               InetAddress addr = InetAddress.getLocalHost();\r
-               IStoredSettings settings = GitBlit.getManager(IRuntimeManager.class).getSettings();\r
-               String federationName = settings.getString(Keys.federation.name, null);\r
-               if (StringUtils.isEmpty(federationName)) {\r
-                       federationName = addr.getHostName();\r
-               }\r
-               FederationUtils.acknowledgeStatus(addr.getHostAddress(), registration);\r
-               logger.info(MessageFormat.format("Pull status sent to {0}", registration.url));\r
-       }\r
-\r
-       /**\r
-        * Schedules the next check of the federated Gitblit instance.\r
-        *\r
-        * @param registration\r
-        */\r
-       private void schedule(FederationModel registration) {\r
-               // schedule the next pull\r
-               int mins = TimeUtils.convertFrequencyToMinutes(registration.frequency);\r
-               registration.nextPull = new Date(System.currentTimeMillis() + (mins * 60 * 1000L));\r
-               GitBlit.self().executor()\r
-                               .schedule(new FederationPullExecutor(registration), mins, TimeUnit.MINUTES);\r
-               logger.info(MessageFormat.format(\r
-                               "Next pull of {0} @ {1} scheduled for {2,date,yyyy-MM-dd HH:mm}",\r
-                               registration.name, registration.url, registration.nextPull));\r
-       }\r
-}\r
+package com.gitblit;
+
+import static org.eclipse.jgit.lib.Constants.DOT_GIT_EXT;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.gitblit.Constants.AccessPermission;
+import com.gitblit.Constants.FederationPullStatus;
+import com.gitblit.Constants.FederationStrategy;
+import com.gitblit.GitBlitException.ForbiddenException;
+import com.gitblit.models.FederationModel;
+import com.gitblit.models.RefModel;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.TeamModel;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.ArrayUtils;
+import com.gitblit.utils.FederationUtils;
+import com.gitblit.utils.FileUtils;
+import com.gitblit.utils.JGitUtils;
+import com.gitblit.utils.JGitUtils.CloneResult;
+import com.gitblit.utils.StringUtils;
+
+public abstract class FederationPullExecutor implements Runnable {
+
+       Logger logger = LoggerFactory.getLogger(getClass());
+
+       Gitblit gitblit;
+
+       private final List<FederationModel> registrations;
+
+       /**
+        * Constructor for specifying a single federation registration. This
+        * constructor is used to schedule the next pull execution.
+        *
+        * @param provider
+        * @param registration
+        */
+       public FederationPullExecutor(FederationModel registration) {
+               this(Arrays.asList(registration));
+       }
+
+       /**
+        * Constructor to specify a group of federation registrations. This is
+        * normally used at startup to pull and then schedule the next update based
+        * on each registrations frequency setting.
+        *
+        * @param provider
+        * @param registrations
+        * @param isDaemon
+        *            if true, registrations are rescheduled in perpetuity. if
+        *            false, the federation pull operation is executed once.
+        */
+       public FederationPullExecutor(List<FederationModel> registrations) {
+               this.registrations = registrations;
+       }
+
+       public abstract void reschedule(FederationModel registration);
+
+       /**
+        * Run method for this pull executor.
+        */
+       @Override
+       public void run() {
+               for (FederationModel registration : registrations) {
+                       FederationPullStatus was = registration.getLowestStatus();
+                       try {
+                               Date now = new Date(System.currentTimeMillis());
+                               pull(registration);
+                               sendStatusAcknowledgment(registration);
+                               registration.lastPull = now;
+                               FederationPullStatus is = registration.getLowestStatus();
+                               if (is.ordinal() < was.ordinal()) {
+                                       // the status for this registration has downgraded
+                                       logger.warn("Federation pull status of {0} is now {1}", registration.name,
+                                                       is.name());
+                                       if (registration.notifyOnError) {
+                                               String message = "Federation pull of " + registration.name + " @ "
+                                                               + registration.url + " is now at " + is.name();
+                                               gitblit.sendMailToAdministrators(
+                                                               "Pull Status of " + registration.name + " is " + is.name(),
+                                                               message);
+                                       }
+                               }
+                       } catch (Throwable t) {
+                               logger.error(MessageFormat.format(
+                                               "Failed to pull from federated gitblit ({0} @ {1})", registration.name,
+                                               registration.url), t);
+                       } finally {
+                               reschedule(registration);
+                       }
+               }
+       }
+
+       /**
+        * Mirrors a repository and, optionally, the server's users, and/or
+        * configuration settings from a origin Gitblit instance.
+        *
+        * @param registration
+        * @throws Exception
+        */
+       private void pull(FederationModel registration) throws Exception {
+               Map<String, RepositoryModel> repositories = FederationUtils.getRepositories(registration,
+                               true);
+               String registrationFolder = registration.folder.toLowerCase().trim();
+               // confirm valid characters in server alias
+               Character c = StringUtils.findInvalidCharacter(registrationFolder);
+               if (c != null) {
+                       logger.error(MessageFormat
+                                       .format("Illegal character ''{0}'' in folder name ''{1}'' of federation registration {2}!",
+                                                       c, registrationFolder, registration.name));
+                       return;
+               }
+               File repositoriesFolder = gitblit.getRepositoriesFolder();
+               File registrationFolderFile = new File(repositoriesFolder, registrationFolder);
+               registrationFolderFile.mkdirs();
+
+               // Clone/Pull the repository
+               for (Map.Entry<String, RepositoryModel> entry : repositories.entrySet()) {
+                       String cloneUrl = entry.getKey();
+                       RepositoryModel repository = entry.getValue();
+                       if (!repository.hasCommits) {
+                               logger.warn(MessageFormat.format(
+                                               "Skipping federated repository {0} from {1} @ {2}. Repository is EMPTY.",
+                                               repository.name, registration.name, registration.url));
+                               registration.updateStatus(repository, FederationPullStatus.SKIPPED);
+                               continue;
+                       }
+
+                       // Determine local repository name
+                       String repositoryName;
+                       if (StringUtils.isEmpty(registrationFolder)) {
+                               repositoryName = repository.name;
+                       } else {
+                               repositoryName = registrationFolder + "/" + repository.name;
+                       }
+
+                       if (registration.bare) {
+                               // bare repository, ensure .git suffix
+                               if (!repositoryName.toLowerCase().endsWith(DOT_GIT_EXT)) {
+                                       repositoryName += DOT_GIT_EXT;
+                               }
+                       } else {
+                               // normal repository, strip .git suffix
+                               if (repositoryName.toLowerCase().endsWith(DOT_GIT_EXT)) {
+                                       repositoryName = repositoryName.substring(0,
+                                                       repositoryName.indexOf(DOT_GIT_EXT));
+                               }
+                       }
+
+                       // confirm that the origin of any pre-existing repository matches
+                       // the clone url
+                       String fetchHead = null;
+                       Repository existingRepository = gitblit.getRepository(repositoryName);
+
+                       if (existingRepository == null && gitblit.isCollectingGarbage(repositoryName)) {
+                               logger.warn(MessageFormat.format("Skipping local repository {0}, busy collecting garbage", repositoryName));
+                               continue;
+                       }
+
+                       if (existingRepository != null) {
+                               StoredConfig config = existingRepository.getConfig();
+                               config.load();
+                               String origin = config.getString("remote", "origin", "url");
+                               RevCommit commit = JGitUtils.getCommit(existingRepository,
+                                               org.eclipse.jgit.lib.Constants.FETCH_HEAD);
+                               if (commit != null) {
+                                       fetchHead = commit.getName();
+                               }
+                               existingRepository.close();
+                               if (!origin.startsWith(registration.url)) {
+                                       logger.warn(MessageFormat
+                                                       .format("Skipping federated repository {0} from {1} @ {2}. Origin does not match, consider EXCLUDING.",
+                                                                       repository.name, registration.name, registration.url));
+                                       registration.updateStatus(repository, FederationPullStatus.SKIPPED);
+                                       continue;
+                               }
+                       }
+
+                       // clone/pull this repository
+                       CredentialsProvider credentials = new UsernamePasswordCredentialsProvider(
+                                       Constants.FEDERATION_USER, registration.token);
+                       logger.info(MessageFormat.format("Pulling federated repository {0} from {1} @ {2}",
+                                       repository.name, registration.name, registration.url));
+
+                       CloneResult result = JGitUtils.cloneRepository(registrationFolderFile, repository.name,
+                                       cloneUrl, registration.bare, credentials);
+                       Repository r = gitblit.getRepository(repositoryName);
+                       RepositoryModel rm = gitblit.getRepositoryModel(repositoryName);
+                       repository.isFrozen = registration.mirror;
+                       if (result.createdRepository) {
+                               // default local settings
+                               repository.federationStrategy = FederationStrategy.EXCLUDE;
+                               repository.isFrozen = registration.mirror;
+                               repository.showRemoteBranches = !registration.mirror;
+                               logger.info(MessageFormat.format("     cloning {0}", repository.name));
+                               registration.updateStatus(repository, FederationPullStatus.MIRRORED);
+                       } else {
+                               // fetch and update
+                               boolean fetched = false;
+                               RevCommit commit = JGitUtils.getCommit(r, org.eclipse.jgit.lib.Constants.FETCH_HEAD);
+                               String newFetchHead = commit.getName();
+                               fetched = fetchHead == null || !fetchHead.equals(newFetchHead);
+
+                               if (registration.mirror) {
+                                       // mirror
+                                       if (fetched) {
+                                               // update local branches to match the remote tracking branches
+                                               for (RefModel ref : JGitUtils.getRemoteBranches(r, false, -1)) {
+                                                       if (ref.displayName.startsWith("origin/")) {
+                                                               String branch = org.eclipse.jgit.lib.Constants.R_HEADS
+                                                                               + ref.displayName.substring(ref.displayName.indexOf('/') + 1);
+                                                               String hash = ref.getReferencedObjectId().getName();
+
+                                                               JGitUtils.setBranchRef(r, branch, hash);
+                                                               logger.info(MessageFormat.format("     resetting {0} of {1} to {2}", branch,
+                                                                               repository.name, hash));
+                                                       }
+                                               }
+
+                                               String newHead;
+                                               if (StringUtils.isEmpty(repository.HEAD)) {
+                                                       newHead = newFetchHead;
+                                               } else {
+                                                       newHead = repository.HEAD;
+                                               }
+                                               JGitUtils.setHEADtoRef(r, newHead);
+                                               logger.info(MessageFormat.format("     resetting HEAD of {0} to {1}",
+                                                               repository.name, newHead));
+                                               registration.updateStatus(repository, FederationPullStatus.MIRRORED);
+                                       } else {
+                                               // indicate no commits pulled
+                                               registration.updateStatus(repository, FederationPullStatus.NOCHANGE);
+                                       }
+                               } else {
+                                       // non-mirror
+                                       if (fetched) {
+                                               // indicate commits pulled to origin/master
+                                               registration.updateStatus(repository, FederationPullStatus.PULLED);
+                                       } else {
+                                               // indicate no commits pulled
+                                               registration.updateStatus(repository, FederationPullStatus.NOCHANGE);
+                                       }
+                               }
+
+                               // preserve local settings
+                               repository.isFrozen = rm.isFrozen;
+                               repository.federationStrategy = rm.federationStrategy;
+
+                               // merge federation sets
+                               Set<String> federationSets = new HashSet<String>();
+                               if (rm.federationSets != null) {
+                                       federationSets.addAll(rm.federationSets);
+                               }
+                               if (repository.federationSets != null) {
+                                       federationSets.addAll(repository.federationSets);
+                               }
+                               repository.federationSets = new ArrayList<String>(federationSets);
+
+                               // merge indexed branches
+                               Set<String> indexedBranches = new HashSet<String>();
+                               if (rm.indexedBranches != null) {
+                                       indexedBranches.addAll(rm.indexedBranches);
+                               }
+                               if (repository.indexedBranches != null) {
+                                       indexedBranches.addAll(repository.indexedBranches);
+                               }
+                               repository.indexedBranches = new ArrayList<String>(indexedBranches);
+
+                       }
+                       // only repositories that are actually _cloned_ from the origin
+                       // Gitblit repository are marked as federated. If the origin
+                       // is from somewhere else, these repositories are not considered
+                       // "federated" repositories.
+                       repository.isFederated = cloneUrl.startsWith(registration.url);
+
+                       gitblit.updateConfiguration(r, repository);
+                       r.close();
+               }
+
+               IUserService userService = null;
+
+               try {
+                       // Pull USERS
+                       // TeamModels are automatically pulled because they are contained
+                       // within the UserModel. The UserService creates unknown teams
+                       // and updates existing teams.
+                       Collection<UserModel> users = FederationUtils.getUsers(registration);
+                       if (users != null && users.size() > 0) {
+                               File realmFile = new File(registrationFolderFile, registration.name + "_users.conf");
+                               realmFile.delete();
+                               userService = new ConfigUserService(realmFile);
+                               for (UserModel user : users) {
+                                       userService.updateUserModel(user.username, user);
+
+                                       // merge the origin permissions and origin accounts into
+                                       // the user accounts of this Gitblit instance
+                                       if (registration.mergeAccounts) {
+                                               // reparent all repository permissions if the local
+                                               // repositories are stored within subfolders
+                                               if (!StringUtils.isEmpty(registrationFolder)) {
+                                                       if (user.permissions != null) {
+                                                               // pulling from >= 1.2 version
+                                                               Map<String, AccessPermission> copy = new HashMap<String, AccessPermission>(user.permissions);
+                                                               user.permissions.clear();
+                                                               for (Map.Entry<String, AccessPermission> entry : copy.entrySet()) {
+                                                                       user.setRepositoryPermission(registrationFolder + "/" + entry.getKey(), entry.getValue());
+                                                               }
+                                                       } else {
+                                                               // pulling from <= 1.1 version
+                                                               List<String> permissions = new ArrayList<String>(user.repositories);
+                                                               user.repositories.clear();
+                                                               for (String permission : permissions) {
+                                                                       user.addRepositoryPermission(registrationFolder + "/" + permission);
+                                                               }
+                                                       }
+                                               }
+
+                                               // insert new user or update local user
+                                               UserModel localUser = gitblit.getUserModel(user.username);
+                                               if (localUser == null) {
+                                                       // create new local user
+                                                       gitblit.updateUserModel(user.username, user, true);
+                                               } else {
+                                                       // update repository permissions of local user
+                                                       if (user.permissions != null) {
+                                                               // pulling from >= 1.2 version
+                                                               Map<String, AccessPermission> copy = new HashMap<String, AccessPermission>(user.permissions);
+                                                               for (Map.Entry<String, AccessPermission> entry : copy.entrySet()) {
+                                                                       localUser.setRepositoryPermission(entry.getKey(), entry.getValue());
+                                                               }
+                                                       } else {
+                                                               // pulling from <= 1.1 version
+                                                               for (String repository : user.repositories) {
+                                                                       localUser.addRepositoryPermission(repository);
+                                                               }
+                                                       }
+                                                       localUser.password = user.password;
+                                                       localUser.canAdmin = user.canAdmin;
+                                                       gitblit.updateUserModel(localUser.username, localUser, false);
+                                               }
+
+                                               for (String teamname : gitblit.getAllTeamNames()) {
+                                                       TeamModel team = gitblit.getTeamModel(teamname);
+                                                       if (user.isTeamMember(teamname) && !team.hasUser(user.username)) {
+                                                               // new team member
+                                                               team.addUser(user.username);
+                                                               gitblit.updateTeamModel(teamname, team);
+                                                       } else if (!user.isTeamMember(teamname) && team.hasUser(user.username)) {
+                                                               // remove team member
+                                                               team.removeUser(user.username);
+                                                               gitblit.updateTeamModel(teamname, team);
+                                                       }
+
+                                                       // update team repositories
+                                                       TeamModel remoteTeam = user.getTeam(teamname);
+                                                       if (remoteTeam != null) {
+                                                               if (remoteTeam.permissions != null) {
+                                                                       // pulling from >= 1.2
+                                                                       for (Map.Entry<String, AccessPermission> entry : remoteTeam.permissions.entrySet()){
+                                                                               team.setRepositoryPermission(entry.getKey(), entry.getValue());
+                                                                       }
+                                                                       gitblit.updateTeamModel(teamname, team);
+                                                               } else if (!ArrayUtils.isEmpty(remoteTeam.repositories)) {
+                                                                       // pulling from <= 1.1
+                                                                       team.addRepositoryPermissions(remoteTeam.repositories);
+                                                                       gitblit.updateTeamModel(teamname, team);
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               } catch (ForbiddenException e) {
+                       // ignore forbidden exceptions
+               } catch (IOException e) {
+                       logger.warn(MessageFormat.format(
+                                       "Failed to retrieve USERS from federated gitblit ({0} @ {1})",
+                                       registration.name, registration.url), e);
+               }
+
+               try {
+                       // Pull TEAMS
+                       // We explicitly pull these even though they are embedded in
+                       // UserModels because it is possible to use teams to specify
+                       // mailing lists or push scripts without specifying users.
+                       if (userService != null) {
+                               Collection<TeamModel> teams = FederationUtils.getTeams(registration);
+                               if (teams != null && teams.size() > 0) {
+                                       for (TeamModel team : teams) {
+                                               userService.updateTeamModel(team);
+                                       }
+                               }
+                       }
+               } catch (ForbiddenException e) {
+                       // ignore forbidden exceptions
+               } catch (IOException e) {
+                       logger.warn(MessageFormat.format(
+                                       "Failed to retrieve TEAMS from federated gitblit ({0} @ {1})",
+                                       registration.name, registration.url), e);
+               }
+
+               try {
+                       // Pull SETTINGS
+                       Map<String, String> settings = FederationUtils.getSettings(registration);
+                       if (settings != null && settings.size() > 0) {
+                               Properties properties = new Properties();
+                               properties.putAll(settings);
+                               FileOutputStream os = new FileOutputStream(new File(registrationFolderFile,
+                                               registration.name + "_" + Constants.PROPERTIES_FILE));
+                               properties.store(os, null);
+                               os.close();
+                       }
+               } catch (ForbiddenException e) {
+                       // ignore forbidden exceptions
+               } catch (IOException e) {
+                       logger.warn(MessageFormat.format(
+                                       "Failed to retrieve SETTINGS from federated gitblit ({0} @ {1})",
+                                       registration.name, registration.url), e);
+               }
+
+               try {
+                       // Pull SCRIPTS
+                       Map<String, String> scripts = FederationUtils.getScripts(registration);
+                       if (scripts != null && scripts.size() > 0) {
+                               for (Map.Entry<String, String> script : scripts.entrySet()) {
+                                       String scriptName = script.getKey();
+                                       if (scriptName.endsWith(".groovy")) {
+                                               scriptName = scriptName.substring(0, scriptName.indexOf(".groovy"));
+                                       }
+                                       File file = new File(registrationFolderFile, registration.name + "_"
+                                                       + scriptName + ".groovy");
+                                       FileUtils.writeContent(file, script.getValue());
+                               }
+                       }
+               } catch (ForbiddenException e) {
+                       // ignore forbidden exceptions
+               } catch (IOException e) {
+                       logger.warn(MessageFormat.format(
+                                       "Failed to retrieve SCRIPTS from federated gitblit ({0} @ {1})",
+                                       registration.name, registration.url), e);
+               }
+       }
+
+       /**
+        * Sends a status acknowledgment to the origin Gitblit instance. This
+        * includes the results of the federated pull.
+        *
+        * @param registration
+        * @throws Exception
+        */
+       private void sendStatusAcknowledgment(FederationModel registration) throws Exception {
+               if (!registration.sendStatus) {
+                       // skip status acknowledgment
+                       return;
+               }
+               InetAddress addr = InetAddress.getLocalHost();
+               String federationName = gitblit.getSettings().getString(Keys.federation.name, null);
+               if (StringUtils.isEmpty(federationName)) {
+                       federationName = addr.getHostName();
+               }
+               FederationUtils.acknowledgeStatus(addr.getHostAddress(), registration);
+               logger.info(MessageFormat.format("Pull status sent to {0}", registration.url));
+       }
+}
\ No newline at end of file
index 493f8fce5ab0d897e95946cc6ff3bce256cc058d..ca676ff92fc777b7b91e951ddad05070334c9106 100644 (file)
  */
 package com.gitblit;
 
-import java.io.BufferedReader;
 import java.io.File;
-import java.io.FileFilter;
-import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.io.OutputStream;
-import java.lang.reflect.Type;
 import java.text.MessageFormat;
 import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
 
 import javax.naming.Context;
 import javax.naming.InitialContext;
 import javax.naming.NamingException;
 import javax.servlet.ServletContext;
 import javax.servlet.annotation.WebListener;
-import javax.servlet.http.HttpServletRequest;
 
-import org.apache.wicket.resource.ContextRelativeResource;
-import org.apache.wicket.util.resource.ResourceStreamNotFoundException;
-import org.slf4j.Logger;
-
-import com.gitblit.Constants.AccessPermission;
-import com.gitblit.Constants.AccessRestrictionType;
-import com.gitblit.Constants.FederationRequest;
-import com.gitblit.Constants.FederationToken;
 import com.gitblit.dagger.DaggerContextListener;
-import com.gitblit.fanout.FanoutNioService;
-import com.gitblit.fanout.FanoutService;
-import com.gitblit.fanout.FanoutSocketService;
-import com.gitblit.git.GitDaemon;
 import com.gitblit.git.GitServlet;
 import com.gitblit.manager.IFederationManager;
 import com.gitblit.manager.IGitblitManager;
@@ -67,80 +42,54 @@ import com.gitblit.manager.INotificationManager;
 import com.gitblit.manager.IProjectManager;
 import com.gitblit.manager.IRepositoryManager;
 import com.gitblit.manager.IRuntimeManager;
+import com.gitblit.manager.IServicesManager;
 import com.gitblit.manager.ISessionManager;
 import com.gitblit.manager.IUserManager;
-import com.gitblit.models.FederationModel;
-import com.gitblit.models.FederationProposal;
-import com.gitblit.models.FederationSet;
-import com.gitblit.models.GitClientApplication;
-import com.gitblit.models.RepositoryModel;
-import com.gitblit.models.RepositoryUrl;
-import com.gitblit.models.ServerSettings;
-import com.gitblit.models.SettingModel;
-import com.gitblit.models.TeamModel;
-import com.gitblit.models.UserModel;
-import com.gitblit.utils.ArrayUtils;
 import com.gitblit.utils.ContainerUtils;
-import com.gitblit.utils.FederationUtils;
-import com.gitblit.utils.HttpUtils;
-import com.gitblit.utils.JGitUtils;
-import com.gitblit.utils.JsonUtils;
-import com.gitblit.utils.ObjectCache;
 import com.gitblit.utils.StringUtils;
 import com.gitblit.wicket.GitblitWicketFilter;
-import com.gitblit.wicket.WicketUtils;
-import com.google.gson.Gson;
-import com.google.gson.JsonIOException;
-import com.google.gson.JsonSyntaxException;
-import com.google.gson.reflect.TypeToken;
 
 import dagger.ObjectGraph;
 
 /**
- * GitBlit is the servlet context listener singleton that acts as the core for
- * the web ui and the servlets. This class is either directly instantiated by
- * the GitBlitServer class (Gitblit GO) or is reflectively instantiated by the
- * servlet 3 container (Gitblit WAR or Express).
+ * This class is the main entry point for the entire webapp.  It is a singleton
+ * created manually by Gitblit GO or dynamically by the WAR/Express servlet
+ * container.  This class instantiates and starts all managers followed by
+ * instantiating and registering all servlets and filters.
  *
- * This class is the central logic processor for Gitblit. All settings, user
- * object, and repository object operations pass through this class.
+ * Leveraging Servlet 3 and Dagger static dependency injection allows Gitblit to
+ * be modular and completely code-driven rather then relying on the fragility of
+ * a web.xml descriptor and the static & monolithic design previously used.
  *
  * @author James Moger
  *
  */
 @WebListener
-public class GitBlit extends DaggerContextListener
-                                        implements IFederationManager,
-                                                               IGitblitManager {
+public class GitBlit extends DaggerContextListener {
 
        private static GitBlit gitblit;
 
-       private final IStoredSettings goSettings;
-
-       private final File goBaseFolder;
-
        private final List<IManager> managers = new ArrayList<IManager>();
 
-       private final ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(10);
-
-       private final List<FederationModel> federationRegistrations = Collections
-                       .synchronizedList(new ArrayList<FederationModel>());
-
-       private final ObjectCache<Collection<GitClientApplication>> clientApplications = new ObjectCache<Collection<GitClientApplication>>();
-
-       private final Map<String, FederationModel> federationPullResults = new ConcurrentHashMap<String, FederationModel>();
-
-       private IStoredSettings settings;
-
-       private FanoutService fanoutService;
+       private final IStoredSettings goSettings;
 
-       private GitDaemon gitDaemon;
+       private final File goBaseFolder;
 
+       /**
+        * Construct a Gitblit WAR/Express context.
+        */
        public GitBlit() {
                this.goSettings = null;
                this.goBaseFolder = null;
+               gitblit = this;
        }
 
+       /**
+        * Construct a Gitblit GO context.
+        *
+        * @param settings
+        * @param baseFolder
+        */
        public GitBlit(IStoredSettings settings, File baseFolder) {
                this.goSettings = settings;
                this.goBaseFolder = baseFolder;
@@ -148,20 +97,13 @@ public class GitBlit extends DaggerContextListener
        }
 
        /**
-        * Returns the Gitblit singleton.
+        * This method is only used for unit and integration testing.
         *
-        * @return gitblit singleton
+        * @param managerClass
+        * @return a manager
         */
-       public static GitBlit self() {
-               return gitblit;
-       }
-
        @SuppressWarnings("unchecked")
-       public static <X> X getManager(Class<X> managerClass) {
-               if (managerClass.isAssignableFrom(GitBlit.class)) {
-                       return (X) gitblit;
-               }
-
+       public static <X extends IManager> X getManager(Class<X> managerClass) {
                for (IManager manager : gitblit.managers) {
                        if (managerClass.isAssignableFrom(manager.getClass())) {
                                return (X) manager;
@@ -171,786 +113,15 @@ public class GitBlit extends DaggerContextListener
        }
 
        /**
-        * Returns the path of the proposals folder. This method checks to see if
-        * Gitblit is running on a cloud service and may return an adjusted path.
-        *
-        * @return the proposals folder path
-        */
-       @Override
-       public File getProposalsFolder() {
-               return getManager(IRuntimeManager.class).getFileOrFolder(Keys.federation.proposalsFolder, "${baseFolder}/proposals");
-       }
-
-       /**
-        * Returns a list of repository URLs and the user access permission.
-        *
-        * @param request
-        * @param user
-        * @param repository
-        * @return a list of repository urls
-        */
-       @Override
-       public List<RepositoryUrl> getRepositoryUrls(HttpServletRequest request, UserModel user, RepositoryModel repository) {
-               if (user == null) {
-                       user = UserModel.ANONYMOUS;
-               }
-               String username = StringUtils.encodeUsername(UserModel.ANONYMOUS.equals(user) ? "" : user.username);
-
-               List<RepositoryUrl> list = new ArrayList<RepositoryUrl>();
-               // http/https url
-               if (settings.getBoolean(Keys.git.enableGitServlet, true)) {
-                       AccessPermission permission = user.getRepositoryPermission(repository).permission;
-                       if (permission.exceeds(AccessPermission.NONE)) {
-                               list.add(new RepositoryUrl(getRepositoryUrl(request, username, repository), permission));
-                       }
-               }
-
-               // git daemon url
-               String gitDaemonUrl = getGitDaemonUrl(request, user, repository);
-               if (!StringUtils.isEmpty(gitDaemonUrl)) {
-                       AccessPermission permission = getGitDaemonAccessPermission(user, repository);
-                       if (permission.exceeds(AccessPermission.NONE)) {
-                               list.add(new RepositoryUrl(gitDaemonUrl, permission));
-                       }
-               }
-
-               // add all other urls
-               // {0} = repository
-               // {1} = username
-               for (String url : settings.getStrings(Keys.web.otherUrls)) {
-                       if (url.contains("{1}")) {
-                               // external url requires username, only add url IF we have one
-                               if(!StringUtils.isEmpty(username)) {
-                                       list.add(new RepositoryUrl(MessageFormat.format(url, repository.name, username), null));
-                               }
-                       } else {
-                               // external url does not require username
-                               list.add(new RepositoryUrl(MessageFormat.format(url, repository.name), null));
-                       }
-               }
-               return list;
-       }
-
-       protected String getRepositoryUrl(HttpServletRequest request, String username, RepositoryModel repository) {
-               StringBuilder sb = new StringBuilder();
-               sb.append(HttpUtils.getGitblitURL(request));
-               sb.append(Constants.GIT_PATH);
-               sb.append(repository.name);
-
-               // inject username into repository url if authentication is required
-               if (repository.accessRestriction.exceeds(AccessRestrictionType.NONE)
-                               && !StringUtils.isEmpty(username)) {
-                       sb.insert(sb.indexOf("://") + 3, username + "@");
-               }
-               return sb.toString();
-       }
-
-       protected String getGitDaemonUrl(HttpServletRequest request, UserModel user, RepositoryModel repository) {
-               if (gitDaemon != null) {
-                       String bindInterface = settings.getString(Keys.git.daemonBindInterface, "localhost");
-                       if (bindInterface.equals("localhost")
-                                       && (!request.getServerName().equals("localhost") && !request.getServerName().equals("127.0.0.1"))) {
-                               // git daemon is bound to localhost and the request is from elsewhere
-                               return null;
-                       }
-                       if (user.canClone(repository)) {
-                               String servername = request.getServerName();
-                               String url = gitDaemon.formatUrl(servername, repository.name);
-                               return url;
-                       }
-               }
-               return null;
-       }
-
-       protected AccessPermission getGitDaemonAccessPermission(UserModel user, RepositoryModel repository) {
-               if (gitDaemon != null && user.canClone(repository)) {
-                       AccessPermission gitDaemonPermission = user.getRepositoryPermission(repository).permission;
-                       if (gitDaemonPermission.atLeast(AccessPermission.CLONE)) {
-                               if (repository.accessRestriction.atLeast(AccessRestrictionType.CLONE)) {
-                                       // can not authenticate clone via anonymous git protocol
-                                       gitDaemonPermission = AccessPermission.NONE;
-                               } else if (repository.accessRestriction.atLeast(AccessRestrictionType.PUSH)) {
-                                       // can not authenticate push via anonymous git protocol
-                                       gitDaemonPermission = AccessPermission.CLONE;
-                               } else {
-                                       // normal user permission
-                               }
-                       }
-                       return gitDaemonPermission;
-               }
-               return AccessPermission.NONE;
-       }
-
-       /**
-        * Returns the list of custom client applications to be used for the
-        * repository url panel;
-        *
-        * @return a collection of client applications
-        */
-       @Override
-       public Collection<GitClientApplication> getClientApplications() {
-               // prefer user definitions, if they exist
-               File userDefs = new File(getManager(IRuntimeManager.class).getBaseFolder(), "clientapps.json");
-               if (userDefs.exists()) {
-                       Date lastModified = new Date(userDefs.lastModified());
-                       if (clientApplications.hasCurrent("user", lastModified)) {
-                               return clientApplications.getObject("user");
-                       } else {
-                               // (re)load user definitions
-                               try {
-                                       InputStream is = new FileInputStream(userDefs);
-                                       Collection<GitClientApplication> clients = readClientApplications(is);
-                                       is.close();
-                                       if (clients != null) {
-                                               clientApplications.updateObject("user", lastModified, clients);
-                                               return clients;
-                                       }
-                               } catch (IOException e) {
-                                       logger.error("Failed to deserialize " + userDefs.getAbsolutePath(), e);
-                               }
-                       }
-               }
-
-               // no user definitions, use system definitions
-               if (!clientApplications.hasCurrent("system", new Date(0))) {
-                       try {
-                               InputStream is = getClass().getResourceAsStream("/clientapps.json");
-                               Collection<GitClientApplication> clients = readClientApplications(is);
-                               is.close();
-                               if (clients != null) {
-                                       clientApplications.updateObject("system", new Date(0), clients);
-                               }
-                       } catch (IOException e) {
-                               logger.error("Failed to deserialize clientapps.json resource!", e);
-                       }
-               }
-
-               return clientApplications.getObject("system");
-       }
-
-       private Collection<GitClientApplication> readClientApplications(InputStream is) {
-               try {
-                       Type type = new TypeToken<Collection<GitClientApplication>>() {
-                       }.getType();
-                       InputStreamReader reader = new InputStreamReader(is);
-                       Gson gson = JsonUtils.gson();
-                       Collection<GitClientApplication> links = gson.fromJson(reader, type);
-                       return links;
-               } catch (JsonIOException e) {
-                       logger.error("Error deserializing client applications!", e);
-               } catch (JsonSyntaxException e) {
-                       logger.error("Error deserializing client applications!", e);
-               }
-               return null;
-       }
-
-       /**
-        * Open a file resource using the Servlet container.
-        * @param file to open
-        * @return InputStream of the opened file
-        * @throws ResourceStreamNotFoundException
-        */
-       public InputStream getResourceAsStream(String file) throws ResourceStreamNotFoundException {
-               ContextRelativeResource res = WicketUtils.getResource(file);
-               return res.getResourceStream().getInputStream();
-       }
-
-       @Override
-       public UserModel getFederationUser() {
-               // the federation user is an administrator
-               UserModel federationUser = new UserModel(Constants.FEDERATION_USER);
-               federationUser.canAdmin = true;
-               return federationUser;
-       }
-
-       /**
-        * Adds/updates a complete user object keyed by username. This method allows
-        * for renaming a user.
-        *
-        * @see IUserService.updateUserModel(String, UserModel)
-        * @param username
-        * @param user
-        * @param isCreate
-        * @throws GitBlitException
-        */
-       @Override
-       public void updateUserModel(String username, UserModel user, boolean isCreate)
-                       throws GitBlitException {
-               if (!username.equalsIgnoreCase(user.username)) {
-                       if (getManager(IUserManager.class).getUserModel(user.username) != null) {
-                               throw new GitBlitException(MessageFormat.format(
-                                               "Failed to rename ''{0}'' because ''{1}'' already exists.", username,
-                                               user.username));
-                       }
-
-                       // rename repositories and owner fields for all repositories
-                       for (RepositoryModel model : getManager(IRepositoryManager.class).getRepositoryModels(user)) {
-                               if (model.isUsersPersonalRepository(username)) {
-                                       // personal repository
-                                       model.addOwner(user.username);
-                                       String oldRepositoryName = model.name;
-                                       model.name = user.getPersonalPath() + model.name.substring(model.projectPath.length());
-                                       model.projectPath = user.getPersonalPath();
-                                       getManager(IRepositoryManager.class).updateRepositoryModel(oldRepositoryName, model, false);
-                               } else if (model.isOwner(username)) {
-                                       // common/shared repo
-                                       model.addOwner(user.username);
-                                       getManager(IRepositoryManager.class).updateRepositoryModel(model.name, model, false);
-                               }
-                       }
-               }
-               if (!getManager(IUserManager.class).updateUserModel(username, user)) {
-                       throw new GitBlitException(isCreate ? "Failed to add user!" : "Failed to update user!");
-               }
-       }
-
-       /**
-        * Updates the TeamModel object for the specified name.
-        *
-        * @param teamname
-        * @param team
-        * @param isCreate
-        */
-       @Override
-       public void updateTeamModel(String teamname, TeamModel team, boolean isCreate)
-                       throws GitBlitException {
-               if (!teamname.equalsIgnoreCase(team.name)) {
-                       if (getManager(IUserManager.class).getTeamModel(team.name) != null) {
-                               throw new GitBlitException(MessageFormat.format(
-                                               "Failed to rename ''{0}'' because ''{1}'' already exists.", teamname,
-                                               team.name));
-                       }
-               }
-               if (!getManager(IUserManager.class).updateTeamModel(teamname, team)) {
-                       throw new GitBlitException(isCreate ? "Failed to add team!" : "Failed to update team!");
-               }
-       }
-
-
-       /**
-        * Returns Gitblit's scheduled executor service for scheduling tasks.
-        *
-        * @return scheduledExecutor
-        */
-       public ScheduledExecutorService executor() {
-               return scheduledExecutor;
-       }
-
-       @Override
-       public boolean canFederate() {
-               String passphrase = settings.getString(Keys.federation.passphrase, "");
-               return !StringUtils.isEmpty(passphrase);
-       }
-
-       /**
-        * Configures this Gitblit instance to pull any registered federated gitblit
-        * instances.
-        */
-       private void configureFederation() {
-               boolean validPassphrase = true;
-               String passphrase = settings.getString(Keys.federation.passphrase, "");
-               if (StringUtils.isEmpty(passphrase)) {
-                       logger.warn("Federation passphrase is blank! This server can not be PULLED from.");
-                       validPassphrase = false;
-               }
-               if (validPassphrase) {
-                       // standard tokens
-                       for (FederationToken tokenType : FederationToken.values()) {
-                               logger.info(MessageFormat.format("Federation {0} token = {1}", tokenType.name(),
-                                               getFederationToken(tokenType)));
-                       }
-
-                       // federation set tokens
-                       for (String set : settings.getStrings(Keys.federation.sets)) {
-                               logger.info(MessageFormat.format("Federation Set {0} token = {1}", set,
-                                               getFederationToken(set)));
-                       }
-               }
-
-               // Schedule the federation executor
-               List<FederationModel> registrations = getFederationRegistrations();
-               if (registrations.size() > 0) {
-                       FederationPullExecutor executor = new FederationPullExecutor(registrations, true);
-                       scheduledExecutor.schedule(executor, 1, TimeUnit.MINUTES);
-               }
-       }
-
-       /**
-        * Returns the list of federated gitblit instances that this instance will
-        * try to pull.
-        *
-        * @return list of registered gitblit instances
-        */
-       @Override
-       public List<FederationModel> getFederationRegistrations() {
-               if (federationRegistrations.isEmpty()) {
-                       federationRegistrations.addAll(FederationUtils.getFederationRegistrations(settings));
-               }
-               return federationRegistrations;
-       }
-
-       /**
-        * Retrieve the specified federation registration.
-        *
-        * @param name
-        *            the name of the registration
-        * @return a federation registration
-        */
-       @Override
-       public FederationModel getFederationRegistration(String url, String name) {
-               // check registrations
-               for (FederationModel r : getFederationRegistrations()) {
-                       if (r.name.equals(name) && r.url.equals(url)) {
-                               return r;
-                       }
-               }
-
-               // check the results
-               for (FederationModel r : getFederationResultRegistrations()) {
-                       if (r.name.equals(name) && r.url.equals(url)) {
-                               return r;
-                       }
-               }
-               return null;
-       }
-
-       /**
-        * Returns the list of federation sets.
-        *
-        * @return list of federation sets
-        */
-       @Override
-       public List<FederationSet> getFederationSets(String gitblitUrl) {
-               List<FederationSet> list = new ArrayList<FederationSet>();
-               // generate standard tokens
-               for (FederationToken type : FederationToken.values()) {
-                       FederationSet fset = new FederationSet(type.toString(), type, getFederationToken(type));
-                       fset.repositories = getRepositories(gitblitUrl, fset.token);
-                       list.add(fset);
-               }
-               // generate tokens for federation sets
-               for (String set : settings.getStrings(Keys.federation.sets)) {
-                       FederationSet fset = new FederationSet(set, FederationToken.REPOSITORIES,
-                                       getFederationToken(set));
-                       fset.repositories = getRepositories(gitblitUrl, fset.token);
-                       list.add(fset);
-               }
-               return list;
-       }
-
-       /**
-        * Returns the list of possible federation tokens for this Gitblit instance.
-        *
-        * @return list of federation tokens
-        */
-       @Override
-       public List<String> getFederationTokens() {
-               List<String> tokens = new ArrayList<String>();
-               // generate standard tokens
-               for (FederationToken type : FederationToken.values()) {
-                       tokens.add(getFederationToken(type));
-               }
-               // generate tokens for federation sets
-               for (String set : settings.getStrings(Keys.federation.sets)) {
-                       tokens.add(getFederationToken(set));
-               }
-               return tokens;
-       }
-
-       /**
-        * Returns the specified federation token for this Gitblit instance.
-        *
-        * @param type
-        * @return a federation token
-        */
-       @Override
-       public String getFederationToken(FederationToken type) {
-               return getFederationToken(type.name());
-       }
-
-       /**
-        * Returns the specified federation token for this Gitblit instance.
-        *
-        * @param value
-        * @return a federation token
-        */
-       @Override
-       public String getFederationToken(String value) {
-               String passphrase = settings.getString(Keys.federation.passphrase, "");
-               return StringUtils.getSHA1(passphrase + "-" + value);
-       }
-
-       /**
-        * Compares the provided token with this Gitblit instance's tokens and
-        * determines if the requested permission may be granted to the token.
-        *
-        * @param req
-        * @param token
-        * @return true if the request can be executed
-        */
-       @Override
-       public boolean validateFederationRequest(FederationRequest req, String token) {
-               String all = getFederationToken(FederationToken.ALL);
-               String unr = getFederationToken(FederationToken.USERS_AND_REPOSITORIES);
-               String jur = getFederationToken(FederationToken.REPOSITORIES);
-               switch (req) {
-               case PULL_REPOSITORIES:
-                       return token.equals(all) || token.equals(unr) || token.equals(jur);
-               case PULL_USERS:
-               case PULL_TEAMS:
-                       return token.equals(all) || token.equals(unr);
-               case PULL_SETTINGS:
-               case PULL_SCRIPTS:
-                       return token.equals(all);
-               default:
-                       break;
-               }
-               return false;
-       }
-
-       /**
-        * Acknowledge and cache the status of a remote Gitblit instance.
-        *
-        * @param identification
-        *            the identification of the pulling Gitblit instance
-        * @param registration
-        *            the registration from the pulling Gitblit instance
-        * @return true if acknowledged
-        */
-       @Override
-       public boolean acknowledgeFederationStatus(String identification, FederationModel registration) {
-               // reset the url to the identification of the pulling Gitblit instance
-               registration.url = identification;
-               String id = identification;
-               if (!StringUtils.isEmpty(registration.folder)) {
-                       id += "-" + registration.folder;
-               }
-               federationPullResults.put(id, registration);
-               return true;
-       }
-
-       /**
-        * Returns the list of registration results.
-        *
-        * @return the list of registration results
-        */
-       @Override
-       public List<FederationModel> getFederationResultRegistrations() {
-               return new ArrayList<FederationModel>(federationPullResults.values());
-       }
-
-       /**
-        * Submit a federation proposal. The proposal is cached locally and the
-        * Gitblit administrator(s) are notified via email.
-        *
-        * @param proposal
-        *            the proposal
-        * @param gitblitUrl
-        *            the url of your gitblit instance to send an email to
-        *            administrators
-        * @return true if the proposal was submitted
-        */
-       @Override
-       public boolean submitFederationProposal(FederationProposal proposal, String gitblitUrl) {
-               // convert proposal to json
-               String json = JsonUtils.toJsonString(proposal);
-
-               try {
-                       // make the proposals folder
-                       File proposalsFolder = getProposalsFolder();
-                       proposalsFolder.mkdirs();
-
-                       // cache json to a file
-                       File file = new File(proposalsFolder, proposal.token + Constants.PROPOSAL_EXT);
-                       com.gitblit.utils.FileUtils.writeContent(file, json);
-               } catch (Exception e) {
-                       logger.error(MessageFormat.format("Failed to cache proposal from {0}", proposal.url), e);
-               }
-
-               // send an email, if possible
-               getManager(INotificationManager.class).sendMailToAdministrators("Federation proposal from " + proposal.url,
-                               "Please review the proposal @ " + gitblitUrl + "/proposal/" + proposal.token);
-               return true;
-       }
-
-       /**
-        * Returns the list of pending federation proposals
-        *
-        * @return list of federation proposals
-        */
-       @Override
-       public List<FederationProposal> getPendingFederationProposals() {
-               List<FederationProposal> list = new ArrayList<FederationProposal>();
-               File folder = getProposalsFolder();
-               if (folder.exists()) {
-                       File[] files = folder.listFiles(new FileFilter() {
-                               @Override
-                               public boolean accept(File file) {
-                                       return file.isFile()
-                                                       && file.getName().toLowerCase().endsWith(Constants.PROPOSAL_EXT);
-                               }
-                       });
-                       for (File file : files) {
-                               String json = com.gitblit.utils.FileUtils.readContent(file, null);
-                               FederationProposal proposal = JsonUtils.fromJsonString(json,
-                                               FederationProposal.class);
-                               list.add(proposal);
-                       }
-               }
-               return list;
-       }
-
-       /**
-        * Get repositories for the specified token.
-        *
-        * @param gitblitUrl
-        *            the base url of this gitblit instance
-        * @param token
-        *            the federation token
-        * @return a map of <cloneurl, RepositoryModel>
-        */
-       @Override
-       public Map<String, RepositoryModel> getRepositories(String gitblitUrl, String token) {
-               Map<String, String> federationSets = new HashMap<String, String>();
-               for (String set : settings.getStrings(Keys.federation.sets)) {
-                       federationSets.put(getFederationToken(set), set);
-               }
-
-               // Determine the Gitblit clone url
-               StringBuilder sb = new StringBuilder();
-               sb.append(gitblitUrl);
-               sb.append(Constants.GIT_PATH);
-               sb.append("{0}");
-               String cloneUrl = sb.toString();
-
-               // Retrieve all available repositories
-               UserModel user = getFederationUser();
-               List<RepositoryModel> list = getManager(IRepositoryManager.class).getRepositoryModels(user);
-
-               // create the [cloneurl, repositoryModel] map
-               Map<String, RepositoryModel> repositories = new HashMap<String, RepositoryModel>();
-               for (RepositoryModel model : list) {
-                       // by default, setup the url for THIS repository
-                       String url = MessageFormat.format(cloneUrl, model.name);
-                       switch (model.federationStrategy) {
-                       case EXCLUDE:
-                               // skip this repository
-                               continue;
-                       case FEDERATE_ORIGIN:
-                               // federate the origin, if it is defined
-                               if (!StringUtils.isEmpty(model.origin)) {
-                                       url = model.origin;
-                               }
-                               break;
-                       default:
-                               break;
-                       }
-
-                       if (federationSets.containsKey(token)) {
-                               // include repositories only for federation set
-                               String set = federationSets.get(token);
-                               if (model.federationSets.contains(set)) {
-                                       repositories.put(url, model);
-                               }
-                       } else {
-                               // standard federation token for ALL
-                               repositories.put(url, model);
-                       }
-               }
-               return repositories;
-       }
-
-       /**
-        * Creates a proposal from the token.
-        *
-        * @param gitblitUrl
-        *            the url of this Gitblit instance
-        * @param token
-        * @return a potential proposal
-        */
-       @Override
-       public FederationProposal createFederationProposal(String gitblitUrl, String token) {
-               FederationToken tokenType = FederationToken.REPOSITORIES;
-               for (FederationToken type : FederationToken.values()) {
-                       if (token.equals(getFederationToken(type))) {
-                               tokenType = type;
-                               break;
-                       }
-               }
-               Map<String, RepositoryModel> repositories = getRepositories(gitblitUrl, token);
-               FederationProposal proposal = new FederationProposal(gitblitUrl, tokenType, token,
-                               repositories);
-               return proposal;
-       }
-
-       /**
-        * Returns the proposal identified by the supplied token.
-        *
-        * @param token
-        * @return the specified proposal or null
-        */
-       @Override
-       public FederationProposal getPendingFederationProposal(String token) {
-               List<FederationProposal> list = getPendingFederationProposals();
-               for (FederationProposal proposal : list) {
-                       if (proposal.token.equals(token)) {
-                               return proposal;
-                       }
-               }
-               return null;
-       }
-
-       /**
-        * Deletes a pending federation proposal.
-        *
-        * @param a
-        *            proposal
-        * @return true if the proposal was deleted
+        * Returns Gitblit's Dagger injection modules.
         */
        @Override
-       public boolean deletePendingFederationProposal(FederationProposal proposal) {
-               File folder = getProposalsFolder();
-               File file = new File(folder, proposal.token + Constants.PROPOSAL_EXT);
-               return file.delete();
-       }
-
-       /**
-        * Parse the properties file and aggregate all the comments by the setting
-        * key. A setting model tracks the current value, the default value, the
-        * description of the setting and and directives about the setting.
-        *
-        * @return Map<String, SettingModel>
-        */
-       private ServerSettings loadSettingModels(ServerSettings settingsModel) {
-               // this entire "supports" concept will go away with user service refactoring
-               UserModel externalUser = new UserModel(Constants.EXTERNAL_ACCOUNT);
-               externalUser.password = Constants.EXTERNAL_ACCOUNT;
-               IUserManager userManager = getManager(IUserManager.class);
-               settingsModel.supportsCredentialChanges = userManager.supportsCredentialChanges(externalUser);
-               settingsModel.supportsDisplayNameChanges = userManager.supportsDisplayNameChanges(externalUser);
-               settingsModel.supportsEmailAddressChanges = userManager.supportsEmailAddressChanges(externalUser);
-               settingsModel.supportsTeamMembershipChanges = userManager.supportsTeamMembershipChanges(externalUser);
-               try {
-                       // Read bundled Gitblit properties to extract setting descriptions.
-                       // This copy is pristine and only used for populating the setting
-                       // models map.
-                       InputStream is = getClass().getResourceAsStream("/reference.properties");
-                       BufferedReader propertiesReader = new BufferedReader(new InputStreamReader(is));
-                       StringBuilder description = new StringBuilder();
-                       SettingModel setting = new SettingModel();
-                       String line = null;
-                       while ((line = propertiesReader.readLine()) != null) {
-                               if (line.length() == 0) {
-                                       description.setLength(0);
-                                       setting = new SettingModel();
-                               } else {
-                                       if (line.charAt(0) == '#') {
-                                               if (line.length() > 1) {
-                                                       String text = line.substring(1).trim();
-                                                       if (SettingModel.CASE_SENSITIVE.equals(text)) {
-                                                               setting.caseSensitive = true;
-                                                       } else if (SettingModel.RESTART_REQUIRED.equals(text)) {
-                                                               setting.restartRequired = true;
-                                                       } else if (SettingModel.SPACE_DELIMITED.equals(text)) {
-                                                               setting.spaceDelimited = true;
-                                                       } else if (text.startsWith(SettingModel.SINCE)) {
-                                                               try {
-                                                                       setting.since = text.split(" ")[1];
-                                                               } catch (Exception e) {
-                                                                       setting.since = text;
-                                                               }
-                                                       } else {
-                                                               description.append(text);
-                                                               description.append('\n');
-                                                       }
-                                               }
-                                       } else {
-                                               String[] kvp = line.split("=", 2);
-                                               String key = kvp[0].trim();
-                                               setting.name = key;
-                                               setting.defaultValue = kvp[1].trim();
-                                               setting.currentValue = setting.defaultValue;
-                                               setting.description = description.toString().trim();
-                                               settingsModel.add(setting);
-                                               description.setLength(0);
-                                               setting = new SettingModel();
-                                       }
-                               }
-                       }
-                       propertiesReader.close();
-               } catch (NullPointerException e) {
-                       logger.error("Failed to find resource copy of gitblit.properties");
-               } catch (IOException e) {
-                       logger.error("Failed to load resource copy of gitblit.properties");
-               }
-               return settingsModel;
-       }
-
-       protected void configureFanout() {
-               // 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 configureGitDaemon() {
-               int port = settings.getInteger(Keys.git.daemonPort, 0);
-               String bindInterface = settings.getString(Keys.git.daemonBindInterface, "localhost");
-               if (port > 0) {
-                       try {
-                               // HACK temporary pending manager separation and injection
-                               Gitblit gitblit = new Gitblit(
-                                               getManager(IRuntimeManager.class),
-                                               getManager(INotificationManager.class),
-                                               getManager(IUserManager.class),
-                                               getManager(ISessionManager.class),
-                                               getManager(IRepositoryManager.class),
-                                               getManager(IProjectManager.class),
-                                               this,
-                                               this);
-                               gitDaemon = new GitDaemon(gitblit);
-                               gitDaemon.start();
-                       } catch (IOException e) {
-                               gitDaemon = null;
-                               logger.error(MessageFormat.format("Failed to start Git daemon on {0}:{1,number,0}", bindInterface, port), e);
-                       }
-               }
-       }
-
-       protected final Logger getLogger() {
-               return logger;
-       }
-
-       protected final ScheduledExecutorService getScheduledExecutor() {
-               return scheduledExecutor;
+       protected Object [] getModules() {
+               return new Object [] { new DaggerModule() };
        }
 
        /**
-        * Configure Gitblit from the web.xml, if no configuration has already been
-        * specified.
-        *
-        * @see ServletContextListener.contextInitialize(ServletContextEvent)
+        * Prepare runtime settings and start all manager instances.
         */
        @Override
        protected void beforeServletInjection(ServletContext context) {
@@ -958,12 +129,10 @@ public class GitBlit extends DaggerContextListener
 
                // create the runtime settings object
                IStoredSettings runtimeSettings = injector.get(IStoredSettings.class);
-               this.settings = runtimeSettings; // XXX remove me eventually
                final File baseFolder;
 
                if (goSettings != null) {
                        // Gitblit GO
-                       logger.debug("configuring Gitblit GO");
                        baseFolder = configureGO(context, goSettings, goBaseFolder, runtimeSettings);
                } else {
                        // servlet container
@@ -973,11 +142,9 @@ public class GitBlit extends DaggerContextListener
 
                        if (!StringUtils.isEmpty(System.getenv("OPENSHIFT_DATA_DIR"))) {
                                // RedHat OpenShift
-                               logger.debug("configuring Gitblit Express");
                                baseFolder = configureExpress(context, webxmlSettings, contextFolder, runtimeSettings);
                        } else {
                                // standard WAR
-                               logger.debug("configuring Gitblit WAR");
                                baseFolder = configureWAR(context, webxmlSettings, contextFolder, runtimeSettings);
                        }
 
@@ -985,27 +152,85 @@ public class GitBlit extends DaggerContextListener
                        ContainerUtils.CVE_2007_0450.test(runtimeSettings);
                }
 
-               // Runtime manager is a container for settings and other parameters
-               IRuntimeManager runtime = startManager(injector, IRuntimeManager.class);
+               // Manually configure IRuntimeManager
+               logManager(IRuntimeManager.class);
+               IRuntimeManager runtime = injector.get(IRuntimeManager.class);
                runtime.setBaseFolder(baseFolder);
                runtime.getStatus().isGO = goSettings != null;
                runtime.getStatus().servletContainer = context.getServerInfo();
+               runtime.start();
+               managers.add(runtime);
 
+               // start all other managers
                startManager(injector, INotificationManager.class);
                startManager(injector, IUserManager.class);
                startManager(injector, ISessionManager.class);
                startManager(injector, IRepositoryManager.class);
                startManager(injector, IProjectManager.class);
+               startManager(injector, IGitblitManager.class);
+               startManager(injector, IFederationManager.class);
+               startManager(injector, IServicesManager.class);
+
+               logger.info("");
+               logger.info("All managers started.");
+               logger.info("");
+       }
+
+       protected <X extends IManager> X startManager(ObjectGraph injector, Class<X> clazz) {
+               logManager(clazz);
+               X x = injector.get(clazz);
+               x.start();
+               managers.add(x);
+               return x;
+       }
+
+       protected void logManager(Class<? extends IManager> clazz) {
+               logger.info("");
+               logger.info("----[{}]----", clazz.getName());
+       }
+
+       /**
+        * Instantiate and inject all filters and servlets into the container using
+        * the servlet 3 specification.
+        */
+       @Override
+       protected void injectServlets(ServletContext context) {
+               // access restricted servlets
+               serve(context, Constants.GIT_PATH, GitServlet.class, GitFilter.class);
+               serve(context, Constants.PAGES, PagesServlet.class, PagesFilter.class);
+               serve(context, Constants.RPC_PATH, RpcServlet.class, RpcFilter.class);
+               serve(context, Constants.ZIP_PATH, DownloadZipServlet.class, DownloadZipFilter.class);
+               serve(context, Constants.SYNDICATION_PATH, SyndicationServlet.class, SyndicationFilter.class);
+
+               // servlets
+               serve(context, Constants.FEDERATION_PATH, FederationServlet.class);
+               serve(context, Constants.SPARKLESHARE_INVITE_PATH, SparkleShareInviteServlet.class);
+               serve(context, Constants.BRANCH_GRAPH_PATH, BranchGraphServlet.class);
+               file(context, "/robots.txt", RobotsTxtServlet.class);
+               file(context, "/logo.png", LogoServlet.class);
 
-               logger.info("Gitblit base folder     = " + baseFolder.getAbsolutePath());
+               // optional force basic authentication
+               filter(context, "/*", EnforceAuthenticationFilter.class, null);
 
-               loadSettingModels(runtime.getSettingsModel());
+               // Wicket
+               String toIgnore = StringUtils.flattenStrings(getRegisteredPaths(), ",");
+               Map<String, String> params = new HashMap<String, String>();
+               params.put(GitblitWicketFilter.FILTER_MAPPING_PARAM, "/*");
+               params.put(GitblitWicketFilter.IGNORE_PATHS_PARAM, toIgnore);
+               filter(context, "/*", GitblitWicketFilter.class, params);
+       }
 
-               if (true/*startFederation*/) {
-                       configureFederation();
+       /**
+        * Gitblit is being shutdown either because the servlet container is
+        * shutting down or because the servlet container is re-deploying Gitblit.
+        */
+       @Override
+       protected void destroyContext(ServletContext context) {
+               logger.info("Gitblit context destroyed by servlet container.");
+               for (IManager manager : managers) {
+                       logger.debug("stopping {}", manager.getClass().getSimpleName());
+                       manager.stop();
                }
-               configureFanout();
-               configureGitDaemon();
        }
 
        /**
@@ -1023,6 +248,8 @@ public class GitBlit extends DaggerContextListener
                        File goBaseFolder,
                        IStoredSettings runtimeSettings) {
 
+               logger.debug("configuring Gitblit GO");
+
                // merge the stored settings into the runtime settings
                //
                // if runtimeSettings is also a FileSettings w/o a specified target file,
@@ -1049,6 +276,7 @@ public class GitBlit extends DaggerContextListener
                        IStoredSettings runtimeSettings) {
 
                // Gitblit is running in a standard servlet container
+               logger.debug("configuring Gitblit WAR");
                logger.info("WAR contextFolder is " + ((contextFolder != null) ? contextFolder.getAbsolutePath() : "<empty>"));
 
                String path = webxmlSettings.getString(Constants.baseFolder, Constants.contextFolder$ + "/WEB-INF/data");
@@ -1112,6 +340,7 @@ public class GitBlit extends DaggerContextListener
                        IStoredSettings runtimeSettings) {
 
                // Gitblit is running in OpenShift/JBoss
+               logger.debug("configuring Gitblit Express");
                String openShift = System.getenv("OPENSHIFT_DATA_DIR");
                File base = new File(openShift);
                logger.info("EXPRESS contextFolder is " + contextFolder.getAbsolutePath());
@@ -1194,141 +423,4 @@ public class GitBlit extends DaggerContextListener
                        }
                }
        }
-
-       /**
-        * Gitblit is being shutdown either because the servlet container is
-        * shutting down or because the servlet container is re-deploying Gitblit.
-        */
-       @Override
-       protected void destroyContext(ServletContext context) {
-               logger.info("Gitblit context destroyed by servlet container.");
-               for (IManager manager : managers) {
-                       logger.debug("stopping {}", manager.getClass().getSimpleName());
-                       manager.stop();
-               }
-
-               scheduledExecutor.shutdownNow();
-               if (fanoutService != null) {
-                       fanoutService.stop();
-               }
-               if (gitDaemon != null) {
-                       gitDaemon.stop();
-               }
-       }
-
-       /**
-        * Creates a personal fork of the specified repository. The clone is view
-        * restricted by default and the owner of the source repository is given
-        * access to the clone.
-        *
-        * @param repository
-        * @param user
-        * @return the repository model of the fork, if successful
-        * @throws GitBlitException
-        */
-       @Override
-       public RepositoryModel fork(RepositoryModel repository, UserModel user) throws GitBlitException {
-               String cloneName = MessageFormat.format("{0}/{1}.git", user.getPersonalPath(), StringUtils.stripDotGit(StringUtils.getLastPathElement(repository.name)));
-               String fromUrl = MessageFormat.format("file://{0}/{1}", getManager(IRepositoryManager.class).getRepositoriesFolder().getAbsolutePath(), repository.name);
-
-               // clone the repository
-               try {
-                       JGitUtils.cloneRepository(getManager(IRepositoryManager.class).getRepositoriesFolder(), cloneName, fromUrl, true, null);
-               } catch (Exception e) {
-                       throw new GitBlitException(e);
-               }
-
-               // create a Gitblit repository model for the clone
-               RepositoryModel cloneModel = repository.cloneAs(cloneName);
-               // owner has REWIND/RW+ permissions
-               cloneModel.addOwner(user.username);
-               getManager(IRepositoryManager.class).updateRepositoryModel(cloneName, cloneModel, false);
-
-               // add the owner of the source repository to the clone's access list
-               if (!ArrayUtils.isEmpty(repository.owners)) {
-                       for (String owner : repository.owners) {
-                               UserModel originOwner = getManager(IUserManager.class).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 = getManager(IRepositoryManager.class).getRepositoryUsers(repository);
-               List<UserModel> cloneUsers = new ArrayList<UserModel>();
-               for (String name : users) {
-                       if (!name.equalsIgnoreCase(user.username)) {
-                               UserModel cloneUser = getManager(IUserManager.class).getUserModel(name);
-                               if (cloneUser.canClone(repository)) {
-                                       // origin user can clone origin, grant clone access to fork
-                                       cloneUser.setRepositoryPermission(cloneName, AccessPermission.CLONE);
-                               }
-                               cloneUsers.add(cloneUser);
-                       }
-               }
-               getManager(IUserManager.class).updateUserModels(cloneUsers);
-
-               // grant origin's team list clone permission to fork
-               List<String> teams = getManager(IRepositoryManager.class).getRepositoryTeams(repository);
-               List<TeamModel> cloneTeams = new ArrayList<TeamModel>();
-               for (String name : teams) {
-                       TeamModel cloneTeam = getManager(IUserManager.class).getTeamModel(name);
-                       if (cloneTeam.canClone(repository)) {
-                               // origin team can clone origin, grant clone access to fork
-                               cloneTeam.setRepositoryPermission(cloneName, AccessPermission.CLONE);
-                       }
-                       cloneTeams.add(cloneTeam);
-               }
-               getManager(IUserManager.class).updateTeamModels(cloneTeams);
-
-               // add this clone to the cached model
-               getManager(IRepositoryManager.class).addToCachedRepositoryList(cloneModel);
-               return cloneModel;
-       }
-
-       @Override
-       protected Object [] getModules() {
-               return new Object [] { new DaggerModule(this) };
-       }
-
-       protected <X extends IManager> X startManager(ObjectGraph injector, Class<X> clazz) {
-               logger.debug("injecting and starting {}", clazz.getSimpleName());
-               X x = injector.get(clazz);
-               x.setup();
-               managers.add(x);
-               return x;
-       }
-
-       /**
-        * Instantiate and inject all filters and servlets into the container using
-        * the servlet 3 specification.
-        */
-       @Override
-       protected void injectServlets(ServletContext context) {
-               // access restricted servlets
-               serve(context, Constants.GIT_PATH, GitServlet.class, GitFilter.class);
-               serve(context, Constants.PAGES, PagesServlet.class, PagesFilter.class);
-               serve(context, Constants.RPC_PATH, RpcServlet.class, RpcFilter.class);
-               serve(context, Constants.ZIP_PATH, DownloadZipServlet.class, DownloadZipFilter.class);
-               serve(context, Constants.SYNDICATION_PATH, SyndicationServlet.class, SyndicationFilter.class);
-
-               // servlets
-               serve(context, Constants.FEDERATION_PATH, FederationServlet.class);
-               serve(context, Constants.SPARKLESHARE_INVITE_PATH, SparkleShareInviteServlet.class);
-               serve(context, Constants.BRANCH_GRAPH_PATH, BranchGraphServlet.class);
-               file(context, "/robots.txt", RobotsTxtServlet.class);
-               file(context, "/logo.png", LogoServlet.class);
-
-               // optional force basic authentication
-               filter(context, "/*", EnforceAuthenticationFilter.class, null);
-
-               // Wicket
-               String toIgnore = StringUtils.flattenStrings(getRegisteredPaths(), ",");
-               Map<String, String> params = new HashMap<String, String>();
-               params.put(GitblitWicketFilter.FILTER_MAPPING_PARAM, "/*");
-               params.put(GitblitWicketFilter.IGNORE_PATHS_PARAM, toIgnore);
-               filter(context, "/*", GitblitWicketFilter.class, params);
-       }
 }
index 7a5c73ea019a4a2f89799b4ec57fef226c31dc82..64316bb4dd351c64bea39fa028f584ebddcc236a 100644 (file)
@@ -22,8 +22,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.TimeZone;
 
-import javax.inject.Inject;
-import javax.inject.Singleton;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
@@ -33,7 +31,6 @@ import com.gitblit.Constants.FederationRequest;
 import com.gitblit.Constants.FederationToken;
 import com.gitblit.manager.IFederationManager;
 import com.gitblit.manager.IGitblitManager;
-import com.gitblit.manager.IManager;
 import com.gitblit.manager.INotificationManager;
 import com.gitblit.manager.IProjectManager;
 import com.gitblit.manager.IRepositoryManager;
@@ -65,7 +62,6 @@ import com.gitblit.models.UserModel;
  * @author James Moger
  *
  */
-@Singleton
 public class Gitblit implements IRuntimeManager,
                                                                INotificationManager,
                                                                IUserManager,
@@ -87,11 +83,10 @@ public class Gitblit implements IRuntimeManager,
 
        private final IProjectManager projectManager;
 
-       private final IFederationManager federationManager;
-
        private final IGitblitManager gitblitManager;
 
-       @Inject
+       private final IFederationManager federationManager;
+
        public Gitblit(
                        IRuntimeManager runtimeManager,
                        INotificationManager notificationManager,
@@ -99,8 +94,8 @@ public class Gitblit implements IRuntimeManager,
                        ISessionManager sessionManager,
                        IRepositoryManager repositoryManager,
                        IProjectManager projectManager,
-                       IFederationManager federationManager,
-                       IGitblitManager gitblitManager) {
+                       IGitblitManager gitblitManager,
+                       IFederationManager federationManager) {
 
                this.runtimeManager = runtimeManager;
                this.notificationManager = notificationManager;
@@ -108,17 +103,17 @@ public class Gitblit implements IRuntimeManager,
                this.sessionManager = sessionManager;
                this.repositoryManager = repositoryManager;
                this.projectManager = projectManager;
-               this.federationManager = federationManager;
                this.gitblitManager = gitblitManager;
+               this.federationManager = federationManager;
        }
 
        @Override
-       public IManager setup() {
+       public Gitblit start() {
                return this;
        }
 
        @Override
-       public IManager stop() {
+       public Gitblit stop() {
                return this;
        }
 
diff --git a/src/main/java/com/gitblit/manager/FederationManager.java b/src/main/java/com/gitblit/manager/FederationManager.java
new file mode 100644 (file)
index 0000000..07d2018
--- /dev/null
@@ -0,0 +1,454 @@
+/*
+ * Copyright 2013 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.manager;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.gitblit.Constants;
+import com.gitblit.Constants.FederationRequest;
+import com.gitblit.Constants.FederationToken;
+import com.gitblit.IStoredSettings;
+import com.gitblit.Keys;
+import com.gitblit.models.FederationModel;
+import com.gitblit.models.FederationProposal;
+import com.gitblit.models.FederationSet;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.FederationUtils;
+import com.gitblit.utils.JsonUtils;
+import com.gitblit.utils.StringUtils;
+
+/**
+ * Federation manager controls all aspects of handling federation sets, tokens,
+ * and proposals.
+ *
+ * @author James Moger
+ *
+ */
+public class FederationManager implements IFederationManager {
+
+       private final Logger logger = LoggerFactory.getLogger(getClass());
+
+       private final List<FederationModel> federationRegistrations = Collections
+                       .synchronizedList(new ArrayList<FederationModel>());
+
+       private final Map<String, FederationModel> federationPullResults = new ConcurrentHashMap<String, FederationModel>();
+
+       private final IStoredSettings settings;
+
+       private final IRuntimeManager runtimeManager;
+
+       private final INotificationManager notificationManager;
+
+       private final IRepositoryManager repositoryManager;
+
+       public FederationManager(
+                       IRuntimeManager runtimeManager,
+                       INotificationManager notificationManager,
+                       IUserManager userManager,
+                       IRepositoryManager repositoryManager) {
+
+               this.settings = runtimeManager.getSettings();
+               this.runtimeManager = runtimeManager;
+               this.notificationManager = notificationManager;
+               this.repositoryManager = repositoryManager;
+       }
+
+       @Override
+       public FederationManager start() {
+               return this;
+       }
+
+       @Override
+       public FederationManager stop() {
+               return this;
+       }
+
+       /**
+        * Returns the path of the proposals folder. This method checks to see if
+        * Gitblit is running on a cloud service and may return an adjusted path.
+        *
+        * @return the proposals folder path
+        */
+       @Override
+       public File getProposalsFolder() {
+               return runtimeManager.getFileOrFolder(Keys.federation.proposalsFolder, "${baseFolder}/proposals");
+       }
+
+       @Override
+       public UserModel getFederationUser() {
+               // the federation user is an administrator
+               UserModel federationUser = new UserModel(Constants.FEDERATION_USER);
+               federationUser.canAdmin = true;
+               return federationUser;
+       }
+
+       @Override
+       public boolean canFederate() {
+               String passphrase = settings.getString(Keys.federation.passphrase, "");
+               return !StringUtils.isEmpty(passphrase);
+       }
+
+       /**
+        * Returns the list of federated gitblit instances that this instance will
+        * try to pull.
+        *
+        * @return list of registered gitblit instances
+        */
+       @Override
+       public List<FederationModel> getFederationRegistrations() {
+               if (federationRegistrations.isEmpty()) {
+                       federationRegistrations.addAll(FederationUtils.getFederationRegistrations(settings));
+               }
+               return federationRegistrations;
+       }
+
+       /**
+        * Retrieve the specified federation registration.
+        *
+        * @param name
+        *            the name of the registration
+        * @return a federation registration
+        */
+       @Override
+       public FederationModel getFederationRegistration(String url, String name) {
+               // check registrations
+               for (FederationModel r : getFederationRegistrations()) {
+                       if (r.name.equals(name) && r.url.equals(url)) {
+                               return r;
+                       }
+               }
+
+               // check the results
+               for (FederationModel r : getFederationResultRegistrations()) {
+                       if (r.name.equals(name) && r.url.equals(url)) {
+                               return r;
+                       }
+               }
+               return null;
+       }
+
+       /**
+        * Returns the list of federation sets.
+        *
+        * @return list of federation sets
+        */
+       @Override
+       public List<FederationSet> getFederationSets(String gitblitUrl) {
+               List<FederationSet> list = new ArrayList<FederationSet>();
+               // generate standard tokens
+               for (FederationToken type : FederationToken.values()) {
+                       FederationSet fset = new FederationSet(type.toString(), type, getFederationToken(type));
+                       fset.repositories = getRepositories(gitblitUrl, fset.token);
+                       list.add(fset);
+               }
+               // generate tokens for federation sets
+               for (String set : settings.getStrings(Keys.federation.sets)) {
+                       FederationSet fset = new FederationSet(set, FederationToken.REPOSITORIES,
+                                       getFederationToken(set));
+                       fset.repositories = getRepositories(gitblitUrl, fset.token);
+                       list.add(fset);
+               }
+               return list;
+       }
+
+       /**
+        * Returns the list of possible federation tokens for this Gitblit instance.
+        *
+        * @return list of federation tokens
+        */
+       @Override
+       public List<String> getFederationTokens() {
+               List<String> tokens = new ArrayList<String>();
+               // generate standard tokens
+               for (FederationToken type : FederationToken.values()) {
+                       tokens.add(getFederationToken(type));
+               }
+               // generate tokens for federation sets
+               for (String set : settings.getStrings(Keys.federation.sets)) {
+                       tokens.add(getFederationToken(set));
+               }
+               return tokens;
+       }
+
+       /**
+        * Returns the specified federation token for this Gitblit instance.
+        *
+        * @param type
+        * @return a federation token
+        */
+       @Override
+       public String getFederationToken(FederationToken type) {
+               return getFederationToken(type.name());
+       }
+
+       /**
+        * Returns the specified federation token for this Gitblit instance.
+        *
+        * @param value
+        * @return a federation token
+        */
+       @Override
+       public String getFederationToken(String value) {
+               String passphrase = settings.getString(Keys.federation.passphrase, "");
+               return StringUtils.getSHA1(passphrase + "-" + value);
+       }
+
+       /**
+        * Compares the provided token with this Gitblit instance's tokens and
+        * determines if the requested permission may be granted to the token.
+        *
+        * @param req
+        * @param token
+        * @return true if the request can be executed
+        */
+       @Override
+       public boolean validateFederationRequest(FederationRequest req, String token) {
+               String all = getFederationToken(FederationToken.ALL);
+               String unr = getFederationToken(FederationToken.USERS_AND_REPOSITORIES);
+               String jur = getFederationToken(FederationToken.REPOSITORIES);
+               switch (req) {
+               case PULL_REPOSITORIES:
+                       return token.equals(all) || token.equals(unr) || token.equals(jur);
+               case PULL_USERS:
+               case PULL_TEAMS:
+                       return token.equals(all) || token.equals(unr);
+               case PULL_SETTINGS:
+               case PULL_SCRIPTS:
+                       return token.equals(all);
+               default:
+                       break;
+               }
+               return false;
+       }
+
+       /**
+        * Acknowledge and cache the status of a remote Gitblit instance.
+        *
+        * @param identification
+        *            the identification of the pulling Gitblit instance
+        * @param registration
+        *            the registration from the pulling Gitblit instance
+        * @return true if acknowledged
+        */
+       @Override
+       public boolean acknowledgeFederationStatus(String identification, FederationModel registration) {
+               // reset the url to the identification of the pulling Gitblit instance
+               registration.url = identification;
+               String id = identification;
+               if (!StringUtils.isEmpty(registration.folder)) {
+                       id += "-" + registration.folder;
+               }
+               federationPullResults.put(id, registration);
+               return true;
+       }
+
+       /**
+        * Returns the list of registration results.
+        *
+        * @return the list of registration results
+        */
+       @Override
+       public List<FederationModel> getFederationResultRegistrations() {
+               return new ArrayList<FederationModel>(federationPullResults.values());
+       }
+
+       /**
+        * Submit a federation proposal. The proposal is cached locally and the
+        * Gitblit administrator(s) are notified via email.
+        *
+        * @param proposal
+        *            the proposal
+        * @param gitblitUrl
+        *            the url of your gitblit instance to send an email to
+        *            administrators
+        * @return true if the proposal was submitted
+        */
+       @Override
+       public boolean submitFederationProposal(FederationProposal proposal, String gitblitUrl) {
+               // convert proposal to json
+               String json = JsonUtils.toJsonString(proposal);
+
+               try {
+                       // make the proposals folder
+                       File proposalsFolder = getProposalsFolder();
+                       proposalsFolder.mkdirs();
+
+                       // cache json to a file
+                       File file = new File(proposalsFolder, proposal.token + Constants.PROPOSAL_EXT);
+                       com.gitblit.utils.FileUtils.writeContent(file, json);
+               } catch (Exception e) {
+                       logger.error(MessageFormat.format("Failed to cache proposal from {0}", proposal.url), e);
+               }
+
+               // send an email, if possible
+               notificationManager.sendMailToAdministrators("Federation proposal from " + proposal.url,
+                               "Please review the proposal @ " + gitblitUrl + "/proposal/" + proposal.token);
+               return true;
+       }
+
+       /**
+        * Returns the list of pending federation proposals
+        *
+        * @return list of federation proposals
+        */
+       @Override
+       public List<FederationProposal> getPendingFederationProposals() {
+               List<FederationProposal> list = new ArrayList<FederationProposal>();
+               File folder = getProposalsFolder();
+               if (folder.exists()) {
+                       File[] files = folder.listFiles(new FileFilter() {
+                               @Override
+                               public boolean accept(File file) {
+                                       return file.isFile()
+                                                       && file.getName().toLowerCase().endsWith(Constants.PROPOSAL_EXT);
+                               }
+                       });
+                       for (File file : files) {
+                               String json = com.gitblit.utils.FileUtils.readContent(file, null);
+                               FederationProposal proposal = JsonUtils.fromJsonString(json,
+                                               FederationProposal.class);
+                               list.add(proposal);
+                       }
+               }
+               return list;
+       }
+
+       /**
+        * Get repositories for the specified token.
+        *
+        * @param gitblitUrl
+        *            the base url of this gitblit instance
+        * @param token
+        *            the federation token
+        * @return a map of <cloneurl, RepositoryModel>
+        */
+       @Override
+       public Map<String, RepositoryModel> getRepositories(String gitblitUrl, String token) {
+               Map<String, String> federationSets = new HashMap<String, String>();
+               for (String set : settings.getStrings(Keys.federation.sets)) {
+                       federationSets.put(getFederationToken(set), set);
+               }
+
+               // Determine the Gitblit clone url
+               StringBuilder sb = new StringBuilder();
+               sb.append(gitblitUrl);
+               sb.append(Constants.GIT_PATH);
+               sb.append("{0}");
+               String cloneUrl = sb.toString();
+
+               // Retrieve all available repositories
+               UserModel user = getFederationUser();
+               List<RepositoryModel> list = repositoryManager.getRepositoryModels(user);
+
+               // create the [cloneurl, repositoryModel] map
+               Map<String, RepositoryModel> repositories = new HashMap<String, RepositoryModel>();
+               for (RepositoryModel model : list) {
+                       // by default, setup the url for THIS repository
+                       String url = MessageFormat.format(cloneUrl, model.name);
+                       switch (model.federationStrategy) {
+                       case EXCLUDE:
+                               // skip this repository
+                               continue;
+                       case FEDERATE_ORIGIN:
+                               // federate the origin, if it is defined
+                               if (!StringUtils.isEmpty(model.origin)) {
+                                       url = model.origin;
+                               }
+                               break;
+                       default:
+                               break;
+                       }
+
+                       if (federationSets.containsKey(token)) {
+                               // include repositories only for federation set
+                               String set = federationSets.get(token);
+                               if (model.federationSets.contains(set)) {
+                                       repositories.put(url, model);
+                               }
+                       } else {
+                               // standard federation token for ALL
+                               repositories.put(url, model);
+                       }
+               }
+               return repositories;
+       }
+
+       /**
+        * Creates a proposal from the token.
+        *
+        * @param gitblitUrl
+        *            the url of this Gitblit instance
+        * @param token
+        * @return a potential proposal
+        */
+       @Override
+       public FederationProposal createFederationProposal(String gitblitUrl, String token) {
+               FederationToken tokenType = FederationToken.REPOSITORIES;
+               for (FederationToken type : FederationToken.values()) {
+                       if (token.equals(getFederationToken(type))) {
+                               tokenType = type;
+                               break;
+                       }
+               }
+               Map<String, RepositoryModel> repositories = getRepositories(gitblitUrl, token);
+               FederationProposal proposal = new FederationProposal(gitblitUrl, tokenType, token,
+                               repositories);
+               return proposal;
+       }
+
+       /**
+        * Returns the proposal identified by the supplied token.
+        *
+        * @param token
+        * @return the specified proposal or null
+        */
+       @Override
+       public FederationProposal getPendingFederationProposal(String token) {
+               List<FederationProposal> list = getPendingFederationProposals();
+               for (FederationProposal proposal : list) {
+                       if (proposal.token.equals(token)) {
+                               return proposal;
+                       }
+               }
+               return null;
+       }
+
+       /**
+        * Deletes a pending federation proposal.
+        *
+        * @param a
+        *            proposal
+        * @return true if the proposal was deleted
+        */
+       @Override
+       public boolean deletePendingFederationProposal(FederationProposal proposal) {
+               File folder = getProposalsFolder();
+               File file = new File(folder, proposal.token + Constants.PROPOSAL_EXT);
+               return file.delete();
+       }
+}
diff --git a/src/main/java/com/gitblit/manager/GitblitManager.java b/src/main/java/com/gitblit/manager/GitblitManager.java
new file mode 100644 (file)
index 0000000..2e6a33e
--- /dev/null
@@ -0,0 +1,463 @@
+/*
+ * Copyright 2013 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.manager;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.reflect.Type;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.gitblit.Constants;
+import com.gitblit.Constants.AccessPermission;
+import com.gitblit.Constants.AccessRestrictionType;
+import com.gitblit.GitBlitException;
+import com.gitblit.IStoredSettings;
+import com.gitblit.Keys;
+import com.gitblit.models.GitClientApplication;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.RepositoryUrl;
+import com.gitblit.models.ServerSettings;
+import com.gitblit.models.SettingModel;
+import com.gitblit.models.TeamModel;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.ArrayUtils;
+import com.gitblit.utils.HttpUtils;
+import com.gitblit.utils.JGitUtils;
+import com.gitblit.utils.JsonUtils;
+import com.gitblit.utils.ObjectCache;
+import com.gitblit.utils.StringUtils;
+import com.google.gson.Gson;
+import com.google.gson.JsonIOException;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.reflect.TypeToken;
+
+public class GitblitManager implements IGitblitManager {
+
+       private final Logger logger = LoggerFactory.getLogger(getClass());
+
+       private final ObjectCache<Collection<GitClientApplication>> clientApplications = new ObjectCache<Collection<GitClientApplication>>();
+
+       private final IStoredSettings settings;
+
+       private final IRuntimeManager runtimeManager;
+
+       private final IUserManager userManager;
+
+       private final IRepositoryManager repositoryManager;
+
+       public GitblitManager(
+                       IRuntimeManager runtimeManager,
+                       IUserManager userManager,
+                       IRepositoryManager repositoryManager) {
+
+               this.settings = runtimeManager.getSettings();
+               this.runtimeManager = runtimeManager;
+               this.userManager = userManager;
+               this.repositoryManager = repositoryManager;
+       }
+
+       @Override
+       public GitblitManager start() {
+               loadSettingModels(runtimeManager.getSettingsModel());
+               return this;
+       }
+
+       @Override
+       public GitblitManager stop() {
+               return this;
+       }
+
+       /**
+        * Parse the properties file and aggregate all the comments by the setting
+        * key. A setting model tracks the current value, the default value, the
+        * description of the setting and and directives about the setting.
+        *
+        * @return Map<String, SettingModel>
+        */
+       private void loadSettingModels(ServerSettings settingsModel) {
+               // this entire "supports" concept will go away with user service refactoring
+               UserModel externalUser = new UserModel(Constants.EXTERNAL_ACCOUNT);
+               externalUser.password = Constants.EXTERNAL_ACCOUNT;
+               settingsModel.supportsCredentialChanges = userManager.supportsCredentialChanges(externalUser);
+               settingsModel.supportsDisplayNameChanges = userManager.supportsDisplayNameChanges(externalUser);
+               settingsModel.supportsEmailAddressChanges = userManager.supportsEmailAddressChanges(externalUser);
+               settingsModel.supportsTeamMembershipChanges = userManager.supportsTeamMembershipChanges(externalUser);
+               try {
+                       // Read bundled Gitblit properties to extract setting descriptions.
+                       // This copy is pristine and only used for populating the setting
+                       // models map.
+                       InputStream is = getClass().getResourceAsStream("/reference.properties");
+                       BufferedReader propertiesReader = new BufferedReader(new InputStreamReader(is));
+                       StringBuilder description = new StringBuilder();
+                       SettingModel setting = new SettingModel();
+                       String line = null;
+                       while ((line = propertiesReader.readLine()) != null) {
+                               if (line.length() == 0) {
+                                       description.setLength(0);
+                                       setting = new SettingModel();
+                               } else {
+                                       if (line.charAt(0) == '#') {
+                                               if (line.length() > 1) {
+                                                       String text = line.substring(1).trim();
+                                                       if (SettingModel.CASE_SENSITIVE.equals(text)) {
+                                                               setting.caseSensitive = true;
+                                                       } else if (SettingModel.RESTART_REQUIRED.equals(text)) {
+                                                               setting.restartRequired = true;
+                                                       } else if (SettingModel.SPACE_DELIMITED.equals(text)) {
+                                                               setting.spaceDelimited = true;
+                                                       } else if (text.startsWith(SettingModel.SINCE)) {
+                                                               try {
+                                                                       setting.since = text.split(" ")[1];
+                                                               } catch (Exception e) {
+                                                                       setting.since = text;
+                                                               }
+                                                       } else {
+                                                               description.append(text);
+                                                               description.append('\n');
+                                                       }
+                                               }
+                                       } else {
+                                               String[] kvp = line.split("=", 2);
+                                               String key = kvp[0].trim();
+                                               setting.name = key;
+                                               setting.defaultValue = kvp[1].trim();
+                                               setting.currentValue = setting.defaultValue;
+                                               setting.description = description.toString().trim();
+                                               settingsModel.add(setting);
+                                               description.setLength(0);
+                                               setting = new SettingModel();
+                                       }
+                               }
+                       }
+                       propertiesReader.close();
+               } catch (NullPointerException e) {
+                       logger.error("Failed to find resource copy of gitblit.properties");
+               } catch (IOException e) {
+                       logger.error("Failed to load resource copy of gitblit.properties");
+               }
+       }
+
+       /**
+        * Returns a list of repository URLs and the user access permission.
+        *
+        * @param request
+        * @param user
+        * @param repository
+        * @return a list of repository urls
+        */
+       @Override
+       public List<RepositoryUrl> getRepositoryUrls(HttpServletRequest request, UserModel user, RepositoryModel repository) {
+               if (user == null) {
+                       user = UserModel.ANONYMOUS;
+               }
+               String username = StringUtils.encodeUsername(UserModel.ANONYMOUS.equals(user) ? "" : user.username);
+
+               List<RepositoryUrl> list = new ArrayList<RepositoryUrl>();
+               // http/https url
+               if (settings.getBoolean(Keys.git.enableGitServlet, true)) {
+                       AccessPermission permission = user.getRepositoryPermission(repository).permission;
+                       if (permission.exceeds(AccessPermission.NONE)) {
+                               list.add(new RepositoryUrl(getRepositoryUrl(request, username, repository), permission));
+                       }
+               }
+
+               // git daemon url
+               String gitDaemonUrl = getGitDaemonUrl(request, user, repository);
+               if (!StringUtils.isEmpty(gitDaemonUrl)) {
+                       AccessPermission permission = getGitDaemonAccessPermission(user, repository);
+                       if (permission.exceeds(AccessPermission.NONE)) {
+                               list.add(new RepositoryUrl(gitDaemonUrl, permission));
+                       }
+               }
+
+               // add all other urls
+               // {0} = repository
+               // {1} = username
+               for (String url : settings.getStrings(Keys.web.otherUrls)) {
+                       if (url.contains("{1}")) {
+                               // external url requires username, only add url IF we have one
+                               if (!StringUtils.isEmpty(username)) {
+                                       list.add(new RepositoryUrl(MessageFormat.format(url, repository.name, username), null));
+                               }
+                       } else {
+                               // external url does not require username
+                               list.add(new RepositoryUrl(MessageFormat.format(url, repository.name), null));
+                       }
+               }
+               return list;
+       }
+
+       protected String getRepositoryUrl(HttpServletRequest request, String username, RepositoryModel repository) {
+               StringBuilder sb = new StringBuilder();
+               sb.append(HttpUtils.getGitblitURL(request));
+               sb.append(Constants.GIT_PATH);
+               sb.append(repository.name);
+
+               // inject username into repository url if authentication is required
+               if (repository.accessRestriction.exceeds(AccessRestrictionType.NONE)
+                               && !StringUtils.isEmpty(username)) {
+                       sb.insert(sb.indexOf("://") + 3, username + "@");
+               }
+               return sb.toString();
+       }
+
+       protected String getGitDaemonUrl(HttpServletRequest request, UserModel user, RepositoryModel repository) {
+//             if (gitDaemon != null) {
+//                     String bindInterface = settings.getString(Keys.git.daemonBindInterface, "localhost");
+//                     if (bindInterface.equals("localhost")
+//                                     && (!request.getServerName().equals("localhost") && !request.getServerName().equals("127.0.0.1"))) {
+//                             // git daemon is bound to localhost and the request is from elsewhere
+//                             return null;
+//                     }
+//                     if (user.canClone(repository)) {
+//                             String servername = request.getServerName();
+//                             String url = gitDaemon.formatUrl(servername, repository.name);
+//                             return url;
+//                     }
+//             }
+               return null;
+       }
+
+       protected AccessPermission getGitDaemonAccessPermission(UserModel user, RepositoryModel repository) {
+//             if (gitDaemon != null && user.canClone(repository)) {
+//                     AccessPermission gitDaemonPermission = user.getRepositoryPermission(repository).permission;
+//                     if (gitDaemonPermission.atLeast(AccessPermission.CLONE)) {
+//                             if (repository.accessRestriction.atLeast(AccessRestrictionType.CLONE)) {
+//                                     // can not authenticate clone via anonymous git protocol
+//                                     gitDaemonPermission = AccessPermission.NONE;
+//                             } else if (repository.accessRestriction.atLeast(AccessRestrictionType.PUSH)) {
+//                                     // can not authenticate push via anonymous git protocol
+//                                     gitDaemonPermission = AccessPermission.CLONE;
+//                             } else {
+//                                     // normal user permission
+//                             }
+//                     }
+//                     return gitDaemonPermission;
+//             }
+               return AccessPermission.NONE;
+       }
+
+       /**
+        * Returns the list of custom client applications to be used for the
+        * repository url panel;
+        *
+        * @return a collection of client applications
+        */
+       @Override
+       public Collection<GitClientApplication> getClientApplications() {
+               // prefer user definitions, if they exist
+               File userDefs = new File(runtimeManager.getBaseFolder(), "clientapps.json");
+               if (userDefs.exists()) {
+                       Date lastModified = new Date(userDefs.lastModified());
+                       if (clientApplications.hasCurrent("user", lastModified)) {
+                               return clientApplications.getObject("user");
+                       } else {
+                               // (re)load user definitions
+                               try {
+                                       InputStream is = new FileInputStream(userDefs);
+                                       Collection<GitClientApplication> clients = readClientApplications(is);
+                                       is.close();
+                                       if (clients != null) {
+                                               clientApplications.updateObject("user", lastModified, clients);
+                                               return clients;
+                                       }
+                               } catch (IOException e) {
+                                       logger.error("Failed to deserialize " + userDefs.getAbsolutePath(), e);
+                               }
+                       }
+               }
+
+               // no user definitions, use system definitions
+               if (!clientApplications.hasCurrent("system", new Date(0))) {
+                       try {
+                               InputStream is = getClass().getResourceAsStream("/clientapps.json");
+                               Collection<GitClientApplication> clients = readClientApplications(is);
+                               is.close();
+                               if (clients != null) {
+                                       clientApplications.updateObject("system", new Date(0), clients);
+                               }
+                       } catch (IOException e) {
+                               logger.error("Failed to deserialize clientapps.json resource!", e);
+                       }
+               }
+
+               return clientApplications.getObject("system");
+       }
+
+       private Collection<GitClientApplication> readClientApplications(InputStream is) {
+               try {
+                       Type type = new TypeToken<Collection<GitClientApplication>>() {
+                       }.getType();
+                       InputStreamReader reader = new InputStreamReader(is);
+                       Gson gson = JsonUtils.gson();
+                       Collection<GitClientApplication> links = gson.fromJson(reader, type);
+                       return links;
+               } catch (JsonIOException e) {
+                       logger.error("Error deserializing client applications!", e);
+               } catch (JsonSyntaxException e) {
+                       logger.error("Error deserializing client applications!", e);
+               }
+               return null;
+       }
+
+       /**
+        * Creates a personal fork of the specified repository. The clone is view
+        * restricted by default and the owner of the source repository is given
+        * access to the clone.
+        *
+        * @param repository
+        * @param user
+        * @return the repository model of the fork, if successful
+        * @throws GitBlitException
+        */
+       @Override
+       public RepositoryModel fork(RepositoryModel repository, UserModel user) throws GitBlitException {
+               String cloneName = MessageFormat.format("{0}/{1}.git", user.getPersonalPath(), StringUtils.stripDotGit(StringUtils.getLastPathElement(repository.name)));
+               String fromUrl = MessageFormat.format("file://{0}/{1}", repositoryManager.getRepositoriesFolder().getAbsolutePath(), repository.name);
+
+               // clone the repository
+               try {
+                       JGitUtils.cloneRepository(repositoryManager.getRepositoriesFolder(), cloneName, fromUrl, true, null);
+               } catch (Exception e) {
+                       throw new GitBlitException(e);
+               }
+
+               // create a Gitblit repository model for the clone
+               RepositoryModel cloneModel = repository.cloneAs(cloneName);
+               // owner has REWIND/RW+ permissions
+               cloneModel.addOwner(user.username);
+               repositoryManager.updateRepositoryModel(cloneName, cloneModel, false);
+
+               // add the owner of the source repository to the clone's access list
+               if (!ArrayUtils.isEmpty(repository.owners)) {
+                       for (String owner : repository.owners) {
+                               UserModel originOwner = userManager.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 = repositoryManager.getRepositoryUsers(repository);
+               List<UserModel> cloneUsers = new ArrayList<UserModel>();
+               for (String name : users) {
+                       if (!name.equalsIgnoreCase(user.username)) {
+                               UserModel cloneUser = userManager.getUserModel(name);
+                               if (cloneUser.canClone(repository)) {
+                                       // origin user can clone origin, grant clone access to fork
+                                       cloneUser.setRepositoryPermission(cloneName, AccessPermission.CLONE);
+                               }
+                               cloneUsers.add(cloneUser);
+                       }
+               }
+               userManager.updateUserModels(cloneUsers);
+
+               // grant origin's team list clone permission to fork
+               List<String> teams = repositoryManager.getRepositoryTeams(repository);
+               List<TeamModel> cloneTeams = new ArrayList<TeamModel>();
+               for (String name : teams) {
+                       TeamModel cloneTeam = userManager.getTeamModel(name);
+                       if (cloneTeam.canClone(repository)) {
+                               // origin team can clone origin, grant clone access to fork
+                               cloneTeam.setRepositoryPermission(cloneName, AccessPermission.CLONE);
+                       }
+                       cloneTeams.add(cloneTeam);
+               }
+               userManager.updateTeamModels(cloneTeams);
+
+               // add this clone to the cached model
+               repositoryManager.addToCachedRepositoryList(cloneModel);
+               return cloneModel;
+       }
+
+       /**
+        * Adds/updates a complete user object keyed by username. This method allows
+        * for renaming a user.
+        *
+        * @see IUserService.updateUserModel(String, UserModel)
+        * @param username
+        * @param user
+        * @param isCreate
+        * @throws GitBlitException
+        */
+       @Override
+       public void updateUserModel(String username, UserModel user, boolean isCreate)
+                       throws GitBlitException {
+               if (!username.equalsIgnoreCase(user.username)) {
+                       if (userManager.getUserModel(user.username) != null) {
+                               throw new GitBlitException(MessageFormat.format(
+                                               "Failed to rename ''{0}'' because ''{1}'' already exists.", username,
+                                               user.username));
+                       }
+
+                       // rename repositories and owner fields for all repositories
+                       for (RepositoryModel model : repositoryManager.getRepositoryModels(user)) {
+                               if (model.isUsersPersonalRepository(username)) {
+                                       // personal repository
+                                       model.addOwner(user.username);
+                                       String oldRepositoryName = model.name;
+                                       model.name = user.getPersonalPath() + model.name.substring(model.projectPath.length());
+                                       model.projectPath = user.getPersonalPath();
+                                       repositoryManager.updateRepositoryModel(oldRepositoryName, model, false);
+                               } else if (model.isOwner(username)) {
+                                       // common/shared repo
+                                       model.addOwner(user.username);
+                                       repositoryManager.updateRepositoryModel(model.name, model, false);
+                               }
+                       }
+               }
+               if (!userManager.updateUserModel(username, user)) {
+                       throw new GitBlitException(isCreate ? "Failed to add user!" : "Failed to update user!");
+               }
+       }
+
+       /**
+        * Updates the TeamModel object for the specified name.
+        *
+        * @param teamname
+        * @param team
+        * @param isCreate
+        */
+       @Override
+       public void updateTeamModel(String teamname, TeamModel team, boolean isCreate)
+                       throws GitBlitException {
+               if (!teamname.equalsIgnoreCase(team.name)) {
+                       if (userManager.getTeamModel(team.name) != null) {
+                               throw new GitBlitException(MessageFormat.format(
+                                               "Failed to rename ''{0}'' because ''{1}'' already exists.", teamname,
+                                               team.name));
+                       }
+               }
+               if (!userManager.updateTeamModel(teamname, team)) {
+                       throw new GitBlitException(isCreate ? "Failed to add team!" : "Failed to update team!");
+               }
+       }
+}
index debe362b0c9995b5e451fb787f9db917c1036ed0..5afdeea1a3fee98a547c8030e9f52a757ee8cfc0 100644 (file)
@@ -27,7 +27,7 @@ import com.gitblit.models.FederationSet;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.UserModel;
 
-public interface IFederationManager {
+public interface IFederationManager extends IManager {
 
        /**
         * Returns the path of the proposals folder. This method checks to see if
index 2f5295bde5871ce69ab3149f964fdfb77b7d325a..21c9e6a84c3e7af355e099023798bc3873973493 100644 (file)
@@ -27,7 +27,7 @@ import com.gitblit.models.RepositoryUrl;
 import com.gitblit.models.TeamModel;
 import com.gitblit.models.UserModel;
 
-public interface IGitblitManager {
+public interface IGitblitManager extends IManager {
 
        /**
         * Returns a list of repository URLs and the user access permission.
index 955c6100af8686a1e0256e122595bf0d8883ea46..115831b962b60484a6a1b4a8215fdca16ae0cb78 100644 (file)
@@ -17,7 +17,8 @@ package com.gitblit.manager;
 
 public interface IManager {
 
-       IManager setup();
+       IManager start();
 
        IManager stop();
+
 }
diff --git a/src/main/java/com/gitblit/manager/IServicesManager.java b/src/main/java/com/gitblit/manager/IServicesManager.java
new file mode 100644 (file)
index 0000000..9fdd063
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2013 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.manager;
+
+
+public interface IServicesManager extends IManager {
+
+}
index eae563f3f5ab03d14bb040f875c73127e06d9a3e..e38e1f1fbecdfd0febad6d96073ffd29ecc9e21f 100644 (file)
@@ -58,18 +58,19 @@ public class NotificationManager implements INotificationManager {
        }
 
        @Override
-       public IManager setup() {
+       public NotificationManager start() {
                if (mailExecutor.isReady()) {
-                       logger.info("Mail executor is scheduled to process the message queue every 2 minutes.");
-                       scheduledExecutor.scheduleAtFixedRate(mailExecutor, 1, 2, TimeUnit.MINUTES);
+                       int period = 2;
+                       logger.info("Mail service will process the queue every {} minutes.", period);
+                       scheduledExecutor.scheduleAtFixedRate(mailExecutor, 1, period, TimeUnit.MINUTES);
                } else {
-                       logger.warn("Mail server is not properly configured.  Mail services disabled.");
+                       logger.warn("Mail service disabled.");
                }
                return this;
        }
 
        @Override
-       public IManager stop() {
+       public NotificationManager stop() {
                scheduledExecutor.shutdownNow();
                return this;
        }
index 83a63102771993ed12b88ea05f7a0e1079368c89..b30f4f17be9eea70a323e107afe29d788bb87283 100644 (file)
@@ -80,7 +80,7 @@ public class ProjectManager implements IProjectManager {
        }
 
        @Override
-       public IManager setup() {
+       public ProjectManager start() {
                // load and cache the project metadata
                projectConfigs = new FileBasedConfig(runtimeManager.getFileOrFolder(Keys.web.projectsFile, "${baseFolder}/projects.conf"), FS.detect());
                getProjectConfigs();
@@ -89,7 +89,7 @@ public class ProjectManager implements IProjectManager {
        }
 
        @Override
-       public IManager stop() {
+       public ProjectManager stop() {
                return this;
        }
 
index 9d38b300333bb1acc9cc1ce18f08e609b348bc40..4845e23e04350112d61a0d20f825f76911ca339b 100644 (file)
@@ -135,8 +135,8 @@ public class RepositoryManager implements IRepositoryManager {
        }
 
        @Override
-       public IManager setup() {
-               logger.info("Git repositories folder = " + repositoriesFolder.getAbsolutePath());
+       public RepositoryManager start() {
+               logger.info("Repositories folder : {}", repositoriesFolder.getAbsolutePath());
 
                // initialize utilities
                String prefix = settings.getString(Keys.git.userRepositoryPrefix, "~");
@@ -147,7 +147,7 @@ public class RepositoryManager implements IRepositoryManager {
 
                // build initial repository list
                if (settings.getBoolean(Keys.git.cacheRepositoryList,  true)) {
-                       logger.info("Identifying available repositories...");
+                       logger.info("Identifying repositories...");
                        getRepositoryList();
                }
 
@@ -161,7 +161,7 @@ public class RepositoryManager implements IRepositoryManager {
        }
 
        @Override
-       public IManager stop() {
+       public RepositoryManager stop() {
                scheduledExecutor.shutdownNow();
                luceneExecutor.close();
                gcExecutor.close();
@@ -1645,15 +1645,16 @@ public class RepositoryManager implements IRepositoryManager {
 
        protected void configureLuceneIndexing() {
                luceneExecutor = new LuceneExecutor(settings, this);
-               scheduledExecutor.scheduleAtFixedRate(luceneExecutor, 1, 2,  TimeUnit.MINUTES);
-               logger.info("Lucene executor is scheduled to process indexed branches every 2 minutes.");
+               int period = 2;
+               scheduledExecutor.scheduleAtFixedRate(luceneExecutor, 1, period,  TimeUnit.MINUTES);
+               logger.info("Lucene will process indexed branches every {} minutes.", period);
        }
 
        protected void configureGarbageCollector() {
                // schedule gc engine
                gcExecutor = new GCExecutor(settings, this);
                if (gcExecutor.isReady()) {
-                       logger.info("GC executor is scheduled to scan repositories every 24 hours.");
+                       logger.info("Garbage Collector (GC) will scan repositories every 24 hours.");
                        Calendar c = Calendar.getInstance();
                        c.set(Calendar.HOUR_OF_DAY, settings.getInteger(Keys.git.garbageCollectionHour, 0));
                        c.set(Calendar.MINUTE, 0);
@@ -1673,6 +1674,8 @@ public class RepositoryManager implements IRepositoryManager {
                        }
                        logger.info(MessageFormat.format("Next scheculed GC scan is in {0}", when));
                        scheduledExecutor.scheduleAtFixedRate(gcExecutor, delay, 60 * 24, TimeUnit.MINUTES);
+               } else {
+                       logger.info("Garbage Collector (GC) is disabled.");
                }
        }
 
@@ -1685,8 +1688,10 @@ public class RepositoryManager implements IRepositoryManager {
                        }
                        int delay = 1;
                        scheduledExecutor.scheduleAtFixedRate(mirrorExecutor, delay, mins,  TimeUnit.MINUTES);
-                       logger.info("Mirror executor is scheduled to fetch updates every {} minutes.", mins);
+                       logger.info("Mirror service will fetch updates every {} minutes.", mins);
                        logger.info("Next scheduled mirror fetch is in {} minutes", delay);
+               } else {
+                       logger.info("Mirror service is disabled.");
                }
        }
 
@@ -1717,12 +1722,12 @@ public class RepositoryManager implements IRepositoryManager {
        protected void configureCommitCache() {
                int daysToCache = settings.getInteger(Keys.web.activityCacheDays, 14);
                if (daysToCache <= 0) {
-                       logger.info("commit cache disabled");
+                       logger.info("Commit cache is disabled");
                } else {
                        long start = System.nanoTime();
                        long repoCount = 0;
                        long commitCount = 0;
-                       logger.info(MessageFormat.format("preparing {0} day commit cache. please wait...", daysToCache));
+                       logger.info(MessageFormat.format("Preparing {0} day commit cache. please wait...", daysToCache));
                        CommitCache.instance().setCacheDays(daysToCache);
                        Date cutoff = CommitCache.instance().getCutoffDate();
                        for (String repositoryName : getRepositoryList()) {
index cfb4543efbf1b6910dd8eb487ed2b79c4aba8c1f..45d1ea1248b53e5d8ea26ca7f29715ba7ac0e0dc 100644 (file)
@@ -40,23 +40,29 @@ public class RuntimeManager implements IRuntimeManager {
 
        private final ServerStatus serverStatus;
 
-       private TimeZone timezone;
+       private final ServerSettings settingsModel;
 
        private File baseFolder;
 
-       private ServerSettings settingsModel;
+       private TimeZone timezone;
 
        public RuntimeManager(IStoredSettings settings) {
+               this(settings, null);
+       }
+
+       public RuntimeManager(IStoredSettings settings, File baseFolder) {
                this.settings = settings;
                this.settingsModel = new ServerSettings();
                this.serverStatus = new ServerStatus();
+               this.baseFolder = baseFolder == null ? new File("") : baseFolder;
        }
 
        @Override
-       public RuntimeManager setup() {
-               logger.info("Gitblit settings        = " + settings.toString());
-               logTimezone("JVM", TimeZone.getDefault());
-               logTimezone(Constants.NAME, getTimezone());
+       public RuntimeManager start() {
+               logger.info("Basefolder  : " + baseFolder.getAbsolutePath());
+               logger.info("Settings    : " + settings.toString());
+               logTimezone("JVM timezone: ", TimeZone.getDefault());
+               logTimezone("App timezone: ", getTimezone());
                return this;
        }
 
@@ -121,7 +127,7 @@ public class RuntimeManager implements IRuntimeManager {
        @Override
        public TimeZone getTimezone() {
                if (timezone == null) {
-                       String tzid = settings.getString("web.timezone", null);
+                       String tzid = settings.getString(Keys.web.timezone, null);
                        if (StringUtils.isEmpty(tzid)) {
                                timezone = TimeZone.getDefault();
                                return timezone;
@@ -135,7 +141,7 @@ public class RuntimeManager implements IRuntimeManager {
                SimpleDateFormat df = new SimpleDateFormat("z Z");
                df.setTimeZone(zone);
                String offset = df.format(new Date());
-               logger.info(type + " timezone is " + zone.getID() + " (" + offset + ")");
+               logger.info("{}{} ({})", new Object [] { type, zone.getID(), offset });
        }
 
        /**
diff --git a/src/main/java/com/gitblit/manager/ServicesManager.java b/src/main/java/com/gitblit/manager/ServicesManager.java
new file mode 100644 (file)
index 0000000..82a8a04
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2013 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.manager;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.gitblit.Constants.FederationToken;
+import com.gitblit.FederationPullExecutor;
+import com.gitblit.Gitblit;
+import com.gitblit.IStoredSettings;
+import com.gitblit.Keys;
+import com.gitblit.fanout.FanoutNioService;
+import com.gitblit.fanout.FanoutService;
+import com.gitblit.fanout.FanoutSocketService;
+import com.gitblit.git.GitDaemon;
+import com.gitblit.models.FederationModel;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.utils.TimeUtils;
+
+/**
+ * Services manager manages long-running services/processes that either have no
+ * direct relation to other managers OR require really high-level manager
+ * integration (i.e. a Gitblit instance).
+ *
+ * @author James Moger
+ *
+ */
+public class ServicesManager implements IServicesManager {
+
+       private final Logger logger = LoggerFactory.getLogger(getClass());
+
+       private final ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(5);
+
+       private final IStoredSettings settings;
+
+       private final Gitblit gitblit;
+
+       private FanoutService fanoutService;
+
+       private GitDaemon gitDaemon;
+
+       public ServicesManager(Gitblit gitblit) {
+               this.settings = gitblit.getSettings();
+               this.gitblit = gitblit;
+       }
+
+       @Override
+       public ServicesManager start() {
+               configureFederation();
+               configureFanout();
+               configureGitDaemon();
+
+               return this;
+       }
+
+       @Override
+       public ServicesManager stop() {
+               scheduledExecutor.shutdownNow();
+               if (fanoutService != null) {
+                       fanoutService.stop();
+               }
+               if (gitDaemon != null) {
+                       gitDaemon.stop();
+               }
+               return this;
+       }
+
+       protected void configureFederation() {
+               boolean validPassphrase = true;
+               String passphrase = settings.getString(Keys.federation.passphrase, "");
+               if (StringUtils.isEmpty(passphrase)) {
+                       logger.info("Federation passphrase is blank! This server can not be PULLED from.");
+                       validPassphrase = false;
+               }
+               if (validPassphrase) {
+                       // standard tokens
+                       for (FederationToken tokenType : FederationToken.values()) {
+                               logger.info(MessageFormat.format("Federation {0} token = {1}", tokenType.name(),
+                                               gitblit.getFederationToken(tokenType)));
+                       }
+
+                       // federation set tokens
+                       for (String set : settings.getStrings(Keys.federation.sets)) {
+                               logger.info(MessageFormat.format("Federation Set {0} token = {1}", set,
+                                               gitblit.getFederationToken(set)));
+                       }
+               }
+
+               // Schedule or run the federation executor
+               List<FederationModel> registrations = gitblit.getFederationRegistrations();
+               if (registrations.size() > 0) {
+                       FederationPuller executor = new FederationPuller(registrations);
+                       scheduledExecutor.schedule(executor, 1, TimeUnit.MINUTES);
+               }
+       }
+
+       protected void configureGitDaemon() {
+               int port = settings.getInteger(Keys.git.daemonPort, 0);
+               String bindInterface = settings.getString(Keys.git.daemonBindInterface, "localhost");
+               if (port > 0) {
+                       try {
+                               gitDaemon = new GitDaemon(gitblit);
+                               gitDaemon.start();
+                       } catch (IOException e) {
+                               gitDaemon = null;
+                               logger.error(MessageFormat.format("Failed to start Git Daemon on {0}:{1,number,0}", bindInterface, port), e);
+                       }
+               } else {
+                       logger.info("Git Daemon is disabled.");
+               }
+       }
+
+       protected void configureFanout() {
+               // 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();
+               } else {
+                       logger.info("Fanout PubSub service is disabled.");
+               }
+       }
+
+       private class FederationPuller extends FederationPullExecutor {
+
+               public FederationPuller(FederationModel registration) {
+                       super(Arrays.asList(registration));
+               }
+
+               public FederationPuller(List<FederationModel> registrations) {
+                       super(registrations);
+               }
+
+               @Override
+               public void reschedule(FederationModel registration) {
+                       // schedule the next pull
+                       int mins = TimeUtils.convertFrequencyToMinutes(registration.frequency);
+                       registration.nextPull = new Date(System.currentTimeMillis() + (mins * 60 * 1000L));
+                       scheduledExecutor.schedule(new FederationPuller(registration), mins, TimeUnit.MINUTES);
+                       logger.info(MessageFormat.format(
+                                       "Next pull of {0} @ {1} scheduled for {2,date,yyyy-MM-dd HH:mm}",
+                                       registration.name, registration.url, registration.nextPull));
+               }
+
+       }
+}
index e6c3a2d3d2f777cdfd6f1e63348008630dccd675..6a85da89d8205148f60c29ab1848ba59cfef72ee 100644 (file)
@@ -65,7 +65,7 @@ public class SessionManager implements ISessionManager {
        }
 
        @Override
-       public IManager setup() {
+       public SessionManager start() {
                List<String> services = settings.getStrings("realm.authenticationServices");
                for (String service : services) {
                        // TODO populate authentication services here
@@ -74,7 +74,7 @@ public class SessionManager implements ISessionManager {
        }
 
        @Override
-       public IManager stop() {
+       public SessionManager stop() {
                return this;
        }
 
index f781c4c7ea774cfee93a037e38b1d1b2771d540c..90b9d1e2233629b55e3efadce200e23d0dc4ee69 100644 (file)
@@ -64,13 +64,13 @@ public class UserManager implements IUserManager {
         * @param userService
         */
        public void setUserService(IUserService userService) {
-               logger.info("Setting up user service " + userService.toString());
+               logger.info("UserService: " + userService.toString());
                this.userService = userService;
                this.userService.setup(runtimeManager);
        }
 
        @Override
-       public IManager setup() {
+       public UserManager start() {
                if (this.userService == null) {
                        String realm = settings.getString(Keys.realm.userService, "${baseFolder}/users.properties");
                        IUserService service = null;
@@ -114,7 +114,7 @@ public class UserManager implements IUserManager {
        }
 
        @Override
-       public IManager stop() {
+       public UserManager stop() {
                return this;
        }
 
index aaa6c6686b4858203e9c240f4f7d8bdf84cceae5..8e518de574307256ce753f8ba60028cfec071ece 100644 (file)
@@ -137,7 +137,7 @@ public class MockRuntimeManager implements IRuntimeManager {
        }
 
        @Override
-       public IRuntimeManager setup() {
+       public IRuntimeManager start() {
                return this;
        }
 }