All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.shibboleth.utilities.java.support.service.AbstractReloadableService Maven / Gradle / Ivy

There is a newer version: 8.0.0
Show newest version
/*
 * Licensed to the University Corporation for Advanced Internet Development,
 * Inc. (UCAID) under one or more contributor license agreements.  See the
 * NOTICE file distributed with this work for additional information regarding
 * copyright ownership. The UCAID licenses this file to You 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 net.shibboleth.utilities.java.support.service;

import java.util.Timer;
import java.util.TimerTask;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import net.shibboleth.utilities.java.support.annotation.Duration;
import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
import net.shibboleth.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
import net.shibboleth.utilities.java.support.component.UnmodifiableComponent;

import org.joda.time.DateTime;
import org.joda.time.chrono.ISOChronology;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Base class for {@link ReloadableService}. This base class will use a background thread that will perform a periodic
 * check, via {@link #shouldReload()}, and, if required, invoke the services {@link #reload()} method. 
* * This class does not deal with any synchronization. That is left to implementing classes. * * @param The sort of service this implements. */ public abstract class AbstractReloadableService extends AbstractIdentifiableInitializableComponent implements ReloadableService, UnmodifiableComponent { /** Class logger. */ @Nonnull private final Logger log = LoggerFactory.getLogger(AbstractReloadableService.class); /** Number of milliseconds between one reload check and another. */ @Duration private long reloadCheckDelay; /** Timer used to schedule configuration reload tasks. */ @Nullable private Timer reloadTaskTimer; /** Timer used to schedule reload tasks if no external one set. */ @Nullable private Timer internalTaskTimer; /** Watcher that monitors the set of configuration resources for this service for changes. */ @Nullable private ServiceReloadTask reloadTask; /** The last time time the service was reloaded, whether successful or not. */ @Nullable private DateTime lastReloadInstant; /** The last time the service was reloaded successfully. */ @Nullable private DateTime lastSuccessfulReleaseInstant; /** The cause of the last reload failure, if the last reload failed. */ @Nullable private Throwable reloadFailureCause; /** Do we fail immediately if the config is bogus? */ private boolean failFast; /** The log prefix. */ @Nullable private String logPrefix; /** Constructor. */ public AbstractReloadableService() { reloadCheckDelay = 0; } /** * Gets the number of milliseconds between one reload check and another. A value of 0 or less indicates that no * reloading will be performed. * *

* Default value: 0 *

* * @return number of milliseconds between one reload check and another */ @Duration public long getReloadCheckDelay() { return reloadCheckDelay; } /** * Sets the number of milliseconds between one reload check and another. A value of 0 or less indicates that no * reloading will be performed. * * This setting can not be changed after the service has been initialized. * * @param delay number of milliseconds between one reload check and another */ @Duration public void setReloadCheckDelay(@Duration final long delay) { ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this); reloadCheckDelay = delay; } /** * Gets the timer used to schedule configuration reload tasks. * * @return timer used to schedule configuration reload tasks */ @Nullable public Timer getReloadTaskTimer() { return reloadTaskTimer; } /** * Sets the timer used to schedule configuration reload tasks. * * This setting can not be changed after the service has been initialized. * * @param timer timer used to schedule configuration reload tasks */ public void setReloadTaskTimer(@Nullable final Timer timer) { ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this); reloadTaskTimer = timer; } /** {@inheritDoc} */ @Override @Nullable public DateTime getLastReloadAttemptInstant() { return lastReloadInstant; } /** {@inheritDoc} */ @Override @Nullable public DateTime getLastSuccessfulReloadInstant() { return lastSuccessfulReleaseInstant; } /** {@inheritDoc} */ @Override @Nullable public Throwable getReloadFailureCause() { return reloadFailureCause; } /** * Do we fail fast? * * @return whether we fail fast. */ public boolean isFailFast() { return failFast; } /** * Sets whether we fail fast. * * @param value what to set. */ public void setFailFast(final boolean value) { ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this); failFast = value; } /** {@inheritDoc} */ @Override protected void doInitialize() throws ComponentInitializationException { super.doInitialize(); log.info("{} Performing initial load", getLogPrefix()); try { lastReloadInstant = new DateTime(ISOChronology.getInstanceUTC()); doReload(); lastSuccessfulReleaseInstant = lastReloadInstant; } catch (final ServiceException e) { if (isFailFast()) { throw new ComponentInitializationException(getLogPrefix() + " could not perform initial load", e); } log.error("{} Initial load failed", getLogPrefix(), e); if (reloadCheckDelay > 0) { log.info("{} Continuing to poll configuration", getLogPrefix()); } else { log.error("{} No further attempts will be made to reload", getLogPrefix()); } } catch (final Exception e) { log.error("{} Unexpected error during initial load {}", getLogPrefix(), e); throw new ComponentInitializationException(getLogPrefix() + " Unexpected error during initial load", e); } if (reloadCheckDelay > 0) { if (null == reloadTaskTimer) { log.debug("{} No reload task timer specified, creating default", getLogPrefix()); internalTaskTimer = new Timer("Timer for " + getId(), true); } else { internalTaskTimer = reloadTaskTimer; } log.info("{} Reload time set to: {}, starting refresh thread", getLogPrefix(), reloadCheckDelay); reloadTask = new ServiceReloadTask(); internalTaskTimer.schedule(reloadTask, reloadCheckDelay, reloadCheckDelay); } } /** {@inheritDoc} */ @Override protected void doDestroy() { log.info("{} Starting shutdown", getLogPrefix()); if (reloadTask != null) { reloadTask.cancel(); reloadTask = null; } if (reloadTaskTimer == null && internalTaskTimer != null) { internalTaskTimer.cancel(); } internalTaskTimer = null; log.info("{} Completing shutdown", getLogPrefix()); super.doDestroy(); } /** {@inheritDoc} */ @Override public final void reload() { final DateTime now = new DateTime(ISOChronology.getInstanceUTC()); lastReloadInstant = now; try { doReload(); lastSuccessfulReleaseInstant = now; reloadFailureCause = null; } catch (final ServiceException e) { log.error("{} Reload for {} failed", getLogPrefix(), getId(), e); reloadFailureCause = e; throw e; } } /** * Called by the {@link ServiceReloadTask} to determine if the service should be reloaded. * *

* No lock is held when this method is called, so any locking needed should be handled internally. *

* * @return true iff the service should be reloaded */ protected abstract boolean shouldReload(); /** * Performs the actual reload. * *

* No lock is held when this method is called, so any locking needed should be handled internally. *

* * @throws ServiceException thrown if there is a problem reloading the service */ protected void doReload() { log.info("{} Reloading service configuration", getLogPrefix()); } /** * Return a string which is to be prepended to all log messages. * * @return "Service '' :" */ @Nonnull @NotEmpty protected String getLogPrefix() { // local cache of cached entry to allow unsynchronized clearing of per class cache. String prefix = logPrefix; if (null == prefix) { if (getId() != null) { final StringBuilder builder = new StringBuilder("Service '").append(getId()).append("':"); prefix = builder.toString(); if (null == logPrefix) { logPrefix = prefix; } } else { prefix = "Service:"; } } return prefix; } /** * A watcher that determines if a service should be reloaded and does so as appropriate. */ protected class ServiceReloadTask extends TimerTask { /** {@inheritDoc} */ @Override public void run() { if (shouldReload()) { try { reload(); } catch (final ServiceException se) { log.debug("{} Previously logged error during reload", getLogPrefix(), se); } catch (final Throwable t) { log.error("{} Unexpected error during reload", getLogPrefix(), t); } } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy