From db4f6b5740c6ea45d9e2209dc569bc18904a8b4d Mon Sep 17 00:00:00 2001
From: James Moger <james.moger@gitblit.com>
Date: Tue, 26 Nov 2013 16:07:04 -0500
Subject: [PATCH] Define manager interfaces and update all of Gitblit to use managers

---
 src/main/java/com/gitblit/GCExecutor.java                       |   11 
 src/test/java/com/gitblit/tests/GitBlitTest.java                |   54 
 src/test/java/com/gitblit/tests/JGitUtilsTest.java              |    8 
 src/main/java/com/gitblit/AccessRestrictionFilter.java          |   17 
 src/main/java/com/gitblit/wicket/GitBlitWebApp.java             |   50 
 src/test/java/com/gitblit/tests/Base64Test.java                 |    4 
 src/test/java/com/gitblit/tests/GitDaemonStopTest.java          |    3 
 src/test/java/com/gitblit/tests/X509UtilsTest.java              |    3 
 src/main/java/com/gitblit/wicket/pages/RootPage.java            |    2 
 src/test/java/com/gitblit/tests/MailTest.java                   |    4 
 src/main/java/com/gitblit/git/GitblitReceivePackFactory.java    |   14 
 src/main/java/com/gitblit/SparkleShareInviteServlet.java        |   19 
 src/main/java/com/gitblit/LogoServlet.java                      |   10 
 src/test/java/com/gitblit/tests/ByteFormatTest.java             |    4 
 src/main/java/com/gitblit/LdapUserService.java                  |    4 
 src/main/java/com/gitblit/RedmineUserService.java               |    4 
 src/main/java/com/gitblit/GitblitUserService.java               |    4 
 src/test/java/com/gitblit/tests/HtpasswdUserServiceTest.java    |    8 
 src/test/java/com/gitblit/tests/UserServiceTest.java            |    6 
 src/test/java/com/gitblit/tests/PermissionsTest.java            |    3 
 src/main/java/com/gitblit/RpcFilter.java                        |   18 
 src/main/java/com/gitblit/LuceneExecutor.java                   |   12 
 src/test/java/de/akquinet/devops/GitBlitServer4UITests.java     |   13 
 src/main/java/com/gitblit/manager/IProjectManager.java          |   63 +
 src/main/java/com/gitblit/AuthenticationFilter.java             |    4 
 src/test/java/com/gitblit/tests/GitblitUnitTest.java            |   67 +
 src/main/java/com/gitblit/utils/ActivityUtils.java              |   21 
 src/main/java/com/gitblit/git/GitServlet.java                   |    4 
 src/main/java/com/gitblit/git/GitblitUploadPackFactory.java     |    4 
 src/test/java/com/gitblit/tests/ObjectCacheTest.java            |    6 
 src/main/java/com/gitblit/manager/INotificationManager.java     |   66 +
 src/main/java/com/gitblit/wicket/pages/EditUserPage.java        |    4 
 src/test/java/com/gitblit/tests/TimeUtilsTest.java              |    5 
 src/main/java/com/gitblit/DownloadZipServlet.java               |   10 
 src/test/java/com/gitblit/tests/RpcTests.java                   |    7 
 src/test/java/com/gitblit/tests/DiffUtilsTest.java              |    5 
 src/test/java/com/gitblit/tests/LdapUserServiceTest.java        |    7 
 src/main/java/com/gitblit/wicket/pages/SessionPage.java         |    2 
 src/test/java/com/gitblit/tests/StringUtilsTest.java            |    6 
 src/main/java/com/gitblit/PagesFilter.java                      |    4 
 src/test/java/com/gitblit/tests/FanoutServiceTest.java          |    4 
 src/main/java/com/gitblit/PAMUserService.java                   |    4 
 src/main/java/com/gitblit/wicket/GitblitWicketFilter.java       |   23 
 src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java |    2 
 src/main/java/com/gitblit/SyndicationFilter.java                |   17 
 src/test/java/com/gitblit/tests/FileUtilsTest.java              |    5 
 src/main/java/com/gitblit/FederationClient.java                 |    3 
 src/main/java/com/gitblit/SyndicationServlet.java               |   24 
 src/main/java/com/gitblit/FederationPullExecutor.java           |   44 
 src/main/java/com/gitblit/manager/IRuntimeManager.java          |  103 ++
 src/main/java/com/gitblit/WindowsUserService.java               |    4 
 src/main/java/com/gitblit/GitFilter.java                        |   21 
 src/main/java/com/gitblit/manager/IRepositoryManager.java       |  396 ++++++++
 src/main/java/com/gitblit/MirrorExecutor.java                   |   11 
 src/test/java/com/gitblit/tests/Issue0271Test.java              |    3 
 src/test/java/com/gitblit/tests/GitBlitSuite.java               |   11 
 src/test/java/de/akquinet/devops/GitBlit4UITests.java           |    3 
 src/test/java/com/gitblit/tests/GitDaemonTest.java              |   30 
 src/main/java/com/gitblit/HtpasswdUserService.java              |    7 
 src/main/java/com/gitblit/manager/IGitblitManager.java          |   83 +
 src/main/java/com/gitblit/manager/ISessionManager.java          |   64 +
 src/main/java/com/gitblit/wicket/pages/ChangePasswordPage.java  |    4 
 src/test/java/com/gitblit/tests/JsonUtilsTest.java              |    4 
 src/main/java/com/gitblit/git/GitblitReceivePack.java           |   21 
 src/test/java/com/gitblit/tests/GitServletTest.java             |   65 
 src/test/java/com/gitblit/tests/RedmineUserServiceTest.java     |    6 
 src/main/java/com/gitblit/GitBlit.java                          |  518 ++++++----
 src/main/java/com/gitblit/RpcServlet.java                       |  104 +
 src/test/java/com/gitblit/tests/LuceneExecutorTest.java         |    6 
 src/main/java/com/gitblit/wicket/pages/ActivityPage.java        |    9 
 src/test/java/com/gitblit/tests/ArrayUtilsTest.java             |    5 
 src/test/java/com/gitblit/tests/JnaUtilsTest.java               |    6 
 src/main/java/com/gitblit/manager/IUserManager.java             |  280 +++++
 src/main/java/com/gitblit/FederationServlet.java                |   56 
 src/test/java/com/gitblit/tests/RepositoryModelTest.java        |   13 
 src/test/java/com/gitblit/tests/ActivityTest.java               |    4 
 src/main/java/com/gitblit/wicket/pages/EditTeamPage.java        |    2 
 src/test/java/com/gitblit/tests/GroovyScriptTest.java           |   15 
 src/test/java/com/gitblit/tests/ModelUtilsTest.java             |    6 
 src/test/java/com/gitblit/tests/SyndicationUtilsTest.java       |    5 
 src/test/java/com/gitblit/tests/MarkdownUtilsTest.java          |    4 
 src/main/java/com/gitblit/BranchGraphServlet.java               |   12 
 src/main/java/com/gitblit/EnforceAuthenticationFilter.java      |   41 
 src/main/java/com/gitblit/manager/IFederationManager.java       |  177 +++
 src/main/java/com/gitblit/git/RepositoryResolver.java           |    8 
 src/main/java/com/gitblit/PagesServlet.java                     |   11 
 src/main/java/com/gitblit/GitBlitServer.java                    |    6 
 src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java  |    2 
 src/main/java/com/gitblit/SalesforceUserService.java            |    4 
 src/main/java/com/gitblit/wicket/pages/RepositoryPage.java      |    2 
 src/main/java/com/gitblit/IStoredSettings.java                  |   10 
 src/main/java/com/gitblit/RobotsTxtServlet.java                 |    4 
 src/test/java/com/gitblit/tests/PushLogTest.java                |    2 
 src/test/java/com/gitblit/tests/FederationTests.java            |    6 
 src/test/java/com/gitblit/tests/MetricUtilsTest.java            |    5 
 src/main/java/com/gitblit/wicket/pages/LogoutPage.java          |    3 
 src/test/java/com/gitblit/tests/Issue0259Test.java              |    3 
 97 files changed, 2,145 insertions(+), 706 deletions(-)

diff --git a/src/main/java/com/gitblit/AccessRestrictionFilter.java b/src/main/java/com/gitblit/AccessRestrictionFilter.java
index cadf915..ac97ba2 100644
--- a/src/main/java/com/gitblit/AccessRestrictionFilter.java
+++ b/src/main/java/com/gitblit/AccessRestrictionFilter.java
@@ -25,6 +25,8 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import com.gitblit.manager.IRepositoryManager;
+import com.gitblit.manager.IRuntimeManager;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.UserModel;
 import com.gitblit.utils.StringUtils;
@@ -126,7 +128,10 @@
 		String fullUrl = getFullUrl(httpRequest);
 		String repository = extractRepositoryName(fullUrl);
 
