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

org.apache.ode.utils.WatchDog Maven / Gradle / Ivy

There is a newer version: 1.3.8
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF 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 org.apache.ode.utils;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.File;
import java.util.Map;
import java.util.HashMap;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * This class is based on {@link org.apache.log4j.helpers.FileWatchdog}.

* Modifications have been made to support additional abstract ressource and more events (creation, deletion and updates), and to allow "manual" * invocations of {@link #check()} (i.e wihtout having to use a thread) while preserving time checking.

* Now two use cases coexist: *

    *
  1. Pass an instance of {@link WatchDog} to a new thread ({@link WatchDog} is a {@link Runnable}). * So that {@link WatchDog# check ()} will be called automatically every {@code delay} milliseconds.
  2. *
  3. Invoke {@link WatchDog# check ()} only when you feel like it. If the expiration date previously set is lower than NOW then event * callback methods will be invoked accordingly.
  4. *
* * @author Alexis Midon */ public class WatchDog implements Runnable { static final public long DEFAULT_DELAY = 30000; final Log log = LogFactory.getLog(WatchDog.class); private long expire; private T lastModif; private long delay = DEFAULT_DELAY; private boolean existedBefore, warnedAlready, interrupted; protected Mutable mutable; protected C observer; public WatchDog() { } /** * @param mutable the object to watch closely * @param delay between two checks */ public WatchDog(Mutable mutable, long delay) { this(mutable); this.delay = delay; } public WatchDog(Mutable mutable, C observer) { this.mutable = mutable; this.observer = observer; } /** * @see #WatchDog(org.apache.ode.utils.WatchDog.Mutable, long) */ public WatchDog(Mutable mutable) { this.mutable = mutable; } public Mutable getMutable() { return mutable; } public C getObserver() { return observer; } public long getDelay() { return delay; } public void setDelay(long delay) { this.delay = delay; } public void run() { try { while (!interrupted) { try { Thread.sleep(delay); } catch (InterruptedException e) { // no interruption expected } check(); } } catch (Exception e) { log.warn("Exception occured. Thread will stop", e); } } public final void check() { long now = System.currentTimeMillis(); if (expire <= now) { /* get a lock on the observer right now It would be overkilled to lock before testing the dates. By locking after the comparaison the worst scenario is that 2 threads get in the "if", thread A gets the lock, thread B waits for it. Once the lock is released, thread B acquires it and do the checks on the mutable. So this scenario is *harmless*. */ observer.getLock().lock(); expire = now + delay; try { if (log.isDebugEnabled()) log.debug("[" + mutable + "]" + " check for changes"); if (mutable.exists()) { existedBefore = true; if (lastModif == null || mutable.hasChangedSince(lastModif)) { lastModif = mutable.lastModified(); observer.onUpdate(); if (log.isInfoEnabled()) log.info("[" + mutable + "]" + " updated"); warnedAlready = false; } else { if (log.isDebugEnabled()) log.debug("[" + mutable + "]" + " has not changed"); } } else if (!observer.isInitialized()) { // no resource and first time observer.init(); if (log.isInfoEnabled()) log.info("[" + mutable + "]" + " initialized"); } else { if (existedBefore) { existedBefore = false; lastModif = null; observer.onDelete(); if (log.isInfoEnabled()) log.info("[" + mutable + "]" + " deleted"); } if (!warnedAlready) { warnedAlready = true; if (log.isInfoEnabled()) log.info("[" + mutable + "]" + " does not exist."); } } }catch(Exception e){ if (log.isDebugEnabled()) log.debug("[" + mutable + "]" + " exception occurred during check.", e); // reset so that the next check retries right away expire = 0; lastModif = null; existedBefore = false; warnedAlready = false; observer.reset(); if (log.isInfoEnabled()) log.info("[" + mutable + "] resetted."); throw new RuntimeException(e); } finally { observer.getLock().unlock(); } } else { if (log.isTraceEnabled()) log.trace("[" + mutable + "]" + " wait period is not over"); } } public static WatchDog watchFile(File file, C handler) { return new WatchDog(new FileMutable(file), handler); } public static WatchDog, C> watchFiles(List files, C handler) { return new WatchDog, C>(new FileSetMutable(files), handler); } /** * have you said that duck typing would be nice? */ public interface Mutable { boolean exists(); boolean hasChangedSince(T since); T lastModified(); } static public class FileMutable implements WatchDog.Mutable { File file; public FileMutable(File file) { this.file = file; } public boolean exists() { return file.exists(); } public boolean hasChangedSince(Long since) { // do use 'greater than' to handle file deletion. The timestamp of a non-exising file is 0L. return lastModified().longValue() != since.longValue(); } public Long lastModified() { return Long.valueOf(file.lastModified()); } public String toString() { return file.toString(); } } static public class FileSetMutable implements WatchDog.Mutable> { File[] files; public FileSetMutable(Collection files) { this.files = new File[files.size()]; files.toArray(this.files); } public FileSetMutable(File[] files) { this.files = files; } public boolean exists() { return true; } public boolean hasChangedSince(Map since) { Map snapshot = lastModified(); return !CollectionUtils.equals(snapshot, since); } public Map lastModified() { Map m = new HashMap(files.length * 15 / 10); for (File f : files) m.put(f, Long.valueOf(f.lastModified())); return m; } } public interface Observer { boolean isInitialized(); /** * Called by {@link WatchDog#check()} if the underlying object is not {@link #isInitialized initialized} and the {@link WatchDog.Mutable#exists()} resource does not exist}. *
This method might called to reset the underlying object. * * @throws Exception */ void init(); void reset(); /** * Called only if the resource previously existed and now does not exist. *
The default implementation invokes {@link #init()} . * * @throws Exception */ void onDelete(); /** * Called only if the resource previously existed but the {@link WatchDog.Mutable#lastModified()} timestamp has changed (greater than the previous value). *
The default implementation invokes {@link #init()} . * * @throws Exception */ void onUpdate(); Lock getLock(); A get(); } /** * A default implementation of #ChangeHandler. Delete and Update will both invoke the #init method which satifies most use cases. * So subclasses may simply override the #init method to fit their own needs. */ public static class DefaultObserver
implements Observer { protected final ReadWriteLock lock = new ReentrantReadWriteLock(); protected A object; /** * @return true if the wrapped if not null */ public boolean isInitialized() { return object != null; } /** * empty implementation */ public void init() { } public void reset() { object = null; } /** * delegate to #init */ public void onDelete() { init(); } /** * delegate to #init */ public void onUpdate() { init(); } public Lock getLock() { return lock.writeLock(); } public A get() { lock.readLock().lock(); try { return object; } finally { lock.readLock().unlock(); } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy