001package org.apache.commons.jcs.auxiliary;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.util.concurrent.atomic.AtomicBoolean;
023import java.util.concurrent.locks.Condition;
024import java.util.concurrent.locks.Lock;
025import java.util.concurrent.locks.ReentrantLock;
026
027import org.apache.commons.logging.Log;
028import org.apache.commons.logging.LogFactory;
029
030/**
031 * Used to monitor and repair any failed connection for the lateral cache service. By default the
032 * monitor operates in a failure driven mode. That is, it goes into a wait state until there is an
033 * error. Upon the notification of a connection error, the monitor changes to operate in a time
034 * driven mode. That is, it attempts to recover the connections on a periodic basis. When all failed
035 * connections are restored, it changes back to the failure driven mode.
036 */
037public abstract class AbstractAuxiliaryCacheMonitor extends Thread
038{
039    /** The logger */
040    protected final Log log = LogFactory.getLog( this.getClass() );
041
042    /** How long to wait between runs */
043    protected static long idlePeriod = 20 * 1000;
044
045    /**
046     * Must make sure AbstractAuxiliaryCacheMonitor is started before any error can be detected!
047     */
048    protected AtomicBoolean allright = new AtomicBoolean(true);
049
050    /**
051     * shutdown flag
052     */
053    private AtomicBoolean shutdown = new AtomicBoolean(false);
054
055    /** Synchronization helper lock */
056    private Lock lock = new ReentrantLock();
057
058    /** Synchronization helper condition */
059    private Condition trigger = lock.newCondition();
060
061    /**
062     * Constructor
063     *
064     * @param name the thread name
065     */
066    public AbstractAuxiliaryCacheMonitor(String name)
067    {
068        super(name);
069    }
070
071    /**
072     * Configures the idle period between repairs.
073     * <p>
074     * @param idlePeriod The new idlePeriod value
075     */
076    public static void setIdlePeriod( long idlePeriod )
077    {
078        if ( idlePeriod > AbstractAuxiliaryCacheMonitor.idlePeriod )
079        {
080            AbstractAuxiliaryCacheMonitor.idlePeriod = idlePeriod;
081        }
082    }
083
084    /**
085     * Notifies the cache monitor that an error occurred, and kicks off the error recovery process.
086     */
087    public void notifyError()
088    {
089        if (allright.compareAndSet(true, false))
090        {
091            signalTrigger();
092        }
093    }
094
095    /**
096     * Notifies the cache monitor that the service shall shut down
097     */
098    public void notifyShutdown()
099    {
100        if (shutdown.compareAndSet(false, true))
101        {
102            signalTrigger();
103        }
104    }
105
106    // Trigger continuation of loop
107    private void signalTrigger()
108    {
109        try
110        {
111            lock.lock();
112            trigger.signal();
113        }
114        finally
115        {
116            lock.unlock();
117        }
118    }
119
120    /**
121     * Clean up all resources before shutdown
122     */
123    protected abstract void dispose();
124
125    /**
126     * do actual work
127     */
128    protected abstract void doWork();
129
130    /**
131     * Main processing method for the AbstractAuxiliaryCacheMonitor object
132     */
133    @Override
134    public void run()
135    {
136        do
137        {
138            if ( log.isDebugEnabled() )
139            {
140                if ( allright.get() )
141                {
142                    log.debug( "ERROR DRIVEN MODE: allright = true, cache monitor will wait for an error." );
143                }
144                else
145                {
146                    log.debug( "ERROR DRIVEN MODE: allright = false cache monitor running." );
147                }
148            }
149
150            if ( allright.get() )
151            {
152                // Failure driven mode.
153                try
154                {
155                    lock.lock();
156                    trigger.await();
157                    // wake up only if there is an error.
158                }
159                catch ( InterruptedException ignore )
160                {
161                    //no op, this is expected
162                }
163                finally
164                {
165                    lock.unlock();
166                }
167            }
168
169            // check for requested shutdown
170            if ( shutdown.get() )
171            {
172                log.info( "Shutting down cache monitor" );
173                dispose();
174                return;
175            }
176
177            // The "allright" flag must be false here.
178            // Simply presume we can fix all the errors until proven otherwise.
179            allright.set(true);
180
181            if ( log.isDebugEnabled() )
182            {
183                log.debug( "Cache monitor running." );
184            }
185
186            doWork();
187
188            try
189            {
190                // don't want to sleep after waking from an error
191                // run immediately and sleep here.
192                if ( log.isDebugEnabled() )
193                {
194                    log.debug( "Cache monitor sleeping for " + idlePeriod + " between runs." );
195                }
196
197                Thread.sleep( idlePeriod );
198            }
199            catch ( InterruptedException ex )
200            {
201                // ignore;
202            }
203        }
204        while ( true );
205    }
206}