package org.openedit.events; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.openedit.data.SearcherManager; import org.openedit.entermedia.util.TimeCalculator; import org.openedit.util.SynchronizedLinkedList; import com.openedit.ModuleManager; import com.openedit.OpenEditException; import com.openedit.WebPageRequest; import com.openedit.error.ErrorHandler; import com.openedit.page.Page; import com.openedit.page.manage.PageManager; import com.openedit.users.User; import com.openedit.users.UserManager; import com.openedit.util.ExecutorManager; import com.openedit.util.PathUtilities; import com.openedit.util.RequestUtils; /** * Loads up the events for this one application i.e. /openedit/events * * Then runs them on a timmer if needed. Also provides a UI to listing and * running the tasks * * @author cburkey * */ public class PathEventManager { protected static final Log log = LogFactory.getLog(PathEventManager.class); protected boolean fieldLogEvents; protected String fieldCatalogId; protected PageManager fieldPageManager; protected ModuleManager fieldModuleManager; protected SearcherManager fieldSearcherManager; protected RequestUtils fieldRequestUtils; protected List fieldPathActions; protected LinkedList<TaskRunner> fieldRunningTasks; protected ErrorHandler fieldErrorHandler; protected Timer fieldTimer; // Should this be shared across the system? protected TimeCalculator fieldTimeCalculator; protected ExecutorManager fieldExecutorManager; public ExecutorManager getExecutorManager() { return fieldExecutorManager; } public void setExecutorManager(ExecutorManager inExecutorManager) { fieldExecutorManager = inExecutorManager; } public TimeCalculator getTimeCalculator() { if (fieldTimeCalculator == null) { fieldTimeCalculator = new TimeCalculator(); } return fieldTimeCalculator; } public void setTimeCalculator(TimeCalculator inTimeCalculator) { fieldTimeCalculator = inTimeCalculator; } public PathEventManager() { } public LinkedList<TaskRunner> getRunningTasks() { if (fieldRunningTasks == null) { fieldRunningTasks = new SynchronizedLinkedList<TaskRunner>(); } return fieldRunningTasks; } public List<TaskRunner> getRunningTasksSorted() { List<TaskRunner> copy = new ArrayList<TaskRunner>(getRunningTasks()); Collections.sort(copy, new Comparator<TaskRunner>() { public int compare(TaskRunner inT1, TaskRunner inT2) { return inT1.getTimeToStart().compareTo(inT2.getTimeToStart()); } }); return copy; } public boolean isLogEvents() { return fieldLogEvents; } public void setLogEvents(boolean fieldLogEvent) { this.fieldLogEvents = fieldLogEvent; } public String getCatalogId() { return fieldCatalogId; } public void setCatalogId(String inCatalogId) { fieldCatalogId = inCatalogId; } public SearcherManager getSearcherManager() { return fieldSearcherManager; } public void setSearcherManager(SearcherManager inSearcherManager) { fieldSearcherManager = inSearcherManager; } public boolean runSharedPathEvent(String runpath) { return runSharedPathEvent(runpath,false); } /** * This will only add the event if it is not already queued up. There is no reason to queue up multiple copies. Kind of like mouse events. * @param runpath * @return */ public boolean runSharedPathEvent(String runpath, boolean inUpdateRunTimes ) { if (runpath == null) { return false; } PathEvent event = getPathEvent(runpath); if (event != null) { String name = event.getName(); // if( name.equals("Run media conversions") ) // { // log.info("Running YYYY conversion task "); // } synchronized (getRunningTasks()) { Date now = new Date(); TaskRunner runner = null; //Date soon = new Date( System.currentTimeMillis() + 10000L);//is it already going to run within the next 10 seconds List<TaskRunner> copy = new ArrayList<TaskRunner>(getRunningTasks()); for (Iterator iterator = copy.iterator(); iterator.hasNext();) { TaskRunner task = (TaskRunner) iterator.next(); if( name.equals( task.getTask().getName() ) ) { // if( name.equals("Run media conversions") ) // { // log.info("Found conversion task " + now + " " + name + " " + task.isRepeating()); // } if( task.getTask().isRunning() ) { //task.run(); task.setRunAgainSoon(true); // if(task.isRepeating()) // { // task.setRunAgainSoon(true); //Will cause it to run again after it finishes // return true; // } } // else if( task.getTimeToStart().before(now) ) // { // return true; // } else { //update the start time and call run now //task.setTimeToStart(now); task.run(); } runner = task; break; //else this will add a duplicate. Since it is already running it will just return } } if( runner == null) { runner = new TaskRunner(event, this); //runner.setWithParameters(true); //To make sure we only run this once since the scheduled one should already be in there //runner.setTimeToStart(now); getRunningTasks().push(runner); runner.run(); } if(runner.isRepeating() && inUpdateRunTimes) { long later = now.getTime() + runner.getTask().getPeriod(); runner.setTimeToStart(new Date(later)); } } return true; } else { //I guess sometimes events fire that are not actually configured if( !runpath.endsWith(".html")) { throw new OpenEditException("Event path must end with .html " + runpath); } if( log.isDebugEnabled() ) { log.debug("No actions enabled for this event: " + runpath); } return false; } } /** * This is the public API that event listeners need to run to exec * /catalogid/events/* actions * * @param runpath * @param inReq * @return */ public boolean runPathEvent(String runpath, WebPageRequest inReq ) { if (runpath == null) { return false; } PathEvent event = getPathEvent(runpath); inReq.putPageValue("ranevent", event); // String force = inReq.getRequestParameter("forcerun"); if (event != null) { // if( Boolean.parseBoolean(force) || event.getDelay() == 0 ) TaskRunner runner = new TaskRunner(event, inReq.getParameterMap(), getRequestUtils().extractValueMap(inReq), this); // { getRunningTasks().push(runner); runner.runBlocking(); //this will remove it again // } // else // { // schedule(event, runner); // } return true; } else { //I guess sometimes events fire that are not actually configured if( !runpath.endsWith(".html")) { throw new OpenEditException("Event path must end with .html " + runpath); } //do nothingthrow new OpenEditException("Event not found " + runpath); if( log.isDebugEnabled() ) { log.debug("No actions enabled for this event: " + runpath); } return false; } } /* protected void schedule(PathEvent event, TaskRunner runner) { //getRunningTasks().push(runner); try { schedule(runner,event.getPeriod()); } catch (Exception e) { fieldTimer = null; getRunningTasks().clear(); getRunningTasks().push(runner); schedule(runner,event.getPeriod() ); //to fix java.lang.IllegalStateException: Timer already cancelled. } } */ public ModuleManager getModuleManager() { return fieldModuleManager; } public void setModuleManager(ModuleManager inModuleManager) { fieldModuleManager = inModuleManager; } public RequestUtils getRequestUtils() { return fieldRequestUtils; } public void setRequestUtils(RequestUtils inRequestUtils) { fieldRequestUtils = inRequestUtils; } public PageManager getPageManager() { return fieldPageManager; } public void setPageManager(PageManager inPageManager) { fieldPageManager = inPageManager; } public Timer getTimer() { if (fieldTimer == null) { fieldTimer = new Timer("Scheduler_" + getCatalogId(), true); } return fieldTimer; } public void removeTask(PathEvent inTask) { getPathEvents().remove(inTask); } public List<PathEvent> getPathEvents() { if (fieldPathActions == null) { fieldPathActions = new ArrayList(); loadPathEvents(); } return fieldPathActions; } public void setPathEvents(List inTaskList) { fieldPathActions = inTaskList; } public ErrorHandler getErrorHandler() { return fieldErrorHandler; } public void setErrorHandler(ErrorHandler inErrorHandler) { fieldErrorHandler = inErrorHandler; } public void shutdown() { if (fieldTimer != null) { try { getTimer().cancel(); } catch ( Throwable ex) { log.error("ignoring error",ex); } fieldTimer = null; } clear(); fieldPathActions = null; fieldRunningTasks = null; } public void removeTask(ScheduledTask inTask) { inTask.setEnabled(false); getPathEvents().remove(inTask); } public void loadTask(PathEvent inTask) throws OpenEditException { //, boolean inRun, boolean inAsync f t if( log.isDebugEnabled() ) { log.debug("Adding new Workflow Task: " + inTask.getPage()); } getPathEvents().add(inTask); if (inTask.isEnabled()) { if (inTask.getPeriod() > 0) { TaskRunner runner = new TaskRunner(inTask, this); //runner.setRepeating(true); getRunningTasks().push(runner); } } } public void clear() { if( fieldPathActions != null) { getPathEvents().clear(); } } protected void loadPathEvents() { clear(); //getPageManager().clearCache(); String root = "/" + getCatalogId() + "/events"; Set duplicates = new HashSet(); loadPathEvents(root, duplicates); Collections.sort(getPathEvents()); reloadScheduler(); } protected void loadPathEvents(String inRoot, Set inDuplicates) { List events = getPageManager().getChildrenPaths(inRoot + "/", true); for (Iterator iterator = events.iterator(); iterator.hasNext();) { String path = (String) iterator.next(); Page page = getPageManager().getPage(path); String vpath = inRoot + "/" + PathUtilities.extractFileName(path); Page vchild = getPageManager().getPage(vpath); if( page.isFolder() && !page.getName().startsWith(".versions") ) { loadPathEvents(vchild.getPath(), inDuplicates); } else { loadByPath(vpath, inRoot, inDuplicates); } } } protected void loadByPath(String path,String root, Set duplicates) { // if( path.endsWith(".xconf")) // { // path = PathUtilities.extractPagePath(path) + ".xconf"; // } if( !path.endsWith(".xconf") || path.endsWith("_site.xconf")) { return; } String htmlpage = PathUtilities.extractPagePath(path) + ".html"; if( duplicates.contains(htmlpage)) { return; } //path = PathUtilities.extractFileName(path); duplicates.add(htmlpage); //make .xconf into .html //ignore folders and html pages //get rid of duplicates from base loadPathEvent(htmlpage); } protected void loadPathEvent(String htmlpage) { Page eventpage = getPageManager().getPage(htmlpage, true); PathEvent event = (PathEvent) getModuleManager().getBean("pathEvent"); event.setPage(eventpage); String username = eventpage.get("eventuser"); if( username == null) { username = "admin"; } UserManager usermanager = (UserManager)getModuleManager().getBean("userManager"); User user = usermanager.getUser(username); if( user == null) { log.error("No such user: " + username); } event.setUser(user); loadTask(event); } public PathEvent getPathEvent(String inPath) { for (Iterator iterator = getPathEvents().iterator(); iterator.hasNext();) { PathEvent event = (PathEvent) iterator.next(); String path =event.getPage().getPath(); if (path.equals(inPath)) { return event; } } return null; } public void addToRunQueue(Runnable inExecrun) { getExecutorManager().getSharedExecutor().execute(inExecrun); } public void reload(String inEventPath) { PathEvent event = getPathEvent(inEventPath); getPathEvents().remove(event); List<TaskRunner> copy = new ArrayList<TaskRunner>(getRunningTasks()); for (Iterator iterator = copy.iterator(); iterator.hasNext();) { TaskRunner runner = (TaskRunner) iterator.next(); if( event == runner.getTask() ) { getRunningTasks().remove(runner); // if(runner.isRepeating()) // { // long later = new Date().getTime() + runner.getTask().getPeriod(); // runner.setTimeToStart(new Date(later)); // } } } loadPathEvent(inEventPath); reloadScheduler(); } private void reloadScheduler() { if (fieldTimer != null) { try { getTimer().cancel(); } catch ( Throwable ex) { log.error("ignoring error",ex); } fieldTimer = null; } for (Iterator iterator = getPathEvents().iterator(); iterator.hasNext();) { PathEvent type = (PathEvent) iterator.next(); if( type.getPeriod() > 0) { RunSharedEventPath runthing = new RunSharedEventPath(type.getPage().getPath() ); getTimer().schedule(runthing, type.getPeriod(), type.getPeriod()); } } } public class RunSharedEventPath extends TimerTask { protected String path; public RunSharedEventPath(String inPath) { path = inPath; } public void run() { runSharedPathEvent(path,true); } } }