-		if (GitBlit.self().isCollectingGarbage(repository)) {
+		IRuntimeManager runtimeManager = GitBlit.getManager(IRuntimeManager.class);
+		IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);
+
+		if (repositoryManager.isCollectingGarbage(repository)) {
 			logger.info(MessageFormat.format("ARF: Rejecting request for {0}, busy collecting garbage!", repository));
 			httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
 			return;
@@ -139,12 +144,12 @@
 		UserModel user = getUser(httpRequest);
 
 		// Load the repository model
-		RepositoryModel model = GitBlit.self().getRepositoryModel(repository);
+		RepositoryModel model = repositoryManager.getRepositoryModel(repository);
 		if (model == null) {
 			if (isCreationAllowed()) {
 				if (user == null) {
 					// challenge client to provide credentials for creation. send 401.
-					if (GitBlit.isDebugMode()) {
+					if (runtimeManager.isDebugMode()) {
 						logger.info(MessageFormat.format("ARF: CREATE CHALLENGE {0}", fullUrl));
 					}
 					httpResponse.setHeader("WWW-Authenticate", CHALLENGE);
@@ -191,7 +196,7 @@
 		if (!StringUtils.isEmpty(urlRequestType) && requiresAuthentication(model, urlRequestType)) {
 			if (user == null) {
 				// challenge client to provide credentials. send 401.
-				if (GitBlit.isDebugMode()) {
+				if (runtimeManager.isDebugMode()) {
 					logger.info(MessageFormat.format("ARF: CHALLENGE {0}", fullUrl));
 				}
 				httpResponse.setHeader("WWW-Authenticate", CHALLENGE);
@@ -209,7 +214,7 @@
 					return;
 				}
 				// valid user, but not for requested access. send 403.
-				if (GitBlit.isDebugMode()) {
+				if (runtimeManager.isDebugMode()) {
 					logger.info(MessageFormat.format("ARF: {0} forbidden to access {1}",
 							user.username, fullUrl));
 				}
@@ -218,7 +223,7 @@
 			}
 		}
 
-		if (GitBlit.isDebugMode()) {
+		if (runtimeManager.isDebugMode()) {
 			logger.info(MessageFormat.format("ARF: {0} ({1}) unauthenticated", fullUrl,
 					HttpServletResponse.SC_CONTINUE));
 		}
diff --git a/src/main/java/com/gitblit/AuthenticationFilter.java b/src/main/java/com/gitblit/AuthenticationFilter.java
index 640bf17..9aeb89f 100644
--- a/src/main/java/com/gitblit/AuthenticationFilter.java
+++ b/src/main/java/com/gitblit/AuthenticationFilter.java
@@ -35,6 +35,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.gitblit.manager.ISessionManager;
 import com.gitblit.models.UserModel;
 import com.gitblit.utils.DeepCopier;
 import com.gitblit.utils.StringUtils;
@@ -100,7 +101,8 @@
 	 * @return user
 	 */
 	protected UserModel getUser(HttpServletRequest httpRequest) {
-		UserModel user = GitBlit.self().authenticate(httpRequest, requiresClientCertificate());
+		ISessionManager sessionManager = GitBlit.getManager(ISessionManager.class);
+		UserModel user = sessionManager.authenticate(httpRequest, requiresClientCertificate());
 		return user;
 	}
 
diff --git a/src/main/java/com/gitblit/BranchGraphServlet.java b/src/main/java/com/gitblit/BranchGraphServlet.java
index 293a291..986560c 100644
--- a/src/main/java/com/gitblit/BranchGraphServlet.java
+++ b/src/main/java/com/gitblit/BranchGraphServlet.java
@@ -50,6 +50,8 @@
 import org.eclipse.jgit.revplot.PlotWalk;
 import org.eclipse.jgit.revwalk.RevCommit;
 
+import com.gitblit.manager.IRepositoryManager;
+import com.gitblit.manager.IRuntimeManager;
 import com.gitblit.utils.JGitUtils;
 import com.gitblit.utils.StringUtils;
 
@@ -102,9 +104,10 @@
 	protected long getLastModified(HttpServletRequest req) {
 		String repository = req.getParameter("r");
 		String objectId = req.getParameter("h");
+		IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);
 		Repository r = null;
 		try {
-			r = GitBlit.self().getRepository(repository);
+			r = repositoryManager.getRepository(repository);
 			if (StringUtils.isEmpty(objectId)) {
 				objectId = JGitUtils.getHEADRef(r);
 			}
@@ -128,7 +131,10 @@
 			String objectId = request.getParameter("h");
 			String length = request.getParameter("l");
 
-			r = GitBlit.self().getRepository(repository);
+			IStoredSettings settings = GitBlit.getManager(IRuntimeManager.class).getSettings();
+			IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);
+
+			r = repositoryManager.getRepository(repository);
 
 			rw = new PlotWalk(r);
 			if (StringUtils.isEmpty(objectId)) {
@@ -138,7 +144,7 @@
 			rw.markStart(rw.lookupCommit(r.resolve(objectId)));
 
 			// default to the items-per-page setting, unless specified
-			int maxCommits = GitBlit.getInteger(Keys.web.itemsPerPage, 50);
+			int maxCommits = settings.getInteger(Keys.web.itemsPerPage, 50);
 			int requestedCommits = maxCommits;
 			if (!StringUtils.isEmpty(length)) {
 				int l = Integer.parseInt(length);
diff --git a/src/main/java/com/gitblit/DownloadZipServlet.java b/src/main/java/com/gitblit/DownloadZipServlet.java
index c8da267..82846da 100644
--- a/src/main/java/com/gitblit/DownloadZipServlet.java
+++ b/src/main/java/com/gitblit/DownloadZipServlet.java
@@ -29,6 +29,8 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.gitblit.manager.IRepositoryManager;
+import com.gitblit.manager.IRuntimeManager;
 import com.gitblit.utils.CompressionUtils;
 import com.gitblit.utils.JGitUtils;
 import com.gitblit.utils.MarkdownUtils;
@@ -101,7 +103,8 @@
 	private void processRequest(javax.servlet.http.HttpServletRequest request,
 			javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
 			java.io.IOException {
-		if (!GitBlit.getBoolean(Keys.web.allowZipDownloads, true)) {
+		IStoredSettings settings = GitBlit.getManager(IRuntimeManager.class).getSettings();
+		if (!settings.getBoolean(Keys.web.allowZipDownloads, true)) {
 			logger.warn("Zip downloads are disabled");
 			response.sendError(HttpServletResponse.SC_FORBIDDEN);
 			return;
@@ -130,9 +133,10 @@
 				name += "-" + objectId;
 			}
 
-			Repository r = GitBlit.self().getRepository(repository);
+			IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);
+			Repository r = repositoryManager.getRepository(repository);
 			if (r == null) {
-				if (GitBlit.self().isCollectingGarbage(repository)) {
+				if (repositoryManager.isCollectingGarbage(repository)) {
 					error(response, MessageFormat.format("# Error\nGitblit is busy collecting garbage in {0}", repository));
 					return;
 				} else {
diff --git a/src/main/java/com/gitblit/EnforceAuthenticationFilter.java b/src/main/java/com/gitblit/EnforceAuthenticationFilter.java
index ae91c32..93057b4 100644
--- a/src/main/java/com/gitblit/EnforceAuthenticationFilter.java
+++ b/src/main/java/com/gitblit/EnforceAuthenticationFilter.java
@@ -30,6 +30,8 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.gitblit.manager.IRuntimeManager;
+import com.gitblit.manager.ISessionManager;
 import com.gitblit.models.UserModel;
 
 /**
@@ -49,9 +51,7 @@
 	 */
 	@Override
 	public void init(FilterConfig filterConfig) throws ServletException {
-		// nothing to be done
-
-	} //init
+	}
 
 
 	/*
@@ -62,32 +62,28 @@
 	@Override
 	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
 
-		/*
-		 * Determine whether to enforce the BASIC authentication:
-		 */
-		@SuppressWarnings("static-access")
-		Boolean mustForceAuth = GitBlit.self().getBoolean(Keys.web.authenticateViewPages, false)
-								&& GitBlit.self().getBoolean(Keys.web.enforceHttpBasicAuthentication, false);
+		IStoredSettings settings = GitBlit.getManager(IRuntimeManager.class).getSettings();
+		ISessionManager sessionManager = GitBlit.getManager(ISessionManager.class);
+		Boolean mustForceAuth = settings.getBoolean(Keys.web.authenticateViewPages, false)
+								&& settings.getBoolean(Keys.web.enforceHttpBasicAuthentication, false);
 
-		HttpServletRequest  HttpRequest  = (HttpServletRequest)request;
-		HttpServletResponse HttpResponse = (HttpServletResponse)response;
-		UserModel user = GitBlit.self().authenticate(HttpRequest);
+		HttpServletRequest  httpRequest  = (HttpServletRequest) request;
+		HttpServletResponse httpResponse = (HttpServletResponse) response;
+		UserModel user = sessionManager.authenticate(httpRequest);
 
 		if (mustForceAuth && (user == null)) {
 			// not authenticated, enforce now:
 			logger.debug(MessageFormat.format("EnforceAuthFilter: user not authenticated for URL {0}!", request.toString()));
-			@SuppressWarnings("static-access")
-			String CHALLENGE = MessageFormat.format("Basic realm=\"{0}\"", GitBlit.self().getString("web.siteName",""));
-			HttpResponse.setHeader("WWW-Authenticate", CHALLENGE);
-			HttpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+			String challenge = MessageFormat.format("Basic realm=\"{0}\"", settings.getString(Keys.web.siteName, ""));
+			httpResponse.setHeader("WWW-Authenticate", challenge);
+			httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
 			return;
 
 		} else {
 			// user is authenticated, or don't care, continue handling
-			chain.doFilter( request, response );
-
-		} // authenticated
-	} // doFilter
+			chain.doFilter(request, response);
+		}
+	}
 
 
 	/*
@@ -95,8 +91,5 @@
 	 */
 	@Override
 	public void destroy() {
-		// Nothing to be done
-
-	} // destroy
-
+	}
 }
diff --git a/src/main/java/com/gitblit/FederationClient.java b/src/main/java/com/gitblit/FederationClient.java
index 6b2161c..eae6b94 100644
--- a/src/main/java/com/gitblit/FederationClient.java
+++ b/src/main/java/com/gitblit/FederationClient.java
@@ -83,7 +83,8 @@
 		}
 
 		// configure the Gitblit singleton for minimal, non-server operation
-		GitBlit.self().configureContext(settings, baseFolder, false);
+		GitBlit gitblit = new GitBlit(settings, baseFolder);
+		gitblit.configureContext(settings, baseFolder, false);
 		FederationPullExecutor executor = new FederationPullExecutor(registrations, params.isDaemon);
 		executor.run();
 		if (!params.isDaemon) {
diff --git a/src/main/java/com/gitblit/FederationPullExecutor.java b/src/main/java/com/gitblit/FederationPullExecutor.java
index 831c7a5..e9a604d 100644
--- a/src/main/java/com/gitblit/FederationPullExecutor.java
+++ b/src/main/java/com/gitblit/FederationPullExecutor.java
@@ -46,6 +46,11 @@
 import com.gitblit.Constants.FederationPullStatus;
 import com.gitblit.Constants.FederationStrategy;
 import com.gitblit.GitBlitException.ForbiddenException;
+import com.gitblit.manager.IGitblitManager;
+import com.gitblit.manager.INotificationManager;
+import com.gitblit.manager.IRepositoryManager;
+import com.gitblit.manager.IRuntimeManager;
+import com.gitblit.manager.IUserManager;
 import com.gitblit.models.FederationModel;
 import com.gitblit.models.RefModel;
 import com.gitblit.models.RepositoryModel;
@@ -116,7 +121,8 @@
 					if (registration.notifyOnError) {
 						String message = "Federation pull of " + registration.name + " @ "
 								+ registration.url + " is now at " + is.name();
-						GitBlit.self()
+						INotificationManager mailManager = GitBlit.getManager(INotificationManager.class);
+						mailManager
 								.sendMailToAdministrators(
 										"Pull Status of " + registration.name + " is " + is.name(),
 										message);
@@ -153,7 +159,8 @@
 							c, registrationFolder, registration.name));
 			return;
 		}
-		File repositoriesFolder = GitBlit.getRepositoriesFolder();
+		IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);
+		File repositoriesFolder = repositoryManager.getRepositoriesFolder();
 		File registrationFolderFile = new File(repositoriesFolder, registrationFolder);
 		registrationFolderFile.mkdirs();
 
@@ -193,9 +200,9 @@
 			// confirm that the origin of any pre-existing repository matches
 			// the clone url
 			String fetchHead = null;
-			Repository existingRepository = GitBlit.self().getRepository(repositoryName);
+			Repository existingRepository = repositoryManager.getRepository(repositoryName);
 
-			if (existingRepository == null && GitBlit.self().isCollectingGarbage(repositoryName)) {
+			if (existingRepository == null && repositoryManager.isCollectingGarbage(repositoryName)) {
 				logger.warn(MessageFormat.format("Skipping local repository {0}, busy collecting garbage", repositoryName));
 				continue;
 			}
@@ -227,8 +234,8 @@
 
 			CloneResult result = JGitUtils.cloneRepository(registrationFolderFile, repository.name,
 					cloneUrl, registration.bare, credentials);
-			Repository r = GitBlit.self().getRepository(repositoryName);
-			RepositoryModel rm = GitBlit.self().getRepositoryModel(repositoryName);
+			Repository r = repositoryManager.getRepository(repositoryName);
+			RepositoryModel rm = repositoryManager.getRepositoryModel(repositoryName);
 			repository.isFrozen = registration.mirror;
 			if (result.createdRepository) {
 				// default local settings
@@ -316,10 +323,12 @@
 			// "federated" repositories.
 			repository.isFederated = cloneUrl.startsWith(registration.url);
 
-			GitBlit.self().updateConfiguration(r, repository);
+			repositoryManager.updateConfiguration(r, repository);
 			r.close();
 		}
 
+		IUserManager userManager = GitBlit.getManager(IUserManager.class);
+		IGitblitManager gitblitManager = GitBlit.getManager(IGitblitManager.class);
 		IUserService userService = null;
 
 		try {
@@ -359,10 +368,10 @@
 						}
 
 						// insert new user or update local user
-						UserModel localUser = GitBlit.self().getUserModel(user.username);
+						UserModel localUser = userManager.getUserModel(user.username);
 						if (localUser == null) {
 							// create new local user
-							GitBlit.self().updateUserModel(user.username, user, true);
+							gitblitManager.updateUserModel(user.username, user, true);
 						} else {
 							// update repository permissions of local user
 							if (user.permissions != null) {
@@ -379,19 +388,19 @@
 							}
 							localUser.password = user.password;
 							localUser.canAdmin = user.canAdmin;
-							GitBlit.self().updateUserModel(localUser.username, localUser, false);
+							gitblitManager.updateUserModel(localUser.username, localUser, false);
 						}
 
-						for (String teamname : GitBlit.self().getAllTeamnames()) {
-							TeamModel team = GitBlit.self().getTeamModel(teamname);
+						for (String teamname : userManager.getAllTeamNames()) {
+							TeamModel team = userManager.getTeamModel(teamname);
 							if (user.isTeamMember(teamname) && !team.hasUser(user.username)) {
 								// new team member
 								team.addUser(user.username);
-								GitBlit.self().updateTeamModel(teamname, team, false);
+								userManager.updateTeamModel(teamname, team);
 							} else if (!user.isTeamMember(teamname) && team.hasUser(user.username)) {
 								// remove team member
 								team.removeUser(user.username);
-								GitBlit.self().updateTeamModel(teamname, team, false);
+								userManager.updateTeamModel(teamname, team);
 							}
 
 							// update team repositories
@@ -402,11 +411,11 @@
 									for (Map.Entry<String, AccessPermission> entry : remoteTeam.permissions.entrySet()){
 										team.setRepositoryPermission(entry.getKey(), entry.getValue());
 									}
-									GitBlit.self().updateTeamModel(teamname, team, false);
+									userManager.updateTeamModel(teamname, team);
 								} else if(!ArrayUtils.isEmpty(remoteTeam.repositories)) {
 									// pulling from <= 1.1
 									team.addRepositoryPermissions(remoteTeam.repositories);
-									GitBlit.self().updateTeamModel(teamname, team, false);
+									userManager.updateTeamModel(teamname, team);
 								}
 							}
 						}
@@ -497,7 +506,8 @@
 			return;
 		}
 		InetAddress addr = InetAddress.getLocalHost();
-		String federationName = GitBlit.getString(Keys.federation.name, null);
+		IStoredSettings settings = GitBlit.getManager(IRuntimeManager.class).getSettings();
+		String federationName = settings.getString(Keys.federation.name, null);
 		if (StringUtils.isEmpty(federationName)) {
 			federationName = addr.getHostName();
 		}
diff --git a/src/main/java/com/gitblit/FederationServlet.java b/src/main/java/com/gitblit/FederationServlet.java
index 5db9645..0d83b4f 100644
--- a/src/main/java/com/gitblit/FederationServlet.java
+++ b/src/main/java/com/gitblit/FederationServlet.java
@@ -28,6 +28,10 @@
 import javax.servlet.http.HttpServletResponse;
 
 import com.gitblit.Constants.FederationRequest;
+import com.gitblit.manager.IFederationManager;
+import com.gitblit.manager.IRepositoryManager;
+import com.gitblit.manager.IRuntimeManager;
+import com.gitblit.manager.IUserManager;
 import com.gitblit.models.FederationModel;
 import com.gitblit.models.FederationProposal;
 import com.gitblit.models.TeamModel;
@@ -65,6 +69,12 @@
 	protected void processRequest(javax.servlet.http.HttpServletRequest request,
 			javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
 			java.io.IOException {
+
+		IStoredSettings settings = GitBlit.getManager(IRuntimeManager.class).getSettings();
+		IUserManager userManager = GitBlit.getManager(IUserManager.class);
+		IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);
+		IFederationManager federationManager = GitBlit.getManager(IFederationManager.class);
+
 		FederationRequest reqType = FederationRequest.fromName(request.getParameter("req"));
 		logger.info(MessageFormat.format("Federation {0} request from {1}", reqType,
 				request.getRemoteAddr()));
@@ -75,13 +85,13 @@
 			return;
 		}
 
-		if (!GitBlit.getBoolean(Keys.git.enableGitServlet, true)) {
+		if (!settings.getBoolean(Keys.git.enableGitServlet, true)) {
 			logger.warn(Keys.git.enableGitServlet + " must be set TRUE for federation requests.");
 			response.sendError(HttpServletResponse.SC_FORBIDDEN);
 			return;
 		}
 
-		String uuid = GitBlit.getString(Keys.federation.passphrase, "");
+		String uuid = settings.getString(Keys.federation.passphrase, "");
 		if (StringUtils.isEmpty(uuid)) {
 			logger.warn(Keys.federation.passphrase
 					+ " is not properly set!  Federation request denied.");
@@ -97,7 +107,7 @@
 			}
 
 			// reject proposal, if not receipt prohibited
-			if (!GitBlit.getBoolean(Keys.federation.allowProposals, false)) {
+			if (!settings.getBoolean(Keys.federation.allowProposals, false)) {
 				logger.error(MessageFormat.format("Rejected {0} federation proposal from {1}",
 						proposal.tokenType.name(), proposal.url));
 				response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
@@ -119,7 +129,7 @@
 			}
 
 			String url = HttpUtils.getGitblitURL(request);
-			GitBlit.self().submitFederationProposal(proposal, url);
+			federationManager.submitFederationProposal(proposal, url);
 			logger.info(MessageFormat.format(
 					"Submitted {0} federation proposal to pull {1} repositories from {2}",
 					proposal.tokenType.name(), proposal.repositories.size(), proposal.url));
@@ -145,7 +155,7 @@
 			results.nextPull = new Date(System.currentTimeMillis() + (mins * 60 * 1000L));
 
 			// acknowledge the receipt of status
-			GitBlit.self().acknowledgeFederationStatus(identification, results);
+			federationManager.acknowledgeFederationStatus(identification, results);
 			logger.info(MessageFormat.format(
 					"Received status of {0} federated repositories from {1}", results
 							.getStatusList().size(), identification));
@@ -155,7 +165,7 @@
 
 		// Determine the federation tokens for this gitblit instance
 		String token = request.getParameter("token");
-		List<String> tokens = GitBlit.self().getFederationTokens();
+		List<String> tokens = federationManager.getFederationTokens();
 		if (!tokens.contains(token)) {
 			logger.warn(MessageFormat.format(
 					"Received Federation token ''{0}'' does not match the server tokens", token));
@@ -166,11 +176,11 @@
 		Object result = null;
 		if (FederationRequest.PULL_REPOSITORIES.equals(reqType)) {
 			String gitblitUrl = HttpUtils.getGitblitURL(request);
-			result = GitBlit.self().getRepositories(gitblitUrl, token);
+			result = federationManager.getRepositories(gitblitUrl, token);
 		} else {
 			if (FederationRequest.PULL_SETTINGS.equals(reqType)) {
 				// pull settings
-				if (!GitBlit.self().validateFederationRequest(reqType, token)) {
+				if (!federationManager.validateFederationRequest(reqType, token)) {
 					// invalid token to pull users or settings
 					logger.warn(MessageFormat.format(
 							"Federation token from {0} not authorized to pull SETTINGS",
@@ -178,15 +188,15 @@
 					response.sendError(HttpServletResponse.SC_FORBIDDEN);
 					return;
 				}
-				Map<String, String> settings = new HashMap<String, String>();
-				List<String> keys = GitBlit.getAllKeys(null);
+				Map<String, String> map = new HashMap<String, String>();
+				List<String> keys = settings.getAllKeys(null);
 				for (String key : keys) {
-					settings.put(key, GitBlit.getString(key, ""));
+					map.put(key, settings.getString(key, ""));
 				}
-				result = settings;
+				result = map;
 			} else if (FederationRequest.PULL_USERS.equals(reqType)) {
 				// pull users
-				if (!GitBlit.self().validateFederationRequest(reqType, token)) {
+				if (!federationManager.validateFederationRequest(reqType, token)) {
 					// invalid token to pull users or settings
 					logger.warn(MessageFormat.format(
 							"Federation token from {0} not authorized to pull USERS",
@@ -194,10 +204,10 @@
 					response.sendError(HttpServletResponse.SC_FORBIDDEN);
 					return;
 				}
-				List<String> usernames = GitBlit.self().getAllUsernames();
+				List<String> usernames = userManager.getAllUsernames();
 				List<UserModel> users = new ArrayList<UserModel>();
 				for (String username : usernames) {
-					UserModel user = GitBlit.self().getUserModel(username);
+					UserModel user = userManager.getUserModel(username);
 					if (!user.excludeFromFederation) {
 						users.add(user);
 					}
@@ -205,7 +215,7 @@
 				result = users;
 			} else if (FederationRequest.PULL_TEAMS.equals(reqType)) {
 				// pull teams
-				if (!GitBlit.self().validateFederationRequest(reqType, token)) {
+				if (!federationManager.validateFederationRequest(reqType, token)) {
 					// invalid token to pull teams
 					logger.warn(MessageFormat.format(
 							"Federation token from {0} not authorized to pull TEAMS",
@@ -213,16 +223,16 @@
 					response.sendError(HttpServletResponse.SC_FORBIDDEN);
 					return;
 				}
-				List<String> teamnames = GitBlit.self().getAllTeamnames();
+				List<String> teamnames = userManager.getAllTeamNames();
 				List<TeamModel> teams = new ArrayList<TeamModel>();
 				for (String teamname : teamnames) {
-					TeamModel user = GitBlit.self().getTeamModel(teamname);
+					TeamModel user = userManager.getTeamModel(teamname);
 					teams.add(user);
 				}
 				result = teams;
 			} else if (FederationRequest.PULL_SCRIPTS.equals(reqType)) {
 				// pull scripts
-				if (!GitBlit.self().validateFederationRequest(reqType, token)) {
+				if (!federationManager.validateFederationRequest(reqType, token)) {
 					// invalid token to pull script
 					logger.warn(MessageFormat.format(
 							"Federation token from {0} not authorized to pull SCRIPTS",
@@ -233,13 +243,13 @@
 				Map<String, String> scripts = new HashMap<String, String>();
 
 				Set<String> names = new HashSet<String>();
-				names.addAll(GitBlit.getStrings(Keys.groovy.preReceiveScripts));
-				names.addAll(GitBlit.getStrings(Keys.groovy.postReceiveScripts));
-				for (TeamModel team :  GitBlit.self().getAllTeams()) {
+				names.addAll(settings.getStrings(Keys.groovy.preReceiveScripts));
+				names.addAll(settings.getStrings(Keys.groovy.postReceiveScripts));
+				for (TeamModel team :  userManager.getAllTeams()) {
 					names.addAll(team.preReceiveScripts);
 					names.addAll(team.postReceiveScripts);
 				}
-				File scriptsFolder = GitBlit.getFileOrFolder(Keys.groovy.scriptsFolder, "groovy");
+				File scriptsFolder = repositoryManager.getHooksFolder();
 				for (String name : names) {
 					File file = new File(scriptsFolder, name);
 					if (!file.exists() && !file.getName().endsWith(".groovy")) {
diff --git a/src/main/java/com/gitblit/GCExecutor.java b/src/main/java/com/gitblit/GCExecutor.java
index 681065b..837741f 100644
--- a/src/main/java/com/gitblit/GCExecutor.java
+++ b/src/main/java/com/gitblit/GCExecutor.java
@@ -31,6 +31,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.gitblit.manager.IRepositoryManager;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.utils.FileUtils;
 
@@ -131,7 +132,9 @@
 		running.set(true);
 		Date now = new Date();
 
-		for (String repositoryName : GitBlit.self().getRepositoryList()) {
+		IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);
+
+		for (String repositoryName : repositoryManager.getRepositoryList()) {
 			if (forceClose.get()) {
 				break;
 			}
@@ -143,8 +146,8 @@
 			RepositoryModel model = null;
 			Repository repository = null;
 			try {
-				model = GitBlit.self().getRepositoryModel(repositoryName);
-				repository = GitBlit.self().getRepository(repositoryName);
+				model = repositoryManager.getRepositoryModel(repositoryName);
+				repository = repositoryManager.getRepository(repositoryName);
 				if (repository == null) {
 					logger.warn(MessageFormat.format("GCExecutor is missing repository {0}?!?", repositoryName));
 					continue;
@@ -204,7 +207,7 @@
 					if (garbageCollected) {
 						// update the last GC date
 						model.lastGC = new Date();
-						GitBlit.self().updateConfiguration(repository, model);
+						repositoryManager.updateConfiguration(repository, model);
 					}
 
 					repository.close();
diff --git a/src/main/java/com/gitblit/GitBlit.java b/src/main/java/com/gitblit/GitBlit.java
index 6d52e76..5fbc876 100644
--- a/src/main/java/com/gitblit/GitBlit.java
+++ b/src/main/java/com/gitblit/GitBlit.java
@@ -69,9 +69,9 @@
 import javax.servlet.ServletContextListener;
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 
 import org.apache.wicket.RequestCycle;
-import org.apache.wicket.protocol.http.WebResponse;
 import org.apache.wicket.resource.ContextRelativeResource;
 import org.apache.wicket.util.resource.ResourceStreamNotFoundException;
 import org.eclipse.jgit.lib.Repository;
@@ -100,6 +100,14 @@
 import com.gitblit.fanout.FanoutService;
 import com.gitblit.fanout.FanoutSocketService;
 import com.gitblit.git.GitDaemon;
+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.ISessionManager;
+import com.gitblit.manager.IUserManager;
 import com.gitblit.models.FederationModel;
 import com.gitblit.models.FederationProposal;
 import com.gitblit.models.FederationSet;
@@ -158,9 +166,19 @@
  * @author James Moger
  *
  */
-public class GitBlit implements ServletContextListener {
+public class GitBlit implements ServletContextListener,
+								IRuntimeManager,
+								INotificationManager,
+								IUserManager,
+								ISessionManager,
+								IRepositoryManager,
+								IProjectManager,
+								IFederationManager,
+								IGitblitManager {
 
 	private static GitBlit gitblit;
+
+	private final IStoredSettings goSettings;
 
 	private final Logger logger = LoggerFactory.getLogger(GitBlit.class);
 
@@ -218,14 +236,18 @@
 	private GitDaemon gitDaemon;
 
 	public GitBlit() {
-		if (gitblit == null) {
-			// set the static singleton reference
-			gitblit = this;
-		}
+		this.goSettings = null;
 	}
 
-	public GitBlit(final IUserService userService) {
+	protected GitBlit(final IUserService userService) {
+		this.goSettings = null;
 		this.userService = userService;
+		gitblit = this;
+	}
+
+	public GitBlit(IStoredSettings settings, File baseFolder) {
+		this.goSettings = settings;
+		this.baseFolder = baseFolder;
 		gitblit = this;
 	}
 
@@ -235,10 +257,25 @@
 	 * @return gitblit singleton
 	 */
 	public static GitBlit self() {
-		if (gitblit == null) {
-			new GitBlit();
-		}
 		return gitblit;
+	}
+
+	@SuppressWarnings("unchecked")
+	public static <X> X getManager(Class<X> managerClass) {
+		if (managerClass.isAssignableFrom(GitBlit.class)) {
+			return (X) gitblit;
+		}
+		return null;
+	}
+
+	@Override
+	public File getBaseFolder() {
+		return baseFolder;
+	}
+
+	@Override
+	public void setBaseFolder(File folder) {
+		this.baseFolder = folder;
 	}
 
 	/**
@@ -246,8 +283,9 @@
 	 *
 	 * @return the boot date of Gitblit
 	 */
-	public static Date getBootDate() {
-		return self().serverStatus.bootDate;
+	@Override
+	public Date getBootDate() {
+		return serverStatus.bootDate;
 	}
 
 	/**
@@ -255,10 +293,11 @@
 	 *
 	 * @return a date
 	 */
-	public static Date getLastActivityDate() {
+	@Override
+	public Date getLastActivityDate() {
 		Date date = null;
-		for (String name : self().getRepositoryList()) {
-			Repository r = self().getRepository(name);
+		for (String name : getRepositoryList()) {
+			Repository r = getRepository(name);
 			Date lastChange = JGitUtils.getLastChange(r).when;
 			r.close();
 			if (lastChange != null && (date == null || lastChange.after(date))) {
@@ -269,32 +308,14 @@
 	}
 
 	/**
-	 * Determine if this is the GO variant of Gitblit.
-	 *
-	 * @return true if this is the GO variant of Gitblit.
-	 */
-	public static boolean isGO() {
-		return self().settings instanceof FileSettings;
-	}
-
-	/**
 	 * Determine if this Gitblit instance is actively serving git repositories
 	 * or if it is merely a repository viewer.
 	 *
 	 * @return true if Gitblit is serving repositories
 	 */
-	public static boolean isServingRepositories() {
-		return getBoolean(Keys.git.enableGitServlet, true) || (getInteger(Keys.git.daemonPort, 0) > 0);
-	}
-
-	/**
-	 * Determine if this Gitblit instance is actively serving git repositories
-	 * or if it is merely a repository viewer.
-	 *
-	 * @return true if Gitblit is serving repositories
-	 */
-	public static boolean isSendingMail() {
-		return self().mailExecutor.isReady();
+	@Override
+	public boolean isServingRepositories() {
+		return settings.getBoolean(Keys.git.enableGitServlet, true) || (settings.getInteger(Keys.git.daemonPort, 0) > 0);
 	}
 
 	/**
@@ -302,167 +323,17 @@
 	 *
 	 * @return a timezone
 	 */
-	public static TimeZone getTimezone() {
-		if (self().timezone == null) {
-			String tzid = getString("web.timezone", null);
+	@Override
+	public TimeZone getTimezone() {
+		if (timezone == null) {
+			String tzid = settings.getString("web.timezone", null);
 			if (StringUtils.isEmpty(tzid)) {
-				self().timezone = TimeZone.getDefault();
-				return self().timezone;
+				timezone = TimeZone.getDefault();
+				return timezone;
 			}
-			self().timezone = TimeZone.getTimeZone(tzid);
+			timezone = TimeZone.getTimeZone(tzid);
 		}
-		return self().timezone;
-	}
-
-	/**
-	 * Returns the active settings.
-	 *
-	 * @return the active settings
-	 */
-	public static IStoredSettings getSettings() {
-		return self().settings;
-	}
-
-	/**
-	 * Returns the user-defined blob encodings.
-	 *
-	 * @return an array of encodings, may be empty
-	 */
-	public static String [] getEncodings() {
-		return getStrings(Keys.web.blobEncodings).toArray(new String[0]);
-	}
-
-
-	/**
-	 * Returns the boolean value for the specified key. If the key does not
-	 * exist or the value for the key can not be interpreted as a boolean, the
-	 * defaultValue is returned.
-	 *
-	 * @see IStoredSettings.getBoolean(String, boolean)
-	 * @param key
-	 * @param defaultValue
-	 * @return key value or defaultValue
-	 */
-	public static boolean getBoolean(String key, boolean defaultValue) {
-		return self().settings.getBoolean(key, defaultValue);
-	}
-
-	/**
-	 * Returns the integer value for the specified key. If the key does not
-	 * exist or the value for the key can not be interpreted as an integer, the
-	 * defaultValue is returned.
-	 *
-	 * @see IStoredSettings.getInteger(String key, int defaultValue)
-	 * @param key
-	 * @param defaultValue
-	 * @return key value or defaultValue
-	 */
-	public static int getInteger(String key, int defaultValue) {
-		return self().settings.getInteger(key, defaultValue);
-	}
-
-	/**
-	 * Returns the integer list for the specified key. If the key does not
-	 * exist or the value for the key can not be interpreted as an integer, an
-	 * empty list is returned.
-	 *
-	 * @see IStoredSettings.getIntegers(String key)
-	 * @param key
-	 * @return key value or defaultValue
-	 */
-	public static List<Integer> getIntegers(String key) {
-		return self().settings.getIntegers(key);
-	}
-
-	/**
-	 * Returns the value in bytes for the specified key. If the key does not
-	 * exist or the value for the key can not be interpreted as an integer, the
-	 * defaultValue is returned.
-	 *
-	 * @see IStoredSettings.getFilesize(String key, int defaultValue)
-	 * @param key
-	 * @param defaultValue
-	 * @return key value or defaultValue
-	 */
-	public static int getFilesize(String key, int defaultValue) {
-		return self().settings.getFilesize(key, defaultValue);
-	}
-
-	/**
-	 * Returns the value in bytes for the specified key. If the key does not
-	 * exist or the value for the key can not be interpreted as a long, the
-	 * defaultValue is returned.
-	 *
-	 * @see IStoredSettings.getFilesize(String key, long defaultValue)
-	 * @param key
-	 * @param defaultValue
-	 * @return key value or defaultValue
-	 */
-	public static long getFilesize(String key, long defaultValue) {
-		return self().settings.getFilesize(key, defaultValue);
-	}
-
-	/**
-	 * Returns the char value for the specified key. If the key does not exist
-	 * or the value for the key can not be interpreted as a character, the
-	 * defaultValue is returned.
-	 *
-	 * @see IStoredSettings.getChar(String key, char defaultValue)
-	 * @param key
-	 * @param defaultValue
-	 * @return key value or defaultValue
-	 */
-	public static char getChar(String key, char defaultValue) {
-		return self().settings.getChar(key, defaultValue);
-	}
-
-	/**
-	 * Returns the string value for the specified key. If the key does not exist
-	 * or the value for the key can not be interpreted as a string, the
-	 * defaultValue is returned.
-	 *
-	 * @see IStoredSettings.getString(String key, String defaultValue)
-	 * @param key
-	 * @param defaultValue
-	 * @return key value or defaultValue
-	 */
-	public static String getString(String key, String defaultValue) {
-		return self().settings.getString(key, defaultValue);
-	}
-
-	/**
-	 * Returns a list of space-separated strings from the specified key.
-	 *
-	 * @see IStoredSettings.getStrings(String key)
-	 * @param n
-	 * @return list of strings
-	 */
-	public static List<String> getStrings(String key) {
-		return self().settings.getStrings(key);
-	}
-
-	/**
-	 * Returns a map of space-separated key-value pairs from the specified key.
-	 *
-	 * @see IStoredSettings.getStrings(String key)
-	 * @param n
-	 * @return map of string, string
-	 */
-	public static Map<String, String> getMap(String key) {
-		return self().settings.getMap(key);
-	}
-
-	/**
-	 * Returns the list of keys whose name starts with the specified prefix. If
-	 * the prefix is null or empty, all key names are returned.
-	 *
-	 * @see IStoredSettings.getAllKeys(String key)
-	 * @param startingWith
-	 * @return list of keys
-	 */
-
-	public static List<String> getAllKeys(String startingWith) {
-		return self().settings.getAllKeys(startingWith);
+		return timezone;
 	}
 
 	/**
@@ -470,8 +341,9 @@
 	 *
 	 * @return true if Gitblit is running in debug mode
 	 */
-	public static boolean isDebugMode() {
-		return self().settings.getBoolean(Keys.web.debugMode, false);
+	@Override
+	public boolean isDebugMode() {
+		return settings.getBoolean(Keys.web.debugMode, false);
 	}
 
 	/**
@@ -479,8 +351,9 @@
 	 *
 	 * @return the file
 	 */
-	public static File getFileOrFolder(String key, String defaultFileOrFolder) {
-		String fileOrFolder = GitBlit.getString(key, defaultFileOrFolder);
+	@Override
+	public File getFileOrFolder(String key, String defaultFileOrFolder) {
+		String fileOrFolder = settings.getString(key, defaultFileOrFolder);
 		return getFileOrFolder(fileOrFolder);
 	}
 
@@ -493,9 +366,10 @@
 	 *
 	 * @return the file
 	 */
-	public static File getFileOrFolder(String fileOrFolder) {
+	@Override
+	public File getFileOrFolder(String fileOrFolder) {
 		return com.gitblit.utils.FileUtils.resolveParameter(Constants.baseFolder$,
-				self().baseFolder, fileOrFolder);
+				baseFolder, fileOrFolder);
 	}
 
 	/**
@@ -504,7 +378,8 @@
 	 *
 	 * @return the repositories folder path
 	 */
-	public static File getRepositoriesFolder() {
+	@Override
+	public File getRepositoriesFolder() {
 		return getFileOrFolder(Keys.git.repositoriesFolder, "${baseFolder}/git");
 	}
 
@@ -514,7 +389,8 @@
 	 *
 	 * @return the proposals folder path
 	 */
-	public static File getProposalsFolder() {
+	@Override
+	public File getProposalsFolder() {
 		return getFileOrFolder(Keys.federation.proposalsFolder, "${baseFolder}/proposals");
 	}
 
@@ -524,20 +400,44 @@
 	 *
 	 * @return the Groovy scripts folder path
 	 */
-	public static File getGroovyScriptsFolder() {
+	@Override
+	public File getHooksFolder() {
 		return getFileOrFolder(Keys.groovy.scriptsFolder, "${baseFolder}/groovy");
 	}
 
 	/**
-	 * Updates the list of server settings.
+	 * Returns the path of the Groovy Grape folder. This method checks to see if
+	 * Gitblit is running on a cloud service and may return an adjusted path.
+	 *
+	 * @return the Groovy Grape folder path
+	 */
+	@Override
+	public File getGrapesFolder() {
+		return getFileOrFolder(Keys.groovy.grapeFolder, "${baseFolder}/groovy/grape");
+	}
+
+	/**
+	 * Returns the runtime settings.
+	 *
+	 * @return runtime settings
+	 */
+	@Override
+	public IStoredSettings getSettings() {
+		return settings;
+	}
+
+	/**
+	 * Updates the runtime settings.
 	 *
 	 * @param settings
 	 * @return true if the update succeeded
 	 */
+	@Override
 	public boolean updateSettings(Map<String, String> updatedSettings) {
 		return settings.saveSettings(updatedSettings);
 	}
 
+	@Override
 	public ServerStatus getStatus() {
 		// update heap memory status
 		serverStatus.heapAllocated = Runtime.getRuntime().totalMemory();
@@ -553,6 +453,7 @@
 	 * @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;
@@ -650,6 +551,7 @@
 	 *
 	 * @return a collection of client applications
 	 */
+	@Override
 	public Collection<GitClientApplication> getClientApplications() {
 		// prefer user definitions, if they exist
 		File userDefs = new File(baseFolder, "clientapps.json");
@@ -718,6 +620,7 @@
 		this.userService.setup(settings);
 	}
 
+	@Override
 	public boolean supportsAddUser() {
 		return supportsCredentialChanges(new UserModel(""));
 	}
@@ -728,6 +631,7 @@
 	 * @param user
 	 * @return true if the user service supports credential changes
 	 */
+	@Override
 	public boolean supportsCredentialChanges(UserModel user) {
 		if (user == null) {
 			return false;
@@ -746,6 +650,7 @@
 	 * @param user
 	 * @return true if the user service supports display name changes
 	 */
+	@Override
 	public boolean supportsDisplayNameChanges(UserModel user) {
 		return (user != null && user.isLocalAccount()) || userService.supportsDisplayNameChanges();
 	}
@@ -756,6 +661,7 @@
 	 * @param user
 	 * @return true if the user service supports email address changes
 	 */
+	@Override
 	public boolean supportsEmailAddressChanges(UserModel user) {
 		return (user != null && user.isLocalAccount()) || userService.supportsEmailAddressChanges();
 	}
@@ -766,6 +672,7 @@
 	 * @param user
 	 * @return true if the user service supports team membership changes
 	 */
+	@Override
 	public boolean supportsTeamMembershipChanges(UserModel user) {
 		return (user != null && user.isLocalAccount()) || userService.supportsTeamMembershipChanges();
 	}
@@ -790,6 +697,7 @@
 	 * @param password
 	 * @return a user object or null
 	 */
+	@Override
 	public UserModel authenticate(String username, char[] password) {
 		if (StringUtils.isEmpty(username)) {
 			// can not authenticate empty username
@@ -850,6 +758,7 @@
 	 * @param httpRequest
 	 * @return a user object or null
 	 */
+	@Override
 	public UserModel authenticate(HttpServletRequest httpRequest) {
 		return authenticate(httpRequest, false);
 	}
@@ -864,10 +773,11 @@
 	 * @param requiresCertificate
 	 * @return a user object or null
 	 */
+	@Override
 	public UserModel authenticate(HttpServletRequest httpRequest, boolean requiresCertificate) {
 		// try to authenticate by certificate
 		boolean checkValidity = settings.getBoolean(Keys.git.enforceCertificateValidity, true);
-		String [] oids = getStrings(Keys.git.certificateUsernameOIDs).toArray(new String[0]);
+		String [] oids = settings.getStrings(Keys.git.certificateUsernameOIDs).toArray(new String[0]);
 		UserModel model = HttpUtils.getUserModelFromCertificate(httpRequest, checkValidity, oids);
 		if (model != null) {
 			// grab real user model and preserve certificate serial number
@@ -921,7 +831,7 @@
 		}
 
 		// try to authenticate by cookie
-		if (allowCookieAuthentication()) {
+		if (supportsCookies()) {
 			UserModel user = authenticate(httpRequest.getCookies());
 			if (user != null) {
 				flagWicketSession(AuthenticationType.COOKIE);
@@ -985,7 +895,8 @@
 	 * @param response
 	 * @param user
 	 */
-	public void setCookie(WebResponse response, UserModel user) {
+	@Override
+	public void setCookie(HttpServletResponse response, UserModel user) {
 		if (userService == null) {
 			return;
 		}
@@ -1019,6 +930,7 @@
 	 *
 	 * @param user
 	 */
+	@Override
 	public void logout(UserModel user) {
 		if (userService == null) {
 			return;
@@ -1052,6 +964,7 @@
 	 * @see IUserService.getAllUsernames()
 	 * @return list of all usernames
 	 */
+	@Override
 	public List<String> getAllUsernames() {
 		List<String> names = new ArrayList<String>(userService.getAllUsernames());
 		return names;
@@ -1063,6 +976,7 @@
 	 * @see IUserService.getAllUsernames()
 	 * @return list of all usernames
 	 */
+	@Override
 	public List<UserModel> getAllUsers() {
 		List<UserModel> users = userService.getAllUsers();
 		return users;
@@ -1075,6 +989,7 @@
 	 * @param username
 	 * @return true if successful
 	 */
+	@Override
 	public boolean deleteUser(String username) {
 		if (StringUtils.isEmpty(username)) {
 			return false;
@@ -1083,7 +998,8 @@
 		return userService.deleteUser(usernameDecoded);
 	}
 
-	protected UserModel getFederationUser() {
+	@Override
+	public UserModel getFederationUser() {
 		// the federation user is an administrator
 		UserModel federationUser = new UserModel(Constants.FEDERATION_USER);
 		federationUser.canAdmin = true;
@@ -1097,6 +1013,7 @@
 	 * @param username
 	 * @return a user object or null
 	 */
+	@Override
 	public UserModel getUserModel(String username) {
 		if (StringUtils.isEmpty(username)) {
 			return null;
@@ -1113,6 +1030,7 @@
 	 * @param user
 	 * @return the effective list of permissions for the user
 	 */
+	@Override
 	public List<RegistrantAccessPermission> getUserAccessPermissions(UserModel user) {
 		if (StringUtils.isEmpty(user.username)) {
 			// new user
@@ -1123,7 +1041,7 @@
 		// Flag missing repositories
 		for (RegistrantAccessPermission permission : set) {
 			if (permission.mutable && PermissionType.EXPLICIT.equals(permission.permissionType)) {
-				RepositoryModel rm = GitBlit.self().getRepositoryModel(permission.registrant);
+				RepositoryModel rm = getRepositoryModel(permission.registrant);
 				if (rm == null) {
 					permission.permissionType = PermissionType.MISSING;
 					permission.mutable = false;
@@ -1158,6 +1076,7 @@
 	 * @param repository
 	 * @return a list of RegistrantAccessPermissions
 	 */
+	@Override
 	public List<RegistrantAccessPermission> getUserAccessPermissions(RepositoryModel repository) {
 		List<RegistrantAccessPermission> list = new ArrayList<RegistrantAccessPermission>();
 		if (AccessRestrictionType.NONE.equals(repository.accessRestriction)) {
@@ -1185,6 +1104,7 @@
 	 * @param permissions
 	 * @return true if the user models have been updated
 	 */
+	@Override
 	public boolean setUserAccessPermissions(RepositoryModel repository, Collection<RegistrantAccessPermission> permissions) {
 		List<UserModel> users = new ArrayList<UserModel>();
 		for (RegistrantAccessPermission up : permissions) {
@@ -1206,6 +1126,7 @@
 	 * @param repository
 	 * @return list of all usernames that have an access permission for the repository
 	 */
+	@Override
 	public List<String> getRepositoryUsers(RepositoryModel repository) {
 		return userService.getUsernamesForRepositoryRole(repository.name);
 	}
@@ -1236,6 +1157,7 @@
 	 * @param isCreate
 	 * @throws GitBlitException
 	 */
+	@Override
 	public void updateUserModel(String username, UserModel user, boolean isCreate)
 			throws GitBlitException {
 		if (!username.equalsIgnoreCase(user.username)) {
@@ -1283,6 +1205,7 @@
 	 *
 	 * @return the list of teams
 	 */
+	@Override
 	public List<TeamModel> getAllTeams() {
 		List<TeamModel> teams = userService.getAllTeams();
 		return teams;
@@ -1294,6 +1217,7 @@
 	 * @param teamname
 	 * @return a TeamModel object or null
 	 */
+	@Override
 	public TeamModel getTeamModel(String teamname) {
 		return userService.getTeamModel(teamname);
 	}
@@ -1306,6 +1230,7 @@
 	 * @param repository
 	 * @return a list of RegistrantAccessPermissions
 	 */
+	@Override
 	public List<RegistrantAccessPermission> getTeamAccessPermissions(RepositoryModel repository) {
 		List<RegistrantAccessPermission> list = new ArrayList<RegistrantAccessPermission>();
 		for (TeamModel team : userService.getAllTeams()) {
@@ -1325,6 +1250,7 @@
 	 * @param permissions
 	 * @return true if the team models have been updated
 	 */
+	@Override
 	public boolean setTeamAccessPermissions(RepositoryModel repository, Collection<RegistrantAccessPermission> permissions) {
 		List<TeamModel> teams = new ArrayList<TeamModel>();
 		for (RegistrantAccessPermission tp : permissions) {
@@ -1346,6 +1272,7 @@
 	 * @param repository
 	 * @return list of all teamnames with explicit access permissions to the repository
 	 */
+	@Override
 	public List<String> getRepositoryTeams(RepositoryModel repository) {
 		return userService.getTeamnamesForRepositoryRole(repository.name);
 	}
@@ -1373,6 +1300,7 @@
 	 * @param team
 	 * @param isCreate
 	 */
+	@Override
 	public void updateTeamModel(String teamname, TeamModel team, boolean isCreate)
 			throws GitBlitException {
 		if (!teamname.equalsIgnoreCase(team.name)) {
@@ -1394,6 +1322,7 @@
 	 * @param teamname
 	 * @return true if successful
 	 */
+	@Override
 	public boolean deleteTeam(String teamname) {
 		return userService.deleteTeam(teamname);
 	}
@@ -1404,7 +1333,8 @@
 	 *
 	 * @param model
 	 */
-	private void addToCachedRepositoryList(RepositoryModel model) {
+	@Override
+	public void addToCachedRepositoryList(RepositoryModel model) {
 		if (settings.getBoolean(Keys.git.cacheRepositoryList, true)) {
 			repositoryListCache.put(model.name.toLowerCase(), model);
 
@@ -1445,6 +1375,7 @@
 	 * Resets the repository list cache.
 	 *
 	 */
+	@Override
 	public void resetRepositoryListCache() {
 		logger.info("Repository cache manually reset");
 		repositoryListCache.clear();
@@ -1488,6 +1419,7 @@
 	 *
 	 * @return list of all repositories
 	 */
+	@Override
 	public List<String> getRepositoryList() {
 		if (repositoryListCache.size() == 0 || !isValidRepositoryList()) {
 			// we are not caching OR we have not yet cached OR the cached list is invalid
@@ -1505,7 +1437,7 @@
 			} else {
 				// we are caching this list
 				String msg = "{0} repositories identified in {1} msecs";
-				if (getBoolean(Keys.web.showRepositorySizes, true)) {
+				if (settings.getBoolean(Keys.web.showRepositorySizes, true)) {
 					// optionally (re)calculate repository sizes
 					msg = "{0} repositories identified with calculated folder sizes in {1} msecs";
 				}
@@ -1544,6 +1476,7 @@
 	 * @param repositoryName
 	 * @return repository or null
 	 */
+	@Override
 	public Repository getRepository(String repositoryName) {
 		return getRepository(repositoryName, true);
 	}
@@ -1555,6 +1488,7 @@
 	 * @param logError
 	 * @return repository or null
 	 */
+	@Override
 	public Repository getRepository(String repositoryName, boolean logError) {
 		// Decode url-encoded repository name (issue-278)
 		// http://stackoverflow.com/questions/17183110
@@ -1588,6 +1522,7 @@
 	 * @param user
 	 * @return list of repository models accessible to user
 	 */
+	@Override
 	public List<RepositoryModel> getRepositoryModels(UserModel user) {
 		long methodStart = System.currentTimeMillis();
 		List<String> list = getRepositoryList();
@@ -1620,6 +1555,7 @@
 	 * @param repositoryName
 	 * @return repository model or null
 	 */
+	@Override
 	public RepositoryModel getRepositoryModel(UserModel user, String repositoryName) {
 		RepositoryModel model = getRepositoryModel(repositoryName);
 		if (model == null) {
@@ -1641,6 +1577,7 @@
 	 * @param repositoryName
 	 * @return repository model or null
 	 */
+	@Override
 	public RepositoryModel getRepositoryModel(String repositoryName) {
 		// Decode url-encoded repository name (issue-278)
 		// http://stackoverflow.com/questions/17183110
@@ -1702,6 +1639,7 @@
 	 * @param repository
 	 * @return the star count
 	 */
+	@Override
 	public long getStarCount(RepositoryModel repository) {
 		long count = 0;
 		for (UserModel user : getAllUsers()) {
@@ -1752,7 +1690,7 @@
 			}
 
 			// project configs
-			String rootName = GitBlit.getString(Keys.web.repositoryRootGroupName, "main");
+			String rootName = settings.getString(Keys.web.repositoryRootGroupName, "main");
 			ProjectModel rootProject = new ProjectModel(rootName, true);
 
 			Map<String, ProjectModel> configs = new HashMap<String, ProjectModel>();
@@ -1787,6 +1725,7 @@
 	 * @param includeUsers
 	 * @return list of projects that are accessible to the user
 	 */
+	@Override
 	public List<ProjectModel> getProjectModels(UserModel user, boolean includeUsers) {
 		Map<String, ProjectModel> configs = getProjectConfigs();
 
@@ -1841,6 +1780,7 @@
 	 * @param user
 	 * @return a project model, or null if it does not exist
 	 */
+	@Override
 	public ProjectModel getProjectModel(String name, UserModel user) {
 		for (ProjectModel project : getProjectModels(user, true)) {
 			if (project.name.equalsIgnoreCase(name)) {
@@ -1856,6 +1796,7 @@
 	 * @param name a project name
 	 * @return a project model or null if the project does not exist
 	 */
+	@Override
 	public ProjectModel getProjectModel(String name) {
 		Map<String, ProjectModel> configs = getProjectConfigs();
 		ProjectModel project = configs.get(name.toLowerCase());
@@ -1906,6 +1847,7 @@
 	 * @param includeUsers
 	 * @return a list of project models
 	 */
+	@Override
 	public List<ProjectModel> getProjectModels(List<RepositoryModel> repositoryModels, boolean includeUsers) {
 		Map<String, ProjectModel> projects = new LinkedHashMap<String, ProjectModel>();
 		for (RepositoryModel repository : repositoryModels) {
@@ -2090,6 +2032,7 @@
 	 * @param n
 	 * @return true if the repository exists
 	 */
+	@Override
 	public boolean hasRepository(String repositoryName) {
 		return hasRepository(repositoryName, false);
 	}
@@ -2101,6 +2044,7 @@
 	 * @param caseInsensitive
 	 * @return true if the repository exists
 	 */
+	@Override
 	public boolean hasRepository(String repositoryName, boolean caseSensitiveCheck) {
 		if (!caseSensitiveCheck && settings.getBoolean(Keys.git.cacheRepositoryList, true)) {
 			// if we are caching use the cache to determine availability
@@ -2123,6 +2067,7 @@
 	 * @param origin
 	 * @return true the if the user has a fork
 	 */
+	@Override
 	public boolean hasFork(String username, String origin) {
 		return getFork(username, origin) != null;
 	}
@@ -2135,6 +2080,7 @@
 	 * @param origin
 	 * @return the name of the user's fork, null otherwise
 	 */
+	@Override
 	public String getFork(String username, String origin) {
 		String userProject = ModelUtils.getPersonalPath(username);
 		if (settings.getBoolean(Keys.git.cacheRepositoryList, true)) {
@@ -2200,6 +2146,7 @@
 	 * @param repository
 	 * @return a ForkModel
 	 */
+	@Override
 	public ForkModel getForkNetwork(String repository) {
 		if (settings.getBoolean(Keys.git.cacheRepositoryList, true)) {
 			// find the root, cached
@@ -2263,12 +2210,13 @@
 	 * @param model
 	 * @return size in bytes of the repository
 	 */
+	@Override
 	public long updateLastChangeFields(Repository r, RepositoryModel model) {
 		LastChange lc = JGitUtils.getLastChange(r);
 		model.lastChange = lc.when;
 		model.lastChangeAuthor = lc.who;
 
-		if (!getBoolean(Keys.web.showRepositorySizes, true) || model.skipSizeCalculation) {
+		if (!settings.getBoolean(Keys.web.showRepositorySizes, true) || model.skipSizeCalculation) {
 			model.size = null;
 			return 0L;
 		}
@@ -2336,6 +2284,7 @@
 	 * @param repository
 	 * @return a new array list of metrics
 	 */
+	@Override
 	public List<Metric> getRepositoryDefaultMetrics(RepositoryModel model, Repository repository) {
 		if (repositoryMetricsCache.hasCurrent(model.name, model.lastChange)) {
 			return new ArrayList<Metric>(repositoryMetricsCache.getObject(model.name));
@@ -2410,6 +2359,7 @@
 	 * @param isCreate
 	 * @throws GitBlitException
 	 */
+	@Override
 	public void updateRepositoryModel(String repositoryName, RepositoryModel repository,
 			boolean isCreate) throws GitBlitException {
 		if (gcExecutor.isCollectingGarbage(repositoryName)) {
@@ -2419,7 +2369,7 @@
 		Repository r = null;
 		String projectPath = StringUtils.getFirstPathElement(repository.name);
 		if (!StringUtils.isEmpty(projectPath)) {
-			if (projectPath.equalsIgnoreCase(getString(Keys.web.repositoryRootGroupName, "main"))) {
+			if (projectPath.equalsIgnoreCase(settings.getString(Keys.web.repositoryRootGroupName, "main"))) {
 				// strip leading group name
 				repository.name = repository.name.substring(projectPath.length() + 1);
 			}
@@ -2436,7 +2386,7 @@
 			}
 			// create repository
 			logger.info("create repository " + repository.name);
-			String shared = getString(Keys.git.createRepositoriesShared, "FALSE");
+			String shared = settings.getString(Keys.git.createRepositoriesShared, "FALSE");
 			r = JGitUtils.createRepository(repositoriesFolder, repository.name, shared);
 		} else {
 			// rename repository
@@ -2538,9 +2488,9 @@
 
 			// Adjust permissions in case we updated the config files
 			JGitUtils.adjustSharedPerm(new File(r.getDirectory().getAbsolutePath(), "config"),
-					getString(Keys.git.createRepositoriesShared, "FALSE"));
+					settings.getString(Keys.git.createRepositoriesShared, "FALSE"));
 			JGitUtils.adjustSharedPerm(new File(r.getDirectory().getAbsolutePath(), "HEAD"),
-					getString(Keys.git.createRepositoriesShared, "FALSE"));
+					settings.getString(Keys.git.createRepositoriesShared, "FALSE"));
 
 			// close the repository object
 			r.close();
@@ -2560,6 +2510,7 @@
 	 * @param repository
 	 *            the Gitblit repository model
 	 */
+	@Override
 	public void updateConfiguration(Repository r, RepositoryModel repository) {
 		StoredConfig config = r.getConfig();
 		config.setString(Constants.CONFIG_GITBLIT, null, "description", repository.description);
@@ -2659,6 +2610,7 @@
 	 * @param model
 	 * @return true if successful
 	 */
+	@Override
 	public boolean deleteRepositoryModel(RepositoryModel model) {
 		return deleteRepository(model.name);
 	}
@@ -2670,6 +2622,7 @@
 	 * @param repositoryName
 	 * @return true if successful
 	 */
+	@Override
 	public boolean deleteRepository(String repositoryName) {
 		try {
 			closeRepository(repositoryName);
@@ -2791,8 +2744,9 @@
 		return scheduledExecutor;
 	}
 
-	public static boolean canFederate() {
-		String passphrase = getString(Keys.federation.passphrase, "");
+	@Override
+	public boolean canFederate() {
+		String passphrase = settings.getString(Keys.federation.passphrase, "");
 		return !StringUtils.isEmpty(passphrase);
 	}
 
@@ -2835,6 +2789,7 @@
 	 *
 	 * @return list of registered gitblit instances
 	 */
+	@Override
 	public List<FederationModel> getFederationRegistrations() {
 		if (federationRegistrations.isEmpty()) {
 			federationRegistrations.addAll(FederationUtils.getFederationRegistrations(settings));
@@ -2849,6 +2804,7 @@
 	 *            the name of the registration
 	 * @return a federation registration
 	 */
+	@Override
 	public FederationModel getFederationRegistration(String url, String name) {
 		// check registrations
 		for (FederationModel r : getFederationRegistrations()) {
@@ -2871,6 +2827,7 @@
 	 *
 	 * @return list of federation sets
 	 */
+	@Override
 	public List<FederationSet> getFederationSets(String gitblitUrl) {
 		List<FederationSet> list = new ArrayList<FederationSet>();
 		// generate standard tokens
@@ -2894,6 +2851,7 @@
 	 *
 	 * @return list of federation tokens
 	 */
+	@Override
 	public List<String> getFederationTokens() {
 		List<String> tokens = new ArrayList<String>();
 		// generate standard tokens
@@ -2913,6 +2871,7 @@
 	 * @param type
 	 * @return a federation token
 	 */
+	@Override
 	public String getFederationToken(FederationToken type) {
 		return getFederationToken(type.name());
 	}
@@ -2923,6 +2882,7 @@
 	 * @param value
 	 * @return a federation token
 	 */
+	@Override
 	public String getFederationToken(String value) {
 		String passphrase = settings.getString(Keys.federation.passphrase, "");
 		return StringUtils.getSHA1(passphrase + "-" + value);
@@ -2936,6 +2896,7 @@
 	 * @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);
@@ -2964,6 +2925,7 @@
 	 *            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;
@@ -2980,6 +2942,7 @@
 	 *
 	 * @return the list of registration results
 	 */
+	@Override
 	public List<FederationModel> getFederationResultRegistrations() {
 		return new ArrayList<FederationModel>(federationPullResults.values());
 	}
@@ -2995,6 +2958,7 @@
 	 *            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);
@@ -3022,6 +2986,7 @@
 	 *
 	 * @return list of federation proposals
 	 */
+	@Override
 	public List<FederationProposal> getPendingFederationProposals() {
 		List<FederationProposal> list = new ArrayList<FederationProposal>();
 		File folder = getProposalsFolder();
@@ -3052,9 +3017,10 @@
 	 *            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 : getStrings(Keys.federation.sets)) {
+		for (String set : settings.getStrings(Keys.federation.sets)) {
 			federationSets.put(getFederationToken(set), set);
 		}
 
@@ -3110,6 +3076,7 @@
 	 * @param token
 	 * @return a potential proposal
 	 */
+	@Override
 	public FederationProposal createFederationProposal(String gitblitUrl, String token) {
 		FederationToken tokenType = FederationToken.REPOSITORIES;
 		for (FederationToken type : FederationToken.values()) {
@@ -3130,6 +3097,7 @@
 	 * @param token
 	 * @return the specified proposal or null
 	 */
+	@Override
 	public FederationProposal getPendingFederationProposal(String token) {
 		List<FederationProposal> list = getPendingFederationProposals();
 		for (FederationProposal proposal : list) {
@@ -3147,6 +3115,7 @@
 	 *            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);
@@ -3159,8 +3128,9 @@
 	 *
 	 * @return list of available hook scripts
 	 */
+	@Override
 	public List<String> getAllScripts() {
-		File groovyFolder = getGroovyScriptsFolder();
+		File groovyFolder = getHooksFolder();
 		File[] files = groovyFolder.listFiles(new FileFilter() {
 			@Override
 			public boolean accept(File pathname) {
@@ -3185,10 +3155,11 @@
 	 *            if null only the globally specified scripts are returned
 	 * @return a list of scripts
 	 */
+	@Override
 	public List<String> getPreReceiveScriptsInherited(RepositoryModel repository) {
 		Set<String> scripts = new LinkedHashSet<String>();
 		// Globals
-		for (String script : getStrings(Keys.groovy.preReceiveScripts)) {
+		for (String script : settings.getStrings(Keys.groovy.preReceiveScripts)) {
 			if (script.endsWith(".groovy")) {
 				scripts.add(script.substring(0, script.lastIndexOf('.')));
 			} else {
@@ -3217,6 +3188,7 @@
 	 *            optional parameter
 	 * @return list of available hook scripts
 	 */
+	@Override
 	public List<String> getPreReceiveScriptsUnused(RepositoryModel repository) {
 		Set<String> inherited = new TreeSet<String>(getPreReceiveScriptsInherited(repository));
 
@@ -3238,10 +3210,11 @@
 	 *            if null only the globally specified scripts are returned
 	 * @return a list of scripts
 	 */
+	@Override
 	public List<String> getPostReceiveScriptsInherited(RepositoryModel repository) {
 		Set<String> scripts = new LinkedHashSet<String>();
 		// Global Scripts
-		for (String script : getStrings(Keys.groovy.postReceiveScripts)) {
+		for (String script : settings.getStrings(Keys.groovy.postReceiveScripts)) {
 			if (script.endsWith(".groovy")) {
 				scripts.add(script.substring(0, script.lastIndexOf('.')));
 			} else {
@@ -3269,6 +3242,7 @@
 	 *            optional parameter
 	 * @return list of available hook scripts
 	 */
+	@Override
 	public List<String> getPostReceiveScriptsUnused(RepositoryModel repository) {
 		Set<String> inherited = new TreeSet<String>(getPostReceiveScriptsInherited(repository));
 
@@ -3291,6 +3265,7 @@
 	 * @param repositories
 	 * @return
 	 */
+	@Override
 	public List<SearchResult> search(String query, int page, int pageSize, List<String> repositories) {
 		List<SearchResult> srs = luceneExecutor.search(query, page, pageSize, repositories);
 		return srs;
@@ -3302,6 +3277,7 @@
 	 * @param subject
 	 * @param message
 	 */
+	@Override
 	public void sendMailToAdministrators(String subject, String message) {
 		List<String> toAddresses = settings.getStrings(Keys.mail.adminAddresses);
 		sendMail(subject, message, toAddresses);
@@ -3314,6 +3290,7 @@
 	 * @param message
 	 * @param toAddresses
 	 */
+	@Override
 	public void sendMail(String subject, String message, Collection<String> toAddresses) {
 		this.sendMail(subject, message, toAddresses.toArray(new String[0]));
 	}
@@ -3325,6 +3302,7 @@
 	 * @param message
 	 * @param toAddresses
 	 */
+	@Override
 	public void sendMail(String subject, String message, String... toAddresses) {
 		if (toAddresses == null || toAddresses.length == 0) {
 			logger.debug(MessageFormat.format("Dropping message {0} because there are no recipients", subject));
@@ -3358,6 +3336,7 @@
 	 * @param message
 	 * @param toAddresses
 	 */
+	@Override
 	public void sendHtmlMail(String subject, String message, Collection<String> toAddresses) {
 		this.sendHtmlMail(subject, message, toAddresses.toArray(new String[0]));
 	}
@@ -3369,6 +3348,7 @@
 	 * @param message
 	 * @param toAddresses
 	 */
+	@Override
 	public void sendHtmlMail(String subject, String message, String... toAddresses) {
 		if (toAddresses == null || toAddresses.length == 0) {
 			logger.debug(MessageFormat.format("Dropping message {0} because there are no recipients", subject));
@@ -3400,6 +3380,7 @@
 	 *
 	 * @return SettingsModel
 	 */
+	@Override
 	public ServerSettings getSettingsModel() {
 		// ensure that the current values are updated in the setting models
 		for (String key : settings.getAllKeys(null)) {
@@ -3524,7 +3505,7 @@
 		logTimezone("JVM", TimeZone.getDefault());
 		logTimezone(Constants.NAME, getTimezone());
 
-		serverStatus = new ServerStatus(isGO());
+		serverStatus = new ServerStatus(goSettings != null);
 
 		if (this.userService == null) {
 			String realm = settings.getString(Keys.realm.userService, "${baseFolder}/users.properties");
@@ -3899,6 +3880,7 @@
 	 *
 	 * @return true if we are running the gc executor
 	 */
+	@Override
 	public boolean isCollectingGarbage() {
 		return gcExecutor.isRunning();
 	}
@@ -3909,6 +3891,7 @@
 	 * @param repositoryName
 	 * @return true if actively collecting garbage
 	 */
+	@Override
 	public boolean isCollectingGarbage(String repositoryName) {
 		return gcExecutor.isCollectingGarbage(repositoryName);
 	}
@@ -3923,6 +3906,7 @@
 	 * @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}", repositoriesFolder.getAbsolutePath(), repository.name);
@@ -3990,7 +3974,89 @@
 	 *
 	 * @return status of Cookie authentication enablement.
 	 */
-	public boolean allowCookieAuthentication() {
-		return GitBlit.getBoolean(Keys.web.allowCookieAuthentication, true) && userService.supportsCookies();
+	@Override
+	public boolean supportsCookies() {
+		return settings.getBoolean(Keys.web.allowCookieAuthentication, true) && userService.supportsCookies();
+	}
+
+	@Override
+	public String getCookie(UserModel model) {
+		return userService.getCookie(model);
+	}
+
+	@Override
+	public UserModel authenticate(char[] cookie) {
+		return userService.authenticate(cookie);
+	}
+
+	@Override
+	public boolean updateUserModel(UserModel model) {
+		return userService.updateUserModel(model);
+	}
+
+	@Override
+	public boolean updateUserModels(Collection<UserModel> models) {
+		return userService.updateUserModels(models);
+	}
+
+	@Override
+	public boolean updateUserModel(String username, UserModel model) {
+		return userService.updateUserModel(username, model);
+	}
+
+	@Override
+	public boolean deleteUserModel(UserModel model) {
+		return userService.deleteUserModel(model);
+	}
+
+	@Override
+	public List<String> getAllTeamNames() {
+		return userService.getAllTeamNames();
+	}
+
+	@Override
+	public List<String> getTeamnamesForRepositoryRole(String role) {
+		return userService.getTeamnamesForRepositoryRole(role);
+	}
+
+	@Override
+	public boolean updateTeamModel(TeamModel model) {
+		return userService.updateTeamModel(model);
+	}
+
+	@Override
+	public boolean updateTeamModels(Collection<TeamModel> models) {
+		return userService.updateTeamModels(models);
+	}
+
+	@Override
+	public boolean updateTeamModel(String teamname, TeamModel model) {
+		return userService.updateTeamModel(teamname, model);
+	}
+
+	@Override
+	public boolean deleteTeamModel(TeamModel model) {
+		return userService.deleteTeamModel(model);
+	}
+
+	@Override
+	public List<String> getUsernamesForRepositoryRole(String role) {
+		return userService.getUsernamesForRepositoryRole(role);
+	}
+
+	@Override
+	public boolean renameRepositoryRole(String oldRole, String newRole) {
+		return userService.renameRepositoryRole(oldRole, newRole);
+	}
+
+	@Override
+	public boolean deleteRepositoryRole(String role) {
+		return userService.deleteRepositoryRole(role);
+	}
+
+	@Override
+	public void logout(HttpServletResponse response, UserModel user) {
+		setCookie(response,  null);
+		userService.logout(user);
 	}
 }
diff --git a/src/main/java/com/gitblit/GitBlitServer.java b/src/main/java/com/gitblit/GitBlitServer.java
index 0c5000c..ecb1001 100644
--- a/src/main/java/com/gitblit/GitBlitServer.java
+++ b/src/main/java/com/gitblit/GitBlitServer.java
@@ -410,7 +410,7 @@
 		}
 
 		// Setup the GitBlit context
-		GitBlit gitblit = getGitBlitInstance();
+		GitBlit gitblit = newGitblit(settings, baseFolder);
 		gitblit.configureContext(settings, baseFolder, true);
 		rootContext.addEventListener(gitblit);
 
@@ -430,8 +430,8 @@
 		}
 	}
 
-	protected GitBlit getGitBlitInstance() {
-		return GitBlit.self();
+	protected GitBlit newGitblit(IStoredSettings settings, File baseFolder) {
+		return new GitBlit(settings, baseFolder);
 	}
 
 	/**
diff --git a/src/main/java/com/gitblit/GitFilter.java b/src/main/java/com/gitblit/GitFilter.java
index 5e18f5f..f52963f 100644
--- a/src/main/java/com/gitblit/GitFilter.java
+++ b/src/main/java/com/gitblit/GitFilter.java
@@ -19,6 +19,8 @@
 
 import com.gitblit.Constants.AccessRestrictionType;
 import com.gitblit.Constants.AuthorizationControl;
+import com.gitblit.manager.IRepositoryManager;
+import com.gitblit.manager.IRuntimeManager;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.UserModel;
 import com.gitblit.utils.StringUtils;
@@ -100,7 +102,8 @@
 	 */
 	@Override
 	protected boolean isCreationAllowed() {
-		return GitBlit.getBoolean(Keys.git.allowCreateOnPush, true);
+		IStoredSettings settings = GitBlit.getManager(IRuntimeManager.class).getSettings();
+		return settings.getBoolean(Keys.git.allowCreateOnPush, true);
 	}
 
 	/**
@@ -119,7 +122,8 @@
 
 	@Override
 	protected boolean requiresClientCertificate() {
-		return GitBlit.getBoolean(Keys.git.requiresClientCertificate, false);
+		IStoredSettings settings = GitBlit.getManager(IRuntimeManager.class).getSettings();
+		return settings.getBoolean(Keys.git.requiresClientCertificate, false);
 	}
 
 	/**
@@ -152,7 +156,8 @@
 	 */
 	@Override
 	protected boolean canAccess(RepositoryModel repository, UserModel user, String action) {
-		if (!GitBlit.getBoolean(Keys.git.enableGitServlet, true)) {
+		IStoredSettings settings = GitBlit.getManager(IRuntimeManager.class).getSettings();
+		if (!settings.getBoolean(Keys.git.enableGitServlet, true)) {
 			// Git Servlet disabled
 			return false;
 		}
@@ -223,15 +228,17 @@
 					model.accessRestriction = AccessRestrictionType.VIEW;
 				} else {
 					// common repository, user default server settings
-					model.authorizationControl = AuthorizationControl.fromName(GitBlit.getString(Keys.git.defaultAuthorizationControl, ""));
-					model.accessRestriction = AccessRestrictionType.fromName(GitBlit.getString(Keys.git.defaultAccessRestriction, "PUSH"));
+					IStoredSettings settings = GitBlit.getManager(IRuntimeManager.class).getSettings();
+					model.authorizationControl = AuthorizationControl.fromName(settings.getString(Keys.git.defaultAuthorizationControl, ""));
+					model.accessRestriction = AccessRestrictionType.fromName(settings.getString(Keys.git.defaultAccessRestriction, "PUSH"));
 				}
 
 				// create the repository
 				try {
-					GitBlit.self().updateRepositoryModel(model.name, model, true);
+					IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);
+					repositoryManager.updateRepositoryModel(model.name, model, true);
 					logger.info(MessageFormat.format("{0} created {1} ON-PUSH", user.username, model.name));
-					return GitBlit.self().getRepositoryModel(model.name);
+					return repositoryManager.getRepositoryModel(model.name);
 				} catch (GitBlitException e) {
 					logger.error(MessageFormat.format("{0} failed to create repository {1} ON-PUSH!", user.username, model.name), e);
 				}
diff --git a/src/main/java/com/gitblit/GitblitUserService.java b/src/main/java/com/gitblit/GitblitUserService.java
index 677d63a..ba4c9c5 100644
--- a/src/main/java/com/gitblit/GitblitUserService.java
+++ b/src/main/java/com/gitblit/GitblitUserService.java
@@ -25,6 +25,7 @@
 import org.slf4j.LoggerFactory;
 
 import com.gitblit.Constants.AccountType;
+import com.gitblit.manager.IRuntimeManager;
 import com.gitblit.models.TeamModel;
 import com.gitblit.models.UserModel;
 import com.gitblit.utils.DeepCopier;
@@ -59,7 +60,8 @@
 
 	@Override
 	public void setup(IStoredSettings settings) {
-		File realmFile = GitBlit.getFileOrFolder(Keys.realm.userService, "${baseFolder}/users.conf");
+		IRuntimeManager runtimeManager = GitBlit.getManager(IRuntimeManager.class);
+		File realmFile = runtimeManager.getFileOrFolder(Keys.realm.userService, "${baseFolder}/users.conf");
 		serviceImpl = createUserService(realmFile);
 		logger.info("GUS delegating to " + serviceImpl.toString());
 	}
diff --git a/src/main/java/com/gitblit/HtpasswdUserService.java b/src/main/java/com/gitblit/HtpasswdUserService.java
index 881f843..a98777b 100644
--- a/src/main/java/com/gitblit/HtpasswdUserService.java
+++ b/src/main/java/com/gitblit/HtpasswdUserService.java
@@ -33,6 +33,7 @@
 import org.slf4j.LoggerFactory;
 
 import com.gitblit.Constants.AccountType;
+import com.gitblit.manager.IRuntimeManager;
 import com.gitblit.models.UserModel;
 import com.gitblit.utils.ArrayUtils;
 import com.gitblit.utils.StringUtils;
@@ -125,8 +126,9 @@
         this.settings = settings;
 
         // This is done in two steps in order to avoid calling GitBlit.getFileOrFolder(String, String) which will segfault for unit tests.
+        IRuntimeManager runtimeManager = GitBlit.getManager(IRuntimeManager.class);
         String file = settings.getString(KEY_BACKING_US, DEFAULT_BACKING_US);
-        File realmFile = GitBlit.getFileOrFolder(file);
+        File realmFile = runtimeManager.getFileOrFolder(file);
         serviceImpl = createUserService(realmFile);
         logger.info("Htpasswd User Service backed by " + serviceImpl.toString());
 
@@ -291,7 +293,8 @@
         if ( !file.equals(htpasswdFilePath) ) {
             // The htpasswd file setting changed. Rediscover the file.
             this.htpasswdFilePath = file;
-            this.htpasswdFile = GitBlit.getFileOrFolder(file);
+            IRuntimeManager runtimeManager = GitBlit.getManager(IRuntimeManager.class);
+            this.htpasswdFile = runtimeManager.getFileOrFolder(file);
             this.htUsers.clear();
             this.forceReload = true;
         }
diff --git a/src/main/java/com/gitblit/IStoredSettings.java b/src/main/java/com/gitblit/IStoredSettings.java
index 33c36ac..01c478f 100644
--- a/src/main/java/com/gitblit/IStoredSettings.java
+++ b/src/main/java/com/gitblit/IStoredSettings.java
@@ -333,6 +333,16 @@
 	}
 
 	/**
+	 * Override the specified key with the specified value.
+	 *
+	 * @param key
+	 * @param value
+	 */
+	public void overrideSetting(String key, boolean value) {
+		overrides.put(key, "" + value);
+	}
+
+	/**
 	 * Updates the values for the specified keys and persists the entire
 	 * configuration file.
 	 *
diff --git a/src/main/java/com/gitblit/LdapUserService.java b/src/main/java/com/gitblit/LdapUserService.java
index 888d13c..e6951b4 100644
--- a/src/main/java/com/gitblit/LdapUserService.java
+++ b/src/main/java/com/gitblit/LdapUserService.java
@@ -31,6 +31,7 @@
 import org.slf4j.LoggerFactory;
 
 import com.gitblit.Constants.AccountType;
+import com.gitblit.manager.IRuntimeManager;
 import com.gitblit.models.TeamModel;
 import com.gitblit.models.UserModel;
 import com.gitblit.utils.ArrayUtils;
@@ -83,7 +84,8 @@
 	public void setup(IStoredSettings settings) {
 		this.settings = settings;
 		String file = settings.getString(Keys.realm.ldap.backingUserService, "${baseFolder}/users.conf");
-		File realmFile = GitBlit.getFileOrFolder(file);
+		IRuntimeManager runtimeManager = GitBlit.getManager(IRuntimeManager.class);
+		File realmFile = runtimeManager.getFileOrFolder(file);
 
 		serviceImpl = createUserService(realmFile);
 		logger.info("LDAP User Service backed by " + serviceImpl.toString());
diff --git a/src/main/java/com/gitblit/LogoServlet.java b/src/main/java/com/gitblit/LogoServlet.java
index eb167e7..4222f8f 100644
--- a/src/main/java/com/gitblit/LogoServlet.java
+++ b/src/main/java/com/gitblit/LogoServlet.java
@@ -27,6 +27,8 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import com.gitblit.manager.IRuntimeManager;
+
 /**
  * Handles requests for logo.png
  *
@@ -45,7 +47,8 @@
 
 	@Override
 	protected long getLastModified(HttpServletRequest req) {
-		File file = GitBlit.getFileOrFolder(Keys.web.headerLogo, "${baseFolder}/logo.png");
+		IRuntimeManager runtimeManager = GitBlit.getManager(IRuntimeManager.class);
+		File file = runtimeManager.getFileOrFolder(Keys.web.headerLogo, "${baseFolder}/logo.png");
 		if (file.exists()) {
 			return Math.max(lastModified, file.lastModified());
 		} else {
@@ -59,7 +62,8 @@
 		InputStream is = null;
 		try {
 			String contentType = null;
-			File file = GitBlit.getFileOrFolder(Keys.web.headerLogo, "${baseFolder}/logo.png");
+			IRuntimeManager runtimeManager = GitBlit.getManager(IRuntimeManager.class);
+			File file = runtimeManager.getFileOrFolder(Keys.web.headerLogo, "${baseFolder}/logo.png");
 			if (file.exists()) {
 				// custom logo
 				ServletContext context = request.getSession().getServletContext();
@@ -88,7 +92,7 @@
 		} catch (Exception e) {
 			e.printStackTrace();
 		} finally {
-			if(is != null) {
+			if (is != null) {
 				is.close();
 			}
 		}
diff --git a/src/main/java/com/gitblit/LuceneExecutor.java b/src/main/java/com/gitblit/LuceneExecutor.java
index 28523ce..19395b3 100644
--- a/src/main/java/com/gitblit/LuceneExecutor.java
+++ b/src/main/java/com/gitblit/LuceneExecutor.java
@@ -85,6 +85,7 @@
 import org.slf4j.LoggerFactory;
 
 import com.gitblit.Constants.SearchObjectType;
+import com.gitblit.manager.IRepositoryManager;
 import com.gitblit.models.PathModel.PathChangeModel;
 import com.gitblit.models.RefModel;
 import com.gitblit.models.RepositoryModel;
@@ -160,17 +161,18 @@
 		String exts = storedSettings.getString(Keys.web.luceneIgnoreExtensions, luceneIgnoreExtensions);
 		excludedExtensions = new TreeSet<String>(StringUtils.getStringsFromValue(exts));
 
-		if (GitBlit.self().isCollectingGarbage()) {
+		IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);
+		if (repositoryManager.isCollectingGarbage()) {
 			// busy collecting garbage, try again later
 			return;
 		}
 
-		for (String repositoryName: GitBlit.self().getRepositoryList()) {
-			RepositoryModel model = GitBlit.self().getRepositoryModel(repositoryName);
+		for (String repositoryName: repositoryManager.getRepositoryList()) {
+			RepositoryModel model = repositoryManager.getRepositoryModel(repositoryName);
 			if (model.hasCommits && !ArrayUtils.isEmpty(model.indexedBranches)) {
-				Repository repository = GitBlit.self().getRepository(model.name);
+				Repository repository = repositoryManager.getRepository(model.name);
 				if (repository == null) {
-					if (GitBlit.self().isCollectingGarbage(model.name)) {
+					if (repositoryManager.isCollectingGarbage(model.name)) {
 						logger.info(MessageFormat.format("Skipping Lucene index of {0}, busy garbage collecting", repositoryName));
 					}
 					continue;
diff --git a/src/main/java/com/gitblit/MirrorExecutor.java b/src/main/java/com/gitblit/MirrorExecutor.java
index 21c194f..a96c955 100644
--- a/src/main/java/com/gitblit/MirrorExecutor.java
+++ b/src/main/java/com/gitblit/MirrorExecutor.java
@@ -33,6 +33,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.gitblit.manager.IRepositoryManager;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.UserModel;
 import com.gitblit.utils.JGitUtils;
@@ -83,25 +84,27 @@
 
 		running.set(true);
 
-		for (String repositoryName : GitBlit.self().getRepositoryList()) {
+		IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);
+
+		for (String repositoryName : repositoryManager.getRepositoryList()) {
 			if (forceClose.get()) {
 				break;
 			}
-			if (GitBlit.self().isCollectingGarbage(repositoryName)) {
+			if (repositoryManager.isCollectingGarbage(repositoryName)) {
 				logger.debug("mirror is skipping {} garbagecollection", repositoryName);
 				continue;
 			}
 			RepositoryModel model = null;
 			Repository repository = null;
 			try {
-				model = GitBlit.self().getRepositoryModel(repositoryName);
+				model = repositoryManager.getRepositoryModel(repositoryName);
 				if (!model.isMirror && !model.isBare) {
 					// repository must be a valid bare git mirror
 					logger.debug("mirror is skipping {} !mirror !bare", repositoryName);
 					continue;
 				}
 
-				repository = GitBlit.self().getRepository(repositoryName);
+				repository = repositoryManager.getRepository(repositoryName);
 				if (repository == null) {
 					logger.warn(MessageFormat.format("MirrorExecutor is missing repository {0}?!?", repositoryName));
 					continue;
diff --git a/src/main/java/com/gitblit/PAMUserService.java b/src/main/java/com/gitblit/PAMUserService.java
index 2134023..e6ab04a 100644
--- a/src/main/java/com/gitblit/PAMUserService.java
+++ b/src/main/java/com/gitblit/PAMUserService.java
@@ -24,6 +24,7 @@
 import org.slf4j.LoggerFactory;
 
 import com.gitblit.Constants.AccountType;
+import com.gitblit.manager.IRuntimeManager;
 import com.gitblit.models.UserModel;
 import com.gitblit.utils.ArrayUtils;
 import com.gitblit.utils.StringUtils;
@@ -48,7 +49,8 @@
         this.settings = settings;
 
         String file = settings.getString(Keys.realm.pam.backingUserService, "${baseFolder}/users.conf");
-        File realmFile = GitBlit.getFileOrFolder(file);
+        IRuntimeManager runtimeManager = GitBlit.getManager(IRuntimeManager.class);
+        File realmFile = runtimeManager.getFileOrFolder(file);
 
         serviceImpl = createUserService(realmFile);
         logger.info("PAM User Service backed by " + serviceImpl.toString());
diff --git a/src/main/java/com/gitblit/PagesFilter.java b/src/main/java/com/gitblit/PagesFilter.java
index e8deb5d..a42d8a4 100644
--- a/src/main/java/com/gitblit/PagesFilter.java
+++ b/src/main/java/com/gitblit/PagesFilter.java
@@ -18,6 +18,7 @@
 import org.eclipse.jgit.lib.Repository;
 
 import com.gitblit.Constants.AccessRestrictionType;
+import com.gitblit.manager.IRepositoryManager;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.UserModel;
 
@@ -49,7 +50,8 @@
 			} else {
 				repository = url.substring(0, slash);
 			}
-			r = GitBlit.self().getRepository(repository, false);
+			IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);
+			r = repositoryManager.getRepository(repository, false);
 			if (r == null) {
 				// try again
 				offset = slash + 1;
diff --git a/src/main/java/com/gitblit/PagesServlet.java b/src/main/java/com/gitblit/PagesServlet.java
index 3ed5b26..5569ab3 100644
--- a/src/main/java/com/gitblit/PagesServlet.java
+++ b/src/main/java/com/gitblit/PagesServlet.java
@@ -35,6 +35,8 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.gitblit.manager.IRepositoryManager;
+import com.gitblit.manager.IRuntimeManager;
 import com.gitblit.models.PathModel;
 import com.gitblit.models.RefModel;
 import com.gitblit.utils.ArrayUtils;
@@ -99,6 +101,9 @@
 			path = path.substring(1);
 		}
 
+		IStoredSettings settings = GitBlit.getManager(IRuntimeManager.class).getSettings();
+		IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);
+
 		// determine repository and resource from url
 		String repository = "";
 		String resource = "";
@@ -111,7 +116,7 @@
 			} else {
 				repository = path.substring(0, slash);
 			}
-			r = GitBlit.self().getRepository(repository, false);
+			r = repositoryManager.getRepository(repository, false);
 			offset = slash + 1;
 			if (offset > 0) {
 				resource = path.substring(offset);
@@ -148,8 +153,8 @@
 				return;
 			}
 
-			MarkupProcessor processor = new MarkupProcessor(GitBlit.getSettings());
-			String [] encodings = GitBlit.getEncodings();
+			MarkupProcessor processor = new MarkupProcessor(settings);
+			String [] encodings = settings.getStrings(Keys.web.blobEncodings).toArray(new String[0]);
 
 			RevTree tree = commit.getTree();
 
diff --git a/src/main/java/com/gitblit/RedmineUserService.java b/src/main/java/com/gitblit/RedmineUserService.java
index f1a6742..1148925 100644
--- a/src/main/java/com/gitblit/RedmineUserService.java
+++ b/src/main/java/com/gitblit/RedmineUserService.java
@@ -25,6 +25,7 @@
 import org.slf4j.LoggerFactory;
 
 import com.gitblit.Constants.AccountType;
+import com.gitblit.manager.IRuntimeManager;
 import com.gitblit.models.UserModel;
 import com.gitblit.utils.ArrayUtils;
 import com.gitblit.utils.ConnectionUtils;
@@ -63,7 +64,8 @@
         this.settings = settings;
 
         String file = settings.getString(Keys.realm.redmine.backingUserService, "${baseFolder}/users.conf");
-        File realmFile = GitBlit.getFileOrFolder(file);
+        IRuntimeManager runtimeManager = GitBlit.getManager(IRuntimeManager.class);
+        File realmFile = runtimeManager.getFileOrFolder(file);
 
         serviceImpl = createUserService(realmFile);
         logger.info("Redmine User Service backed by " + serviceImpl.toString());
diff --git a/src/main/java/com/gitblit/RobotsTxtServlet.java b/src/main/java/com/gitblit/RobotsTxtServlet.java
index dfc06b5..6a7c02d 100644
--- a/src/main/java/com/gitblit/RobotsTxtServlet.java
+++ b/src/main/java/com/gitblit/RobotsTxtServlet.java
@@ -23,6 +23,7 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import com.gitblit.manager.IRuntimeManager;
 import com.gitblit.utils.FileUtils;
 
 /**
@@ -54,7 +55,8 @@
 	protected void processRequest(javax.servlet.http.HttpServletRequest request,
 			javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
 			java.io.IOException {
-		File file = GitBlit.getFileOrFolder(Keys.web.robots.txt, null);
+		IRuntimeManager runtimeManager = GitBlit.getManager(IRuntimeManager.class);
+		File file = runtimeManager.getFileOrFolder(Keys.web.robots.txt, null);
 		String content = "";
 		if (file.exists()) {
 			content = FileUtils.readContent(file, "\n");
diff --git a/src/main/java/com/gitblit/RpcFilter.java b/src/main/java/com/gitblit/RpcFilter.java
index 4c9e12b..161af9d 100644
--- a/src/main/java/com/gitblit/RpcFilter.java
+++ b/src/main/java/com/gitblit/RpcFilter.java
@@ -26,6 +26,7 @@
 import javax.servlet.http.HttpServletResponse;
 
 import com.gitblit.Constants.RpcRequest;
+import com.gitblit.manager.IRuntimeManager;
 import com.gitblit.models.UserModel;
 
 /**
@@ -64,17 +65,20 @@
 			return;
 		}
 
+		IRuntimeManager runtimeManager = GitBlit.getManager(IRuntimeManager.class);
+		IStoredSettings settings = runtimeManager.getSettings();
+
 		boolean adminRequest = requestType.exceeds(RpcRequest.LIST_SETTINGS);
 
 		// conditionally reject all rpc requests
-		if (!GitBlit.getBoolean(Keys.web.enableRpcServlet, true)) {
+		if (!settings.getBoolean(Keys.web.enableRpcServlet, true)) {
 			logger.warn(Keys.web.enableRpcServlet + " must be set TRUE for rpc requests.");
 			httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
 			return;
 		}
 
-		boolean authenticateView = GitBlit.getBoolean(Keys.web.authenticateViewPages, false);
-		boolean authenticateAdmin = GitBlit.getBoolean(Keys.web.authenticateAdminPages, true);
+		boolean authenticateView = settings.getBoolean(Keys.web.authenticateViewPages, false);
+		boolean authenticateAdmin = settings.getBoolean(Keys.web.authenticateAdminPages, true);
 
 		// Wrap the HttpServletRequest with the RpcServletRequest which
 		// overrides the servlet container user principal methods.
@@ -85,7 +89,7 @@
 		}
 
 		// conditionally reject rpc management/administration requests
-		if (adminRequest && !GitBlit.getBoolean(Keys.web.enableRpcManagement, false)) {
+		if (adminRequest && !settings.getBoolean(Keys.web.enableRpcManagement, false)) {
 			logger.warn(MessageFormat.format("{0} must be set TRUE for {1} rpc requests.",
 					Keys.web.enableRpcManagement, requestType.toString()));
 			httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
@@ -96,7 +100,7 @@
 		if ((adminRequest && authenticateAdmin) || (!adminRequest && authenticateView)) {
 			if (user == null) {
 				// challenge client to provide credentials. send 401.
-				if (GitBlit.isDebugMode()) {
+				if (runtimeManager.isDebugMode()) {
 					logger.info(MessageFormat.format("RPC: CHALLENGE {0}", fullUrl));
 
 				}
@@ -115,7 +119,7 @@
 					return;
 				}
 				// valid user, but not for requested access. send 403.
-				if (GitBlit.isDebugMode()) {
+				if (runtimeManager.isDebugMode()) {
 					logger.info(MessageFormat.format("RPC: {0} forbidden to access {1}",
 							user.username, fullUrl));
 				}
@@ -124,7 +128,7 @@
 			}
 		}
 
-		if (GitBlit.isDebugMode()) {
+		if (runtimeManager.isDebugMode()) {
 			logger.info(MessageFormat.format("RPC: {0} ({1}) unauthenticated", fullUrl,
 					HttpServletResponse.SC_CONTINUE));
 		}
diff --git a/src/main/java/com/gitblit/RpcServlet.java b/src/main/java/com/gitblit/RpcServlet.java
index 8b48fbe..a8fa6f8 100644
--- a/src/main/java/com/gitblit/RpcServlet.java
+++ b/src/main/java/com/gitblit/RpcServlet.java
@@ -30,6 +30,11 @@
 import org.eclipse.jgit.lib.Repository;
 
 import com.gitblit.Constants.RpcRequest;
+import com.gitblit.manager.IFederationManager;
+import com.gitblit.manager.IGitblitManager;
+import com.gitblit.manager.IRepositoryManager;
+import com.gitblit.manager.IRuntimeManager;
+import com.gitblit.manager.IUserManager;
 import com.gitblit.models.RefModel;
 import com.gitblit.models.RegistrantAccessPermission;
 import com.gitblit.models.RepositoryModel;
@@ -74,13 +79,20 @@
 		logger.info(MessageFormat.format("Rpc {0} request from {1}", reqType,
 				request.getRemoteAddr()));
 
+		IRuntimeManager runtimeManager = GitBlit.getManager(IRuntimeManager.class);
+		IUserManager userManager = GitBlit.getManager(IUserManager.class);
+		IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);
+		IGitblitManager gitblitManager = GitBlit.getManager(IGitblitManager.class);
+		IFederationManager federationManager = GitBlit.getManager(IFederationManager.class);
+		IStoredSettings settings = runtimeManager.getSettings();
+
 		UserModel user = (UserModel) request.getUserPrincipal();
 
 		boolean allowManagement = user != null && user.canAdmin()
-				&& GitBlit.getBoolean(Keys.web.enableRpcManagement, false);
+				&& settings.getBoolean(Keys.web.enableRpcManagement, false);
 
 		boolean allowAdmin = user != null && user.canAdmin()
-				&& GitBlit.getBoolean(Keys.web.enableRpcAdministration, false);
+				&& settings.getBoolean(Keys.web.enableRpcAdministration, false);
 
 		Object result = null;
 		if (RpcRequest.GET_PROTOCOL.equals(reqType)) {
@@ -96,7 +108,7 @@
 			String cloneUrl = sb.toString();
 
 			// list repositories
-			List<RepositoryModel> list = GitBlit.self().getRepositoryModels(user);
+			List<RepositoryModel> list = repositoryManager.getRepositoryModels(user);
 			Map<String, RepositoryModel> repositories = new HashMap<String, RepositoryModel>();
 			for (RepositoryModel model : list) {
 				String url = MessageFormat.format(cloneUrl, model.name);
@@ -106,7 +118,7 @@
 		} else if (RpcRequest.LIST_BRANCHES.equals(reqType)) {
 			// list all local branches in all repositories accessible to user
 			Map<String, List<String>> localBranches = new HashMap<String, List<String>>();
-			List<RepositoryModel> models = GitBlit.self().getRepositoryModels(user);
+			List<RepositoryModel> models = repositoryManager.getRepositoryModels(user);
 			for (RepositoryModel model : models) {
 				if (!model.hasCommits) {
 					// skip empty repository
@@ -118,7 +130,7 @@
 					continue;
 				}
 				// get local branches
-				Repository repository = GitBlit.self().getRepository(model.name);
+				Repository repository = repositoryManager.getRepository(model.name);
 				List<RefModel> refs = JGitUtils.getLocalBranches(repository, false, -1);
 				if (model.showRemoteBranches) {
 					// add remote branches if repository displays them
@@ -146,7 +158,7 @@
 			} else {
 				if (user.canAdmin() || objectName.equals(user.username)) {
 					// return the specified user
-					UserModel requestedUser = GitBlit.self().getUserModel(objectName);
+					UserModel requestedUser = userManager.getUserModel(objectName);
 					if (requestedUser == null) {
 						response.setStatus(failureCode);
 					} else {
@@ -158,25 +170,25 @@
 			}
 		} else if (RpcRequest.LIST_USERS.equals(reqType)) {
 			// list users
-			List<String> names = GitBlit.self().getAllUsernames();
+			List<String> names = userManager.getAllUsernames();
 			List<UserModel> users = new ArrayList<UserModel>();
 			for (String name : names) {
-				users.add(GitBlit.self().getUserModel(name));
+				users.add(userManager.getUserModel(name));
 			}
 			result = users;
 		} else if (RpcRequest.LIST_TEAMS.equals(reqType)) {
 			// list teams
-			List<String> names = GitBlit.self().getAllTeamnames();
+			List<String> names = userManager.getAllTeamNames();
 			List<TeamModel> teams = new ArrayList<TeamModel>();
 			for (String name : names) {
-				teams.add(GitBlit.self().getTeamModel(name));
+				teams.add(userManager.getTeamModel(name));
 			}
 			result = teams;
 		} else if (RpcRequest.CREATE_REPOSITORY.equals(reqType)) {
 			// create repository
 			RepositoryModel model = deserialize(request, response, RepositoryModel.class);
 			try {
-				GitBlit.self().updateRepositoryModel(model.name, model, true);
+				repositoryManager.updateRepositoryModel(model.name, model, true);
 			} catch (GitBlitException e) {
 				response.setStatus(failureCode);
 			}
@@ -189,19 +201,19 @@
 				repoName = model.name;
 			}
 			try {
-				GitBlit.self().updateRepositoryModel(repoName, model, false);
+				repositoryManager.updateRepositoryModel(repoName, model, false);
 			} catch (GitBlitException e) {
 				response.setStatus(failureCode);
 			}
 		} else if (RpcRequest.DELETE_REPOSITORY.equals(reqType)) {
 			// delete repository
 			RepositoryModel model = deserialize(request, response, RepositoryModel.class);
-			GitBlit.self().deleteRepositoryModel(model);
+			repositoryManager.deleteRepositoryModel(model);
 		} else if (RpcRequest.CREATE_USER.equals(reqType)) {
 			// create user
 			UserModel model = deserialize(request, response, UserModel.class);
 			try {
-				GitBlit.self().updateUserModel(model.username, model, true);
+				gitblitManager.updateUserModel(model.username, model, true);
 			} catch (GitBlitException e) {
 				response.setStatus(failureCode);
 			}
@@ -214,21 +226,21 @@
 				username = model.username;
 			}
 			try {
-				GitBlit.self().updateUserModel(username, model, false);
+				gitblitManager.updateUserModel(username, model, false);
 			} catch (GitBlitException e) {
 				response.setStatus(failureCode);
 			}
 		} else if (RpcRequest.DELETE_USER.equals(reqType)) {
 			// delete user
 			UserModel model = deserialize(request, response, UserModel.class);
-			if (!GitBlit.self().deleteUser(model.username)) {
+			if (!userManager.deleteUser(model.username)) {
 				response.setStatus(failureCode);
 			}
 		} else if (RpcRequest.CREATE_TEAM.equals(reqType)) {
 			// create team
 			TeamModel model = deserialize(request, response, TeamModel.class);
 			try {
-				GitBlit.self().updateTeamModel(model.name, model, true);
+				gitblitManager.updateTeamModel(model.name, model, true);
 			} catch (GitBlitException e) {
 				response.setStatus(failureCode);
 			}
@@ -241,83 +253,83 @@
 				teamname = model.name;
 			}
 			try {
-				GitBlit.self().updateTeamModel(teamname, model, false);
+				gitblitManager.updateTeamModel(teamname, model, false);
 			} catch (GitBlitException e) {
 				response.setStatus(failureCode);
 			}
 		} else if (RpcRequest.DELETE_TEAM.equals(reqType)) {
 			// delete team
 			TeamModel model = deserialize(request, response, TeamModel.class);
-			if (!GitBlit.self().deleteTeam(model.name)) {
+			if (!userManager.deleteTeam(model.name)) {
 				response.setStatus(failureCode);
 			}
 		} else if (RpcRequest.LIST_REPOSITORY_MEMBERS.equals(reqType)) {
 			// get repository members
-			RepositoryModel model = GitBlit.self().getRepositoryModel(objectName);
-			result = GitBlit.self().getRepositoryUsers(model);
+			RepositoryModel model = repositoryManager.getRepositoryModel(objectName);
+			result = repositoryManager.getRepositoryUsers(model);
 		} else if (RpcRequest.SET_REPOSITORY_MEMBERS.equals(reqType)) {
 			// rejected since 1.2.0
 			response.setStatus(failureCode);
 		} else if (RpcRequest.LIST_REPOSITORY_MEMBER_PERMISSIONS.equals(reqType)) {
 			// get repository member permissions
-			RepositoryModel model = GitBlit.self().getRepositoryModel(objectName);
-			result = GitBlit.self().getUserAccessPermissions(model);
+			RepositoryModel model = repositoryManager.getRepositoryModel(objectName);
+			result = repositoryManager.getUserAccessPermissions(model);
 		} else if (RpcRequest.SET_REPOSITORY_MEMBER_PERMISSIONS.equals(reqType)) {
 			// set the repository permissions for the specified users
-			RepositoryModel model = GitBlit.self().getRepositoryModel(objectName);
+			RepositoryModel model = repositoryManager.getRepositoryModel(objectName);
 			Collection<RegistrantAccessPermission> permissions = deserialize(request, response, RpcUtils.REGISTRANT_PERMISSIONS_TYPE);
-			result = GitBlit.self().setUserAccessPermissions(model, permissions);
+			result = repositoryManager.setUserAccessPermissions(model, permissions);
 		} else if (RpcRequest.LIST_REPOSITORY_TEAMS.equals(reqType)) {
 			// get repository teams
-			RepositoryModel model = GitBlit.self().getRepositoryModel(objectName);
-			result = GitBlit.self().getRepositoryTeams(model);
+			RepositoryModel model = repositoryManager.getRepositoryModel(objectName);
+			result = repositoryManager.getRepositoryTeams(model);
 		} else if (RpcRequest.SET_REPOSITORY_TEAMS.equals(reqType)) {
 			// rejected since 1.2.0
 			response.setStatus(failureCode);
 		} else if (RpcRequest.LIST_REPOSITORY_TEAM_PERMISSIONS.equals(reqType)) {
 			// get repository team permissions
-			RepositoryModel model = GitBlit.self().getRepositoryModel(objectName);
-			result = GitBlit.self().getTeamAccessPermissions(model);
+			RepositoryModel model = repositoryManager.getRepositoryModel(objectName);
+			result = repositoryManager.getTeamAccessPermissions(model);
 		} else if (RpcRequest.SET_REPOSITORY_TEAM_PERMISSIONS.equals(reqType)) {
 			// set the repository permissions for the specified teams
-			RepositoryModel model = GitBlit.self().getRepositoryModel(objectName);
+			RepositoryModel model = repositoryManager.getRepositoryModel(objectName);
 			Collection<RegistrantAccessPermission> permissions = deserialize(request, response, RpcUtils.REGISTRANT_PERMISSIONS_TYPE);
-			result = GitBlit.self().setTeamAccessPermissions(model, permissions);
+			result = repositoryManager.setTeamAccessPermissions(model, permissions);
 		} else if (RpcRequest.LIST_FEDERATION_REGISTRATIONS.equals(reqType)) {
 			// return the list of federation registrations
 			if (allowAdmin) {
-				result = GitBlit.self().getFederationRegistrations();
+				result = federationManager.getFederationRegistrations();
 			} else {
 				response.sendError(notAllowedCode);
 			}
 		} else if (RpcRequest.LIST_FEDERATION_RESULTS.equals(reqType)) {
 			// return the list of federation result registrations
-			if (allowAdmin && GitBlit.canFederate()) {
-				result = GitBlit.self().getFederationResultRegistrations();
+			if (allowAdmin && federationManager.canFederate()) {
+				result = federationManager.getFederationResultRegistrations();
 			} else {
 				response.sendError(notAllowedCode);
 			}
 		} else if (RpcRequest.LIST_FEDERATION_PROPOSALS.equals(reqType)) {
 			// return the list of federation proposals
-			if (allowAdmin && GitBlit.canFederate()) {
-				result = GitBlit.self().getPendingFederationProposals();
+			if (allowAdmin && federationManager.canFederate()) {
+				result = federationManager.getPendingFederationProposals();
 			} else {
 				response.sendError(notAllowedCode);
 			}
 		} else if (RpcRequest.LIST_FEDERATION_SETS.equals(reqType)) {
 			// return the list of federation sets
-			if (allowAdmin && GitBlit.canFederate()) {
+			if (allowAdmin && federationManager.canFederate()) {
 				String gitblitUrl = HttpUtils.getGitblitURL(request);
-				result = GitBlit.self().getFederationSets(gitblitUrl);
+				result = federationManager.getFederationSets(gitblitUrl);
 			} else {
 				response.sendError(notAllowedCode);
 			}
 		} else if (RpcRequest.LIST_SETTINGS.equals(reqType)) {
 			// return the server's settings
-			ServerSettings settings = GitBlit.self().getSettingsModel();
+			ServerSettings serverSettings = runtimeManager.getSettingsModel();
 			if (allowAdmin) {
 				// return all settings
-				result = settings;
+				result = serverSettings;
 			} else {
 				// anonymous users get a few settings to allow browser launching
 				List<String> keys = new ArrayList<String>();
@@ -334,33 +346,33 @@
 				// build the settings
 				ServerSettings managementSettings = new ServerSettings();
 				for (String key : keys) {
-					managementSettings.add(settings.get(key));
+					managementSettings.add(serverSettings.get(key));
 				}
 				if (allowManagement) {
-					managementSettings.pushScripts = settings.pushScripts;
+					managementSettings.pushScripts = serverSettings.pushScripts;
 				}
 				result = managementSettings;
 			}
 		} else if (RpcRequest.EDIT_SETTINGS.equals(reqType)) {
 			// update settings on the server
 			if (allowAdmin) {
-				Map<String, String> settings = deserialize(request, response,
+				Map<String, String> map = deserialize(request, response,
 						RpcUtils.SETTINGS_TYPE);
-				GitBlit.self().updateSettings(settings);
+				runtimeManager.updateSettings(map);
 			} else {
 				response.sendError(notAllowedCode);
 			}
 		} else if (RpcRequest.LIST_STATUS.equals(reqType)) {
 			// return the server's status information
 			if (allowAdmin) {
-				result = GitBlit.self().getStatus();
+				result = runtimeManager.getStatus();
 			} else {
 				response.sendError(notAllowedCode);
 			}
 		} else if (RpcRequest.CLEAR_REPOSITORY_CACHE.equals(reqType)) {
 			// clear the repository list cache
 			if (allowManagement) {
-				GitBlit.self().resetRepositoryListCache();
+				repositoryManager.resetRepositoryListCache();
 			} else {
 				response.sendError(notAllowedCode);
 			}
diff --git a/src/main/java/com/gitblit/SalesforceUserService.java b/src/main/java/com/gitblit/SalesforceUserService.java
index 5979f63..bfc96be 100644
--- a/src/main/java/com/gitblit/SalesforceUserService.java
+++ b/src/main/java/com/gitblit/SalesforceUserService.java
@@ -6,6 +6,7 @@
 import org.slf4j.LoggerFactory;
 
 import com.gitblit.Constants.AccountType;
+import com.gitblit.manager.IRuntimeManager;
 import com.gitblit.models.UserModel;
 import com.gitblit.utils.ArrayUtils;
 import com.gitblit.utils.StringUtils;
@@ -31,7 +32,8 @@
 		String file = settings.getString(
 				Keys.realm.salesforce.backingUserService,
 				"${baseFolder}/users.conf");
-		File realmFile = GitBlit.getFileOrFolder(file);
+		IRuntimeManager runtimeManager = GitBlit.getManager(IRuntimeManager.class);
+		File realmFile = runtimeManager.getFileOrFolder(file);
 
 		serviceImpl = createUserService(realmFile);
 
diff --git a/src/main/java/com/gitblit/SparkleShareInviteServlet.java b/src/main/java/com/gitblit/SparkleShareInviteServlet.java
index 11a58dd..9f6618b 100644
--- a/src/main/java/com/gitblit/SparkleShareInviteServlet.java
+++ b/src/main/java/com/gitblit/SparkleShareInviteServlet.java
@@ -23,6 +23,10 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import com.gitblit.manager.IRepositoryManager;
+import com.gitblit.manager.IRuntimeManager;
+import com.gitblit.manager.ISessionManager;
+import com.gitblit.manager.IUserManager;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.UserModel;
 import com.gitblit.utils.StringUtils;
@@ -57,6 +61,11 @@
 			javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
 			java.io.IOException {
 
+		IStoredSettings settings = GitBlit.getManager(IRuntimeManager.class).getSettings();
+		IUserManager userManager = GitBlit.getManager(IUserManager.class);
+		ISessionManager sessionManager = GitBlit.getManager(ISessionManager.class);
+		IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);
+
 		// extract repo name from request
 		String repoUrl = request.getPathInfo().substring(1);
 
@@ -77,9 +86,9 @@
 		}
 		UserModel user;
 		if (StringUtils.isEmpty(username)) {
-			user = GitBlit.self().authenticate(request);
+			user = sessionManager.authenticate(request);
 		} else {
-			user = GitBlit.self().getUserModel(username);
+			user = userManager.getUserModel(username);
 		}
 		if (user == null) {
 			user = UserModel.ANONYMOUS;
@@ -87,7 +96,7 @@
 		}
 
 		// ensure that the requested repository exists
-		RepositoryModel model = GitBlit.self().getRepositoryModel(path);
+		RepositoryModel model = repositoryManager.getRepositoryModel(path);
 		if (model == null) {
 			response.setStatus(HttpServletResponse.SC_NOT_FOUND);
 			response.getWriter().append(MessageFormat.format("Repository \"{0}\" not found!", path));
@@ -99,9 +108,9 @@
 		sb.append("<sparkleshare><invite>\n");
 		sb.append(MessageFormat.format("<address>{0}</address>\n", host));
 		sb.append(MessageFormat.format("<remote_path>{0}{1}</remote_path>\n", servletPath, model.name));
-		if (GitBlit.getInteger(Keys.fanout.port, 0) > 0) {
+		if (settings.getInteger(Keys.fanout.port, 0) > 0) {
 			// Gitblit is running it's own fanout service for pubsub notifications
-			sb.append(MessageFormat.format("<announcements_url>tcp://{0}:{1}</announcements_url>\n", request.getServerName(), GitBlit.getString(Keys.fanout.port, "")));
+			sb.append(MessageFormat.format("<announcements_url>tcp://{0}:{1}</announcements_url>\n", request.getServerName(), settings.getString(Keys.fanout.port, "")));
 		}
 		sb.append("</invite></sparkleshare>\n");
 
diff --git a/src/main/java/com/gitblit/SyndicationFilter.java b/src/main/java/com/gitblit/SyndicationFilter.java
index 2c232ad..ab854bb 100644
--- a/src/main/java/com/gitblit/SyndicationFilter.java
+++ b/src/main/java/com/gitblit/SyndicationFilter.java
@@ -26,6 +26,9 @@
 import javax.servlet.http.HttpServletResponse;
 
 import com.gitblit.Constants.AccessRestrictionType;
+import com.gitblit.manager.IProjectManager;
+import com.gitblit.manager.IRepositoryManager;
+import com.gitblit.manager.IRuntimeManager;
 import com.gitblit.models.ProjectModel;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.UserModel;
@@ -70,12 +73,16 @@
 		String fullUrl = getFullUrl(httpRequest);
 		String name = extractRequestedName(fullUrl);
 
-		ProjectModel project = GitBlit.self().getProjectModel(name);
+		IRuntimeManager runtimeManager = GitBlit.getManager(IRuntimeManager.class);
+		IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);
+		IProjectManager projectManager = GitBlit.getManager(IProjectManager.class);
+
+		ProjectModel project = projectManager.getProjectModel(name);
 		RepositoryModel model = null;
 
 		if (project == null) {
 			// try loading a repository model
-			model = GitBlit.self().getRepositoryModel(name);
+			model = repositoryManager.getRepositoryModel(name);
 			if (model == null) {
 				// repository not found. send 404.
 				logger.info(MessageFormat.format("ARF: {0} ({1})", fullUrl,
@@ -105,7 +112,7 @@
 			if (model.accessRestriction.atLeast(AccessRestrictionType.VIEW)) {
 				if (user == null) {
 					// challenge client to provide credentials. send 401.
-					if (GitBlit.isDebugMode()) {
+					if (runtimeManager.isDebugMode()) {
 						logger.info(MessageFormat.format("ARF: CHALLENGE {0}", fullUrl));
 					}
 					httpResponse.setHeader("WWW-Authenticate", CHALLENGE);
@@ -123,7 +130,7 @@
 						return;
 					}
 					// valid user, but not for requested access. send 403.
-					if (GitBlit.isDebugMode()) {
+					if (runtimeManager.isDebugMode()) {
 						logger.info(MessageFormat.format("ARF: {0} forbidden to access {1}",
 								user.username, fullUrl));
 					}
@@ -133,7 +140,7 @@
 			}
 		}
 
-		if (GitBlit.isDebugMode()) {
+		if (runtimeManager.isDebugMode()) {
 			logger.info(MessageFormat.format("ARF: {0} ({1}) unauthenticated", fullUrl,
 					HttpServletResponse.SC_CONTINUE));
 		}
diff --git a/src/main/java/com/gitblit/SyndicationServlet.java b/src/main/java/com/gitblit/SyndicationServlet.java
index 6b3c01c..ae50c3e 100644
--- a/src/main/java/com/gitblit/SyndicationServlet.java
+++ b/src/main/java/com/gitblit/SyndicationServlet.java
@@ -31,6 +31,9 @@
 import org.slf4j.LoggerFactory;
 
 import com.gitblit.AuthenticationFilter.AuthenticatedRequest;
+import com.gitblit.manager.IProjectManager;
+import com.gitblit.manager.IRepositoryManager;
+import com.gitblit.manager.IRuntimeManager;
 import com.gitblit.models.FeedEntryModel;
 import com.gitblit.models.ProjectModel;
 import com.gitblit.models.RefModel;
@@ -38,6 +41,7 @@
 import com.gitblit.models.UserModel;
 import com.gitblit.utils.HttpUtils;
 import com.gitblit.utils.JGitUtils;
+import com.gitblit.utils.MessageProcessor;
 import com.gitblit.utils.StringUtils;
 import com.gitblit.utils.SyndicationUtils;
 
@@ -126,6 +130,10 @@
 			javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
 			java.io.IOException {
 
+		IStoredSettings settings = GitBlit.getManager(IRuntimeManager.class).getSettings();
+		IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);
+		IProjectManager projectManager = GitBlit.getManager(IProjectManager.class);
+
 		String servletUrl = request.getContextPath() + request.getServletPath();
 		String url = request.getRequestURI().substring(servletUrl.length());
 		if (url.charAt(0) == '/' && url.length() > 1) {
@@ -143,7 +151,7 @@
 				searchType = type;
 			}
 		}
-		int length = GitBlit.getInteger(Keys.web.syndicationEntries, 25);
+		int length = settings.getInteger(Keys.web.syndicationEntries, 25);
 		if (StringUtils.isEmpty(objectId)) {
 			objectId = org.eclipse.jgit.lib.Constants.HEAD;
 		}
@@ -175,7 +183,7 @@
 			if (request instanceof AuthenticatedRequest) {
 				user = ((AuthenticatedRequest) request).getUser();
 			}
-			ProjectModel project = GitBlit.self().getProjectModel(repositoryName, user);
+			ProjectModel project = projectManager.getProjectModel(repositoryName, user);
 			if (project != null) {
 				isProjectFeed = true;
 				repositories = new ArrayList<String>(project.repositories);
@@ -193,7 +201,7 @@
 		}
 
 
-		boolean mountParameters = GitBlit.getBoolean(Keys.web.mountParameters, true);
+		boolean mountParameters = settings.getBoolean(Keys.web.mountParameters, true);
 		String urlPattern;
 		if (mountParameters) {
 			// mounted parameters
@@ -203,13 +211,13 @@
 			urlPattern = "{0}/commit/?r={1}&h={2}";
 		}
 		String gitblitUrl = HttpUtils.getGitblitURL(request);
-		char fsc = GitBlit.getChar(Keys.web.forwardSlashCharacter, '/');
+		char fsc = settings.getChar(Keys.web.forwardSlashCharacter, '/');
 
 		List<FeedEntryModel> entries = new ArrayList<FeedEntryModel>();
 
 		for (String name : repositories) {
-			Repository repository = GitBlit.self().getRepository(name);
-			RepositoryModel model = GitBlit.self().getRepositoryModel(name);
+			Repository repository = repositoryManager.getRepository(name);
+			RepositoryModel model = repositoryManager.getRepositoryModel(name);
 
 			if (repository == null) {
 				if (model.isCollectingGarbage) {
@@ -234,6 +242,7 @@
 						offset, length);
 			}
 			Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(repository, model.showRemoteBranches);
+			MessageProcessor processor = new MessageProcessor(settings);
 
 			// convert RevCommit to SyndicatedEntryModel
 			for (RevCommit commit : commits) {
@@ -244,8 +253,7 @@
 						StringUtils.encodeURL(model.name.replace('/', fsc)), commit.getName());
 				entry.published = commit.getCommitterIdent().getWhen();
 				entry.contentType = "text/html";
-				String message = GitBlit.self().processCommitMessage(model,
-						commit.getFullMessage());
+				String message = processor.processCommitMessage(model, commit.getFullMessage());
 				entry.content = message;
 				entry.repository = model.name;
 				entry.branch = objectId;
diff --git a/src/main/java/com/gitblit/WindowsUserService.java b/src/main/java/com/gitblit/WindowsUserService.java
index a65f44e..03d65e0 100644
--- a/src/main/java/com/gitblit/WindowsUserService.java
+++ b/src/main/java/com/gitblit/WindowsUserService.java
@@ -29,6 +29,7 @@
 import waffle.windows.auth.impl.WindowsAuthProviderImpl;
 
 import com.gitblit.Constants.AccountType;
+import com.gitblit.manager.IRuntimeManager;
 import com.gitblit.models.UserModel;
 import com.gitblit.utils.ArrayUtils;
 import com.gitblit.utils.StringUtils;
@@ -56,7 +57,8 @@
         this.settings = settings;
 
         String file = settings.getString(Keys.realm.windows.backingUserService, "${baseFolder}/users.conf");
-        File realmFile = GitBlit.getFileOrFolder(file);
+        IRuntimeManager runtimeManager = GitBlit.getManager(IRuntimeManager.class);
+        File realmFile = runtimeManager.getFileOrFolder(file);
 
         serviceImpl = createUserService(realmFile);
         logger.info("Windows User Service backed by " + serviceImpl.toString());
diff --git a/src/main/java/com/gitblit/git/GitServlet.java b/src/main/java/com/gitblit/git/GitServlet.java
index 310d4da..0233cd9 100644
--- a/src/main/java/com/gitblit/git/GitServlet.java
+++ b/src/main/java/com/gitblit/git/GitServlet.java
@@ -20,6 +20,7 @@
 import javax.servlet.http.HttpServletRequest;
 
 import com.gitblit.GitBlit;
+import com.gitblit.manager.IRepositoryManager;
 
 /**
  * The GitServlet provides http/https access to Git repositories.
@@ -34,7 +35,8 @@
 
 	@Override
 	public void init(ServletConfig config) throws ServletException {
-		setRepositoryResolver(new RepositoryResolver<HttpServletRequest>(GitBlit.getRepositoriesFolder()));
+		IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);
+		setRepositoryResolver(new RepositoryResolver<HttpServletRequest>(repositoryManager.getRepositoriesFolder()));
 		setUploadPackFactory(new GitblitUploadPackFactory<HttpServletRequest>());
 		setReceivePackFactory(new GitblitReceivePackFactory<HttpServletRequest>());
 		super.init(config);
diff --git a/src/main/java/com/gitblit/git/GitblitReceivePack.java b/src/main/java/com/gitblit/git/GitblitReceivePack.java
index ba200b2..8da603a 100644
--- a/src/main/java/com/gitblit/git/GitblitReceivePack.java
+++ b/src/main/java/com/gitblit/git/GitblitReceivePack.java
@@ -45,8 +45,11 @@
 import com.gitblit.Constants;
 import com.gitblit.Constants.AccessRestrictionType;
 import com.gitblit.GitBlit;
+import com.gitblit.IStoredSettings;
 import com.gitblit.Keys;
 import com.gitblit.client.Translation;
+import com.gitblit.manager.IRepositoryManager;
+import com.gitblit.manager.IRuntimeManager;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.UserModel;
 import com.gitblit.utils.ArrayUtils;
@@ -90,12 +93,15 @@
 
 	public GitblitReceivePack(Repository db, RepositoryModel repository, UserModel user) {
 		super(db);
+
+		IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);
+
 		this.repository = repository;
 		this.user = user == null ? UserModel.ANONYMOUS : user;
-		this.groovyDir = GitBlit.getGroovyScriptsFolder();
+		this.groovyDir = repositoryManager.getHooksFolder();
 		try {
 			// set Grape root
-			File grapeRoot = GitBlit.getFileOrFolder(Keys.groovy.grapeFolder, "${baseFolder}/groovy/grape").getAbsoluteFile();
+			File grapeRoot = repositoryManager.getGrapesFolder();
 			grapeRoot.mkdirs();
 			System.setProperty("grape.root", grapeRoot.getAbsolutePath());
 			this.gse = new GroovyScriptEngine(groovyDir.getAbsolutePath());
@@ -233,8 +239,9 @@
 			}
 		}
 
+		IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);
 		Set<String> scripts = new LinkedHashSet<String>();
-		scripts.addAll(GitBlit.self().getPreReceiveScriptsInherited(repository));
+		scripts.addAll(repositoryManager.getPreReceiveScriptsInherited(repository));
 		if (!ArrayUtils.isEmpty(repository.preReceiveScripts)) {
 			scripts.addAll(repository.preReceiveScripts);
 		}
@@ -258,6 +265,8 @@
 			LOGGER.debug("skipping post-receive hooks, no refs created, updated, or removed");
 			return;
 		}
+
+		IStoredSettings settings = GitBlit.getManager(IRuntimeManager.class).getSettings();
 
 		// log ref changes
 		for (ReceiveCommand cmd : commands) {
@@ -303,7 +312,7 @@
 					String msg = MessageFormat.format(template, branch);
 					String prefix;
 					if (StringUtils.isEmpty(repository.incrementalPushTagPrefix)) {
-						prefix = GitBlit.getString(Keys.git.defaultIncrementalPushTagPrefix, "r");
+						prefix = settings.getString(Keys.git.defaultIncrementalPushTagPrefix, "r");
 					} else {
 						prefix = repository.incrementalPushTagPrefix;
 					}
@@ -327,9 +336,11 @@
 			LOGGER.error(MessageFormat.format("Failed to update {0} pushlog", repository.name), e);
 		}
 
+		IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);
+
 		// run Groovy hook scripts
 		Set<String> scripts = new LinkedHashSet<String>();
-		scripts.addAll(GitBlit.self().getPostReceiveScriptsInherited(repository));
+		scripts.addAll(repositoryManager.getPostReceiveScriptsInherited(repository));
 		if (!ArrayUtils.isEmpty(repository.postReceiveScripts)) {
 			scripts.addAll(repository.postReceiveScripts);
 		}
diff --git a/src/main/java/com/gitblit/git/GitblitReceivePackFactory.java b/src/main/java/com/gitblit/git/GitblitReceivePackFactory.java
index b2862f0..a90c36b 100644
--- a/src/main/java/com/gitblit/git/GitblitReceivePackFactory.java
+++ b/src/main/java/com/gitblit/git/GitblitReceivePackFactory.java
@@ -27,7 +27,11 @@
 import org.slf4j.LoggerFactory;
 
 import com.gitblit.GitBlit;
+import com.gitblit.IStoredSettings;
 import com.gitblit.Keys;
+import com.gitblit.manager.IRepositoryManager;
+import com.gitblit.manager.IRuntimeManager;
+import com.gitblit.manager.IUserManager;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.UserModel;
 import com.gitblit.utils.HttpUtils;
@@ -48,6 +52,10 @@
 	public ReceivePack create(X req, Repository db)
 			throws ServiceNotEnabledException, ServiceNotAuthorizedException {
 
+		IStoredSettings settings = GitBlit.getManager(IRuntimeManager.class).getSettings();
+		IUserManager userManager = GitBlit.getManager(IUserManager.class);
+		IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);
+
 		UserModel user = UserModel.ANONYMOUS;
 		String repositoryName = "";
 		String origin = "";
@@ -66,7 +74,7 @@
 			// determine pushing user
 			String username = request.getRemoteUser();
 			if (!StringUtils.isEmpty(username)) {
-				UserModel u = GitBlit.self().getUserModel(username);
+				UserModel u = userManager.getUserModel(username);
 				if (u != null) {
 					user = u;
 				}
@@ -81,13 +89,13 @@
 			timeout = client.getDaemon().getTimeout();
 		}
 
-		boolean allowAnonymousPushes = GitBlit.getBoolean(Keys.git.allowAnonymousPushes, false);
+		boolean allowAnonymousPushes = settings.getBoolean(Keys.git.allowAnonymousPushes, false);
 		if (!allowAnonymousPushes && UserModel.ANONYMOUS.equals(user)) {
 			// prohibit anonymous pushes
 			throw new ServiceNotEnabledException();
 		}
 
-		final RepositoryModel repository = GitBlit.self().getRepositoryModel(repositoryName);
+		final RepositoryModel repository = repositoryManager.getRepositoryModel(repositoryName);
 
 		final GitblitReceivePack rp = new GitblitReceivePack(db, repository, user);
 		rp.setGitblitUrl(gitblitUrl);
diff --git a/src/main/java/com/gitblit/git/GitblitUploadPackFactory.java b/src/main/java/com/gitblit/git/GitblitUploadPackFactory.java
index 90875b5..180e8b5 100644
--- a/src/main/java/com/gitblit/git/GitblitUploadPackFactory.java
+++ b/src/main/java/com/gitblit/git/GitblitUploadPackFactory.java
@@ -24,6 +24,7 @@
 import org.eclipse.jgit.transport.resolver.UploadPackFactory;
 
 import com.gitblit.GitBlit;
+import com.gitblit.manager.ISessionManager;
 import com.gitblit.models.UserModel;
 
 /**
@@ -40,12 +41,13 @@
 	public UploadPack create(X req, Repository db)
 			throws ServiceNotEnabledException, ServiceNotAuthorizedException {
 
+		ISessionManager sessionManager = GitBlit.getManager(ISessionManager.class);
 		UserModel user = UserModel.ANONYMOUS;
 		int timeout = 0;
 
 		if (req instanceof HttpServletRequest) {
 			// http/https request may or may not be authenticated
-			user = GitBlit.self().authenticate((HttpServletRequest) req);
+			user = sessionManager.authenticate((HttpServletRequest) req);
 			if (user == null) {
 				user = UserModel.ANONYMOUS;
 			}
diff --git a/src/main/java/com/gitblit/git/RepositoryResolver.java b/src/main/java/com/gitblit/git/RepositoryResolver.java
index e44b153..5441596 100644
--- a/src/main/java/com/gitblit/git/RepositoryResolver.java
+++ b/src/main/java/com/gitblit/git/RepositoryResolver.java
@@ -29,6 +29,8 @@
 import org.slf4j.LoggerFactory;
 
 import com.gitblit.GitBlit;
+import com.gitblit.manager.IRepositoryManager;
+import com.gitblit.manager.ISessionManager;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.UserModel;
 
@@ -74,7 +76,9 @@
 	 */
 	@Override
 	protected boolean isExportOk(X req, String repositoryName, Repository db) throws IOException {
-		RepositoryModel model = GitBlit.self().getRepositoryModel(repositoryName);
+		IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);
+		ISessionManager sessionManager = GitBlit.getManager(ISessionManager.class);
+		RepositoryModel model = repositoryManager.getRepositoryModel(repositoryName);
 
 		String scheme = null;
 		UserModel user = null;
@@ -92,7 +96,7 @@
 			HttpServletRequest httpRequest = (HttpServletRequest) req;
 			scheme = httpRequest.getScheme();
 			origin = httpRequest.getRemoteAddr();
-			user = GitBlit.self().authenticate(httpRequest);
+			user = sessionManager.authenticate(httpRequest);
 			if (user == null) {
 				user = UserModel.ANONYMOUS;
 			}
diff --git a/src/main/java/com/gitblit/manager/IFederationManager.java b/src/main/java/com/gitblit/manager/IFederationManager.java
new file mode 100644
index 0000000..debe362
--- /dev/null
+++ b/src/main/java/com/gitblit/manager/IFederationManager.java
@@ -0,0 +1,177 @@
+/*
+ * 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.util.List;
+import java.util.Map;
+
+import com.gitblit.Constants.FederationRequest;
+import com.gitblit.Constants.FederationToken;
+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;
+
+public interface IFederationManager {
+
+	/**
+	 * 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
+	 */
+	File getProposalsFolder();
+
+	UserModel getFederationUser();
+
+	boolean canFederate();
+
+	/**
+	 * Returns the list of federated gitblit instances that this instance will
+	 * try to pull.
+	 *
+	 * @return list of registered gitblit instances
+	 */
+	List<FederationModel> getFederationRegistrations();
+
+	/**
+	 * Retrieve the specified federation registration.
+	 *
+	 * @param name
+	 *            the name of the registration
+	 * @return a federation registration
+	 */
+	FederationModel getFederationRegistration(String url, String name);
+
+	/**
+	 * Returns the list of federation sets.
+	 *
+	 * @return list of federation sets
+	 */
+	List<FederationSet> getFederationSets(String gitblitUrl);
+
+	/**
+	 * Returns the list of possible federation tokens for this Gitblit instance.
+	 *
+	 * @return list of federation tokens
+	 */
+	List<String> getFederationTokens();
+
+	/**
+	 * Returns the specified federation token for this Gitblit instance.
+	 *
+	 * @param type
+	 * @return a federation token
+	 */
+	String getFederationToken(FederationToken type);
+
+	/**
+	 * Returns the specified federation token for this Gitblit instance.
+	 *
+	 * @param value
+	 * @return a federation token
+	 */
+	String getFederationToken(String 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
+	 */
+	boolean validateFederationRequest(FederationRequest req, String token);
+
+	/**
+	 * 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
+	 */
+	boolean acknowledgeFederationStatus(String identification, FederationModel registration);
+
+	/**
+	 * Returns the list of registration results.
+	 *
+	 * @return the list of registration results
+	 */
+	List<FederationModel> getFederationResultRegistrations();
+
+	/**
+	 * 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
+	 */
+	boolean submitFederationProposal(FederationProposal proposal, String gitblitUrl);
+
+	/**
+	 * Returns the list of pending federation proposals
+	 *
+	 * @return list of federation proposals
+	 */
+	List<FederationProposal> getPendingFederationProposals();
+
+	/**
+	 * 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>
+	 */
+	Map<String, RepositoryModel> getRepositories(String gitblitUrl, String token);
+
+	/**
+	 * Creates a proposal from the token.
+	 *
+	 * @param gitblitUrl
+	 *            the url of this Gitblit instance
+	 * @param token
+	 * @return a potential proposal
+	 */
+	FederationProposal createFederationProposal(String gitblitUrl, String token);
+
+	/**
+	 * Returns the proposal identified by the supplied token.
+	 *
+	 * @param token
+	 * @return the specified proposal or null
+	 */
+	FederationProposal getPendingFederationProposal(String token);
+
+	/**
+	 * Deletes a pending federation proposal.
+	 *
+	 * @param a
+	 *            proposal
+	 * @return true if the proposal was deleted
+	 */
+	boolean deletePendingFederationProposal(FederationProposal proposal);
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/manager/IGitblitManager.java b/src/main/java/com/gitblit/manager/IGitblitManager.java
new file mode 100644
index 0000000..2f5295b
--- /dev/null
+++ b/src/main/java/com/gitblit/manager/IGitblitManager.java
@@ -0,0 +1,83 @@
+/*
+ * 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.util.Collection;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import com.gitblit.GitBlitException;
+import com.gitblit.models.GitClientApplication;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.RepositoryUrl;
+import com.gitblit.models.TeamModel;
+import com.gitblit.models.UserModel;
+
+public interface IGitblitManager {
+
+	/**
+	 * Returns a list of repository URLs and the user access permission.
+	 *
+	 * @param request
+	 * @param user
+	 * @param repository
+	 * @return a list of repository urls
+	 */
+	List<RepositoryUrl> getRepositoryUrls(HttpServletRequest request, UserModel user, RepositoryModel repository);
+
+	/**
+	 * 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
+	 */
+	void updateUserModel(String username, UserModel user, boolean isCreate) throws GitBlitException;
+
+	/**
+	 * Updates the TeamModel object for the specified name.
+	 *
+	 * @param teamname
+	 * @param team
+	 * @param isCreate
+	 */
+	void updateTeamModel(String teamname, TeamModel team, boolean isCreate) throws GitBlitException;
+
+	/**
+	 * 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
+	 */
+	RepositoryModel fork(RepositoryModel repository, UserModel user) throws GitBlitException;
+
+	/**
+	 * Returns the list of custom client applications to be used for the
+	 * repository url panel;
+	 *
+	 * @return a collection of client applications
+	 */
+	Collection<GitClientApplication> getClientApplications();
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/manager/INotificationManager.java b/src/main/java/com/gitblit/manager/INotificationManager.java
new file mode 100644
index 0000000..f53ae68
--- /dev/null
+++ b/src/main/java/com/gitblit/manager/INotificationManager.java
@@ -0,0 +1,66 @@
+/*
+ * 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.util.Collection;
+
+public interface INotificationManager {
+
+	/**
+	 * Notify the administrators by email.
+	 *
+	 * @param subject
+	 * @param message
+	 */
+	void sendMailToAdministrators(String subject, String message);
+
+	/**
+	 * Notify users by email of something.
+	 *
+	 * @param subject
+	 * @param message
+	 * @param toAddresses
+	 */
+	void sendMail(String subject, String message, Collection<String> toAddresses);
+
+	/**
+	 * Notify users by email of something.
+	 *
+	 * @param subject
+	 * @param message
+	 * @param toAddresses
+	 */
+	void sendMail(String subject, String message, String... toAddresses);
+
+	/**
+	 * Notify users by email of something.
+	 *
+	 * @param subject
+	 * @param message
+	 * @param toAddresses
+	 */
+	void sendHtmlMail(String subject, String message, Collection<String> toAddresses);
+
+	/**
+	 * Notify users by email of something.
+	 *
+	 * @param subject
+	 * @param message
+	 * @param toAddresses
+	 */
+	void sendHtmlMail(String subject, String message, String... toAddresses);
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/manager/IProjectManager.java b/src/main/java/com/gitblit/manager/IProjectManager.java
new file mode 100644
index 0000000..b2577f5
--- /dev/null
+++ b/src/main/java/com/gitblit/manager/IProjectManager.java
@@ -0,0 +1,63 @@
+/*
+ * 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.util.List;
+
+import com.gitblit.models.ProjectModel;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.UserModel;
+
+public interface IProjectManager {
+
+	/**
+	 * Returns a list of project models for the user.
+	 *
+	 * @param user
+	 * @param includeUsers
+	 * @return list of projects that are accessible to the user
+	 */
+	List<ProjectModel> getProjectModels(UserModel user, boolean includeUsers);
+
+	/**
+	 * Returns the project model for the specified user.
+	 *
+	 * @param name
+	 * @param user
+	 * @return a project model, or null if it does not exist
+	 */
+	ProjectModel getProjectModel(String name, UserModel user);
+
+	/**
+	 * Returns a project model for the Gitblit/system user.
+	 *
+	 * @param name a project name
+	 * @return a project model or null if the project does not exist
+	 */
+	ProjectModel getProjectModel(String name);
+
+	/**
+	 * Returns the list of project models that are referenced by the supplied
+	 * repository model	list.  This is an alternative method exists to ensure
+	 * Gitblit does not call getRepositoryModels(UserModel) twice in a request.
+	 *
+	 * @param repositoryModels
+	 * @param includeUsers
+	 * @return a list of project models
+	 */
+	List<ProjectModel> getProjectModels(List<RepositoryModel> repositoryModels, boolean includeUsers);
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/manager/IRepositoryManager.java b/src/main/java/com/gitblit/manager/IRepositoryManager.java
new file mode 100644
index 0000000..383ca0d
--- /dev/null
+++ b/src/main/java/com/gitblit/manager/IRepositoryManager.java
@@ -0,0 +1,396 @@
+/*
+ * 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.util.Collection;
+import java.util.Date;
+import java.util.List;
+
+import org.eclipse.jgit.lib.Repository;
+
+import com.gitblit.GitBlitException;
+import com.gitblit.models.ForkModel;
+import com.gitblit.models.Metric;
+import com.gitblit.models.RegistrantAccessPermission;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.SearchResult;
+import com.gitblit.models.UserModel;
+
+public interface IRepositoryManager {
+
+	/**
+	 * Returns the path of the repositories folder. This method checks to see if
+	 * Gitblit is running on a cloud service and may return an adjusted path.
+	 *
+	 * @return the repositories folder path
+	 */
+	File getRepositoriesFolder();
+
+	/**
+	 * Returns the path of the hooks folder. This method checks to see if
+	 * Gitblit is running on a cloud service and may return an adjusted path.
+	 *
+	 * @return the Groovy hook scripts folder path
+	 */
+	File getHooksFolder();
+
+	/**
+	 * Returns the path of the grapes folder. This method checks to see if
+	 * Gitblit is running on a cloud service and may return an adjusted path.
+	 *
+	 * @return the Groovy grapes folder path
+	 */
+	File getGrapesFolder();
+
+	/**
+	 * Returns the most recent change date of any repository served by Gitblit.
+	 *
+	 * @return a date
+	 */
+	Date getLastActivityDate();
+
+	/**
+	 * Returns the effective list of permissions for this user, taking into account
+	 * team memberships, ownerships.
+	 *
+	 * @param user
+	 * @return the effective list of permissions for the user
+	 */
+	List<RegistrantAccessPermission> getUserAccessPermissions(UserModel user);
+
+	/**
+	 * Returns the list of users and their access permissions for the specified
+	 * repository including permission source information such as the team or
+	 * regular expression which sets the permission.
+	 *
+	 * @param repository
+	 * @return a list of RegistrantAccessPermissions
+	 */
+	List<RegistrantAccessPermission> getUserAccessPermissions(RepositoryModel repository);
+
+	/**
+	 * Sets the access permissions to the specified repository for the specified users.
+	 *
+	 * @param repository
+	 * @param permissions
+	 * @return true if the user models have been updated
+	 */
+	boolean setUserAccessPermissions(RepositoryModel repository, Collection<RegistrantAccessPermission> permissions);
+
+	/**
+	 * Returns the list of all users who have an explicit access permission
+	 * for the specified repository.
+	 *
+	 * @see IUserService.getUsernamesForRepositoryRole(String)
+	 * @param repository
+	 * @return list of all usernames that have an access permission for the repository
+	 */
+	List<String> getRepositoryUsers(RepositoryModel repository);
+
+	/**
+	 * Returns the list of teams and their access permissions for the specified
+	 * repository including the source of the permission such as the admin flag
+	 * or a regular expression.
+	 *
+	 * @param repository
+	 * @return a list of RegistrantAccessPermissions
+	 */
+	List<RegistrantAccessPermission> getTeamAccessPermissions(RepositoryModel repository);
+
+	/**
+	 * Sets the access permissions to the specified repository for the specified teams.
+	 *
+	 * @param repository
+	 * @param permissions
+	 * @return true if the team models have been updated
+	 */
+	boolean setTeamAccessPermissions(RepositoryModel repository, Collection<RegistrantAccessPermission> permissions);
+
+	/**
+	 * Returns the list of all teams who have an explicit access permission for
+	 * the specified repository.
+	 *
+	 * @see IUserService.getTeamnamesForRepositoryRole(String)
+	 * @param repository
+	 * @return list of all teamnames with explicit access permissions to the repository
+	 */
+	List<String> getRepositoryTeams(RepositoryModel repository);
+
+	/**
+	 * Adds the repository to the list of cached repositories if Gitblit is
+	 * configured to cache the repository list.
+	 *
+	 * @param model
+	 */
+	void addToCachedRepositoryList(RepositoryModel model);
+
+	/**
+	 * Resets the repository list cache.
+	 *
+	 */
+	void resetRepositoryListCache();
+
+	/**
+	 * Returns the list of all repositories available to Gitblit. This method
+	 * does not consider user access permissions.
+	 *
+	 * @return list of all repositories
+	 */
+	List<String> getRepositoryList();
+
+	/**
+	 * Returns the JGit repository for the specified name.
+	 *
+	 * @param repositoryName
+	 * @return repository or null
+	 */
+	Repository getRepository(String repositoryName);
+
+	/**
+	 * Returns the JGit repository for the specified name.
+	 *
+	 * @param repositoryName
+	 * @param logError
+	 * @return repository or null
+	 */
+	Repository getRepository(String repositoryName, boolean logError);
+
+	/**
+	 * Returns the list of repository models that are accessible to the user.
+	 *
+	 * @param user
+	 * @return list of repository models accessible to user
+	 */
+	List<RepositoryModel> getRepositoryModels(UserModel user);
+
+	/**
+	 * Returns a repository model if the repository exists and the user may
+	 * access the repository.
+	 *
+	 * @param user
+	 * @param repositoryName
+	 * @return repository model or null
+	 */
+	RepositoryModel getRepositoryModel(UserModel user, String repositoryName);
+
+	/**
+	 * Returns the repository model for the specified repository. This method
+	 * does not consider user access permissions.
+	 *
+	 * @param repositoryName
+	 * @return repository model or null
+	 */
+	RepositoryModel getRepositoryModel(String repositoryName);
+
+	/**
+	 * Returns the star count of the repository.
+	 *
+	 * @param repository
+	 * @return the star count
+	 */
+	long getStarCount(RepositoryModel repository);
+
+	/**
+	 * Determines if this server has the requested repository.
+	 *
+	 * @param n
+	 * @return true if the repository exists
+	 */
+	boolean hasRepository(String repositoryName);
+
+	/**
+	 * Determines if this server has the requested repository.
+	 *
+	 * @param n
+	 * @param caseInsensitive
+	 * @return true if the repository exists
+	 */
+	boolean hasRepository(String repositoryName, boolean caseSensitiveCheck);
+
+	/**
+	 * Determines if the specified user has a fork of the specified origin
+	 * repository.
+	 *
+	 * @param username
+	 * @param origin
+	 * @return true the if the user has a fork
+	 */
+	boolean hasFork(String username, String origin);
+
+	/**
+	 * Gets the name of a user's fork of the specified origin
+	 * repository.
+	 *
+	 * @param username
+	 * @param origin
+	 * @return the name of the user's fork, null otherwise
+	 */
+	String getFork(String username, String origin);
+
+	/**
+	 * Returns the fork network for a repository by traversing up the fork graph
+	 * to discover the root and then down through all children of the root node.
+	 *
+	 * @param repository
+	 * @return a ForkModel
+	 */
+	ForkModel getForkNetwork(String repository);
+
+	/**
+	 * Updates the last changed fields and optionally calculates the size of the
+	 * repository.  Gitblit caches the repository sizes to reduce the performance
+	 * penalty of recursive calculation. The cache is updated if the repository
+	 * has been changed since the last calculation.
+	 *
+	 * @param model
+	 * @return size in bytes of the repository
+	 */
+	long updateLastChangeFields(Repository r, RepositoryModel model);
+
+	/**
+	 * Returns the metrics for the default branch of the specified repository.
+	 * This method builds a metrics cache. The cache is updated if the
+	 * repository is updated. A new copy of the metrics list is returned on each
+	 * call so that modifications to the list are non-destructive.
+	 *
+	 * @param model
+	 * @param repository
+	 * @return a new array list of metrics
+	 */
+	List<Metric> getRepositoryDefaultMetrics(RepositoryModel model, Repository repository);
+
+	/**
+	 * Creates/updates the repository model keyed by reopsitoryName. Saves all
+	 * repository settings in .git/config. This method allows for renaming
+	 * repositories and will update user access permissions accordingly.
+	 *
+	 * All repositories created by this method are bare and automatically have
+	 * .git appended to their names, which is the standard convention for bare
+	 * repositories.
+	 *
+	 * @param repositoryName
+	 * @param repository
+	 * @param isCreate
+	 * @throws GitBlitException
+	 */
+	void updateRepositoryModel(String repositoryName, RepositoryModel repository, boolean isCreate)
+			throws GitBlitException;
+
+	/**
+	 * Updates the Gitblit configuration for the specified repository.
+	 *
+	 * @param r
+	 *            the Git repository
+	 * @param repository
+	 *            the Gitblit repository model
+	 */
+	void updateConfiguration(Repository r, RepositoryModel repository);
+
+	/**
+	 * Deletes the repository from the file system and removes the repository
+	 * permission from all repository users.
+	 *
+	 * @param model
+	 * @return true if successful
+	 */
+	boolean deleteRepositoryModel(RepositoryModel model);
+
+	/**
+	 * Deletes the repository from the file system and removes the repository
+	 * permission from all repository users.
+	 *
+	 * @param repositoryName
+	 * @return true if successful
+	 */
+	boolean deleteRepository(String repositoryName);
+
+	/**
+	 * Returns the list of all Groovy push hook scripts. Script files must have
+	 * .groovy extension
+	 *
+	 * @return list of available hook scripts
+	 */
+	List<String> getAllScripts();
+
+	/**
+	 * Returns the list of pre-receive scripts the repository inherited from the
+	 * global settings and team affiliations.
+	 *
+	 * @param repository
+	 *            if null only the globally specified scripts are returned
+	 * @return a list of scripts
+	 */
+	List<String> getPreReceiveScriptsInherited(RepositoryModel repository);
+
+	/**
+	 * Returns the list of all available Groovy pre-receive push hook scripts
+	 * that are not already inherited by the repository. Script files must have
+	 * .groovy extension
+	 *
+	 * @param repository
+	 *            optional parameter
+	 * @return list of available hook scripts
+	 */
+	List<String> getPreReceiveScriptsUnused(RepositoryModel repository);
+
+	/**
+	 * Returns the list of post-receive scripts the repository inherited from
+	 * the global settings and team affiliations.
+	 *
+	 * @param repository
+	 *            if null only the globally specified scripts are returned
+	 * @return a list of scripts
+	 */
+	List<String> getPostReceiveScriptsInherited(RepositoryModel repository);
+
+	/**
+	 * Returns the list of unused Groovy post-receive push hook scripts that are
+	 * not already inherited by the repository. Script files must have .groovy
+	 * extension
+	 *
+	 * @param repository
+	 *            optional parameter
+	 * @return list of available hook scripts
+	 */
+	List<String> getPostReceiveScriptsUnused(RepositoryModel repository);
+
+	/**
+	 * Search the specified repositories using the Lucene query.
+	 *
+	 * @param query
+	 * @param page
+	 * @param pageSize
+	 * @param repositories
+	 * @return
+	 */
+	List<SearchResult> search(String query, int page, int pageSize, List<String> repositories);
+
+	/**
+	 *
+	 * @return true if we are running the gc executor
+	 */
+	boolean isCollectingGarbage();
+
+	/**
+	 * Returns true if Gitblit is actively collecting garbage in this repository.
+	 *
+	 * @param repositoryName
+	 * @return true if actively collecting garbage
+	 */
+	boolean isCollectingGarbage(String repositoryName);
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/manager/IRuntimeManager.java b/src/main/java/com/gitblit/manager/IRuntimeManager.java
new file mode 100644
index 0000000..178b93c
--- /dev/null
+++ b/src/main/java/com/gitblit/manager/IRuntimeManager.java
@@ -0,0 +1,103 @@
+/*
+ * 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.util.Date;
+import java.util.Map;
+import java.util.TimeZone;
+
+import com.gitblit.IStoredSettings;
+import com.gitblit.models.ServerSettings;
+import com.gitblit.models.ServerStatus;
+
+public interface IRuntimeManager {
+
+	void setBaseFolder(File folder);
+
+	File getBaseFolder();
+
+	/**
+	 * Returns the preferred timezone for the Gitblit instance.
+	 *
+	 * @return a timezone
+	 */
+	TimeZone getTimezone();
+
+	/**
+	 * Determine if this Gitblit instance is actively serving git repositories
+	 * or if it is merely a repository viewer.
+	 *
+	 * @return true if Gitblit is serving repositories
+	 */
+	boolean isServingRepositories();
+
+	/**
+	 * Determine if this Gitblit instance is running in debug mode
+	 *
+	 * @return true if Gitblit is running in debug mode
+	 */
+	boolean isDebugMode();
+
+	/**
+	 * Returns the boot date of the Gitblit server.
+	 *
+	 * @return the boot date of Gitblit
+	 */
+	Date getBootDate();
+
+	ServerStatus getStatus();
+
+	/**
+	 * Returns the descriptions/comments of the Gitblit config settings.
+	 *
+	 * @return SettingsModel
+	 */
+	ServerSettings getSettingsModel();
+
+	/**
+	 * Returns the file object for the specified configuration key.
+	 *
+	 * @return the file
+	 */
+	File getFileOrFolder(String key, String defaultFileOrFolder);
+
+	/**
+	 * Returns the file object which may have it's base-path determined by
+	 * environment variables for running on a cloud hosting service. All Gitblit
+	 * file or folder retrievals are (at least initially) funneled through this
+	 * method so it is the correct point to globally override/alter filesystem
+	 * access based on environment or some other indicator.
+	 *
+	 * @return the file
+	 */
+	File getFileOrFolder(String fileOrFolder);
+
+	/**
+	 * Returns the runtime settings.
+	 *
+	 * @return settings
+	 */
+	IStoredSettings getSettings();
+
+	/**
+	 * Updates the runtime settings.
+	 *
+	 * @param settings
+	 * @return true if the update succeeded
+	 */
+	boolean updateSettings(Map<String, String> updatedSettings);
+}
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/manager/ISessionManager.java b/src/main/java/com/gitblit/manager/ISessionManager.java
new file mode 100644
index 0000000..09e306a
--- /dev/null
+++ b/src/main/java/com/gitblit/manager/ISessionManager.java
@@ -0,0 +1,64 @@
+/*
+ * 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 javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.gitblit.models.UserModel;
+
+public interface ISessionManager {
+
+	/**
+	 * Authenticate a user based on HTTP request parameters.
+	 *
+	 * Authentication by X509Certificate is tried first and then by cookie.
+	 *
+	 * @param httpRequest
+	 * @return a user object or null
+	 */
+	UserModel authenticate(HttpServletRequest httpRequest);
+
+	/**
+	 * Authenticate a user based on HTTP request parameters.
+	 *
+	 * Authentication by X509Certificate, servlet container principal, cookie,
+	 * and BASIC header.
+	 *
+	 * @param httpRequest
+	 * @param requiresCertificate
+	 * @return a user object or null
+	 */
+	UserModel authenticate(HttpServletRequest httpRequest, boolean requiresCertificate);
+
+	UserModel authenticate(String username, char[] password);
+
+	/**
+	 * Sets a cookie for the specified user.
+	 *
+	 * @param response
+	 * @param user
+	 */
+	void setCookie(HttpServletResponse response, UserModel user);
+
+	/**
+	 * Logout a user.
+	 *
+	 * @param user
+	 */
+	void logout(HttpServletResponse response, UserModel user);
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/manager/IUserManager.java b/src/main/java/com/gitblit/manager/IUserManager.java
new file mode 100644
index 0000000..3ce1e74
--- /dev/null
+++ b/src/main/java/com/gitblit/manager/IUserManager.java
@@ -0,0 +1,280 @@
+/*
+ * 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.util.Collection;
+import java.util.List;
+
+import com.gitblit.models.TeamModel;
+import com.gitblit.models.UserModel;
+
+public interface IUserManager {
+
+	boolean supportsAddUser();
+
+	/**
+	 * Does the user service support changes to credentials?
+	 *
+	 * @return true or false
+	 * @since 1.0.0
+	 */
+	boolean supportsCredentialChanges(UserModel user);
+
+	/**
+	 * Returns true if the user's display name can be changed.
+	 *
+	 * @param user
+	 * @return true if the user service supports display name changes
+	 */
+	boolean supportsDisplayNameChanges(UserModel user);
+
+	/**
+	 * Returns true if the user's email address can be changed.
+	 *
+	 * @param user
+	 * @return true if the user service supports email address changes
+	 */
+	boolean supportsEmailAddressChanges(UserModel user);
+
+	/**
+	 * Returns true if the user's team memberships can be changed.
+	 *
+	 * @param user
+	 * @return true if the user service supports team membership changes
+	 */
+	boolean supportsTeamMembershipChanges(UserModel user);
+
+	/**
+	 * Does the user service support cookie authentication?
+	 *
+	 * @return true or false
+	 */
+	boolean supportsCookies();
+
+	/**
+	 * Returns the cookie value for the specified user.
+	 *
+	 * @param model
+	 * @return cookie value
+	 */
+	String getCookie(UserModel model);
+
+	/**
+	 * Authenticate a user based on their cookie.
+	 *
+	 * @param cookie
+	 * @return a user object or null
+	 */
+	UserModel authenticate(char[] cookie);
+
+	/**
+	 * Authenticate a user based on a username and password.
+	 *
+	 * @param username
+	 * @param password
+	 * @return a user object or null
+	 */
+	UserModel authenticate(String username, char[] password);
+
+	/**
+	 * Logout a user.
+	 *
+	 * @param user
+	 */
+	void logout(UserModel user);
+
+	/**
+	 * Retrieve the user object for the specified username.
+	 *
+	 * @param username
+	 * @return a user object or null
+	 */
+	UserModel getUserModel(String username);
+
+	/**
+	 * Updates/writes a complete user object.
+	 *
+	 * @param model
+	 * @return true if update is successful
+	 */
+	boolean updateUserModel(UserModel model);
+
+	/**
+	 * Updates/writes all specified user objects.
+	 *
+	 * @param models a list of user models
+	 * @return true if update is successful
+	 * @since 1.2.0
+	 */
+	boolean updateUserModels(Collection<UserModel> models);
+
+	/**
+	 * Adds/updates a user object keyed by username. This method allows for
+	 * renaming a user.
+	 *
+	 * @param username
+	 *            the old username
+	 * @param model
+	 *            the user object to use for username
+	 * @return true if update is successful
+	 */
+	boolean updateUserModel(String username, UserModel model);
+
+	/**
+	 * Deletes the user object from the user service.
+	 *
+	 * @param model
+	 * @return true if successful
+	 */
+	boolean deleteUserModel(UserModel model);
+
+	/**
+	 * Delete the user object with the specified username
+	 *
+	 * @param username
+	 * @return true if successful
+	 */
+	boolean deleteUser(String username);
+
+	/**
+	 * Returns the list of all users available to the login service.
+	 *
+	 * @return list of all usernames
+	 */
+	List<String> getAllUsernames();
+
+	/**
+	 * Returns the list of all users available to the login service.
+	 *
+	 * @return list of all users
+	 * @since 0.8.0
+	 */
+	List<UserModel> getAllUsers();
+
+	/**
+	 * Returns the list of all teams available to the login service.
+	 *
+	 * @return list of all teams
+	 * @since 0.8.0
+	 */
+	List<String> getAllTeamNames();
+
+	/**
+	 * Returns the list of all teams available to the login service.
+	 *
+	 * @return list of all teams
+	 * @since 0.8.0
+	 */
+	List<TeamModel> getAllTeams();
+
+	/**
+	 * Returns the list of all users who are allowed to bypass the access
+	 * restriction placed on the specified repository.
+	 *
+	 * @param role
+	 *            the repository name
+	 * @return list of all usernames that can bypass the access restriction
+	 * @since 0.8.0
+	 */
+	List<String> getTeamnamesForRepositoryRole(String role);
+
+	/**
+	 * Retrieve the team object for the specified team name.
+	 *
+	 * @param teamname
+	 * @return a team object or null
+	 * @since 0.8.0
+	 */
+	TeamModel getTeamModel(String teamname);
+
+	/**
+	 * Updates/writes a complete team object.
+	 *
+	 * @param model
+	 * @return true if update is successful
+	 * @since 0.8.0
+	 */
+	boolean updateTeamModel(TeamModel model);
+
+	/**
+	 * Updates/writes all specified team objects.
+	 *
+	 * @param models a list of team models
+	 * @return true if update is successful
+	 * @since 1.2.0
+	 */
+	boolean updateTeamModels(Collection<TeamModel> models);
+
+	/**
+	 * Updates/writes and replaces a complete team object keyed by teamname.
+	 * This method allows for renaming a team.
+	 *
+	 * @param teamname
+	 *            the old teamname
+	 * @param model
+	 *            the team object to use for teamname
+	 * @return true if update is successful
+	 * @since 0.8.0
+	 */
+	boolean updateTeamModel(String teamname, TeamModel model);
+
+	/**
+	 * Deletes the team object from the user service.
+	 *
+	 * @param model
+	 * @return true if successful
+	 * @since 0.8.0
+	 */
+	boolean deleteTeamModel(TeamModel model);
+
+	/**
+	 * Delete the team object with the specified teamname
+	 *
+	 * @param teamname
+	 * @return true if successful
+	 * @since 0.8.0
+	 */
+	boolean deleteTeam(String teamname);
+
+	/**
+	 * Returns the list of all users who are allowed to bypass the access
+	 * restriction placed on the specified repository.
+	 *
+	 * @param role
+	 *            the repository name
+	 * @return list of all usernames that can bypass the access restriction
+	 * @since 0.8.0
+	 */
+	List<String> getUsernamesForRepositoryRole(String role);
+
+	/**
+	 * Renames a repository role.
+	 *
+	 * @param oldRole
+	 * @param newRole
+	 * @return true if successful
+	 */
+	boolean renameRepositoryRole(String oldRole, String newRole);
+
+	/**
+	 * Removes a repository role from all users.
+	 *
+	 * @param role
+	 * @return true if successful
+	 */
+	boolean deleteRepositoryRole(String role);
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/utils/ActivityUtils.java b/src/main/java/com/gitblit/utils/ActivityUtils.java
index ddd7e37..3a54d33 100644
--- a/src/main/java/com/gitblit/utils/ActivityUtils.java
+++ b/src/main/java/com/gitblit/utils/ActivityUtils.java
@@ -34,8 +34,9 @@
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.Repository;
 
-import com.gitblit.GitBlit;
+import com.gitblit.IStoredSettings;
 import com.gitblit.Keys;
+import com.gitblit.manager.IRepositoryManager;
 import com.gitblit.models.Activity;
 import com.gitblit.models.GravatarProfile;
 import com.gitblit.models.RefModel;
@@ -55,6 +56,10 @@
 	 * Gets the recent activity from the repositories for the last daysBack days
 	 * on the specified branch.
 	 *
+	 * @param settings
+	 *            the runtime settings
+	 * @param repositoryManager
+	 *            the repository manager
 	 * @param models
 	 *            the list of repositories to query
 	 * @param daysBack
@@ -66,8 +71,13 @@
 	 *            the timezone for aggregating commits
 	 * @return
 	 */
-	public static List<Activity> getRecentActivity(List<RepositoryModel> models, int daysBack,
-			String objectId, TimeZone timezone) {
+	public static List<Activity> getRecentActivity(
+					IStoredSettings settings,
+					IRepositoryManager repositoryManager,
+					List<RepositoryModel> models,
+					int daysBack,
+					String objectId,
+					TimeZone timezone) {
 
 		// Activity panel shows last daysBack of activity across all
 		// repositories.
@@ -82,7 +92,7 @@
 
 		// aggregate author exclusions
 		Set<String> authorExclusions = new TreeSet<String>();
-		authorExclusions.addAll(GitBlit.getStrings(Keys.web.metricAuthorExclusions));
+		authorExclusions.addAll(settings.getStrings(Keys.web.metricAuthorExclusions));
 		for (RepositoryModel model : models) {
 			if (!ArrayUtils.isEmpty(model.metricAuthorExclusions)) {
 				authorExclusions.addAll(model.metricAuthorExclusions);
@@ -99,8 +109,7 @@
 				if (model.isCollectingGarbage) {
 					continue;
 				}
-				Repository repository = GitBlit.self()
-						.getRepository(model.name);
+				Repository repository = repositoryManager.getRepository(model.name);
 				List<String> branches = new ArrayList<String>();
 				if (StringUtils.isEmpty(objectId)) {
 					for (RefModel local : JGitUtils.getLocalBranches(
diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp.java b/src/main/java/com/gitblit/wicket/GitBlitWebApp.java
index 1a46a1f..46a3b06 100644
--- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.java
+++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.java
@@ -31,6 +31,14 @@
 import com.gitblit.GitBlit;
 import com.gitblit.IStoredSettings;
 import com.gitblit.Keys;
+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.ISessionManager;
+import com.gitblit.manager.IUserManager;
 import com.gitblit.utils.StringUtils;
 import com.gitblit.wicket.pages.ActivityPage;
 import com.gitblit.wicket.pages.BlamePage;
@@ -80,7 +88,7 @@
 	public void init() {
 		super.init();
 
-		settings = GitBlit.getSettings();
+		settings = runtime().getSettings();
 
 		// Setup page authorization mechanism
 		boolean useAuthentication = settings.getBoolean(Keys.web.authenticateViewPages, false)
@@ -197,7 +205,7 @@
 	 * @return true if Gitblit is running in debug mode
 	 */
 	public boolean isDebugMode() {
-		return GitBlit.isDebugMode();
+		return runtime().isDebugMode();
 	}
 
 	/*
@@ -205,47 +213,47 @@
 	 * step towards modularization across multiple commits.
 	 */
 	public Date getBootDate() {
-		return GitBlit.getBootDate();
+		return runtime().getBootDate();
 	}
 
 	public Date getLastActivityDate() {
-		return GitBlit.getLastActivityDate();
+		return repositories().getLastActivityDate();
 	}
 
-	public GitBlit runtime() {
-		return GitBlit.self();
+	public IRuntimeManager runtime() {
+		return GitBlit.getManager(IRuntimeManager.class);
 	}
 
-	public GitBlit mail() {
-		return GitBlit.self();
+	public INotificationManager notifier() {
+		return GitBlit.getManager(INotificationManager.class);
 	}
 
-	public GitBlit users() {
-		return GitBlit.self();
+	public IUserManager users() {
+		return GitBlit.getManager(IUserManager.class);
 	}
 
-	public GitBlit session() {
-		return GitBlit.self();
+	public ISessionManager session() {
+		return GitBlit.getManager(ISessionManager.class);
 	}
 
-	public GitBlit repositories() {
-		return GitBlit.self();
+	public IRepositoryManager repositories() {
+		return GitBlit.getManager(IRepositoryManager.class);
 	}
 
-	public GitBlit projects() {
-		return GitBlit.self();
+	public IProjectManager projects() {
+		return GitBlit.getManager(IProjectManager.class);
 	}
 
-	public GitBlit federation() {
-		return GitBlit.self();
+	public IFederationManager federation() {
+		return GitBlit.getManager(IFederationManager.class);
 	}
 
-	public GitBlit gitblit() {
-		return GitBlit.self();
+	public IGitblitManager gitblit() {
+		return GitBlit.getManager(IGitblitManager.class);
 	}
 
 	public TimeZone getTimezone() {
-		return GitBlit.getTimezone();
+		return runtime().getTimezone();
 	}
 
 	@Override
diff --git a/src/main/java/com/gitblit/wicket/GitblitWicketFilter.java b/src/main/java/com/gitblit/wicket/GitblitWicketFilter.java
index 0f639c7..fbe68fe 100644
--- a/src/main/java/com/gitblit/wicket/GitblitWicketFilter.java
+++ b/src/main/java/com/gitblit/wicket/GitblitWicketFilter.java
@@ -25,7 +25,11 @@
 import org.eclipse.jgit.revwalk.RevCommit;
 
 import com.gitblit.GitBlit;
+import com.gitblit.IStoredSettings;
 import com.gitblit.Keys;
+import com.gitblit.manager.IProjectManager;
+import com.gitblit.manager.IRepositoryManager;
+import com.gitblit.manager.IRuntimeManager;
 import com.gitblit.models.ProjectModel;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.utils.JGitUtils;
@@ -77,18 +81,23 @@
 			commitId = servletRequest.getParameter("h");
 		}
 
-		repo = repo.replace("%2f", "/").replace("%2F", "/").replace(GitBlit.getChar(Keys.web.forwardSlashCharacter, '/'), '/');
+		IRuntimeManager runtimeManager = GitBlit.getManager(IRuntimeManager.class);
+		IStoredSettings settings = runtimeManager.getSettings();
+		IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);
+		IProjectManager projectManager = GitBlit.getManager(IProjectManager.class);
+
+		repo = repo.replace("%2f", "/").replace("%2F", "/").replace(settings.getChar(Keys.web.forwardSlashCharacter, '/'), '/');
 
 		GitBlitWebApp app = (GitBlitWebApp) getWebApplication();
-		int expires = GitBlit.getInteger(Keys.web.pageCacheExpires, 0);
+		int expires = settings.getInteger(Keys.web.pageCacheExpires, 0);
 		if (!StringUtils.isEmpty(page) && app.isCacheablePage(page) && expires > 0) {
 			// page can be cached by the browser
 			CacheControl cacheControl = app.getCacheControl(page);
-			Date bootDate = GitBlit.getBootDate();
+			Date bootDate = runtimeManager.getBootDate();
 			switch (cacheControl.value()) {
 			case ACTIVITY:
 				// returns the last activity date of the server
-				Date activityDate = GitBlit.getLastActivityDate();
+				Date activityDate = repositoryManager.getLastActivityDate();
 				if (activityDate != null) {
 					return activityDate.after(bootDate) ? activityDate.getTime() : bootDate.getTime();
 				}
@@ -98,7 +107,7 @@
 				return bootDate.getTime();
 			case PROJECT:
 				// return the latest change date for the project OR the boot date
-				ProjectModel project = GitBlit.self().getProjectModel(StringUtils.getRootPath(repo));
+				ProjectModel project = projectManager.getProjectModel(StringUtils.getRootPath(repo));
 				if (project != null) {
 					return project.lastChange.after(bootDate) ? project.lastChange.getTime() : bootDate.getTime();
 				}
@@ -106,7 +115,7 @@
 			case REPOSITORY:
 				// return the lastest change date for the repository OR the boot
 				// date, whichever is latest
-				RepositoryModel repository = GitBlit.self().getRepositoryModel(repo);
+				RepositoryModel repository = repositoryManager.getRepositoryModel(repo);
 				if (repository != null && repository.lastChange != null) {
 					return repository.lastChange.after(bootDate) ? repository.lastChange.getTime() : bootDate.getTime();
 				}
@@ -121,7 +130,7 @@
 					Repository r = null;
 					try {
 						// return the timestamp of the associated commit
-						r = GitBlit.self().getRepository(repo);
+						r = repositoryManager.getRepository(repo);
 						if (r != null) {
 							RevCommit commit = JGitUtils.getCommit(r, commitId);
 							if (commit != null) {
diff --git a/src/main/java/com/gitblit/wicket/pages/ActivityPage.java b/src/main/java/com/gitblit/wicket/pages/ActivityPage.java
index 5e7332f..070caf3 100644
--- a/src/main/java/com/gitblit/wicket/pages/ActivityPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/ActivityPage.java
@@ -71,8 +71,13 @@
 
 		// determine repositories to view and retrieve the activity
 		List<RepositoryModel> models = getRepositories(params);
-		List<Activity> recentActivity = ActivityUtils.getRecentActivity(models,
-				daysBack, objectId, getTimeZone());
+		List<Activity> recentActivity = ActivityUtils.getRecentActivity(
+				app().settings(),
+				app().repositories(),
+				models,
+				daysBack,
+				objectId,
+				getTimeZone());
 
 		String headerPattern;
 		if (daysBack == 1) {
diff --git a/src/main/java/com/gitblit/wicket/pages/ChangePasswordPage.java b/src/main/java/com/gitblit/wicket/pages/ChangePasswordPage.java
index 3e308f1..a3c1ece 100644
--- a/src/main/java/com/gitblit/wicket/pages/ChangePasswordPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/ChangePasswordPage.java
@@ -97,10 +97,10 @@
 
 				user.password = password;
 				try {
-					app().users().updateUserModel(user.username, user, false);
+					app().gitblit().updateUserModel(user.username, user, false);
 					if (app().settings().getBoolean(Keys.web.allowCookieAuthentication, false)) {
 						WebResponse response = (WebResponse) getRequestCycle().getResponse();
-						app().session().setCookie(response, user);
+						app().session().setCookie(response.getHttpServletResponse(), user);
 					}
 				} catch (GitBlitException e) {
 					error(e.getMessage());
diff --git a/src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java b/src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java
index 76b34f3..a986fd5 100644
--- a/src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java
@@ -169,7 +169,7 @@
 		final RegistrantPermissionsPanel usersPalette = new RegistrantPermissionsPanel("users",
 				RegistrantType.USER, app().users().getAllUsernames(), repositoryUsers, getAccessPermissions());
 		final RegistrantPermissionsPanel teamsPalette = new RegistrantPermissionsPanel("teams",
-				RegistrantType.TEAM, app().users().getAllTeamnames(), repositoryTeams, getAccessPermissions());
+				RegistrantType.TEAM, app().users().getAllTeamNames(), repositoryTeams, getAccessPermissions());
 
 		// owners palette
 		List<String> owners = new ArrayList<String>(repositoryModel.owners);
diff --git a/src/main/java/com/gitblit/wicket/pages/EditTeamPage.java b/src/main/java/com/gitblit/wicket/pages/EditTeamPage.java
index 32905c9..4f548d4 100644
--- a/src/main/java/com/gitblit/wicket/pages/EditTeamPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/EditTeamPage.java
@@ -196,7 +196,7 @@
 				teamModel.postReceiveScripts.addAll(postReceiveScripts);
 
 				try {
-					app().users().updateTeamModel(oldName, teamModel, isCreate);
+					app().gitblit().updateTeamModel(oldName, teamModel, isCreate);
 				} catch (GitBlitException e) {
 					error(e.getMessage());
 					return;
diff --git a/src/main/java/com/gitblit/wicket/pages/EditUserPage.java b/src/main/java/com/gitblit/wicket/pages/EditUserPage.java
index d0e52f4..b2d3d3b 100644
--- a/src/main/java/com/gitblit/wicket/pages/EditUserPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/EditUserPage.java
@@ -110,7 +110,7 @@
 
 		final Palette<String> teams = new Palette<String>("teams", new ListModel<String>(
 				new ArrayList<String>(userTeams)), new CollectionModel<String>(app().users()
-				.getAllTeamnames()), new StringChoiceRenderer(), 10, false);
+				.getAllTeamNames()), new StringChoiceRenderer(), 10, false);
 		Form<UserModel> form = new Form<UserModel>("editForm", model) {
 
 			private static final long serialVersionUID = 1L;
@@ -192,7 +192,7 @@
 				}
 
 				try {
-					app().users().updateUserModel(oldName, userModel, isCreate);
+					app().gitblit().updateUserModel(oldName, userModel, isCreate);
 				} catch (GitBlitException e) {
 					error(e.getMessage());
 					return;
diff --git a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java
index 9e4d355..06eb72a 100644
--- a/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java
@@ -58,7 +58,7 @@
 		}
 
 		HttpServletRequest req = ((WebRequest) getRequest()).getHttpServletRequest();
-		List<RepositoryUrl> repositoryUrls = app().repositories().getRepositoryUrls(req, user, repository);
+		List<RepositoryUrl> repositoryUrls = app().gitblit().getRepositoryUrls(req, user, repository);
 		RepositoryUrl primaryUrl = repositoryUrls.size() == 0 ? null : repositoryUrls.get(0);
 		String url = primaryUrl != null ? primaryUrl.url : "";
 
diff --git a/src/main/java/com/gitblit/wicket/pages/LogoutPage.java b/src/main/java/com/gitblit/wicket/pages/LogoutPage.java
index f7be99a..d99c146 100644
--- a/src/main/java/com/gitblit/wicket/pages/LogoutPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/LogoutPage.java
@@ -27,8 +27,7 @@
 		super();
 		GitBlitWebSession session = GitBlitWebSession.get();
 		UserModel user = session.getUser();
-		app().session().setCookie((WebResponse) getResponse(), null);
-		app().session().logout(user);
+		app().session().logout(((WebResponse) getResponse()).getHttpServletResponse(), user);
 		session.invalidate();
 
 		/*
diff --git a/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java
index e19c195..0552c30 100644
--- a/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java
@@ -139,7 +139,7 @@
 				UserRepositoryPreferences prefs = user.getPreferences().getRepositoryPreferences(getRepositoryModel().name);
 				prefs.starred = star;
 				try {
-					app().users().updateUserModel(user.username, user, false);
+					app().gitblit().updateUserModel(user.username, user, false);
 				} catch (GitBlitException e) {
 					logger.error("Failed to update user " + user.username, e);
 					error(getString("gb.failedToUpdateUser"), false);
diff --git a/src/main/java/com/gitblit/wicket/pages/RootPage.java b/src/main/java/com/gitblit/wicket/pages/RootPage.java
index 98a8cb3..1a43bf1 100644
--- a/src/main/java/com/gitblit/wicket/pages/RootPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/RootPage.java
@@ -252,7 +252,7 @@
 			// Set Cookie
 			if (app().settings().getBoolean(Keys.web.allowCookieAuthentication, false)) {
 				WebResponse response = (WebResponse) getRequestCycle().getResponse();
-				app().session().setCookie(response, user);
+				app().session().setCookie(response.getHttpServletResponse(), user);
 			}
 
 			if (!session.continueRequest()) {
diff --git a/src/main/java/com/gitblit/wicket/pages/SessionPage.java b/src/main/java/com/gitblit/wicket/pages/SessionPage.java
index 886dd6e..a10102f 100644
--- a/src/main/java/com/gitblit/wicket/pages/SessionPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/SessionPage.java
@@ -70,7 +70,7 @@
 
 			// Set Cookie
 			WebResponse response = (WebResponse) getRequestCycle().getResponse();
-			app().session().setCookie(response, user);
+			app().session().setCookie(response.getHttpServletResponse(), user);
 
 			session.continueRequest();
 		}
diff --git a/src/test/java/com/gitblit/tests/ActivityTest.java b/src/test/java/com/gitblit/tests/ActivityTest.java
index 3c8d990..b7382b8 100644
--- a/src/test/java/com/gitblit/tests/ActivityTest.java
+++ b/src/test/java/com/gitblit/tests/ActivityTest.java
@@ -15,8 +15,6 @@
  */
 package com.gitblit.tests;
 
-import static org.junit.Assert.assertEquals;
-
 import java.io.IOException;
 
 import org.junit.Test;
@@ -24,7 +22,7 @@
 import com.gitblit.models.GravatarProfile;
 import com.gitblit.utils.ActivityUtils;
 
-public class ActivityTest {
+public class ActivityTest extends GitblitUnitTest {
 
 	@Test
 	public void testGravatarProfile() throws IOException {
diff --git a/src/test/java/com/gitblit/tests/ArrayUtilsTest.java b/src/test/java/com/gitblit/tests/ArrayUtilsTest.java
index 45964a1..9308978 100644
--- a/src/test/java/com/gitblit/tests/ArrayUtilsTest.java
+++ b/src/test/java/com/gitblit/tests/ArrayUtilsTest.java
@@ -15,9 +15,6 @@
  */
 package com.gitblit.tests;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
@@ -28,7 +25,7 @@
 
 import com.gitblit.utils.ArrayUtils;
 
-public class ArrayUtilsTest {
+public class ArrayUtilsTest extends GitblitUnitTest {
 
 	@Test
 	public void testArrays() {
diff --git a/src/test/java/com/gitblit/tests/Base64Test.java b/src/test/java/com/gitblit/tests/Base64Test.java
index 2962c36..16ca2ad 100644
--- a/src/test/java/com/gitblit/tests/Base64Test.java
+++ b/src/test/java/com/gitblit/tests/Base64Test.java
@@ -15,13 +15,11 @@
  */
 package com.gitblit.tests;
 
-import static org.junit.Assert.assertEquals;
-
 import org.junit.Test;
 
 import com.gitblit.utils.Base64;
 
-public class Base64Test {
+public class Base64Test extends GitblitUnitTest {
 
 	@Test
 	public void testBase64() {
diff --git a/src/test/java/com/gitblit/tests/ByteFormatTest.java b/src/test/java/com/gitblit/tests/ByteFormatTest.java
index ec3b395..a6a9870 100644
--- a/src/test/java/com/gitblit/tests/ByteFormatTest.java
+++ b/src/test/java/com/gitblit/tests/ByteFormatTest.java
@@ -15,15 +15,13 @@
  */
 package com.gitblit.tests;
 
-import static org.junit.Assert.assertEquals;
-
 import java.util.Locale;
 
 import org.junit.Test;
 
 import com.gitblit.utils.ByteFormat;
 
-public class ByteFormatTest {
+public class ByteFormatTest extends GitblitUnitTest {
 
 	@Test
 	public void testByteFormat() throws Exception {
diff --git a/src/test/java/com/gitblit/tests/DiffUtilsTest.java b/src/test/java/com/gitblit/tests/DiffUtilsTest.java
index 48cd19f..9d627b8 100644
--- a/src/test/java/com/gitblit/tests/DiffUtilsTest.java
+++ b/src/test/java/com/gitblit/tests/DiffUtilsTest.java
@@ -15,9 +15,6 @@
  */
 package com.gitblit.tests;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
 import java.util.List;
 
 import org.eclipse.jgit.lib.Repository;
@@ -29,7 +26,7 @@
 import com.gitblit.utils.DiffUtils.DiffOutputType;
 import com.gitblit.utils.JGitUtils;
 
-public class DiffUtilsTest {
+public class DiffUtilsTest extends GitblitUnitTest {
 
 	@Test
 	public void testDiffOutputTypes() throws Exception {
diff --git a/src/test/java/com/gitblit/tests/FanoutServiceTest.java b/src/test/java/com/gitblit/tests/FanoutServiceTest.java
index 056a6c3..5ee0ac9 100644
--- a/src/test/java/com/gitblit/tests/FanoutServiceTest.java
+++ b/src/test/java/com/gitblit/tests/FanoutServiceTest.java
@@ -15,8 +15,6 @@
  */
 package com.gitblit.tests;
 
-import static org.junit.Assert.assertEquals;
-
 import java.text.MessageFormat;
 import java.util.Date;
 import java.util.Map;
@@ -31,7 +29,7 @@
 import com.gitblit.fanout.FanoutService;
 import com.gitblit.fanout.FanoutSocketService;
 
-public class FanoutServiceTest {
+public class FanoutServiceTest extends GitblitUnitTest {
 
 	int fanoutPort = FanoutService.DEFAULT_PORT;
 
diff --git a/src/test/java/com/gitblit/tests/FederationTests.java b/src/test/java/com/gitblit/tests/FederationTests.java
index 688ae28..144fe3d 100644
--- a/src/test/java/com/gitblit/tests/FederationTests.java
+++ b/src/test/java/com/gitblit/tests/FederationTests.java
@@ -15,10 +15,6 @@
  */
 package com.gitblit.tests;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
@@ -42,7 +38,7 @@
 import com.gitblit.utils.JsonUtils;
 import com.gitblit.utils.RpcUtils;
 
-public class FederationTests {
+public class FederationTests extends GitblitUnitTest {
 
 	String url = GitBlitSuite.url;
 	String account = GitBlitSuite.account;
diff --git a/src/test/java/com/gitblit/tests/FileUtilsTest.java b/src/test/java/com/gitblit/tests/FileUtilsTest.java
index f345690..fd97084 100644
--- a/src/test/java/com/gitblit/tests/FileUtilsTest.java
+++ b/src/test/java/com/gitblit/tests/FileUtilsTest.java
@@ -15,16 +15,13 @@
  */
 package com.gitblit.tests;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
 import java.io.File;
 
 import org.junit.Test;
 
 import com.gitblit.utils.FileUtils;
 
-public class FileUtilsTest {
+public class FileUtilsTest extends GitblitUnitTest {
 
 	@Test
 	public void testReadContent() throws Exception {
diff --git a/src/test/java/com/gitblit/tests/GitBlitSuite.java b/src/test/java/com/gitblit/tests/GitBlitSuite.java
index 96787fd..f623032 100644
--- a/src/test/java/com/gitblit/tests/GitBlitSuite.java
+++ b/src/test/java/com/gitblit/tests/GitBlitSuite.java
@@ -36,6 +36,7 @@
 import com.gitblit.GitBlit;
 import com.gitblit.GitBlitException;
 import com.gitblit.GitBlitServer;
+import com.gitblit.manager.IRepositoryManager;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.utils.JGitUtils;
 
@@ -179,9 +180,10 @@
 
 	private static void showRemoteBranches(String repositoryName) {
 		try {
-			RepositoryModel model = GitBlit.self().getRepositoryModel(repositoryName);
+			IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);
+			RepositoryModel model = repositoryManager.getRepositoryModel(repositoryName);
 			model.showRemoteBranches = true;
-			GitBlit.self().updateRepositoryModel(model.name, model, false);
+			repositoryManager.updateRepositoryModel(model.name, model, false);
 		} catch (GitBlitException g) {
 			g.printStackTrace();
 		}
@@ -189,9 +191,10 @@
 
 	private static void automaticallyTagBranchTips(String repositoryName) {
 		try {
-			RepositoryModel model = GitBlit.self().getRepositoryModel(repositoryName);
+			IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class);
+			RepositoryModel model = repositoryManager.getRepositoryModel(repositoryName);
 			model.useIncrementalPushTags = true;
-			GitBlit.self().updateRepositoryModel(model.name, model, false);
+			repositoryManager.updateRepositoryModel(model.name, model, false);
 		} catch (GitBlitException g) {
 			g.printStackTrace();
 		}
diff --git a/src/test/java/com/gitblit/tests/GitBlitTest.java b/src/test/java/com/gitblit/tests/GitBlitTest.java
index 4fb5864..692afb7 100644
--- a/src/test/java/com/gitblit/tests/GitBlitTest.java
+++ b/src/test/java/com/gitblit/tests/GitBlitTest.java
@@ -15,11 +15,6 @@
  */
 package com.gitblit.tests;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
 import java.util.List;
 
 import org.eclipse.jgit.lib.Repository;
@@ -27,48 +22,47 @@
 
 import com.gitblit.Constants.AccessRestrictionType;
 import com.gitblit.FileSettings;
-import com.gitblit.GitBlit;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.UserModel;
 
-public class GitBlitTest {
+public class GitBlitTest extends GitblitUnitTest {
 
 	@Test
 	public void testRepositoryModel() throws Exception {
-		List<String> repositories = GitBlit.self().getRepositoryList();
+		List<String> repositories = repositories().getRepositoryList();
 		assertTrue("Repository list is empty!", repositories.size() > 0);
 		assertTrue(
 				"Missing Helloworld repository!",
 				repositories.contains(GitBlitSuite.getHelloworldRepository().getDirectory()
 						.getName()));
 		Repository r = GitBlitSuite.getHelloworldRepository();
-		RepositoryModel model = GitBlit.self().getRepositoryModel(r.getDirectory().getName());
+		RepositoryModel model = repositories().getRepositoryModel(r.getDirectory().getName());
 		assertTrue("Helloworld model is null!", model != null);
 		assertEquals(GitBlitSuite.getHelloworldRepository().getDirectory().getName(), model.name);
-		assertTrue(GitBlit.self().updateLastChangeFields(r, model) > 22000L);
+		assertTrue(repositories().updateLastChangeFields(r, model) > 22000L);
 		r.close();
 	}
 
 	@Test
 	public void testUserModel() throws Exception {
-		List<String> users = GitBlit.self().getAllUsernames();
+		List<String> users = users().getAllUsernames();
 		assertTrue("No users found!", users.size() > 0);
 		assertTrue("Admin not found", users.contains("admin"));
-		UserModel user = GitBlit.self().getUserModel("admin");
+		UserModel user = users().getUserModel("admin");
 		assertEquals("admin", user.toString());
 		assertTrue("Admin missing #admin role!", user.canAdmin);
 		user.canAdmin = false;
 		assertFalse("Admin should not have #admin!", user.canAdmin);
 		String repository = GitBlitSuite.getHelloworldRepository().getDirectory().getName();
-		RepositoryModel repositoryModel = GitBlit.self().getRepositoryModel(repository);
+		RepositoryModel repositoryModel = repositories().getRepositoryModel(repository);
 		repositoryModel.accessRestriction = AccessRestrictionType.VIEW;
 		assertFalse("Admin can still access repository!",
 				user.canView(repositoryModel));
 		user.addRepositoryPermission(repository);
 		assertTrue("Admin can't access repository!", user.canView(repositoryModel));
-		assertEquals(GitBlit.self().getRepositoryModel(user, "pretend"), null);
-		assertNotNull(GitBlit.self().getRepositoryModel(user, repository));
-		assertTrue(GitBlit.self().getRepositoryModels(user).size() > 0);
+		assertEquals(repositories().getRepositoryModel(user, "pretend"), null);
+		assertNotNull(repositories().getRepositoryModel(user, repository));
+		assertTrue(repositories().getRepositoryModels(user).size() > 0);
 	}
 
 	@Test
@@ -156,34 +150,34 @@
 	@Test
 	public void testGitblitSettings() throws Exception {
 		// These are already tested by above test method.
-		assertTrue(GitBlit.getBoolean("missing", true));
-		assertEquals("default", GitBlit.getString("missing", "default"));
-		assertEquals(10, GitBlit.getInteger("missing", 10));
-		assertEquals(5, GitBlit.getInteger("realm.userService", 5));
+		assertTrue(settings().getBoolean("missing", true));
+		assertEquals("default", settings().getString("missing", "default"));
+		assertEquals(10, settings().getInteger("missing", 10));
+		assertEquals(5, settings().getInteger("realm.userService", 5));
 
-		assertTrue(GitBlit.getBoolean("git.enableGitServlet", false));
-		assertEquals(GitBlitSuite.USERSCONF.getAbsolutePath(), GitBlit.getString("realm.userService", null));
-		assertEquals(5, GitBlit.getInteger("realm.minPasswordLength", 0));
-		List<String> mdExtensions = GitBlit.getStrings("web.markdownExtensions");
+		assertTrue(settings().getBoolean("git.enableGitServlet", false));
+		assertEquals(GitBlitSuite.USERSCONF.getAbsolutePath(), settings().getString("realm.userService", null));
+		assertEquals(5, settings().getInteger("realm.minPasswordLength", 0));
+		List<String> mdExtensions = settings().getStrings("web.markdownExtensions");
 		assertTrue(mdExtensions.size() > 0);
 		assertTrue(mdExtensions.contains("md"));
 
-		List<String> keys = GitBlit.getAllKeys("server");
+		List<String> keys = settings().getAllKeys("server");
 		assertTrue(keys.size() > 0);
 		assertTrue(keys.contains("server.httpsPort"));
 
-		assertTrue(GitBlit.getChar("web.forwardSlashCharacter", ' ') == '/');
-		assertFalse(GitBlit.isDebugMode());
+		assertTrue(settings().getChar("web.forwardSlashCharacter", ' ') == '/');
+		assertFalse(runtime().isDebugMode());
 	}
 
 	@Test
 	public void testAuthentication() throws Exception {
-		assertTrue(GitBlit.self().authenticate("admin", "admin".toCharArray()) != null);
+		assertTrue(session().authenticate("admin", "admin".toCharArray()) != null);
 	}
 
 	@Test
 	public void testRepositories() throws Exception {
-		assertTrue(GitBlit.self().getRepository("missing") == null);
-		assertTrue(GitBlit.self().getRepositoryModel("missing") == null);
+		assertTrue(repositories().getRepository("missing") == null);
+		assertTrue(repositories().getRepositoryModel("missing") == null);
 	}
 }
diff --git a/src/test/java/com/gitblit/tests/GitDaemonStopTest.java b/src/test/java/com/gitblit/tests/GitDaemonStopTest.java
index 7febc7a..6e940cb 100644
--- a/src/test/java/com/gitblit/tests/GitDaemonStopTest.java
+++ b/src/test/java/com/gitblit/tests/GitDaemonStopTest.java
@@ -15,12 +15,11 @@
  */
 package com.gitblit.tests;
 
-import org.junit.Assert;
 import org.junit.Test;
 
 import com.gitblit.git.GitDaemon;
 
-public class GitDaemonStopTest extends Assert {
+public class GitDaemonStopTest extends GitblitUnitTest {
 
 	@Test
 	public void testGitDaemonStop() throws Exception {
diff --git a/src/test/java/com/gitblit/tests/GitDaemonTest.java b/src/test/java/com/gitblit/tests/GitDaemonTest.java
index a283e6b..74af251 100644
--- a/src/test/java/com/gitblit/tests/GitDaemonTest.java
+++ b/src/test/java/com/gitblit/tests/GitDaemonTest.java
@@ -31,16 +31,14 @@
 import org.eclipse.jgit.transport.RemoteRefUpdate.Status;
 import org.eclipse.jgit.util.FileUtils;
 import org.junit.AfterClass;
-import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
 import com.gitblit.Constants.AccessRestrictionType;
 import com.gitblit.Constants.AuthorizationControl;
-import com.gitblit.GitBlit;
 import com.gitblit.models.RepositoryModel;
 
-public class GitDaemonTest extends Assert {
+public class GitDaemonTest extends GitblitUnitTest {
 
 	static File ticgitFolder = new File(GitBlitSuite.REPOSITORIES, "working/ticgit");
 
@@ -94,10 +92,10 @@
 		}
 
 		// set push restriction
-		RepositoryModel model = GitBlit.self().getRepositoryModel("ticgit.git");
+		RepositoryModel model = repositories().getRepositoryModel("ticgit.git");
 		model.accessRestriction = AccessRestrictionType.PUSH;
 		model.authorizationControl = AuthorizationControl.NAMED;
-		GitBlit.self().updateRepositoryModel(model.name, model, false);
+		repositories().updateRepositoryModel(model.name, model, false);
 
 		CloneCommand clone = Git.cloneRepository();
 		clone.setURI(MessageFormat.format("{0}/ticgit.git", url));
@@ -110,7 +108,7 @@
 		// restore anonymous repository access
 		model.accessRestriction = AccessRestrictionType.NONE;
 		model.authorizationControl = AuthorizationControl.NAMED;
-		GitBlit.self().updateRepositoryModel(model.name, model, false);
+		repositories().updateRepositoryModel(model.name, model, false);
 	}
 
 	@Test
@@ -121,10 +119,10 @@
 		}
 
 		// restrict repository access
-		RepositoryModel model = GitBlit.self().getRepositoryModel("ticgit.git");
+		RepositoryModel model = repositories().getRepositoryModel("ticgit.git");
 		model.accessRestriction = AccessRestrictionType.CLONE;
 		model.authorizationControl = AuthorizationControl.NAMED;
-		GitBlit.self().updateRepositoryModel(model.name, model, false);
+		repositories().updateRepositoryModel(model.name, model, false);
 
 		// delete any existing working folder
 		boolean cloned = false;
@@ -147,7 +145,7 @@
 		// restore anonymous repository access
 		model.accessRestriction = AccessRestrictionType.NONE;
 		model.authorizationControl = AuthorizationControl.NAMED;
-		GitBlit.self().updateRepositoryModel(model.name, model, false);
+		repositories().updateRepositoryModel(model.name, model, false);
 	}
 
 	@Test
@@ -158,10 +156,10 @@
 		}
 
 		// restore anonymous repository access
-		RepositoryModel model = GitBlit.self().getRepositoryModel("ticgit.git");
+		RepositoryModel model = repositories().getRepositoryModel("ticgit.git");
 		model.accessRestriction = AccessRestrictionType.NONE;
 		model.authorizationControl = AuthorizationControl.NAMED;
-		GitBlit.self().updateRepositoryModel(model.name, model, false);
+		repositories().updateRepositoryModel(model.name, model, false);
 
 		CloneCommand clone = Git.cloneRepository();
 		clone.setURI(MessageFormat.format("{0}/ticgit.git", url));
@@ -196,10 +194,10 @@
 		}
 
 		// restore anonymous repository access
-		RepositoryModel model = GitBlit.self().getRepositoryModel("ticgit.git");
+		RepositoryModel model = repositories().getRepositoryModel("ticgit.git");
 		model.accessRestriction = AccessRestrictionType.PUSH;
 		model.authorizationControl = AuthorizationControl.NAMED;
-		GitBlit.self().updateRepositoryModel(model.name, model, false);
+		repositories().updateRepositoryModel(model.name, model, false);
 
 		CloneCommand clone = Git.cloneRepository();
 		clone.setURI(MessageFormat.format("{0}/ticgit.git", url));
@@ -242,9 +240,9 @@
 		assertTrue(true);
 
 		// freeze repo
-		RepositoryModel model = GitBlit.self().getRepositoryModel("test/jgit.git");
+		RepositoryModel model = repositories().getRepositoryModel("test/jgit.git");
 		model.isFrozen = true;
-		GitBlit.self().updateRepositoryModel(model.name, model, false);
+		repositories().updateRepositoryModel(model.name, model, false);
 
 		Git git = Git.open(jgitFolder);
 		File file = new File(jgitFolder, "TODO");
@@ -264,7 +262,7 @@
 
 		// unfreeze repo
 		model.isFrozen = false;
-		GitBlit.self().updateRepositoryModel(model.name, model, false);
+		repositories().updateRepositoryModel(model.name, model, false);
 
 		results = git.push().setPushAll().call();
 		GitBlitSuite.close(git);
diff --git a/src/test/java/com/gitblit/tests/GitServletTest.java b/src/test/java/com/gitblit/tests/GitServletTest.java
index 08c4a23..b197a91 100644
--- a/src/test/java/com/gitblit/tests/GitServletTest.java
+++ b/src/test/java/com/gitblit/tests/GitServletTest.java
@@ -15,10 +15,6 @@
  */
 package com.gitblit.tests;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
 import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileOutputStream;
@@ -53,7 +49,6 @@
 import com.gitblit.Constants.AccessPermission;
 import com.gitblit.Constants.AccessRestrictionType;
 import com.gitblit.Constants.AuthorizationControl;
-import com.gitblit.GitBlit;
 import com.gitblit.Keys;
 import com.gitblit.models.RefLogEntry;
 import com.gitblit.models.RepositoryModel;
@@ -62,7 +57,7 @@
 import com.gitblit.utils.JGitUtils;
 import com.gitblit.utils.RefLogUtils;
 
-public class GitServletTest {
+public class GitServletTest extends GitblitUnitTest {
 
 	static File ticgitFolder = new File(GitBlitSuite.REPOSITORIES, "working/ticgit");
 
@@ -87,8 +82,8 @@
 	}
 
 	private static void delete(UserModel user) {
-		if (GitBlit.self().getUserModel(user.username) != null) {
-			GitBlit.self().deleteUser(user.username);
+		if (users().getUserModel(user.username) != null) {
+			users().deleteUser(user.username);
 		}
 	}
 
@@ -148,9 +143,9 @@
 	@Test
 	public void testBogusLoginClone() throws Exception {
 		// restrict repository access
-		RepositoryModel model = GitBlit.self().getRepositoryModel("ticgit.git");
+		RepositoryModel model = repositories().getRepositoryModel("ticgit.git");
 		model.accessRestriction = AccessRestrictionType.CLONE;
-		GitBlit.self().updateRepositoryModel(model.name, model, false);
+		repositories().updateRepositoryModel(model.name, model, false);
 
 		// delete any existing working folder
 		boolean cloned = false;
@@ -169,7 +164,7 @@
 
 		// restore anonymous repository access
 		model.accessRestriction = AccessRestrictionType.NONE;
-		GitBlit.self().updateRepositoryModel(model.name, model, false);
+		repositories().updateRepositoryModel(model.name, model, false);
 
 		assertFalse("Bogus login cloned a repository?!", cloned);
 	}
@@ -177,13 +172,13 @@
 	@Test
 	public void testUnauthorizedLoginClone() throws Exception {
 		// restrict repository access
-		RepositoryModel model = GitBlit.self().getRepositoryModel("ticgit.git");
+		RepositoryModel model = repositories().getRepositoryModel("ticgit.git");
 		model.accessRestriction = AccessRestrictionType.CLONE;
 		model.authorizationControl = AuthorizationControl.NAMED;
 		UserModel user = new UserModel("james");
 		user.password = "james";
-		GitBlit.self().updateUserModel(user.username, user, true);
-		GitBlit.self().updateRepositoryModel(model.name, model, false);
+		gitblit().updateUserModel(user.username, user, true);
+		repositories().updateRepositoryModel(model.name, model, false);
 
 		FileUtils.delete(ticgit2Folder, FileUtils.RECURSIVE);
 
@@ -208,7 +203,7 @@
 
 		// switch to authenticated
 		model.authorizationControl = AuthorizationControl.AUTHENTICATED;
-		GitBlit.self().updateRepositoryModel(model.name, model, false);
+		repositories().updateRepositoryModel(model.name, model, false);
 
 		// try clone again
 		cloned = false;
@@ -228,7 +223,7 @@
 		// restore anonymous repository access
 		model.accessRestriction = AccessRestrictionType.NONE;
 		model.authorizationControl = AuthorizationControl.NAMED;
-		GitBlit.self().updateRepositoryModel(model.name, model, false);
+		repositories().updateRepositoryModel(model.name, model, false);
 
 		delete(user);
 	}
@@ -240,9 +235,9 @@
 			FileUtils.delete(ticgitFolder, FileUtils.RECURSIVE | FileUtils.RETRY);
 		}
 
-		RepositoryModel model = GitBlit.self().getRepositoryModel("ticgit.git");
+		RepositoryModel model = repositories().getRepositoryModel("ticgit.git");
 		model.accessRestriction = AccessRestrictionType.NONE;
-		GitBlit.self().updateRepositoryModel(model.name, model, false);
+		repositories().updateRepositoryModel(model.name, model, false);
 
 		CloneCommand clone = Git.cloneRepository();
 		clone.setURI(MessageFormat.format("{0}/ticgit.git", url));
@@ -320,9 +315,9 @@
 		assertTrue(true);
 
 		// freeze repo
-		RepositoryModel model = GitBlit.self().getRepositoryModel("test/jgit.git");
+		RepositoryModel model = repositories().getRepositoryModel("test/jgit.git");
 		model.isFrozen = true;
-		GitBlit.self().updateRepositoryModel(model.name, model, false);
+		repositories().updateRepositoryModel(model.name, model, false);
 
 		Git git = Git.open(jgitFolder);
 		File file = new File(jgitFolder, "TODO");
@@ -342,7 +337,7 @@
 
 		// unfreeze repo
 		model.isFrozen = false;
-		GitBlit.self().updateRepositoryModel(model.name, model, false);
+		repositories().updateRepositoryModel(model.name, model, false);
 
 		results = git.push().setPushAll().setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, password)).call();
 		GitBlitSuite.close(git);
@@ -428,7 +423,7 @@
 		GitBlitSuite.close(clone.call());
 
 		// require push permissions and committer verification
-		RepositoryModel model = GitBlit.self().getRepositoryModel("refchecks/verify-committer.git");
+		RepositoryModel model = repositories().getRepositoryModel("refchecks/verify-committer.git");
 		model.authorizationControl = AuthorizationControl.NAMED;
 		model.accessRestriction = AccessRestrictionType.PUSH;
 		model.verifyCommitter = true;
@@ -436,8 +431,8 @@
 		// grant user push permission
 		user.setRepositoryPermission(model.name, AccessPermission.PUSH);
 
-		GitBlit.self().updateUserModel(user.username, user, true);
-		GitBlit.self().updateRepositoryModel(model.name, model, false);
+		gitblit().updateUserModel(user.username, user, true);
+		repositories().updateRepositoryModel(model.name, model, false);
 
 		// clone temp bare repo to working copy
 		File local = new File(GitBlitSuite.REPOSITORIES, "refchecks/verify-wc");
@@ -513,7 +508,7 @@
 		GitBlitSuite.close(clone.call());
 
 		// require push permissions and committer verification
-		RepositoryModel model = GitBlit.self().getRepositoryModel("refchecks/verify-committer.git");
+		RepositoryModel model = repositories().getRepositoryModel("refchecks/verify-committer.git");
 		model.authorizationControl = AuthorizationControl.NAMED;
 		model.accessRestriction = AccessRestrictionType.PUSH;
 		model.verifyCommitter = true;
@@ -521,8 +516,8 @@
 		// grant user push permission
 		user.setRepositoryPermission(model.name, AccessPermission.PUSH);
 
-		GitBlit.self().updateUserModel(user.username, user, true);
-		GitBlit.self().updateRepositoryModel(model.name, model, false);
+		gitblit().updateUserModel(user.username, user, true);
+		repositories().updateRepositoryModel(model.name, model, false);
 
 		// clone temp bare repo to working copy
 		File local = new File(GitBlitSuite.REPOSITORIES, "refchecks/verify-wc");
@@ -648,7 +643,7 @@
 		GitBlitSuite.close(clone.call());
 
 		// elevate repository to clone permission
-		RepositoryModel model = GitBlit.self().getRepositoryModel("refchecks/ticgit.git");
+		RepositoryModel model = repositories().getRepositoryModel("refchecks/ticgit.git");
 		switch (permission) {
 			case VIEW:
 				model.accessRestriction = AccessRestrictionType.CLONE;
@@ -664,8 +659,8 @@
 		// grant user specified
 		user.setRepositoryPermission(model.name, permission);
 
-		GitBlit.self().updateUserModel(user.username, user, true);
-		GitBlit.self().updateRepositoryModel(model.name, model, false);
+		gitblit().updateUserModel(user.username, user, true);
+		repositories().updateRepositoryModel(model.name, model, false);
 
 		// clone temp bare repo to working copy
 		File local = new File(GitBlitSuite.REPOSITORIES, "refchecks/ticgit-wc");
@@ -834,7 +829,7 @@
 		user.canCreate = canCreate;
 		user.canAdmin = canAdmin;
 
-		GitBlit.self().updateUserModel(user.username, user, true);
+		gitblit().updateUserModel(user.username, user, true);
 
 		CredentialsProvider cp = new UsernamePasswordCredentialsProvider(user.username, user.password);
 
@@ -885,7 +880,7 @@
 			assertTrue("User canAdmin:" + user.canAdmin + " canCreate:" + user.canCreate, user.canAdmin || user.canCreate);
 
 			// confirm default personal repository permissions
-			RepositoryModel model = GitBlit.self().getRepositoryModel(MessageFormat.format("~{0}/ticgit.git", user.username));
+			RepositoryModel model = repositories().getRepositoryModel(MessageFormat.format("~{0}/ticgit.git", user.username));
 			assertEquals("Unexpected owner", user.username, ArrayUtils.toString(model.owners));
 			assertEquals("Unexpected authorization control", AuthorizationControl.NAMED, model.authorizationControl);
 			assertEquals("Unexpected access restriction", AccessRestrictionType.VIEW, model.accessRestriction);
@@ -909,10 +904,10 @@
 			assertTrue("User canAdmin:" + user.canAdmin, user.canAdmin);
 
 			// confirm default project repository permissions
-			RepositoryModel model = GitBlit.self().getRepositoryModel("project/ticgit.git");
+			RepositoryModel model = repositories().getRepositoryModel("project/ticgit.git");
 			assertEquals("Unexpected owner", user.username, ArrayUtils.toString(model.owners));
-			assertEquals("Unexpected authorization control", AuthorizationControl.fromName(GitBlit.getString(Keys.git.defaultAuthorizationControl, "NAMED")), model.authorizationControl);
-			assertEquals("Unexpected access restriction", AccessRestrictionType.fromName(GitBlit.getString(Keys.git.defaultAccessRestriction, "NONE")), model.accessRestriction);
+			assertEquals("Unexpected authorization control", AuthorizationControl.fromName(settings().getString(Keys.git.defaultAuthorizationControl, "NAMED")), model.authorizationControl);
+			assertEquals("Unexpected access restriction", AccessRestrictionType.fromName(settings().getString(Keys.git.defaultAccessRestriction, "NONE")), model.accessRestriction);
 
 		} catch (GitAPIException e) {
 			assertTrue(e.getMessage(), e.getMessage().contains("git-receive-pack not found"));
diff --git a/src/test/java/com/gitblit/tests/GitblitUnitTest.java b/src/test/java/com/gitblit/tests/GitblitUnitTest.java
new file mode 100644
index 0000000..fc70e10
--- /dev/null
+++ b/src/test/java/com/gitblit/tests/GitblitUnitTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.tests;
+
+import com.gitblit.GitBlit;
+import com.gitblit.IStoredSettings;
+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.ISessionManager;
+import com.gitblit.manager.IUserManager;
+
+
+public class GitblitUnitTest extends org.junit.Assert {
+
+	public static IStoredSettings settings() {
+		return runtime().getSettings();
+	}
+
+	public static IRuntimeManager runtime() {
+		return GitBlit.getManager(IRuntimeManager.class);
+	}
+
+	public static INotificationManager notifier() {
+		return GitBlit.getManager(INotificationManager.class);
+	}
+
+	public static IUserManager users() {
+		return GitBlit.getManager(IUserManager.class);
+	}
+
+	public static ISessionManager session() {
+		return GitBlit.getManager(ISessionManager.class);
+	}
+
+	public static IRepositoryManager repositories() {
+		return GitBlit.getManager(IRepositoryManager.class);
+	}
+
+	public static IProjectManager projects() {
+		return GitBlit.getManager(IProjectManager.class);
+	}
+
+	public static IFederationManager federation() {
+		return GitBlit.getManager(IFederationManager.class);
+	}
+
+	public static IGitblitManager gitblit() {
+		return GitBlit.getManager(IGitblitManager.class);
+	}
+}
diff --git a/src/test/java/com/gitblit/tests/GroovyScriptTest.java b/src/test/java/com/gitblit/tests/GroovyScriptTest.java
index 2aa881f..1b5b0f3 100644
--- a/src/test/java/com/gitblit/tests/GroovyScriptTest.java
+++ b/src/test/java/com/gitblit/tests/GroovyScriptTest.java
@@ -15,8 +15,6 @@
  */
 package com.gitblit.tests;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
 import groovy.lang.Binding;
 import groovy.util.GroovyScriptEngine;
 
@@ -41,7 +39,6 @@
 import org.junit.BeforeClass;
 import org.junit.Test;
 
-import com.gitblit.GitBlit;
 import com.gitblit.GitBlitException;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.TeamModel;
@@ -54,7 +51,7 @@
  * @author James Moger
  *
  */
-public class GroovyScriptTest {
+public class GroovyScriptTest extends GitblitUnitTest {
 
 	private static final AtomicBoolean started = new AtomicBoolean(false);
 
@@ -83,7 +80,7 @@
 				.fromString("c18877690322dfc6ae3e37bb7f7085a24e94e887"), ObjectId
 				.fromString("3fa7c46d11b11d61f1cbadc6888be5d0eae21969"), "refs/heads/master2"));
 
-		RepositoryModel repository = GitBlit.self().getRepositoryModel("helloworld.git");
+		RepositoryModel repository = repositories().getRepositoryModel("helloworld.git");
 		repository.customFields = new HashMap<String,String>();
 		repository.customFields.put( "fogbugzUrl", "http://bugs.test.com" );
 		repository.customFields.put( "fogbugzRepositoryId", "1" );
@@ -105,7 +102,7 @@
 				.fromString("c18877690322dfc6ae3e37bb7f7085a24e94e887"), ObjectId
 				.fromString("3fa7c46d11b11d61f1cbadc6888be5d0eae21969"), "refs/heads/master2"));
 
-		RepositoryModel repository = GitBlit.self().getRepositoryModel("helloworld.git");
+		RepositoryModel repository = repositories().getRepositoryModel("helloworld.git");
 		repository.mailingLists.add("list@helloworld.git");
 
 		test("sendmail-html.groovy", gitblit, logger, clientLogger, commands, repository);
@@ -130,7 +127,7 @@
 				.fromString("c18877690322dfc6ae3e37bb7f7085a24e94e887"), ObjectId
 				.fromString("3fa7c46d11b11d61f1cbadc6888be5d0eae21969"), "refs/heads/master2"));
 
-		RepositoryModel repository = GitBlit.self().getRepositoryModel("helloworld.git");
+		RepositoryModel repository = repositories().getRepositoryModel("helloworld.git");
 		repository.mailingLists.add("list@helloworld.git");
 
 		test("sendmail.groovy", gitblit, logger, clientLogger, commands, repository);
@@ -270,7 +267,7 @@
 
 		RepositoryModel repository = new RepositoryModel("ex@mple.git", "", "admin", new Date());
 
-		File groovyDir = GitBlit.getGroovyScriptsFolder();
+		File groovyDir = repositories().getHooksFolder();
 		File tempScript = File.createTempFile("testClientLogging", "groovy", groovyDir);
 		tempScript.deleteOnExit();
 
@@ -291,7 +288,7 @@
 
 		String gitblitUrl = GitBlitSuite.url;
 
-		File groovyDir = GitBlit.getGroovyScriptsFolder();
+		File groovyDir = repositories().getHooksFolder();
 		GroovyScriptEngine gse = new GroovyScriptEngine(groovyDir.getAbsolutePath());
 
 		Binding binding = new Binding();
diff --git a/src/test/java/com/gitblit/tests/HtpasswdUserServiceTest.java b/src/test/java/com/gitblit/tests/HtpasswdUserServiceTest.java
index e223e6b..e20c82a 100644
--- a/src/test/java/com/gitblit/tests/HtpasswdUserServiceTest.java
+++ b/src/test/java/com/gitblit/tests/HtpasswdUserServiceTest.java
@@ -15,12 +15,6 @@
  */
 package com.gitblit.tests;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
 import java.io.File;
 import java.io.FilenameFilter;
 import java.io.IOException;
@@ -40,7 +34,7 @@
  * Test the Htpasswd user service.
  *
  */
-public class HtpasswdUserServiceTest {
+public class HtpasswdUserServiceTest extends GitblitUnitTest {
 
     private static final String RESOURCE_DIR = "src/test/resources/htpasswdUSTest/";
     private static final String KEY_SUPPORT_PLAINTEXT_PWD = "realm.htpasswd.supportPlaintextPasswords";
diff --git a/src/test/java/com/gitblit/tests/Issue0259Test.java b/src/test/java/com/gitblit/tests/Issue0259Test.java
index 8e2acaf..df2ccd0 100644
--- a/src/test/java/com/gitblit/tests/Issue0259Test.java
+++ b/src/test/java/com/gitblit/tests/Issue0259Test.java
@@ -17,7 +17,6 @@
 
 import java.io.File;
 
-import org.junit.Assert;
 import org.junit.Test;
 
 import com.gitblit.ConfigUserService;
@@ -40,7 +39,7 @@
  * @author James Moger
  *
  */
-public class Issue0259Test extends Assert {
+public class Issue0259Test extends GitblitUnitTest {
 
 	RepositoryModel repo(String name, AccessRestrictionType restriction) {
 		RepositoryModel repo = new RepositoryModel();
diff --git a/src/test/java/com/gitblit/tests/Issue0271Test.java b/src/test/java/com/gitblit/tests/Issue0271Test.java
index 8bab8f2..15c6ff2 100644
--- a/src/test/java/com/gitblit/tests/Issue0271Test.java
+++ b/src/test/java/com/gitblit/tests/Issue0271Test.java
@@ -17,7 +17,6 @@
 
 import java.io.File;
 
-import org.junit.Assert;
 import org.junit.Test;
 
 import com.gitblit.ConfigUserService;
@@ -37,7 +36,7 @@
  * @author James Moger
  *
  */
-public class Issue0271Test extends Assert {
+public class Issue0271Test extends GitblitUnitTest {
 
 	RepositoryModel repo(String name, AccessRestrictionType restriction) {
 		RepositoryModel repo = new RepositoryModel();
diff --git a/src/test/java/com/gitblit/tests/JGitUtilsTest.java b/src/test/java/com/gitblit/tests/JGitUtilsTest.java
index a937495..f60343a 100644
--- a/src/test/java/com/gitblit/tests/JGitUtilsTest.java
+++ b/src/test/java/com/gitblit/tests/JGitUtilsTest.java
@@ -15,12 +15,6 @@
  */
 package com.gitblit.tests;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
 import java.io.File;
 import java.io.FileOutputStream;
 import java.text.SimpleDateFormat;
@@ -57,7 +51,7 @@
 import com.gitblit.utils.JnaUtils;
 import com.gitblit.utils.StringUtils;
 
-public class JGitUtilsTest {
+public class JGitUtilsTest extends GitblitUnitTest {
 
 	@Test
 	public void testDisplayName() throws Exception {
diff --git a/src/test/java/com/gitblit/tests/JnaUtilsTest.java b/src/test/java/com/gitblit/tests/JnaUtilsTest.java
index 9678215..c7d5be9 100644
--- a/src/test/java/com/gitblit/tests/JnaUtilsTest.java
+++ b/src/test/java/com/gitblit/tests/JnaUtilsTest.java
@@ -15,10 +15,6 @@
  */
 package com.gitblit.tests;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
 import java.io.File;
 import java.io.IOException;
 
@@ -36,7 +32,7 @@
  *
  * @author Florian Zschocke
  */
-public class JnaUtilsTest {
+public class JnaUtilsTest extends GitblitUnitTest {
 
 	@Test
 	public void testGetgid() {
diff --git a/src/test/java/com/gitblit/tests/JsonUtilsTest.java b/src/test/java/com/gitblit/tests/JsonUtilsTest.java
index 40e8f97..1cd31b8 100644
--- a/src/test/java/com/gitblit/tests/JsonUtilsTest.java
+++ b/src/test/java/com/gitblit/tests/JsonUtilsTest.java
@@ -15,8 +15,6 @@
  */
 package com.gitblit.tests;
 
-import static org.junit.Assert.assertEquals;
-
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.HashMap;
@@ -27,7 +25,7 @@
 import com.gitblit.utils.JsonUtils;
 import com.google.gson.reflect.TypeToken;
 
-public class JsonUtilsTest {
+public class JsonUtilsTest extends GitblitUnitTest {
 
 	@Test
 	public void testSerialization() {
diff --git a/src/test/java/com/gitblit/tests/LdapUserServiceTest.java b/src/test/java/com/gitblit/tests/LdapUserServiceTest.java
index ae8933e..86d1f3c 100644
--- a/src/test/java/com/gitblit/tests/LdapUserServiceTest.java
+++ b/src/test/java/com/gitblit/tests/LdapUserServiceTest.java
@@ -16,11 +16,6 @@
  */
 package com.gitblit.tests;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
 import java.util.HashMap;
 import java.util.Map;
 
@@ -44,7 +39,7 @@
  * @author jcrygier
  *
  */
-public class LdapUserServiceTest {
+public class LdapUserServiceTest extends GitblitUnitTest {
 
 	private LdapUserService ldapUserService;
 
diff --git a/src/test/java/com/gitblit/tests/LuceneExecutorTest.java b/src/test/java/com/gitblit/tests/LuceneExecutorTest.java
index ec25c51..0f7e55c 100644
--- a/src/test/java/com/gitblit/tests/LuceneExecutorTest.java
+++ b/src/test/java/com/gitblit/tests/LuceneExecutorTest.java
@@ -15,10 +15,6 @@
  */
 package com.gitblit.tests;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -43,7 +39,7 @@
  * @author James Moger
  *
  */
-public class LuceneExecutorTest {
+public class LuceneExecutorTest extends GitblitUnitTest {
 
 	LuceneExecutor lucene;
 
diff --git a/src/test/java/com/gitblit/tests/MailTest.java b/src/test/java/com/gitblit/tests/MailTest.java
index 4feedb0..7598568 100644
--- a/src/test/java/com/gitblit/tests/MailTest.java
+++ b/src/test/java/com/gitblit/tests/MailTest.java
@@ -15,8 +15,6 @@
  */
 package com.gitblit.tests;
 
-import static org.junit.Assert.assertTrue;
-
 import javax.mail.Message;
 
 import org.junit.Test;
@@ -25,7 +23,7 @@
 import com.gitblit.Keys;
 import com.gitblit.MailExecutor;
 
-public class MailTest {
+public class MailTest extends GitblitUnitTest {
 
 	@Test
 	public void testSendMail() throws Exception {
diff --git a/src/test/java/com/gitblit/tests/MarkdownUtilsTest.java b/src/test/java/com/gitblit/tests/MarkdownUtilsTest.java
index 3458374..e40f105 100644
--- a/src/test/java/com/gitblit/tests/MarkdownUtilsTest.java
+++ b/src/test/java/com/gitblit/tests/MarkdownUtilsTest.java
@@ -15,13 +15,11 @@
  */
 package com.gitblit.tests;
 
-import static org.junit.Assert.assertEquals;
-
 import org.junit.Test;
 
 import com.gitblit.utils.MarkdownUtils;
 
-public class MarkdownUtilsTest {
+public class MarkdownUtilsTest extends GitblitUnitTest {
 
 	@Test
 	public void testMarkdown() throws Exception {
diff --git a/src/test/java/com/gitblit/tests/MetricUtilsTest.java b/src/test/java/com/gitblit/tests/MetricUtilsTest.java
index bb8261a..4d620ae 100644
--- a/src/test/java/com/gitblit/tests/MetricUtilsTest.java
+++ b/src/test/java/com/gitblit/tests/MetricUtilsTest.java
@@ -15,9 +15,6 @@
  */
 package com.gitblit.tests;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
 import java.util.List;
 import java.util.TimeZone;
 
@@ -27,7 +24,7 @@
 import com.gitblit.models.Metric;
 import com.gitblit.utils.MetricUtils;
 
-public class MetricUtilsTest {
+public class MetricUtilsTest extends GitblitUnitTest {
 
 	@Test
 	public void testMetrics() throws Exception {
diff --git a/src/test/java/com/gitblit/tests/ModelUtilsTest.java b/src/test/java/com/gitblit/tests/ModelUtilsTest.java
index 9dd29b6..e34e0aa 100644
--- a/src/test/java/com/gitblit/tests/ModelUtilsTest.java
+++ b/src/test/java/com/gitblit/tests/ModelUtilsTest.java
@@ -1,16 +1,12 @@
 package com.gitblit.tests;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
 import org.junit.After;
 import org.junit.Test;
 
 import com.gitblit.Constants;
 import com.gitblit.utils.ModelUtils;
 
-public class ModelUtilsTest {
+public class ModelUtilsTest extends GitblitUnitTest {
 
 	@After
 	public void resetPrefix()
diff --git a/src/test/java/com/gitblit/tests/ObjectCacheTest.java b/src/test/java/com/gitblit/tests/ObjectCacheTest.java
index 8d07fe6..0330f06 100644
--- a/src/test/java/com/gitblit/tests/ObjectCacheTest.java
+++ b/src/test/java/com/gitblit/tests/ObjectCacheTest.java
@@ -15,17 +15,13 @@
  */
 package com.gitblit.tests;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
 import java.util.Date;
 
 import org.junit.Test;
 
 import com.gitblit.utils.ObjectCache;
 
-public class ObjectCacheTest {
+public class ObjectCacheTest extends GitblitUnitTest {
 
 	@Test
 	public void testCache() throws Exception {
diff --git a/src/test/java/com/gitblit/tests/PermissionsTest.java b/src/test/java/com/gitblit/tests/PermissionsTest.java
index 42ef993..cffce51 100644
--- a/src/test/java/com/gitblit/tests/PermissionsTest.java
+++ b/src/test/java/com/gitblit/tests/PermissionsTest.java
@@ -17,7 +17,6 @@
 
 import java.util.Date;
 
-import org.junit.Assert;
 import org.junit.Test;
 
 import com.gitblit.Constants.AccessPermission;
@@ -33,7 +32,7 @@
  * @author James Moger
  *
  */
-public class PermissionsTest extends Assert {
+public class PermissionsTest extends GitblitUnitTest {
 
 	/**
 	 * Admin access rights/permissions
diff --git a/src/test/java/com/gitblit/tests/PushLogTest.java b/src/test/java/com/gitblit/tests/PushLogTest.java
index f5d5965..be097cc 100644
--- a/src/test/java/com/gitblit/tests/PushLogTest.java
+++ b/src/test/java/com/gitblit/tests/PushLogTest.java
@@ -28,7 +28,7 @@
 import com.gitblit.models.RefLogEntry;
 import com.gitblit.utils.RefLogUtils;
 
-public class PushLogTest {
+public class PushLogTest extends GitblitUnitTest {
 
 	@Test
 	public void testPushLog() throws IOException {
diff --git a/src/test/java/com/gitblit/tests/RedmineUserServiceTest.java b/src/test/java/com/gitblit/tests/RedmineUserServiceTest.java
index 7e04a91..7537623 100644
--- a/src/test/java/com/gitblit/tests/RedmineUserServiceTest.java
+++ b/src/test/java/com/gitblit/tests/RedmineUserServiceTest.java
@@ -1,10 +1,6 @@
 package com.gitblit.tests;
 
 import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
 
 import java.util.HashMap;
 
@@ -15,7 +11,7 @@
 import com.gitblit.tests.mock.MemorySettings;
 import com.gitblit.utils.StringUtils;
 
-public class RedmineUserServiceTest {
+public class RedmineUserServiceTest extends GitblitUnitTest {
 
     private static final String JSON = "{\"user\":{\"created_on\":\"2011-03-28T00:41:29Z\",\"lastname\":\"foo\","
         + "\"last_login_on\":\"2012-09-06T23:59:26Z\",\"firstname\":\"baz\","
diff --git a/src/test/java/com/gitblit/tests/RepositoryModelTest.java b/src/test/java/com/gitblit/tests/RepositoryModelTest.java
index 56c1ebb..4520ada 100644
--- a/src/test/java/com/gitblit/tests/RepositoryModelTest.java
+++ b/src/test/java/com/gitblit/tests/RepositoryModelTest.java
@@ -16,8 +16,6 @@
  */
 package com.gitblit.tests;
 
-import static org.junit.Assert.assertEquals;
-
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.StoredConfig;
 import org.junit.After;
@@ -27,10 +25,9 @@
 import org.junit.Test;
 
 import com.gitblit.Constants;
-import com.gitblit.GitBlit;
 import com.gitblit.models.RepositoryModel;
 
-public class RepositoryModelTest {
+public class RepositoryModelTest extends GitblitUnitTest {
 
 	private static boolean wasStarted = false;
 
@@ -68,7 +65,7 @@
 
 	@Test
 	public void testGetCustomProperty() throws Exception {
-		RepositoryModel model = GitBlit.self().getRepositoryModel(
+		RepositoryModel model = repositories().getRepositoryModel(
 				GitBlitSuite.getHelloworldRepository().getDirectory().getName());
 
 		assertEquals("\\d", model.customFields.get("commitMessageRegEx"));
@@ -77,16 +74,16 @@
 
 	@Test
 	public void testSetCustomProperty() throws Exception {
-		RepositoryModel model = GitBlit.self().getRepositoryModel(
+		RepositoryModel model = repositories().getRepositoryModel(
 				GitBlitSuite.getHelloworldRepository().getDirectory().getName());
 
 		assertEquals("\\d", model.customFields.get("commitMessageRegEx"));
 		assertEquals("Hello", model.customFields.get("anotherProperty"));
 
 		assertEquals("Hello", model.customFields.put("anotherProperty", "GoodBye"));
-		GitBlit.self().updateRepositoryModel(model.name, model, false);
+		repositories().updateRepositoryModel(model.name, model, false);
 
-		model = GitBlit.self().getRepositoryModel(
+		model = repositories().getRepositoryModel(
 				GitBlitSuite.getHelloworldRepository().getDirectory().getName());
 
 		assertEquals("\\d", model.customFields.get("commitMessageRegEx"));
diff --git a/src/test/java/com/gitblit/tests/RpcTests.java b/src/test/java/com/gitblit/tests/RpcTests.java
index f816ff6..33e8505 100644
--- a/src/test/java/com/gitblit/tests/RpcTests.java
+++ b/src/test/java/com/gitblit/tests/RpcTests.java
@@ -15,11 +15,6 @@
  */
 package com.gitblit.tests;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -58,7 +53,7 @@
  * @author James Moger
  *
  */
-public class RpcTests {
+public class RpcTests extends GitblitUnitTest {
 
 	String url = GitBlitSuite.url;
 	String account = GitBlitSuite.account;
diff --git a/src/test/java/com/gitblit/tests/StringUtilsTest.java b/src/test/java/com/gitblit/tests/StringUtilsTest.java
index bec5c54..0fd42aa 100644
--- a/src/test/java/com/gitblit/tests/StringUtilsTest.java
+++ b/src/test/java/com/gitblit/tests/StringUtilsTest.java
@@ -15,10 +15,6 @@
  */
 package com.gitblit.tests;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
 import java.util.Arrays;
 import java.util.List;
 
@@ -26,7 +22,7 @@
 
 import com.gitblit.utils.StringUtils;
 
-public class StringUtilsTest {
+public class StringUtilsTest extends GitblitUnitTest {
 
 	@Test
 	public void testIsEmpty() throws Exception {
diff --git a/src/test/java/com/gitblit/tests/SyndicationUtilsTest.java b/src/test/java/com/gitblit/tests/SyndicationUtilsTest.java
index 66d0c3a..d206bbd 100644
--- a/src/test/java/com/gitblit/tests/SyndicationUtilsTest.java
+++ b/src/test/java/com/gitblit/tests/SyndicationUtilsTest.java
@@ -15,9 +15,6 @@
  */
 package com.gitblit.tests;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
 import java.io.ByteArrayOutputStream;
 import java.util.ArrayList;
 import java.util.Date;
@@ -31,7 +28,7 @@
 import com.gitblit.models.FeedEntryModel;
 import com.gitblit.utils.SyndicationUtils;
 
-public class SyndicationUtilsTest {
+public class SyndicationUtilsTest extends GitblitUnitTest {
 
 	@Test
 	public void testSyndication() throws Exception {
diff --git a/src/test/java/com/gitblit/tests/TimeUtilsTest.java b/src/test/java/com/gitblit/tests/TimeUtilsTest.java
index 851fb45..fb38ffd 100644
--- a/src/test/java/com/gitblit/tests/TimeUtilsTest.java
+++ b/src/test/java/com/gitblit/tests/TimeUtilsTest.java
@@ -15,16 +15,13 @@
  */
 package com.gitblit.tests;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
 import java.util.Date;
 
 import org.junit.Test;
 
 import com.gitblit.utils.TimeUtils;
 
-public class TimeUtilsTest {
+public class TimeUtilsTest extends GitblitUnitTest {
 
 	private Date offset(long subtract) {
 		return new Date(System.currentTimeMillis() - subtract);
diff --git a/src/test/java/com/gitblit/tests/UserServiceTest.java b/src/test/java/com/gitblit/tests/UserServiceTest.java
index f6cdd6a..613e46d 100644
--- a/src/test/java/com/gitblit/tests/UserServiceTest.java
+++ b/src/test/java/com/gitblit/tests/UserServiceTest.java
@@ -15,10 +15,6 @@
  */
 package com.gitblit.tests;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
 import java.io.File;
 import java.io.IOException;
 
@@ -31,7 +27,7 @@
 import com.gitblit.models.TeamModel;
 import com.gitblit.models.UserModel;
 
-public class UserServiceTest {
+public class UserServiceTest extends GitblitUnitTest {
 
 	@Test
 	public void testConfigUserService() throws IOException {
diff --git a/src/test/java/com/gitblit/tests/X509UtilsTest.java b/src/test/java/com/gitblit/tests/X509UtilsTest.java
index 668626b..740f908 100644
--- a/src/test/java/com/gitblit/tests/X509UtilsTest.java
+++ b/src/test/java/com/gitblit/tests/X509UtilsTest.java
@@ -26,7 +26,6 @@
 
 import org.eclipse.jgit.util.FileUtils;
 import org.junit.After;
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -43,7 +42,7 @@
  * @author James Moger
  *
  */
-public class X509UtilsTest extends Assert {
+public class X509UtilsTest extends GitblitUnitTest {
 
 	// passwords are case-sensitive and may be length-limited
 	// based on the JCE policy files
diff --git a/src/test/java/de/akquinet/devops/GitBlit4UITests.java b/src/test/java/de/akquinet/devops/GitBlit4UITests.java
index 966d72b..d4e3222 100644
--- a/src/test/java/de/akquinet/devops/GitBlit4UITests.java
+++ b/src/test/java/de/akquinet/devops/GitBlit4UITests.java
@@ -9,6 +9,7 @@
 	private boolean luceneIndexingEnabled;
 
 	public GitBlit4UITests(boolean luceneIndexingEnabled) {
+		super(null);
 		this.luceneIndexingEnabled = luceneIndexingEnabled;
 	}
 
@@ -21,5 +22,5 @@
 					.info("Lucene executor is scheduled to process indexed branches every 2 minutes.");
 		}
 	}
-	
+
 }
diff --git a/src/test/java/de/akquinet/devops/GitBlitServer4UITests.java b/src/test/java/de/akquinet/devops/GitBlitServer4UITests.java
index 2d54be2..6ee98a0 100644
--- a/src/test/java/de/akquinet/devops/GitBlitServer4UITests.java
+++ b/src/test/java/de/akquinet/devops/GitBlitServer4UITests.java
@@ -1,5 +1,6 @@
 package de.akquinet.devops;
 
+import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -7,6 +8,8 @@
 import com.beust.jcommander.ParameterException;
 import com.gitblit.GitBlit;
 import com.gitblit.GitBlitServer;
+import com.gitblit.IStoredSettings;
+import com.gitblit.Keys;
 
 public class GitBlitServer4UITests extends GitBlitServer {
 
@@ -50,13 +53,9 @@
 		}
 	}
 
-	private GitBlit4UITests instance;
-
 	@Override
-	protected GitBlit getGitBlitInstance() {
-		if (instance == null) {
-			instance = new GitBlit4UITests(false);
-		}
-		return instance;
+	protected GitBlit newGitblit(IStoredSettings settings, File baseFolder) {
+		settings.overrideSetting(Keys.web.allowLuceneIndexing, false);
+		return new GitBlit(settings, baseFolder);
 	}
 }

--
Gitblit v1.9.1