| /* |
| 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.wiki; |
| |
| import org.apache.log4j.Logger; |
| import org.apache.wiki.api.core.Engine; |
| import org.apache.wiki.event.WikiEngineEvent; |
| import org.apache.wiki.event.WikiEvent; |
| import org.apache.wiki.event.WikiEventListener; |
| |
| |
| /** |
| * Abstract Thread subclass that operates in the background; when it detects the {@link WikiEngineEvent#SHUTDOWN} event, |
| * it terminates itself. Subclasses of this method need only implement the method {@link #backgroundTask()}, instead of |
| * the normal {@link Thread#run()}, and provide a constructor that passes the Engine and sleep interval. This class is thread-safe. |
| */ |
| public abstract class WikiBackgroundThread extends Thread implements WikiEventListener { |
| |
| private static final Logger LOG = Logger.getLogger( WikiBackgroundThread.class ); |
| private volatile boolean m_killMe = false; |
| private final Engine m_engine; |
| private final int m_interval; |
| private static final long POLLING_INTERVAL = 1_000L; |
| |
| /** |
| * Constructs a new instance of this background thread with a specified sleep interval, and adds the new instance |
| * to the wiki engine's event listeners. |
| * |
| * @param engine the wiki engine |
| * @param sleepInterval the interval between invocations of |
| * the thread's {@link Thread#run()} method, in seconds |
| */ |
| public WikiBackgroundThread( final Engine engine, final int sleepInterval ) { |
| super(); |
| m_engine = engine; |
| m_interval = sleepInterval; |
| engine.addWikiEventListener( this ); |
| setDaemon( false ); |
| } |
| |
| /** |
| * Listens for {@link org.apache.wiki.event.WikiEngineEvent#SHUTDOWN} and, if detected, marks the thread for death. |
| * |
| * @param event {@inheritDoc} |
| * @see org.apache.wiki.event.WikiEventListener#actionPerformed(org.apache.wiki.event.WikiEvent) |
| */ |
| @Override |
| public final void actionPerformed( final WikiEvent event ) { |
| if ( event instanceof WikiEngineEvent ) { |
| if ( event.getType() == WikiEngineEvent.SHUTDOWN ) { |
| LOG.warn( "Detected wiki engine shutdown: killing " + getName() + "." ); |
| m_killMe = true; |
| } |
| } |
| } |
| |
| /** |
| * Abstract method that performs the actual work for this background thread; subclasses must implement this method. |
| * |
| * @throws Exception Any exception can be thrown |
| */ |
| public abstract void backgroundTask() throws Exception; |
| |
| /** |
| * Returns the Engine that created this background thread. |
| * |
| * @return the wiki engine |
| */ |
| public Engine getEngine() { |
| return m_engine; |
| } |
| |
| /** |
| * Requests the shutdown of this background thread. Note that the shutdown is not immediate. |
| * |
| * @since 2.4.92 |
| */ |
| public void shutdown() { |
| m_killMe = true; |
| } |
| |
| /** |
| * Runs the background thread's {@link #backgroundTask()} method at the interval specified at construction. |
| * The thread will initially pause for a full sleep interval before starting, after which it will execute |
| * {@link #startupTask()}. This method will cleanly terminate the thread if it has previously been marked as |
| * dead, before which it will execute {@link #shutdownTask()}. If any of the three methods return an exception, |
| * it will be re-thrown as a {@link org.apache.wiki.InternalWikiException}. |
| * |
| * @see java.lang.Thread#run() |
| */ |
| @Override |
| public final void run() { |
| try { |
| // Perform the initial startup task |
| final String name = getName(); |
| LOG.warn( "Starting up background thread: " + name + "."); |
| startupTask(); |
| |
| // Perform the background task; check every second for thread death |
| while( !m_killMe ) { |
| // Perform the background task |
| // log.debug( "Running background task: " + name + "." ); |
| backgroundTask(); |
| |
| // Sleep for the interval we're supposed to, but wake up every POLLING_INTERVAL to see if thread should die |
| boolean interrupted = false; |
| try { |
| for( int i = 0; i < m_interval; i++ ) { |
| Thread.sleep( POLLING_INTERVAL ); |
| if( m_killMe ) { |
| interrupted = true; |
| LOG.warn( "Interrupted background thread: " + name + "." ); |
| break; |
| } |
| } |
| if( interrupted ) { |
| break; |
| } |
| } catch( final Throwable t ) { |
| LOG.error( "Background thread error: (stack trace follows)", t ); |
| } |
| } |
| |
| // Perform the shutdown task |
| shutdownTask(); |
| } catch( final Throwable t ) { |
| LOG.error( "Background thread error: (stack trace follows)", t ); |
| throw new InternalWikiException( t.getMessage() ,t ); |
| } |
| } |
| |
| /** |
| * Executes a task after shutdown signal was detected. By default, this method does nothing; override it |
| * to implement custom functionality. |
| * |
| * @throws Exception Any exception can be thrown. |
| */ |
| public void shutdownTask() throws Exception { |
| } |
| |
| /** |
| * Executes a task just after the thread's {@link Thread#run()} method starts, but before the {@link #backgroundTask()} task executes. |
| * By default, this method does nothing; override it to implement custom functionality. |
| * |
| * @throws Exception Any exception can be thrown. |
| */ |
| public void startupTask() throws Exception { |
| } |
| |
| } |