| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. 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. For additional information regarding |
| * copyright in this work, please see the NOTICE file in the top level |
| * directory of this distribution. |
| */ |
| |
| package org.apache.roller.weblogger.business.runnable; |
| |
| import java.util.ArrayList; |
| import java.util.Date; |
| import java.util.List; |
| import java.util.concurrent.ExecutorService; |
| import java.util.concurrent.Executors; |
| import java.util.concurrent.Future; |
| import java.util.concurrent.TimeUnit; |
| |
| import org.apache.commons.lang3.StringUtils; |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.roller.util.RollerConstants; |
| import org.apache.roller.weblogger.WebloggerException; |
| import org.apache.roller.weblogger.business.InitializationException; |
| import org.apache.roller.weblogger.config.WebloggerConfig; |
| import org.apache.roller.weblogger.pojos.TaskLock; |
| |
| import static org.apache.roller.util.RollerConstants.GRACEFUL_SHUTDOWN_WAIT_IN_MILLISECONDS; |
| import static org.apache.roller.util.RollerConstants.GRACEFUL_SHUTDOWN_WAIT_IN_SECONDS; |
| |
| /** |
| * Manage Roller's thread use. |
| */ |
| @com.google.inject.Singleton |
| public abstract class ThreadManagerImpl implements ThreadManager { |
| |
| private static final Log LOG = LogFactory.getLog(ThreadManagerImpl.class); |
| |
| // our own scheduler thread |
| private Thread schedulerThread = null; |
| |
| // a simple thread executor |
| private final ExecutorService serviceScheduler; |
| |
| |
| public ThreadManagerImpl() { |
| |
| LOG.info("Instantiating Thread Manager"); |
| |
| serviceScheduler = Executors.newCachedThreadPool(); |
| } |
| |
| |
| public void initialize() throws InitializationException { |
| |
| // initialize tasks, making sure that each task has a tasklock record in the db |
| List<RollerTask> webloggerTasks = new ArrayList<RollerTask>(); |
| String tasksStr = WebloggerConfig.getProperty("tasks.enabled"); |
| String[] tasks = StringUtils.stripAll(StringUtils.split(tasksStr, ",")); |
| for ( String taskName : tasks ) { |
| |
| String taskClassName = WebloggerConfig.getProperty("tasks."+taskName+".class"); |
| if(taskClassName != null) { |
| LOG.info("Initializing task: " + taskName); |
| |
| try { |
| Class taskClass = Class.forName(taskClassName); |
| RollerTask task = (RollerTask) taskClass.newInstance(); |
| task.init(taskName); |
| |
| // make sure there is a tasklock record in the db |
| TaskLock taskLock = getTaskLockByName(task.getName()); |
| if (taskLock == null) { |
| LOG.debug("Task record does not exist, inserting empty record to start with"); |
| |
| // insert an empty record |
| taskLock = new TaskLock(); |
| taskLock.setName(task.getName()); |
| taskLock.setLastRun(new Date(0)); |
| taskLock.setTimeAcquired(new Date(0)); |
| taskLock.setTimeLeased(0); |
| |
| // save it |
| this.saveTaskLock(taskLock); |
| } |
| |
| // add it to the list of configured tasks |
| webloggerTasks.add(task); |
| |
| } catch (ClassCastException ex) { |
| LOG.warn("Task does not extend RollerTask class", ex); |
| } catch (WebloggerException ex) { |
| LOG.error("Error scheduling task", ex); |
| } catch (Exception ex) { |
| LOG.error("Error instantiating task", ex); |
| } |
| } |
| } |
| |
| // create scheduler |
| TaskScheduler scheduler = new TaskScheduler(webloggerTasks); |
| |
| // start scheduler thread, but only if it's not already running |
| if (schedulerThread == null) { |
| LOG.debug("Starting scheduler thread"); |
| schedulerThread = new Thread(scheduler, "Roller Weblogger Task Scheduler"); |
| // set thread priority between MAX and NORM so we get slightly preferential treatment |
| schedulerThread.setPriority((Thread.MAX_PRIORITY + Thread.NORM_PRIORITY)/2); |
| schedulerThread.start(); |
| } |
| } |
| |
| |
| public void executeInBackground(Runnable runnable) |
| throws InterruptedException { |
| serviceScheduler.submit(runnable); |
| } |
| |
| |
| public void executeInForeground(Runnable runnable) |
| throws InterruptedException { |
| Future task = serviceScheduler.submit(runnable); |
| |
| // since this task is really meant to be executed within this calling |
| // thread, here we can add a little code here to loop until it realizes |
| // the task is done |
| while(!task.isDone()) { |
| Thread.sleep(RollerConstants.HALF_SEC_IN_MS); |
| } |
| } |
| |
| |
| public void shutdown() { |
| |
| LOG.debug("starting shutdown sequence"); |
| |
| // trigger an immediate shutdown of any backgrounded tasks |
| serviceScheduler.shutdownNow(); |
| try { |
| serviceScheduler.awaitTermination(GRACEFUL_SHUTDOWN_WAIT_IN_SECONDS, TimeUnit.SECONDS); |
| } catch (InterruptedException e) { |
| LOG.debug(e.getMessage(), e); |
| } |
| |
| // only stop if we are already running |
| if(schedulerThread != null) { |
| LOG.debug("Stopping scheduler"); |
| schedulerThread.interrupt(); |
| try { |
| schedulerThread.join(GRACEFUL_SHUTDOWN_WAIT_IN_MILLISECONDS); |
| LOG.debug("Scheduler was stopped successfully"); |
| } catch (InterruptedException e) { |
| LOG.debug(e.getMessage(), e); |
| } |
| } |
| } |
| |
| |
| public void release() { |
| // no-op |
| } |
| |
| |
| /** |
| * Default implementation of lease registration, always returns true. |
| * |
| * Subclasses should override this method if they plan to run in an |
| * environment that supports clustered deployments. |
| */ |
| public boolean registerLease(RollerTask task) { |
| return true; |
| } |
| |
| |
| /** |
| * Default implementation of lease unregistration, always returns true. |
| * |
| * Subclasses should override this method if they plan to run in an |
| * environment that supports clustered deployments. |
| */ |
| public boolean unregisterLease(RollerTask task) { |
| return true; |
| } |
| |
| } |