From e24670533215141f7146349f782d3c4a2d3ee535 Mon Sep 17 00:00:00 2001 From: James Moger Date: Wed, 20 Nov 2013 15:26:24 -0500 Subject: [PATCH] Extract RuntimeManager from GitBlit singleton Change-Id: I5358389396f816da979ec18a31421c2d2b67b3d9 --- src/main/java/com/gitblit/DaggerModule.java | 11 +- .../java/com/gitblit/FederationClient.java | 2 +- src/main/java/com/gitblit/FileSettings.java | 29 +- src/main/java/com/gitblit/GitBlit.java | 576 ++++++++---------- src/main/java/com/gitblit/GitBlitServer.java | 3 +- src/main/java/com/gitblit/Gitblit.java | 11 + .../java/com/gitblit/IStoredSettings.java | 9 + .../java/com/gitblit/manager/IManager.java | 23 + .../com/gitblit/manager/IRuntimeManager.java | 2 +- .../com/gitblit/manager/RuntimeManager.java | 205 +++++++ .../java/com/gitblit/models/ServerStatus.java | 7 +- .../tests/mock/MockRuntimeManager.java | 12 +- 12 files changed, 565 insertions(+), 325 deletions(-) create mode 100644 src/main/java/com/gitblit/manager/IManager.java create mode 100644 src/main/java/com/gitblit/manager/RuntimeManager.java diff --git a/src/main/java/com/gitblit/DaggerModule.java b/src/main/java/com/gitblit/DaggerModule.java index d25126a1..840ba609 100644 --- a/src/main/java/com/gitblit/DaggerModule.java +++ b/src/main/java/com/gitblit/DaggerModule.java @@ -28,6 +28,7 @@ import com.gitblit.manager.IRepositoryManager; import com.gitblit.manager.IRuntimeManager; import com.gitblit.manager.ISessionManager; import com.gitblit.manager.IUserManager; +import com.gitblit.manager.RuntimeManager; import com.gitblit.wicket.GitBlitWebApp; import com.gitblit.wicket.GitblitWicketFilter; @@ -42,6 +43,8 @@ import dagger.Provides; */ @Module( injects = { + IStoredSettings.class, + // core managers IRuntimeManager.class, INotificationManager.class, @@ -84,8 +87,12 @@ public class DaggerModule { this.gitblit = gitblit; } - @Provides @Singleton IRuntimeManager provideRuntimeManager() { - return gitblit; + @Provides @Singleton IStoredSettings provideSettings() { + return new FileSettings(); + } + + @Provides @Singleton IRuntimeManager provideRuntimeManager(IStoredSettings settings) { + return new RuntimeManager(settings); } @Provides @Singleton INotificationManager provideNotificationManager() { diff --git a/src/main/java/com/gitblit/FederationClient.java b/src/main/java/com/gitblit/FederationClient.java index eae6b94f..862b64c1 100644 --- a/src/main/java/com/gitblit/FederationClient.java +++ b/src/main/java/com/gitblit/FederationClient.java @@ -84,7 +84,7 @@ public class FederationClient { // configure the Gitblit singleton for minimal, non-server operation GitBlit gitblit = new GitBlit(settings, baseFolder); - gitblit.configureContext(settings, baseFolder, false); + gitblit.beforeServletInjection(null); // XXX broken FederationPullExecutor executor = new FederationPullExecutor(registrations, params.isDaemon); executor.run(); if (!params.isDaemon) { diff --git a/src/main/java/com/gitblit/FileSettings.java b/src/main/java/com/gitblit/FileSettings.java index 12739d20..d31fc2fb 100644 --- a/src/main/java/com/gitblit/FileSettings.java +++ b/src/main/java/com/gitblit/FileSettings.java @@ -32,7 +32,7 @@ import com.gitblit.utils.FileUtils; */ public class FileSettings extends IStoredSettings { - protected final File propertiesFile; + protected File propertiesFile; private final Properties properties = new Properties(); @@ -40,18 +40,41 @@ public class FileSettings extends IStoredSettings { private volatile boolean forceReload; - public FileSettings(String file) { + public FileSettings() { super(FileSettings.class); + } + + public FileSettings(String file) { + this(); + load(file); + } + + public void load(String file) { this.propertiesFile = new File(file); } + /** + * Merges the provided settings into this instance. This will also + * set the target file for this instance IFF it is unset AND the merge + * source is also a FileSettings. This is a little sneaky. + */ + @Override + public void merge(IStoredSettings settings) { + super.merge(settings); + + // sneaky: set the target file from the merge source + if (propertiesFile == null && settings instanceof FileSettings) { + this.propertiesFile = ((FileSettings) settings).propertiesFile; + } + } + /** * Returns a properties object which contains the most recent contents of * the properties file. */ @Override protected synchronized Properties read() { - if (propertiesFile.exists() && (forceReload || (propertiesFile.lastModified() > lastModified))) { + if (propertiesFile != null && propertiesFile.exists() && (forceReload || (propertiesFile.lastModified() > lastModified))) { FileInputStream is = null; try { Properties props = new Properties(); diff --git a/src/main/java/com/gitblit/GitBlit.java b/src/main/java/com/gitblit/GitBlit.java index b121b2b9..91a44b7e 100644 --- a/src/main/java/com/gitblit/GitBlit.java +++ b/src/main/java/com/gitblit/GitBlit.java @@ -47,7 +47,6 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import java.util.TimeZone; import java.util.TreeMap; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; @@ -102,6 +101,7 @@ import com.gitblit.git.GitDaemon; import com.gitblit.git.GitServlet; import com.gitblit.manager.IFederationManager; import com.gitblit.manager.IGitblitManager; +import com.gitblit.manager.IManager; import com.gitblit.manager.INotificationManager; import com.gitblit.manager.IProjectManager; import com.gitblit.manager.IRepositoryManager; @@ -121,7 +121,6 @@ import com.gitblit.models.RepositoryModel; import com.gitblit.models.RepositoryUrl; import com.gitblit.models.SearchResult; import com.gitblit.models.ServerSettings; -import com.gitblit.models.ServerStatus; import com.gitblit.models.SettingModel; import com.gitblit.models.TeamModel; import com.gitblit.models.UserModel; @@ -151,6 +150,8 @@ import com.google.gson.JsonIOException; import com.google.gson.JsonSyntaxException; import com.google.gson.reflect.TypeToken; +import dagger.ObjectGraph; + /** * GitBlit is the servlet context listener singleton that acts as the core for * the web ui and the servlets. This class is either directly instantiated by @@ -165,8 +166,7 @@ import com.google.gson.reflect.TypeToken; */ @WebListener public class GitBlit extends DaggerContextListener - implements IRuntimeManager, - INotificationManager, + implements INotificationManager, IUserManager, ISessionManager, IRepositoryManager, @@ -178,6 +178,10 @@ public class GitBlit extends DaggerContextListener private final IStoredSettings goSettings; + private final File goBaseFolder; + + private final List managers = new ArrayList(); + private final ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(10); private final List federationRegistrations = Collections @@ -201,18 +205,12 @@ public class GitBlit extends DaggerContextListener private final ObjectCache projectRepositoriesMarkdownCache = new ObjectCache(); - private File baseFolder; - private File repositoriesFolder; private IUserService userService; private IStoredSettings settings; - private ServerSettings settingsModel; - - private ServerStatus serverStatus; - private MailExecutor mailExecutor; private LuceneExecutor luceneExecutor; @@ -221,8 +219,6 @@ public class GitBlit extends DaggerContextListener private MirrorExecutor mirrorExecutor; - private TimeZone timezone; - private FileBasedConfig projectConfigs; private FanoutService fanoutService; @@ -231,17 +227,19 @@ public class GitBlit extends DaggerContextListener public GitBlit() { this.goSettings = null; + this.goBaseFolder = null; } protected GitBlit(final IUserService userService) { this.goSettings = null; + this.goBaseFolder = null; this.userService = userService; gitblit = this; } public GitBlit(IStoredSettings settings, File baseFolder) { this.goSettings = settings; - this.baseFolder = baseFolder; + this.goBaseFolder = baseFolder; gitblit = this; } @@ -259,27 +257,13 @@ public class GitBlit extends DaggerContextListener 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; - } - /** - * Returns the boot date of the Gitblit server. - * - * @return the boot date of Gitblit - */ - @Override - public Date getBootDate() { - return serverStatus.bootDate; + for (IManager manager : gitblit.managers) { + if (managerClass.isAssignableFrom(manager.getClass())) { + return (X) manager; + } + } + return null; } /** @@ -301,71 +285,6 @@ public class GitBlit extends DaggerContextListener return date; } - /** - * 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 - */ - @Override - public boolean isServingRepositories() { - return settings.getBoolean(Keys.git.enableGitServlet, true) || (settings.getInteger(Keys.git.daemonPort, 0) > 0); - } - - /** - * Returns the preferred timezone for the Gitblit instance. - * - * @return a timezone - */ - @Override - public TimeZone getTimezone() { - if (timezone == null) { - String tzid = settings.getString("web.timezone", null); - if (StringUtils.isEmpty(tzid)) { - timezone = TimeZone.getDefault(); - return timezone; - } - timezone = TimeZone.getTimeZone(tzid); - } - return timezone; - } - - /** - * Is Gitblit running in debug mode? - * - * @return true if Gitblit is running in debug mode - */ - @Override - public boolean isDebugMode() { - return settings.getBoolean(Keys.web.debugMode, false); - } - - /** - * Returns the file object for the specified configuration key. - * - * @return the file - */ - @Override - public File getFileOrFolder(String key, String defaultFileOrFolder) { - String fileOrFolder = settings.getString(key, defaultFileOrFolder); - return getFileOrFolder(fileOrFolder); - } - - /** - * 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 - */ - @Override - public File getFileOrFolder(String fileOrFolder) { - return com.gitblit.utils.FileUtils.resolveParameter(Constants.baseFolder$, - baseFolder, fileOrFolder); - } - /** * 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. @@ -374,7 +293,7 @@ public class GitBlit extends DaggerContextListener */ @Override public File getRepositoriesFolder() { - return getFileOrFolder(Keys.git.repositoriesFolder, "${baseFolder}/git"); + return getManager(IRuntimeManager.class).getFileOrFolder(Keys.git.repositoriesFolder, "${baseFolder}/git"); } /** @@ -385,7 +304,7 @@ public class GitBlit extends DaggerContextListener */ @Override public File getProposalsFolder() { - return getFileOrFolder(Keys.federation.proposalsFolder, "${baseFolder}/proposals"); + return getManager(IRuntimeManager.class).getFileOrFolder(Keys.federation.proposalsFolder, "${baseFolder}/proposals"); } /** @@ -396,7 +315,7 @@ public class GitBlit extends DaggerContextListener */ @Override public File getHooksFolder() { - return getFileOrFolder(Keys.groovy.scriptsFolder, "${baseFolder}/groovy"); + return getManager(IRuntimeManager.class).getFileOrFolder(Keys.groovy.scriptsFolder, "${baseFolder}/groovy"); } /** @@ -407,36 +326,7 @@ public class GitBlit extends DaggerContextListener */ @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 updatedSettings) { - return settings.saveSettings(updatedSettings); - } - - @Override - public ServerStatus getStatus() { - // update heap memory status - serverStatus.heapAllocated = Runtime.getRuntime().totalMemory(); - serverStatus.heapFree = Runtime.getRuntime().freeMemory(); - return serverStatus; + return getManager(IRuntimeManager.class).getFileOrFolder(Keys.groovy.grapeFolder, "${baseFolder}/groovy/grape"); } /** @@ -548,7 +438,7 @@ public class GitBlit extends DaggerContextListener @Override public Collection getClientApplications() { // prefer user definitions, if they exist - File userDefs = new File(baseFolder, "clientapps.json"); + File userDefs = new File(getManager(IRuntimeManager.class).getBaseFolder(), "clientapps.json"); if (userDefs.exists()) { Date lastModified = new Date(userDefs.lastModified()); if (clientApplications.hasCurrent("user", lastModified)) { @@ -1912,7 +1802,7 @@ public class GitBlit extends DaggerContextListener } RepositoryModel model = new RepositoryModel(); model.isBare = r.isBare(); - File basePath = getFileOrFolder(Keys.git.repositoriesFolder, "${baseFolder}/git"); + File basePath = getRepositoriesFolder(); if (model.isBare) { model.name = com.gitblit.utils.FileUtils.getRelativePath(basePath, r.getDirectory()); } else { @@ -2283,7 +2173,7 @@ public class GitBlit extends DaggerContextListener if (repositoryMetricsCache.hasCurrent(model.name, model.lastChange)) { return new ArrayList(repositoryMetricsCache.getObject(model.name)); } - List metrics = MetricUtils.getDateMetrics(repository, null, true, null, getTimezone()); + List metrics = MetricUtils.getDateMetrics(repository, null, true, null, getManager(IRuntimeManager.class).getTimezone()); repositoryMetricsCache.updateObject(model.name, model.lastChange, metrics); return new ArrayList(metrics); } @@ -3369,28 +3259,6 @@ public class GitBlit extends DaggerContextListener } } - /** - * Returns the descriptions/comments of the Gitblit config settings. - * - * @return SettingsModel - */ - @Override - public ServerSettings getSettingsModel() { - // ensure that the current values are updated in the setting models - for (String key : settings.getAllKeys(null)) { - SettingModel setting = settingsModel.get(key); - if (setting == null) { - // unreferenced setting, create a setting model - setting = new SettingModel(); - setting.name = key; - settingsModel.add(setting); - } - setting.currentValue = settings.getString(key, ""); - } - settingsModel.pushScripts = getAllScripts(); - return settingsModel; - } - /** * Parse the properties file and aggregate all the comments by the setting * key. A setting model tracks the current value, the default value, the @@ -3398,8 +3266,7 @@ public class GitBlit extends DaggerContextListener * * @return Map */ - private ServerSettings loadSettingModels() { - ServerSettings settingsModel = new ServerSettings(); + private ServerSettings loadSettingModels(ServerSettings settingsModel) { settingsModel.supportsCredentialChanges = userService.supportsCredentialChanges(); settingsModel.supportsDisplayNameChanges = userService.supportsDisplayNameChanges(); settingsModel.supportsEmailAddressChanges = userService.supportsEmailAddressChanges(); @@ -3460,77 +3327,6 @@ public class GitBlit extends DaggerContextListener return settingsModel; } - /** - * Configure the Gitblit singleton with the specified settings source. This - * source may be file settings (Gitblit GO) or may be web.xml settings - * (Gitblit WAR). - * - * @param settings - */ - public void configureContext(IStoredSettings settings, File folder, boolean startFederation) { - this.settings = settings; - this.baseFolder = folder; - - repositoriesFolder = getRepositoriesFolder(); - - logger.info("Gitblit base folder = " + folder.getAbsolutePath()); - logger.info("Git repositories folder = " + repositoriesFolder.getAbsolutePath()); - logger.info("Gitblit settings = " + settings.toString()); - - // prepare service executors - mailExecutor = new MailExecutor(settings); - luceneExecutor = new LuceneExecutor(settings, getManager(IRepositoryManager.class)); - gcExecutor = new GCExecutor(settings, getManager(IRepositoryManager.class)); - mirrorExecutor = new MirrorExecutor(settings, getManager(IRepositoryManager.class)); - - // initialize utilities - String prefix = settings.getString(Keys.git.userRepositoryPrefix, "~"); - ModelUtils.setUserRepoPrefix(prefix); - - // calculate repository list settings checksum for future config changes - repositoryListSettingsChecksum.set(getRepositoryListSettingsChecksum()); - - // build initial repository list - if (settings.getBoolean(Keys.git.cacheRepositoryList, true)) { - logger.info("Identifying available repositories..."); - getRepositoryList(); - } - - logTimezone("JVM", TimeZone.getDefault()); - logTimezone(Constants.NAME, getTimezone()); - - serverStatus = new ServerStatus(goSettings != null); - - if (this.userService == null) { - String realm = settings.getString(Keys.realm.userService, "${baseFolder}/users.properties"); - IUserService loginService = null; - try { - // check to see if this "file" is a login service class - Class realmClass = Class.forName(realm); - loginService = (IUserService) realmClass.newInstance(); - } catch (Throwable t) { - loginService = new GitblitUserService(); - } - setUserService(loginService); - } - - // load and cache the project metadata - projectConfigs = new FileBasedConfig(getFileOrFolder(Keys.web.projectsFile, "${baseFolder}/projects.conf"), FS.detect()); - getProjectConfigs(); - - configureMailExecutor(); - configureLuceneIndexing(); - configureGarbageCollector(); - configureMirrorExecutor(); - if (startFederation) { - configureFederation(); - } - configureJGit(); - configureFanout(); - configureGitDaemon(); - configureCommitCache(); - } - protected void configureMailExecutor() { if (mailExecutor.isReady()) { logger.info("Mail executor is scheduled to process the message queue every 2 minutes."); @@ -3642,7 +3438,15 @@ public class GitBlit extends DaggerContextListener if (port > 0) { try { // HACK temporary pending manager separation and injection - Gitblit gitblit = new Gitblit(this, this, this, this, this, this, this, this); + Gitblit gitblit = new Gitblit( + getManager(IRuntimeManager.class), + this, + this, + this, + this, + this, + this, + this); gitDaemon = new GitDaemon(gitblit); gitDaemon.start(); } catch (IOException e) { @@ -3700,13 +3504,6 @@ public class GitBlit extends DaggerContextListener return luceneExecutor; } - private void logTimezone(String type, TimeZone zone) { - SimpleDateFormat df = new SimpleDateFormat("z Z"); - df.setTimeZone(zone); - String offset = df.format(new Date()); - logger.info(type + " timezone is " + zone.getID() + " (" + offset + ")"); - } - /** * Configure Gitblit from the web.xml, if no configuration has already been * specified. @@ -3715,93 +3512,237 @@ public class GitBlit extends DaggerContextListener */ @Override protected void beforeServletInjection(ServletContext context) { - if (settings == null) { - // Gitblit is running in a servlet container + ObjectGraph injector = getInjector(context); + + // create the runtime settings object + IStoredSettings runtimeSettings = injector.get(IStoredSettings.class); + this.settings = runtimeSettings; // XXX remove me eventually + final File baseFolder; + + if (goSettings != null) { + // Gitblit GO + logger.debug("configuring Gitblit GO"); + baseFolder = configureGO(context, goSettings, goBaseFolder, runtimeSettings); + } else { + // servlet container WebXmlSettings webxmlSettings = new WebXmlSettings(context); String contextRealPath = context.getRealPath("/"); File contextFolder = (contextRealPath != null) ? new File(contextRealPath) : null; - String openShift = System.getenv("OPENSHIFT_DATA_DIR"); - - if (!StringUtils.isEmpty(openShift)) { - // Gitblit is running in OpenShift/JBoss - File base = new File(openShift); - logger.info("EXPRESS contextFolder is " + contextFolder.getAbsolutePath()); - - // gitblit.properties setting overrides - File overrideFile = new File(base, "gitblit.properties"); - webxmlSettings.applyOverrides(overrideFile); - - // Copy the included scripts to the configured groovy folder - String path = webxmlSettings.getString(Keys.groovy.scriptsFolder, "groovy"); - File localScripts = com.gitblit.utils.FileUtils.resolveParameter(Constants.baseFolder$, base, path); - if (!localScripts.exists()) { - File warScripts = new File(contextFolder, "/WEB-INF/data/groovy"); - if (!warScripts.equals(localScripts)) { - try { - com.gitblit.utils.FileUtils.copy(localScripts, warScripts.listFiles()); - } catch (IOException e) { - logger.error(MessageFormat.format( - "Failed to copy included Groovy scripts from {0} to {1}", - warScripts, localScripts)); - } - } - } - - // disable Git daemon on Express - we can't bind 9418 and we - // can't port-forward to the daemon - webxmlSettings.overrideSetting(Keys.git.daemonPort, 0); - // configure context using the web.xml - configureContext(webxmlSettings, base, true); + if (!StringUtils.isEmpty(System.getenv("OPENSHIFT_DATA_DIR"))) { + // RedHat OpenShift + logger.debug("configuring Gitblit Express"); + baseFolder = configureExpress(context, webxmlSettings, contextFolder, runtimeSettings); } else { - // Gitblit is running in a standard servlet container - logger.info("WAR contextFolder is " + ((contextFolder != null) ? contextFolder.getAbsolutePath() : "")); - - String path = webxmlSettings.getString(Constants.baseFolder, Constants.contextFolder$ + "/WEB-INF/data"); - - if (path.contains(Constants.contextFolder$) && contextFolder == null) { - // warn about null contextFolder (issue-199) - logger.error(""); - logger.error(MessageFormat.format("\"{0}\" depends on \"{1}\" but \"{2}\" is returning NULL for \"{1}\"!", - Constants.baseFolder, Constants.contextFolder$, context.getServerInfo())); - logger.error(MessageFormat.format("Please specify a non-parameterized path for {0} in web.xml!!", Constants.baseFolder)); - logger.error(MessageFormat.format("OR configure your servlet container to specify a \"{0}\" parameter in the context configuration!!", Constants.baseFolder)); - logger.error(""); - } + // standard WAR + logger.debug("configuring Gitblit WAR"); + baseFolder = configureWAR(context, webxmlSettings, contextFolder, runtimeSettings); + } - try { - // try to lookup JNDI env-entry for the baseFolder - InitialContext ic = new InitialContext(); - Context env = (Context) ic.lookup("java:comp/env"); - String val = (String) env.lookup("baseFolder"); - if (!StringUtils.isEmpty(val)) { - path = val; - } - } catch (NamingException n) { - logger.error("Failed to get JNDI env-entry: " + n.getExplanation()); - } + // Test for Tomcat forward-slash/%2F issue and auto-adjust settings + ContainerUtils.CVE_2007_0450.test(runtimeSettings); + } - File base = com.gitblit.utils.FileUtils.resolveParameter(Constants.contextFolder$, contextFolder, path); - base.mkdirs(); + // Runtime manager is a container for settings and other parameters + IRuntimeManager runtime = startManager(injector, IRuntimeManager.class); + runtime.setBaseFolder(baseFolder); + runtime.getStatus().isGO = goSettings != null; + runtime.getStatus().servletContainer = context.getServerInfo(); - // try to extract the data folder resource to the baseFolder - File localSettings = new File(base, "gitblit.properties"); - if (!localSettings.exists()) { - extractResources(context, "/WEB-INF/data/", base); - } + repositoriesFolder = getRepositoriesFolder(); + + logger.info("Gitblit base folder = " + baseFolder.getAbsolutePath()); + logger.info("Git repositories folder = " + repositoriesFolder.getAbsolutePath()); + + // prepare service executors + mailExecutor = new MailExecutor(runtimeSettings); + luceneExecutor = new LuceneExecutor(runtimeSettings, getManager(IRepositoryManager.class)); + gcExecutor = new GCExecutor(runtimeSettings, getManager(IRepositoryManager.class)); + mirrorExecutor = new MirrorExecutor(runtimeSettings, getManager(IRepositoryManager.class)); + + // initialize utilities + String prefix = runtimeSettings.getString(Keys.git.userRepositoryPrefix, "~"); + ModelUtils.setUserRepoPrefix(prefix); + + // calculate repository list settings checksum for future config changes + repositoryListSettingsChecksum.set(getRepositoryListSettingsChecksum()); + + // build initial repository list + if (runtimeSettings.getBoolean(Keys.git.cacheRepositoryList, true)) { + logger.info("Identifying available repositories..."); + getRepositoryList(); + } + + if (this.userService == null) { + String realm = runtimeSettings.getString(Keys.realm.userService, "${baseFolder}/users.properties"); + IUserService loginService = null; + try { + // check to see if this "file" is a login service class + Class realmClass = Class.forName(realm); + loginService = (IUserService) realmClass.newInstance(); + } catch (Throwable t) { + loginService = new GitblitUserService(); + } + setUserService(loginService); + } + + loadSettingModels(runtime.getSettingsModel()); + + // load and cache the project metadata + projectConfigs = new FileBasedConfig(runtime.getFileOrFolder(Keys.web.projectsFile, "${baseFolder}/projects.conf"), FS.detect()); + getProjectConfigs(); + + configureMailExecutor(); + configureLuceneIndexing(); + configureGarbageCollector(); + configureMirrorExecutor(); + if (true/*startFederation*/) { + configureFederation(); + } + configureJGit(); + configureFanout(); + configureGitDaemon(); + configureCommitCache(); + } + + /** + * Configures Gitblit GO + * + * @param context + * @param settings + * @param baseFolder + * @param runtimeSettings + * @return the base folder + */ + protected File configureGO( + ServletContext context, + IStoredSettings goSettings, + File goBaseFolder, + IStoredSettings runtimeSettings) { + + // merge the stored settings into the runtime settings + // + // if runtimeSettings is also a FileSettings w/o a specified target file, + // the target file for runtimeSettings is set to "localSettings". + runtimeSettings.merge(goSettings); + File base = goBaseFolder; + return base; + } + + + /** + * Configures a standard WAR instance of Gitblit. + * + * @param context + * @param webxmlSettings + * @param contextFolder + * @param runtimeSettings + * @return the base folder + */ + protected File configureWAR( + ServletContext context, + WebXmlSettings webxmlSettings, + File contextFolder, + IStoredSettings runtimeSettings) { - // delegate all config to baseFolder/gitblit.properties file - FileSettings settings = new FileSettings(localSettings.getAbsolutePath()); - configureContext(settings, base, true); + // Gitblit is running in a standard servlet container + logger.info("WAR contextFolder is " + ((contextFolder != null) ? contextFolder.getAbsolutePath() : "")); + + String path = webxmlSettings.getString(Constants.baseFolder, Constants.contextFolder$ + "/WEB-INF/data"); + + if (path.contains(Constants.contextFolder$) && contextFolder == null) { + // warn about null contextFolder (issue-199) + logger.error(""); + logger.error(MessageFormat.format("\"{0}\" depends on \"{1}\" but \"{2}\" is returning NULL for \"{1}\"!", + Constants.baseFolder, Constants.contextFolder$, context.getServerInfo())); + logger.error(MessageFormat.format("Please specify a non-parameterized path for {0} in web.xml!!", Constants.baseFolder)); + logger.error(MessageFormat.format("OR configure your servlet container to specify a \"{0}\" parameter in the context configuration!!", Constants.baseFolder)); + logger.error(""); + } + + try { + // try to lookup JNDI env-entry for the baseFolder + InitialContext ic = new InitialContext(); + Context env = (Context) ic.lookup("java:comp/env"); + String val = (String) env.lookup("baseFolder"); + if (!StringUtils.isEmpty(val)) { + path = val; } + } catch (NamingException n) { + logger.error("Failed to get JNDI env-entry: " + n.getExplanation()); + } - // WAR or Express is likely to be running on a Tomcat. - // Test for the forward-slash/%2F issue and auto-adjust settings. - ContainerUtils.CVE_2007_0450.test(settings); + File base = com.gitblit.utils.FileUtils.resolveParameter(Constants.contextFolder$, contextFolder, path); + base.mkdirs(); + + // try to extract the data folder resource to the baseFolder + File localSettings = new File(base, "gitblit.properties"); + if (!localSettings.exists()) { + extractResources(context, "/WEB-INF/data/", base); } - settingsModel = loadSettingModels(); - serverStatus.servletContainer = context.getServerInfo(); + // delegate all config to baseFolder/gitblit.properties file + FileSettings fileSettings = new FileSettings(localSettings.getAbsolutePath()); + + // merge the stored settings into the runtime settings + // + // if runtimeSettings is also a FileSettings w/o a specified target file, + // the target file for runtimeSettings is set to "localSettings". + runtimeSettings.merge(fileSettings); + + return base; + } + + /** + * Configures an OpenShift instance of Gitblit. + * + * @param context + * @param webxmlSettings + * @param contextFolder + * @param runtimeSettings + * @return the base folder + */ + private File configureExpress( + ServletContext context, + WebXmlSettings webxmlSettings, + File contextFolder, + IStoredSettings runtimeSettings) { + + // Gitblit is running in OpenShift/JBoss + String openShift = System.getenv("OPENSHIFT_DATA_DIR"); + File base = new File(openShift); + logger.info("EXPRESS contextFolder is " + contextFolder.getAbsolutePath()); + + // Copy the included scripts to the configured groovy folder + String path = webxmlSettings.getString(Keys.groovy.scriptsFolder, "groovy"); + File localScripts = com.gitblit.utils.FileUtils.resolveParameter(Constants.baseFolder$, base, path); + if (!localScripts.exists()) { + File warScripts = new File(contextFolder, "/WEB-INF/data/groovy"); + if (!warScripts.equals(localScripts)) { + try { + com.gitblit.utils.FileUtils.copy(localScripts, warScripts.listFiles()); + } catch (IOException e) { + logger.error(MessageFormat.format( + "Failed to copy included Groovy scripts from {0} to {1}", + warScripts, localScripts)); + } + } + } + + // merge the WebXmlSettings into the runtime settings (for backwards-compatibilty) + runtimeSettings.merge(webxmlSettings); + + // settings are to be stored in openshift/gitblit.properties + File localSettings = new File(base, "gitblit.properties"); + FileSettings fileSettings = new FileSettings(localSettings.getAbsolutePath()); + + // merge the stored settings into the runtime settings + // + // if runtimeSettings is also a FileSettings w/o a specified target file, + // the target file for runtimeSettings is set to "localSettings". + runtimeSettings.merge(fileSettings); + + return base; } protected void extractResources(ServletContext context, String path, File toDir) { @@ -3858,6 +3799,11 @@ public class GitBlit extends DaggerContextListener @Override protected void destroyContext(ServletContext context) { logger.info("Gitblit context destroyed by servlet container."); + for (IManager manager : managers) { + logger.debug("stopping {}", manager.getClass().getSimpleName()); + manager.stop(); + } + scheduledExecutor.shutdownNow(); luceneExecutor.close(); gcExecutor.close(); @@ -4059,6 +4005,14 @@ public class GitBlit extends DaggerContextListener return new Object [] { new DaggerModule(this) }; } + protected X startManager(ObjectGraph injector, Class clazz) { + logger.debug("injecting and starting {}", clazz.getSimpleName()); + X x = injector.get(clazz); + x.setup(); + managers.add(x); + return x; + } + /** * Instantiate and inject all filters and servlets into the container using * the servlet 3 specification. diff --git a/src/main/java/com/gitblit/GitBlitServer.java b/src/main/java/com/gitblit/GitBlitServer.java index 292eec40..fe29804d 100644 --- a/src/main/java/com/gitblit/GitBlitServer.java +++ b/src/main/java/com/gitblit/GitBlitServer.java @@ -409,9 +409,8 @@ public class GitBlitServer { rootContext.setHandler(sh); } - // Setup the GitBlit context + // Setup the Gitblit context GitBlit gitblit = newGitblit(settings, baseFolder); - gitblit.configureContext(settings, baseFolder, true); rootContext.addEventListener(gitblit); try { diff --git a/src/main/java/com/gitblit/Gitblit.java b/src/main/java/com/gitblit/Gitblit.java index 32157545..687e4e20 100644 --- a/src/main/java/com/gitblit/Gitblit.java +++ b/src/main/java/com/gitblit/Gitblit.java @@ -33,6 +33,7 @@ import com.gitblit.Constants.FederationRequest; import com.gitblit.Constants.FederationToken; import com.gitblit.manager.IFederationManager; import com.gitblit.manager.IGitblitManager; +import com.gitblit.manager.IManager; import com.gitblit.manager.INotificationManager; import com.gitblit.manager.IProjectManager; import com.gitblit.manager.IRepositoryManager; @@ -111,6 +112,16 @@ public class Gitblit implements IRuntimeManager, this.gitblitManager = gitblitManager; } + @Override + public IManager setup() { + return this; + } + + @Override + public IManager stop() { + return this; + } + /* * RUNTIME MANAGER */ diff --git a/src/main/java/com/gitblit/IStoredSettings.java b/src/main/java/com/gitblit/IStoredSettings.java index 01c478f8..6b58e89a 100644 --- a/src/main/java/com/gitblit/IStoredSettings.java +++ b/src/main/java/com/gitblit/IStoredSettings.java @@ -351,4 +351,13 @@ public abstract class IStoredSettings { * @return true if successful */ public abstract boolean saveSettings(Map updatedSettings); + + /** + * Merge all settings from the settings parameter into this instance. + * + * @param settings + */ + public void merge(IStoredSettings settings) { + getSettings().putAll(settings.getSettings()); + } } \ No newline at end of file diff --git a/src/main/java/com/gitblit/manager/IManager.java b/src/main/java/com/gitblit/manager/IManager.java new file mode 100644 index 00000000..955c6100 --- /dev/null +++ b/src/main/java/com/gitblit/manager/IManager.java @@ -0,0 +1,23 @@ +/* + * Copyright 2013 gitblit.com. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gitblit.manager; + +public interface IManager { + + IManager setup(); + + IManager stop(); +} diff --git a/src/main/java/com/gitblit/manager/IRuntimeManager.java b/src/main/java/com/gitblit/manager/IRuntimeManager.java index 178b93ca..94ce3800 100644 --- a/src/main/java/com/gitblit/manager/IRuntimeManager.java +++ b/src/main/java/com/gitblit/manager/IRuntimeManager.java @@ -24,7 +24,7 @@ import com.gitblit.IStoredSettings; import com.gitblit.models.ServerSettings; import com.gitblit.models.ServerStatus; -public interface IRuntimeManager { +public interface IRuntimeManager extends IManager { void setBaseFolder(File folder); diff --git a/src/main/java/com/gitblit/manager/RuntimeManager.java b/src/main/java/com/gitblit/manager/RuntimeManager.java new file mode 100644 index 00000000..cfb4543e --- /dev/null +++ b/src/main/java/com/gitblit/manager/RuntimeManager.java @@ -0,0 +1,205 @@ +/* + * 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.text.SimpleDateFormat; +import java.util.Date; +import java.util.Map; +import java.util.TimeZone; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.gitblit.Constants; +import com.gitblit.IStoredSettings; +import com.gitblit.Keys; +import com.gitblit.models.ServerSettings; +import com.gitblit.models.ServerStatus; +import com.gitblit.models.SettingModel; +import com.gitblit.utils.StringUtils; + +public class RuntimeManager implements IRuntimeManager { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final IStoredSettings settings; + + private final ServerStatus serverStatus; + + private TimeZone timezone; + + private File baseFolder; + + private ServerSettings settingsModel; + + public RuntimeManager(IStoredSettings settings) { + this.settings = settings; + this.settingsModel = new ServerSettings(); + this.serverStatus = new ServerStatus(); + } + + @Override + public RuntimeManager setup() { + logger.info("Gitblit settings = " + settings.toString()); + logTimezone("JVM", TimeZone.getDefault()); + logTimezone(Constants.NAME, getTimezone()); + return this; + } + + @Override + public RuntimeManager stop() { + return this; + } + + @Override + public File getBaseFolder() { + return baseFolder; + } + + @Override + public void setBaseFolder(File folder) { + this.baseFolder = folder; + } + + /** + * Returns the boot date of the Gitblit server. + * + * @return the boot date of Gitblit + */ + @Override + public Date getBootDate() { + return serverStatus.bootDate; + } + + @Override + public ServerSettings getSettingsModel() { + // ensure that the current values are updated in the setting models + for (String key : settings.getAllKeys(null)) { + SettingModel setting = settingsModel.get(key); + if (setting == null) { + // unreferenced setting, create a setting model + setting = new SettingModel(); + setting.name = key; + settingsModel.add(setting); + } + setting.currentValue = settings.getString(key, ""); + } +// settingsModel.pushScripts = getAllScripts(); + return settingsModel; + } + + /** + * 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 + */ + @Override + public boolean isServingRepositories() { + return settings.getBoolean(Keys.git.enableGitServlet, true) || (settings.getInteger(Keys.git.daemonPort, 0) > 0); + } + + /** + * Returns the preferred timezone for the Gitblit instance. + * + * @return a timezone + */ + @Override + public TimeZone getTimezone() { + if (timezone == null) { + String tzid = settings.getString("web.timezone", null); + if (StringUtils.isEmpty(tzid)) { + timezone = TimeZone.getDefault(); + return timezone; + } + timezone = TimeZone.getTimeZone(tzid); + } + return timezone; + } + + private void logTimezone(String type, TimeZone zone) { + SimpleDateFormat df = new SimpleDateFormat("z Z"); + df.setTimeZone(zone); + String offset = df.format(new Date()); + logger.info(type + " timezone is " + zone.getID() + " (" + offset + ")"); + } + + /** + * Is Gitblit running in debug mode? + * + * @return true if Gitblit is running in debug mode + */ + @Override + public boolean isDebugMode() { + return settings.getBoolean(Keys.web.debugMode, false); + } + + /** + * Returns the file object for the specified configuration key. + * + * @return the file + */ + @Override + public File getFileOrFolder(String key, String defaultFileOrFolder) { + String fileOrFolder = settings.getString(key, defaultFileOrFolder); + return getFileOrFolder(fileOrFolder); + } + + /** + * 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 + */ + @Override + public File getFileOrFolder(String fileOrFolder) { + return com.gitblit.utils.FileUtils.resolveParameter(Constants.baseFolder$, + baseFolder, fileOrFolder); + } + + /** + * 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 updatedSettings) { + return settings.saveSettings(updatedSettings); + } + + @Override + public ServerStatus getStatus() { + // update heap memory status + serverStatus.heapAllocated = Runtime.getRuntime().totalMemory(); + serverStatus.heapFree = Runtime.getRuntime().freeMemory(); + return serverStatus; + } +} diff --git a/src/main/java/com/gitblit/models/ServerStatus.java b/src/main/java/com/gitblit/models/ServerStatus.java index f8afd00a..bb6396bc 100644 --- a/src/main/java/com/gitblit/models/ServerStatus.java +++ b/src/main/java/com/gitblit/models/ServerStatus.java @@ -39,8 +39,6 @@ public class ServerStatus implements Serializable { public final String releaseDate; - public final boolean isGO; - public final Map systemProperties; public final long heapMaximum; @@ -49,13 +47,14 @@ public class ServerStatus implements Serializable { public volatile long heapFree; + public boolean isGO; + public String servletContainer; - public ServerStatus(boolean isGO) { + public ServerStatus() { this.bootDate = new Date(); this.version = Constants.getVersion(); this.releaseDate = Constants.getBuildDate(); - this.isGO = isGO; this.heapMaximum = Runtime.getRuntime().maxMemory(); diff --git a/src/test/java/com/gitblit/tests/mock/MockRuntimeManager.java b/src/test/java/com/gitblit/tests/mock/MockRuntimeManager.java index b42d29a5..aaa6c668 100644 --- a/src/test/java/com/gitblit/tests/mock/MockRuntimeManager.java +++ b/src/test/java/com/gitblit/tests/mock/MockRuntimeManager.java @@ -22,6 +22,7 @@ import java.util.TimeZone; import com.gitblit.Constants; import com.gitblit.IStoredSettings; +import com.gitblit.manager.IManager; import com.gitblit.manager.IRuntimeManager; import com.gitblit.models.ServerSettings; import com.gitblit.models.ServerStatus; @@ -48,7 +49,7 @@ public class MockRuntimeManager implements IRuntimeManager { public MockRuntimeManager(IStoredSettings settings) { this.settings = settings; - this.serverStatus = new ServerStatus(true); + this.serverStatus = new ServerStatus(); this.serverStatus.servletContainer = "MockServer"; this.serverSettings = new ServerSettings(); @@ -130,4 +131,13 @@ public class MockRuntimeManager implements IRuntimeManager { return settings.saveSettings(updatedSettings); } + @Override + public IManager stop() { + return this; + } + + @Override + public IRuntimeManager setup() { + return this; + } } -- 2.39.5