/* * Copyright 2000-2013 Enonic AS * http://www.enonic.com/license */ package com.enonic.vertical.adminweb; import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.StringTokenizer; import javax.mail.MessagingException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import javax.xml.transform.Source; import javax.xml.transform.TransformerException; import javax.xml.transform.dom.DOMSource; import org.springframework.beans.factory.annotation.Autowired; import org.w3c.dom.Document; import org.w3c.dom.Element; import com.google.common.collect.Lists; import com.enonic.esl.containers.ExtendedMap; import com.enonic.esl.util.DateUtil; import com.enonic.esl.util.StringUtil; import com.enonic.esl.xml.XMLTool; import com.enonic.vertical.adminweb.wizard.Wizard; import com.enonic.vertical.adminweb.wizard.WizardException; import com.enonic.vertical.adminweb.wizard.WizardLogger; import com.enonic.vertical.engine.VerticalEngineException; import com.enonic.vertical.work.WorkEntry; import com.enonic.vertical.work.WorkEntryComparator; import com.enonic.vertical.work.WorkHelper; import com.enonic.vertical.work.WorkService; import com.enonic.cms.framework.xml.XMLDocument; import com.enonic.cms.framework.xml.XMLDocumentFactory; import com.enonic.cms.api.plugin.ext.TaskHandler; import com.enonic.cms.core.CalendarUtil; import com.enonic.cms.core.plugin.ext.TaskHandlerExtensions; import com.enonic.cms.core.security.user.User; import com.enonic.cms.core.service.AdminService; public class SchedulerServlet extends AdminHandlerBaseServlet { private static SchedulerServlet INSTANCE; private static final String WIZARD_CONFIG_CREATE_UPDATE = "wizardconfig_create_update_workentry.xml"; private static final String ERROR_REPEAT_INTERVAL = "13"; private static final String ERROR_REPEAT_COUNT = "14"; private static final String ERROR_MINUTES = "15"; private static final String ERROR_TIME = "16"; private static final String ERROR_CRON_EXPRESSION = "17"; @Autowired private WorkService workService; @Autowired private TaskHandlerExtensions taskHandlerExtensions; public SchedulerServlet() { INSTANCE = this; } public static class CreateUpdateWorkEntryWizard extends Wizard { /** * */ public CreateUpdateWorkEntryWizard() { super(); } protected void appendCustomData( WizardState wizardState, HttpSession session, AdminService admin, ExtendedMap formItems, ExtendedMap parameters, User user, Document dataconfigDoc, Document wizarddataDoc ) throws WizardException { String key = formItems.getString( "key", null ); if ( key != null ) { WorkService workService = INSTANCE.workService; WorkEntry workEntry = workService.getEntry( key ); if ( workEntry == null ) { WizardLogger.errorWizard( "Unknown work entry {0}", key ); } org.jdom.Document doc = new org.jdom.Document( new org.jdom.Element( "workentries" ) ); org.jdom.Element workentryElem = WorkHelper.convertToElement( workEntry ); doc.getRootElement().addContent( workentryElem ); SchedulerServlet schedulerServlet = (SchedulerServlet) servlet; if ( !wizardState.hasError( ERROR_CRON_EXPRESSION ) ) { schedulerServlet.updateWorkEntryElement( workEntry, workentryElem ); } XMLTool.mergeDocuments( wizarddataDoc, XMLDocumentFactory.create( doc ).getAsDOMDocument(), true ); } XMLTool.mergeDocuments( wizarddataDoc, getTaskPluginsXML(), true ); } private Document getTaskPluginsXML() { Collection<TaskHandler> plugins = Lists.newArrayList( INSTANCE.taskHandlerExtensions ); return createXmlDocument( plugins ).getAsDOMDocument(); } protected boolean evaluate( WizardState wizardState, HttpSession session, AdminService admin, ExtendedMap formItems, String testCondition ) throws WizardException { // not used return false; } protected void initialize( AdminService admin, Document wizardconfigDoc ) throws WizardException { // not used } protected void processWizardData( WizardState wizardState, HttpSession session, AdminService admin, ExtendedMap formItems, User user, Document dataDoc ) throws VerticalAdminException, VerticalEngineException { WorkEntry workEntry = new WorkEntry(); String key = formItems.getString( "key", null ); if ( key != null ) { workEntry.setKey( key ); } workEntry.setName( formItems.getString( "stepstate_workentry_name" ) ); workEntry.setWorkClass( formItems.getString( "stepstate_workentry_workclass" ) ); workEntry.setUserName( user.getName() ); String[] names = formItems.getStringArray( "stepstate_workentry_properties_property_@name" ); String[] values = formItems.getStringArray( "stepstate_workentry_properties_property_@value" ); for ( int i = 0; i < names.length; i++ ) { workEntry.setProperty( names[i], values[i] ); } String type = formItems.getString( "__type" ); if ( "once".equals( type ) ) { workEntry.setMode( WorkEntry.SIMPLE ); workEntry.setRepeatInterval( 1 ); workEntry.setRepeatCount( 0 ); } else if ( "infinite".equals( type ) ) { workEntry.setMode( WorkEntry.SIMPLE ); workEntry.setRepeatInterval( formItems.getInt( "stepstate_workentry_trigger_repeat_@interval" ) ); workEntry.setRepeatCount( -1 ); } else if ( "repeatedly".equals( type ) ) { workEntry.setMode( WorkEntry.SIMPLE ); workEntry.setRepeatInterval( formItems.getInt( "stepstate_workentry_trigger_repeat_@interval" ) ); int repeatCount = formItems.getInt( "stepstate_workentry_trigger_repeat_@count" ); workEntry.setRepeatCount( repeatCount ); } else if ( "hourly".equals( type ) ) { workEntry.setMode( WorkEntry.CRON ); String minutes = formItems.getString( "stepstate_workentry_trigger_hourly_@minutes" ); workEntry.setCronExpression( "0 " + minutes + " * * * ?" ); } else if ( "daily".equals( type ) ) { workEntry.setMode( WorkEntry.CRON ); String time = formItems.getString( "stepstate_workentry_trigger_daily_@time" ); String hours = time.substring( 0, time.indexOf( ':' ) ); String minutes = time.substring( time.indexOf( ':' ) + 1 ); workEntry.setCronExpression( "0 " + minutes + " " + hours + " * * ?" ); } else { workEntry.setMode( WorkEntry.CRON ); String cronExpression = formItems.getString( "stepstate_workentry_trigger_cron" ); workEntry.setCronExpression( cronExpression ); } try { String dateStr = formItems.getString( "datestart", null ); String timeStr = formItems.getString( "timestart", null ); if ( dateStr != null ) { dateStr += " " + timeStr; workEntry.setStartTime( DateUtil.parseDateTime( dateStr, true ) ); } dateStr = formItems.getString( "dateend", null ); timeStr = formItems.getString( "timeend", null ); if ( dateStr != null ) { dateStr += " " + timeStr; workEntry.setEndTime( DateUtil.parseDateTime( dateStr, true ) ); } } catch ( Exception pe ) { WizardLogger.errorWizard( "Failed to parse start or end date", pe ); } INSTANCE.workService.addEntry( workEntry, key != null ); } protected boolean validateState( WizardState wizardState, HttpSession session, AdminService admin, ExtendedMap formItems ) { boolean validated = true; String type = formItems.getString( "__type" ); if ( "infinite".equals( type ) || "repeatedly".equals( type ) ) { int interval = formItems.getInt( "stepstate_workentry_trigger_repeat_@interval" ); if ( interval < 1 ) { wizardState.addError( ERROR_REPEAT_INTERVAL, "stepstate_workentry_trigger_repeat_@interval" ); validated = false; } if ( "repeatedly".equals( type ) ) { int count = formItems.getInt( "stepstate_workentry_trigger_repeat_@count" ); if ( count < -1 ) { wizardState.addError( ERROR_REPEAT_COUNT, "stepstate_workentry_trigger_repeat_@count" ); validated = false; } } } else if ( "hourly".equals( type ) ) { int minutes = formItems.getInt( "stepstate_workentry_trigger_hourly_@minutes" ); if ( minutes < 0 || minutes > 59 ) { wizardState.addError( ERROR_MINUTES, "stepstate_workentry_trigger_hourly_@minutes" ); validated = false; } } else if ( "daily".equals( type ) ) { String time = formItems.getString( "stepstate_workentry_trigger_daily_@time" ); String hoursStr = time.substring( 0, time.indexOf( ':' ) ); String minutesStr = time.substring( time.indexOf( ':' ) + 1 ); try { int hours = Integer.parseInt( hoursStr ); if ( hours < 0 || hours > 23 ) { wizardState.addError( ERROR_TIME, "stepstate_workentry_trigger_daily_@time" ); validated = false; } else { int minutes = Integer.parseInt( minutesStr ); if ( minutes < 0 || minutes > 59 ) { wizardState.addError( ERROR_TIME, "stepstate_workentry_trigger_daily_@time" ); validated = false; } } } catch ( NumberFormatException e ) { wizardState.addError( ERROR_TIME, "stepstate_workentry_trigger_daily_@time" ); validated = false; } } else if ( "custom".equals( type ) ) { WorkEntry workEntry = new WorkEntry(); workEntry.setMode( WorkEntry.CRON ); String cronExpression = formItems.getString( "stepstate_workentry_trigger_cron" ); try { workEntry.setCronExpression( cronExpression ); } catch ( IllegalArgumentException iae ) { WizardLogger.error( "Error in cron expression " + cronExpression ); wizardState.addError( ERROR_CRON_EXPRESSION, "stepstate_workentry_trigger_cron" ); validated = false; Document stateDoc = wizardState.getCurrentStepState().getStateDoc(); Element workentryElem = XMLTool.getElement( stateDoc.getDocumentElement(), "workentry" ); Element triggerElem = XMLTool.getElement( workentryElem, "trigger" ); Element dailyElem = XMLTool.getElement( triggerElem, "daily" ); if ( dailyElem != null ) { triggerElem.removeChild( dailyElem ); } Element hourlyElem = XMLTool.getElement( triggerElem, "hourly" ); if ( hourlyElem != null ) { triggerElem.removeChild( hourlyElem ); } } } return validated; } protected void saveState( WizardState wizardState, HttpServletRequest request, HttpServletResponse response, AdminService admin, User user, ExtendedMap formItems ) throws WizardException { try { String dateStr = formItems.getString( "datestart", null ); String timeStr = formItems.getString( "timestart", null ); if ( dateStr != null ) { if ( timeStr != null ) { dateStr = CalendarUtil.formatDate( DateUtil.parseDate( dateStr ).getTime(), false ).substring( 0, 10 ) + " " + timeStr; } else { dateStr = CalendarUtil.formatDate( DateUtil.parseDate( dateStr ).getTime(), false ).substring( 0, 10 ) + " 00:00:00"; formItems.put( "timestart", "00:00:00" ); } } else if ( timeStr != null ) { dateStr = CalendarUtil.formatCurrentDate().substring( 0, 10 ) + timeStr; } formItems.put( "stepstate_workentry_trigger_time_@start", dateStr ); dateStr = formItems.getString( "dateend", null ); timeStr = formItems.getString( "timeend", null ); if ( dateStr != null ) { if ( timeStr != null ) { dateStr = CalendarUtil.formatDate( DateUtil.parseDate( dateStr ).getTime(), false ).substring( 0, 10 ) + " " + timeStr; } else { dateStr = CalendarUtil.formatDate( DateUtil.parseDate( dateStr ).getTime(), false ).substring( 0, 10 ) + " 00:00:00"; formItems.put( "timeend", "00:00:00" ); } } else if ( timeStr != null ) { dateStr = CalendarUtil.formatCurrentDate().substring( 0, 10 ) + timeStr; } formItems.put( "stepstate_workentry_trigger_time_@end", dateStr ); } catch ( Exception pe ) { WizardLogger.errorWizard( "Failed to parse date", pe ); } String type = formItems.getString( "__type" ); if ( "infinite".equals( type ) && formItems.containsKey( "stepstate_workentry_trigger_repeat_@count" ) == false ) { formItems.put( "stepstate_workentry_trigger_repeat_@count", -1 ); } super.saveState( wizardState, request, response, admin, user, formItems ); } } public void handlerBrowse( HttpServletRequest request, HttpServletResponse response, HttpSession session, AdminService admin, ExtendedMap formItems, ExtendedMap parameters, User user, Document verticalDoc ) throws VerticalAdminException, TransformerException, IOException { WorkEntry[] workEntries = workService.getEntries(); Arrays.sort( workEntries, WorkEntryComparator.INSTANCE ); org.jdom.Document doc = new org.jdom.Document( new org.jdom.Element( "workentries" ) ); org.jdom.Element root = doc.getRootElement(); for ( int i = 0; i < workEntries.length; i++ ) { org.jdom.Element workentryElem = WorkHelper.convertToElement( workEntries[i] ); root.addContent( workentryElem ); updateWorkEntryElement( workEntries[i], workentryElem ); } XMLTool.mergeDocuments( verticalDoc, XMLDocumentFactory.create( doc ).getAsDOMDocument(), true ); // transform document DOMSource xmlSource = new DOMSource( verticalDoc ); Source xslSource = AdminStore.getStylesheet( session, "workentry_browse.xsl" ); transformXML( session, response.getWriter(), xmlSource, xslSource, parameters ); } private void updateWorkEntryElement( WorkEntry entry, org.jdom.Element workentryElem ) { if ( entry.getMode() == WorkEntry.CRON ) { StringTokenizer cronExpression = new StringTokenizer( entry.getCronExpression(), " " ); // seconds String seconds = cronExpression.nextToken(); // minutes String minutes = cronExpression.nextToken(); // hours String hours = cronExpression.nextToken(); // day of month String dayOfMonth = cronExpression.nextToken(); // day of month String month = cronExpression.nextToken(); // day of week String dayOfWeek = cronExpression.nextToken(); // has year boolean hasYear = cronExpression.hasMoreTokens(); org.jdom.Element triggerElem = workentryElem.getChild( "trigger" ); if ( "0".equals( seconds ) && "*".equals( dayOfMonth ) && "*".equals( month ) && "?".equals( dayOfWeek ) && hasYear == false ) { if ( "*".equals( hours ) ) { org.jdom.Element hourlyElem = new org.jdom.Element( "hourly" ); hourlyElem.setAttribute( "minutes", minutes ); triggerElem.addContent( hourlyElem ); } else if ( StringUtil.isIntegerString( hours ) ) { org.jdom.Element dailyElem = new org.jdom.Element( "daily" ); dailyElem.setAttribute( "time", hours + ":" + minutes ); triggerElem.addContent( dailyElem ); } } } } public void handlerWizard( HttpServletRequest request, HttpServletResponse response, HttpSession session, AdminService admin, ExtendedMap formItems, ExtendedMap parameters, User user, String wizardName ) throws VerticalAdminException, VerticalEngineException, TransformerException, IOException, MessagingException { if ( "createupdate".equals( wizardName ) ) { Wizard createUpdateWizard = Wizard.getInstance( admin, applicationContext, this, session, formItems, WIZARD_CONFIG_CREATE_UPDATE ); createUpdateWizard.processRequest( request, response, session, admin, formItems, parameters, user ); } else { super.handlerWizard( request, response, session, admin, formItems, parameters, user, wizardName ); } } public void handlerRemove( HttpServletRequest request, HttpServletResponse response, HttpSession session, AdminService admin, ExtendedMap formItems, String key ) throws VerticalAdminException, VerticalEngineException { workService.deleteEntry( key ); ExtendedMap params = new ExtendedMap(); params.put( "page", formItems.getString( "page" ) ); params.put( "op", "browse" ); redirectClientToAdminPath( "adminpage", params, request, response ); } private static XMLDocument createXmlDocument( Collection<TaskHandler> plugins ) { org.jdom.Element root = new org.jdom.Element( "task-plugins" ); if ( plugins != null ) { for ( TaskHandler plugin : plugins ) { org.jdom.Element pluginElement = new org.jdom.Element( "task-plugin" ); pluginElement.setAttribute( "display-name", plugin.getDisplayName() ); if ( plugin.getName() != null ) { pluginElement.setAttribute( "name", plugin.getName() ); } pluginElement.setAttribute( "class", plugin.getClass().getName() ); root.addContent( pluginElement ); } } return XMLDocumentFactory.create( new org.jdom.Document( root ) ); } }