/** * Copyright (C) 2008-2010, Squale Project - http://www.squale.org * * This file is part of Squale. * * Squale is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or any later version. * * Squale is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Squale. If not, see <http://www.gnu.org/licenses/>. */ package org.squale.welcom.struts.util; import java.util.Collection; import java.util.Hashtable; import java.util.Iterator; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import java.util.Map.Entry; import javax.servlet.http.HttpServletRequest; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.squale.welcom.outils.WelcomConfigurator; /** * Third-party code used for this class. Impossible to find original licence though. Copyright left to original author * if ever found in the future. */ public class WatchedTaskManager { /** logger */ private static Log log = LogFactory.getLog( WatchedTaskManager.class ); private static final int DEFAULT_THREAD_POOL_SIZE = 10; /** * Maximum amount of time allowed for a task execution. After this delay the tasks are removed by calling the * clean() method. */ private static int MAX_TASK_DURATION; private static final int DEFAULT_THREAD_LIFE = 60000; /** * Information on all the tasks currently running. */ private Hashtable activeTasks = new Hashtable(); /** * Last active task. */ private int taskIdCounter = Integer.MIN_VALUE; /** * All possible values have been used at least once */ private boolean taskIdRecyclingMode = false; private Timer gcTimer; /** * Thread pool to execute batches. */ private WorkQueue workQueue; private final class GCTask extends TimerTask { WatchedTaskManager mgr; public void run() { log.debug( "Task clean launch from GC" ); mgr.clean(); } /** * @param mgr */ public GCTask( WatchedTaskManager mgr ) { this.mgr = mgr; } } private void init() { gcTimer = new Timer( true ); int poolSize = 0; try { String strPoolSize = WelcomConfigurator.getMessage( WelcomConfigurator.BATCH_TASKS_POOLSIZE ); poolSize = Integer.parseInt( strPoolSize ); } catch ( Exception e ) { log.error( "Bad format for parameter " + WelcomConfigurator.BATCH_TASKS_POOLSIZE, e ); poolSize = DEFAULT_THREAD_POOL_SIZE; } workQueue = new WorkQueue( poolSize ); // ---------- int taskDuration = 0; try { String strLife = WelcomConfigurator.getMessage( WelcomConfigurator.BATCH_TASKS_MAXLIFEDURATION ); taskDuration = Integer.parseInt( strLife ); } catch ( Exception e ) { log.error( "Bad format for parameter " + WelcomConfigurator.BATCH_TASKS_MAXLIFEDURATION, e ); taskDuration = DEFAULT_THREAD_LIFE; } MAX_TASK_DURATION = taskDuration * 1000; } /** * Initialize a new task progress data structure. * * @return id to use to retrieve/update informations on task progress */ public synchronized Object regTask( WatchedTask task ) { log.debug( "Task " + task + "registred" ); int taskId = getNextId(); TaskProgress progress = new TaskProgress(); task.setProgress( progress ); activeTasks.put( new Integer( taskId ), task ); GCTask tacheGC = new GCTask( this ); gcTimer.schedule( tacheGC, MAX_TASK_DURATION / 2 ); return new Integer( taskId ); } /** * Generate an id for the TaskProgress * * @return ID */ private int getNextId() { if ( ( taskIdCounter < Integer.MAX_VALUE ) && ( taskIdRecyclingMode == false ) ) { return taskIdCounter++; } else { // All Long value have been used at least 1 time // Start again from beginning. if ( taskIdCounter < Integer.MAX_VALUE ) { taskIdCounter = Integer.MIN_VALUE; } while ( activeTasks.containsKey( new Integer( taskIdCounter ) ) ) { taskIdCounter++; } return taskIdCounter; } } /** * Retrieve percentage of work complete. * * @param taskId * @return % of work complete */ public int getTaskProgressPct( Object taskId ) { WatchedTask batch = (WatchedTask) activeTasks.get( taskId ); if ( batch != null ) { return batch.getProgress().getPercentComplete(); } else { return -1; } } /** * GET Accessor. * * @param taskId * @return */ public WatchedTask getTask( Object taskId ) { return (WatchedTask) activeTasks.get( new Integer( taskId.toString() ) ); } /** * GET Accessor. * * @param taskId * @return */ public Object getTaskId( WatchedTask task ) { if ( task == null ) { return null; } Set entries = activeTasks.entrySet(); Iterator iter = entries.iterator(); while ( iter.hasNext() ) { Entry e = (Entry) iter.next(); if ( task.equals( e.getValue() ) ) { return e.getKey(); } } return null; } /** * GET Accessor. * * @param taskId * @return */ public Collection getAllTasks() { return activeTasks.values(); } /** * Remove task information data. * * @param taskId */ private void removeTask( Object taskId ) { activeTasks.remove( new Integer( taskId.toString() ) ); clean(); } /** * Remove all "olds" tasks information in case of a process anormal end. */ public void clean() { log.info( "Task cleaning" ); Iterator iter = activeTasks.values().iterator(); long now = System.currentTimeMillis(); while ( iter.hasNext() ) { WatchedTask element = (WatchedTask) iter.next(); long creationDate = element.getProgress().getCreationDate(); // After 10 minutes, a task is removed from the pool if ( ( now - creationDate ) > MAX_TASK_DURATION ) { iter.remove(); } } } /* * (non-Javadoc) * * @see java.lang.Object#finalize() */ protected void finalize() throws Throwable { gcTimer.cancel(); gcTimer = null; } /** * GET Accessor. * * @return Work queue */ public WorkQueue getWorkQueue() { return workQueue; } /** * @param id */ public synchronized void killTask( Object id ) { WatchedTask batch = (WatchedTask) activeTasks.get( new Integer( id.toString() ) ); if ( batch != null ) { log.info( "killing task " + id ); removeTask( id ); getWorkQueue().stopTask( batch ); } } public static synchronized WatchedTaskManager getInstance( HttpServletRequest request ) { WatchedTaskManager instance = (WatchedTaskManager) request.getSession().getAttribute( WatchedTaskManager.class.getName() ); if ( instance == null ) { instance = new WatchedTaskManager(); instance.init(); log.info( "TaskMgr registred in session" ); request.getSession().setAttribute( WatchedTaskManager.class.getName(), instance ); } return instance; } }