From e6cbb6a4cf268e0b7a93e1a4d28dac11c0bad9a2 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sat, 25 Jan 2014 12:07:38 +0100 Subject: [PATCH] start implementation of repository health checks --- .../AbstractSimpleRepositoryHandler.java | 3 +- .../scm/repository/DirectoryHealthCheck.java | 212 ++++++++++++++++ .../sonia/scm/repository/HealthCheck.java | 54 +++++ .../scm/repository/HealthCheckFailure.java | 212 ++++++++++++++++ .../scm/repository/HealthCheckResult.java | 186 ++++++++++++++ .../java/sonia/scm/repository/Repository.java | 64 ++++- .../RepositoryDirectoryHandler.java | 55 +++++ .../scm/repository/DBFormatHealthCheck.java | 228 ++++++++++++++++++ .../java/sonia/scm/ScmContextListener.java | 3 + .../main/java/sonia/scm/ScmServletModule.java | 5 + .../HealthCheckContextListener.java | 153 ++++++++++++ .../sonia/scm/repository/HealthChecker.java | 158 ++++++++++++ 12 files changed, 1329 insertions(+), 4 deletions(-) create mode 100644 scm-core/src/main/java/sonia/scm/repository/DirectoryHealthCheck.java create mode 100644 scm-core/src/main/java/sonia/scm/repository/HealthCheck.java create mode 100644 scm-core/src/main/java/sonia/scm/repository/HealthCheckFailure.java create mode 100644 scm-core/src/main/java/sonia/scm/repository/HealthCheckResult.java create mode 100644 scm-core/src/main/java/sonia/scm/repository/RepositoryDirectoryHandler.java create mode 100644 scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/DBFormatHealthCheck.java create mode 100644 scm-webapp/src/main/java/sonia/scm/repository/HealthCheckContextListener.java create mode 100644 scm-webapp/src/main/java/sonia/scm/repository/HealthChecker.java diff --git a/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java b/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java index 20710a20ad..b76510a36a 100644 --- a/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java +++ b/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java @@ -64,7 +64,7 @@ import java.net.URL; * @param */ public abstract class AbstractSimpleRepositoryHandler - extends AbstractRepositoryHandler + extends AbstractRepositoryHandler implements RepositoryDirectoryHandler { /** Field description */ @@ -249,6 +249,7 @@ public abstract class AbstractSimpleRepositoryHandlerof()); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param failures + */ + private HealthCheckResult(Set failures) + { + this.failures = failures; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + public static HealthCheckResult healthy() + { + return HEALTHY; + } + + /** + * Method description + * + * + * @param failures + * + * @return + */ + public static HealthCheckResult unhealthy( + Iterable failures) + { + return new HealthCheckResult(ImmutableSet.copyOf(failures)); + } + + /** + * Method description + * + * + * @param failure + * @param otherFailures + * + * @return + */ + public static HealthCheckResult unhealthy(HealthCheckFailure failure, + HealthCheckFailure... otherFailures) + { + //J- + return new HealthCheckResult( + ImmutableSet.builder() + .add(failure) + .add(otherFailures) + .build() + ); + //J+ + } + + /** + * Method description + * + * + * @param otherResult + * + * @return + */ + public HealthCheckResult merge(HealthCheckResult otherResult) + { + HealthCheckResult merged; + + if ((otherResult == null) || otherResult.isHealthy()) + { + merged = this; + } + else + { + //J- + merged = new HealthCheckResult( + ImmutableSet.builder() + .addAll(failures) + .addAll(otherResult.failures) + .build() + ); + //J+ + } + + return merged; + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + public Set getFailures() + { + return failures; + } + + /** + * Method description + * + * + * @return + */ + public boolean isHealthy() + { + return failures.isEmpty(); + } + + /** + * Method description + * + * + * @return + */ + public boolean isUnhealthy() + { + return !failures.isEmpty(); + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private final Set failures; +} diff --git a/scm-core/src/main/java/sonia/scm/repository/Repository.java b/scm-core/src/main/java/sonia/scm/repository/Repository.java index 1193165a23..15bee4093b 100644 --- a/scm-core/src/main/java/sonia/scm/repository/Repository.java +++ b/scm-core/src/main/java/sonia/scm/repository/Repository.java @@ -47,11 +47,13 @@ import sonia.scm.util.ValidationUtil; //~--- JDK imports ------------------------------------------------------------ import java.util.Arrays; +import java.util.Collections; import java.util.List; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; /** @@ -59,8 +61,8 @@ import javax.xml.bind.annotation.XmlRootElement; * * @author Sebastian Sdorra */ -@XmlRootElement(name = "repositories") @XmlAccessorType(XmlAccessType.FIELD) +@XmlRootElement(name = "repositories") public class Repository extends BasicPropertiesAware implements ModelObject { @@ -163,6 +165,8 @@ public class Repository extends BasicPropertiesAware implements ModelObject repository.setUrl(url); repository.setPublicReadable(publicReadable); repository.setArchived(archived); + + // do not copy health check results } /** @@ -216,7 +220,8 @@ public class Repository extends BasicPropertiesAware implements ModelObject && Objects.equal(url, other.url) && Objects.equal(creationDate, other.creationDate) && Objects.equal(lastModified, other.lastModified) - && Objects.equal(properties, other.properties); + && Objects.equal(properties, other.properties) + && Objects.equal(healthCheckFailures, other.healthCheckFailures); //J+ } @@ -230,7 +235,8 @@ public class Repository extends BasicPropertiesAware implements ModelObject public int hashCode() { return Objects.hashCode(id, name, contact, description, publicReadable, - archived, permissions, type, url, creationDate, lastModified, properties); + archived, permissions, type, url, creationDate, lastModified, properties, + healthCheckFailures); } /** @@ -256,6 +262,7 @@ public class Repository extends BasicPropertiesAware implements ModelObject .add("lastModified", lastModified) .add("creationDate", creationDate) .add("properties", properties) + .add("healthCheckFailures", healthCheckFailures) .toString(); //J+ } @@ -296,6 +303,24 @@ public class Repository extends BasicPropertiesAware implements ModelObject return description; } + /** + * Method description + * + * + * @return + * @since 1.36 + */ + @SuppressWarnings("unchecked") + public List getHealthCheckFailures() + { + if (healthCheckFailures == null) + { + healthCheckFailures = Collections.EMPTY_LIST; + } + + return healthCheckFailures; + } + /** * Returns the unique id of the {@link Repository}. * @@ -384,6 +409,19 @@ public class Repository extends BasicPropertiesAware implements ModelObject return archived; } + /** + * Method description + * + * + * @return + * + * @since 1.36 + */ + public boolean isHealthy() + { + return Util.isEmpty(healthCheckFailures); + } + /** * Returns true if the {@link Repository} is public readable. * @@ -542,6 +580,19 @@ public class Repository extends BasicPropertiesAware implements ModelObject this.url = url; } + /** + * Method description + * + * + * @param healthCheckFailures + * + * @since 1.36 + */ + void setHealthCheckFailures(List healthCheckFailures) + { + this.healthCheckFailures = healthCheckFailures; + } + //~--- fields --------------------------------------------------------------- /** Field description */ @@ -553,6 +604,13 @@ public class Repository extends BasicPropertiesAware implements ModelObject /** Field description */ private String description; + /** + * @since 1.36 + */ + @XmlElement(name = "healthCheckFailure") + @XmlElementWrapper(name = "healthCheckFailures") + private List healthCheckFailures; + /** Field description */ private String id; diff --git a/scm-core/src/main/java/sonia/scm/repository/RepositoryDirectoryHandler.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryDirectoryHandler.java new file mode 100644 index 0000000000..3fda4a76be --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/repository/RepositoryDirectoryHandler.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. 2. Redistributions in + * binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. 3. Neither the name of SCM-Manager; + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.repository; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.File; + +/** + * + * @author Sebastian Sdorra + * @since 1.36 + */ +public interface RepositoryDirectoryHandler extends RepositoryHandler +{ + + /** + * Method description + * + * + * @param repository + * + * @return + */ + public File getDirectory(Repository repository); +} diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/DBFormatHealthCheck.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/DBFormatHealthCheck.java new file mode 100644 index 0000000000..c9958f24fb --- /dev/null +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/DBFormatHealthCheck.java @@ -0,0 +1,228 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. 2. Redistributions in + * binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. 3. Neither the name of SCM-Manager; + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.repository; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Charsets; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.io.Files; +import com.google.inject.Inject; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.io.SVNRepository; +import org.tmatesoft.svn.core.io.SVNRepositoryFactory; + +import sonia.scm.plugin.ext.Extension; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.File; +import java.io.IOException; + +import java.util.List; +import java.util.Set; + +/** + * + * @author Sebastian Sdorra + */ +@Extension +public class DBFormatHealthCheck extends DirectoryHealthCheck +{ + + /** + * the logger for DBFormatHealthCheck + */ + private static final Logger logger = + LoggerFactory.getLogger(DBFormatHealthCheck.class); + + /** Field description */ + private static final Set INVALID_DBFORMAT = ImmutableSet.of("5"); + + /** Field description */ + private static final HealthCheckFailure INCOMPATIBLE_DB_FORMAT = + new HealthCheckFailure("AnOTx99ex1", "Incompatible DB Format", + "https://bitbucket.org/sdorra/scm-manager/wiki/healtchecks/svn-incompatible-dbformat", + "The subversion db format is incompatible with the svn version used within scm-manager."); + + /** Field description */ + private static final String DBFORMAT = + "db".concat(File.separator).concat("format"); + + /** Field description */ + private static final HealthCheckFailure COULD_NOT_READ_DB_FILE = + new HealthCheckFailure("4IOTx8pvv1", "Could not read db/format file", + "The db/format file of the repository was not readable."); + + /** Field description */ + private static final HealthCheckFailure COULD_NOT_OPEN_REPOSITORY = + new HealthCheckFailure("6TOTx9RLD1", "Could not open svn repository", + "The repository is not openable."); + + /** Field description */ + private static final HealthCheckFailure COULD_NOT_FIND_DB_FILE = + new HealthCheckFailure("A9OTx8leC1", "Could not find db/format file", + "The subversion repository does not contain the db/format file."); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param repositoryManager + */ + @Inject + public DBFormatHealthCheck(RepositoryManager repositoryManager) + { + super(repositoryManager); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param repository + * @param directory + * + * @return + */ + @Override + protected HealthCheckResult check(Repository repository, File directory) + { + List failures = Lists.newArrayList(); + + checkIfRepositoryIsOpenable(failures, repository, directory); + checkForBadDBVersion(failures, repository, directory); + + return failures.isEmpty() + ? HealthCheckResult.healthy() + : HealthCheckResult.unhealthy(failures); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param repository + * + * @return + */ + @Override + protected boolean isCheckResponsible(Repository repository) + { + return SvnRepositoryHandler.TYPE_NAME.equalsIgnoreCase( + repository.getType()); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param failures + * @param repository + * @param directory + */ + private void checkForBadDBVersion(List failures, + Repository repository, File directory) + { + File dbfile = new File(directory, DBFORMAT); + + if (dbfile.exists()) + { + try + { + String content = Files.readFirstLine(dbfile, Charsets.US_ASCII); + + if ((content != null) && INVALID_DBFORMAT.contains(content.trim())) + { + failures.add(INCOMPATIBLE_DB_FORMAT); + } + } + catch (IOException ex) + { + failures.add(COULD_NOT_READ_DB_FILE); + logger.warn( + "could not read db/format of ".concat(repository.getName()), ex); + } + } + else + { + failures.add(COULD_NOT_FIND_DB_FILE); + logger.warn("repository {} does not have a {} file", + repository.getName(), DBFORMAT); + } + } + + /** + * Method description + * + * + * @param failures + * @param repository + * @param directory + */ + private void checkIfRepositoryIsOpenable(List failures, + Repository repository, File directory) + { + SVNRepository svn = null; + + try + { + svn = SVNRepositoryFactory.create(SVNURL.fromFile(directory)); + } + catch (SVNException ex) + { + + failures.add(COULD_NOT_OPEN_REPOSITORY); + + logger.warn( + "Could not open svn repository ".concat(repository.getName()), ex); + } + finally + { + SvnUtil.closeSession(svn); + } + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java b/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java index aeda5c6ddf..3845c985f7 100644 --- a/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java +++ b/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java @@ -59,6 +59,7 @@ import java.util.List; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; +import sonia.scm.repository.HealthCheckContextListener; /** * @@ -141,6 +142,8 @@ public class ScmContextListener extends GuiceServletContextListener // init servlet context listeners globalInjector.getInstance(ServletContextListenerHolder.class) .contextInitialized(servletContextEvent); + globalInjector.getInstance(HealthCheckContextListener.class) + .contextInitialized(servletContextEvent); //J+ } } diff --git a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java index 6d8b379c64..4ae22b64a3 100644 --- a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java +++ b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java @@ -154,6 +154,8 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; +import sonia.scm.repository.HealthCheckContextListener; +import sonia.scm.repository.HealthChecker; /** * @@ -275,6 +277,9 @@ public class ScmServletModule extends ServletModule bind(CipherHandler.class).toInstance(cu.getCipherHandler()); bind(EncryptionHandler.class, MessageDigestEncryptionHandler.class); bind(FileSystem.class, DefaultFileSystem.class); + + // bind health check stuff + bind(HealthCheckContextListener.class); // bind extensions pluginLoader.processExtensions(binder()); diff --git a/scm-webapp/src/main/java/sonia/scm/repository/HealthCheckContextListener.java b/scm-webapp/src/main/java/sonia/scm/repository/HealthCheckContextListener.java new file mode 100644 index 0000000000..545871b508 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/repository/HealthCheckContextListener.java @@ -0,0 +1,153 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. 2. Redistributions in + * binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. 3. Neither the name of SCM-Manager; + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.repository; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.inject.Inject; + +import org.apache.shiro.SecurityUtils; + +import sonia.scm.plugin.ext.Extension; +import sonia.scm.web.security.AdministrationContext; +import sonia.scm.web.security.PrivilegedAction; + +//~--- JDK imports ------------------------------------------------------------ + +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; + +/** + * + * @author Sebastian Sdorra + */ +@Extension +public class HealthCheckContextListener implements ServletContextListener +{ + + /** + * Constructs ... + * + * + * @param context + */ + @Inject + public HealthCheckContextListener(AdministrationContext context) + { + this.context = context; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param sce + */ + @Override + public void contextDestroyed(ServletContextEvent sce) + { + + // do nothing + } + + /** + * Method description + * + * + * @param sce + */ + @Override + public void contextInitialized(ServletContextEvent sce) + { + context.runAsAdmin(HealthCheckStartupAction.class); + } + + //~--- inner classes -------------------------------------------------------- + + /** + * Class description + * + * + * @version Enter version here..., 14/01/23 + * @author Enter your name here... + */ + static class HealthCheckStartupAction implements PrivilegedAction + { + + /** + * Constructs ... + * + * + * @param healthChecker + */ + @Inject + public HealthCheckStartupAction(HealthChecker healthChecker) + { + this.healthChecker = healthChecker; + } + + //~--- methods ------------------------------------------------------------ + + /** + * Method description + * + */ + @Override + public void run() + { + + // excute health checks for all repsitories asynchronous + SecurityUtils.getSubject().execute(new Runnable() + { + + @Override + public void run() + { + healthChecker.checkAll(); + } + }); + } + + //~--- fields ------------------------------------------------------------- + + /** Field description */ + private final HealthChecker healthChecker; + } + + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private final AdministrationContext context; +} diff --git a/scm-webapp/src/main/java/sonia/scm/repository/HealthChecker.java b/scm-webapp/src/main/java/sonia/scm/repository/HealthChecker.java new file mode 100644 index 0000000000..7446fb0086 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/repository/HealthChecker.java @@ -0,0 +1,158 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. 2. Redistributions in + * binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. 3. Neither the name of SCM-Manager; + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.repository; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.collect.ImmutableList; +import com.google.inject.Inject; + +import org.apache.shiro.SecurityUtils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sonia.scm.security.Role; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.IOException; + +import java.util.Set; + +/** + * + * @author Sebastian Sdorra + */ +public final class HealthChecker +{ + + /** + * the logger for HealthChecker + */ + private static final Logger logger = + LoggerFactory.getLogger(HealthChecker.class); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param checks + * @param repositoryManager + */ + @Inject + public HealthChecker(Set checks, + RepositoryManager repositoryManager) + { + this.checks = checks; + this.repositoryManager = repositoryManager; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param repository + * + * @throws IOException + * @throws RepositoryException + */ + public void check(Repository repository) + throws RepositoryException, IOException + { + logger.info("start health check for repository {}", repository.getName()); + SecurityUtils.getSubject().checkRole(Role.ADMIN); + + HealthCheckResult result = HealthCheckResult.healthy(); + + for (HealthCheck check : checks) + { + logger.trace("execute health check {} for repository {}", + check.getClass(), repository.getName()); + result = result.merge(check.check(repository)); + } + + if (result.isUnhealthy()) + { + logger.warn("repository {} is unhealthy: {}", repository.getName(), + result); + } + else + { + logger.info("repository {} is healthy", repository.getName()); + } + + if (!(repository.isHealthy() && result.isHealthy())) + { + logger.trace("store health check results for repository {}", + repository.getName()); + repository.setHealthCheckFailures( + ImmutableList.copyOf(result.getFailures())); + repositoryManager.modify(repository); + } + } + + /** + * Method description + * + * + */ + public void checkAll() + { + logger.debug("check health of all repositories"); + SecurityUtils.getSubject().checkRole(Role.ADMIN); + + for (Repository repository : repositoryManager.getAll()) + { + try + { + check(repository); + } + catch (Exception ex) + { + logger.error("health check ends with exception", ex); + } + } + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private final Set checks; + + /** Field description */ + private final RepositoryManager repositoryManager; +}