From a610afa957ca2c85fdb5d3b4c6deddf284cb009b Mon Sep 17 00:00:00 2001 From: miciele Ghiorghis Date: Wed, 6 Mar 2013 17:49:20 +0100 Subject: [PATCH] tim-connector: main changes in scheduler * CRUD for scheduler * The UI job_scheduling.zul for CRUD is splits to _editJobScheduling.zul and _listJobScheduling.zul * Lots of changes in scheduler to make it generic - jobs can be scheduled, rescheduled and deleted - check if job has a connector and is enabled - check if job is allowed to be scheduled - do manual is moved to JobConfigurationController * attributes connectorName and scheduled added to JobSchedulerConfiguration modified * triggerGroup and triggerName deleted from JobScheduleerConfiguration * In JobConfigurationDAO findByConnectorName added * New JobClassNameEnum added to be used as data type for JobClassName in JobSchedulerConfiguration * Import/Export Jobs read the beans that do the real work from ApplicationContext * Some minor changes in ConfigurationController --- .../daos/IJobSchedulerConfigurationDAO.java | 2 + .../daos/JobSchedulerConfigurationDAO.java | 11 + .../business/common/entities/Connector.java | 6 + .../common/entities/JobClassNameEnum.java | 52 +++ .../entities/JobSchedulerConfiguration.java | 57 +-- .../src/main/resources/db.changelog-1.3.xml | 32 +- .../JobSchedulerConfiguration.hbm.xml | 14 +- .../importers/ExportTimesheetToTimJob.java | 24 +- .../importers/ISchedulerManager.java | 71 ++-- .../importers/ImportRosterFromTimJob.java | 20 +- .../libreplan/importers/SchedulerManager.java | 347 +++++++++++------- .../web/common/ConfigurationController.java | 6 + .../web/common/ConfigurationModel.java | 12 +- .../web/common/IConfigurationModel.java | 4 + .../web/common/IJobSchedulerModel.java | 110 +++++- .../web/common/JobSchedulerController.java | 301 +++++++++++---- .../web/common/JobSchedulerModel.java | 149 ++++++-- .../orders/TimSynchronizationController.java | 5 +- .../main/webapp/common/_editJobScheduling.zul | 167 +++++++++ .../main/webapp/common/_listJobScheduling.zul | 36 ++ .../src/main/webapp/common/job_scheduling.zul | 98 +---- 21 files changed, 1072 insertions(+), 452 deletions(-) create mode 100644 libreplan-business/src/main/java/org/libreplan/business/common/entities/JobClassNameEnum.java create mode 100644 libreplan-webapp/src/main/webapp/common/_editJobScheduling.zul create mode 100644 libreplan-webapp/src/main/webapp/common/_listJobScheduling.zul diff --git a/libreplan-business/src/main/java/org/libreplan/business/common/daos/IJobSchedulerConfigurationDAO.java b/libreplan-business/src/main/java/org/libreplan/business/common/daos/IJobSchedulerConfigurationDAO.java index 32b6291e5..0e0cfbd4d 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/common/daos/IJobSchedulerConfigurationDAO.java +++ b/libreplan-business/src/main/java/org/libreplan/business/common/daos/IJobSchedulerConfigurationDAO.java @@ -33,6 +33,8 @@ public interface IJobSchedulerConfigurationDAO extends List getAll(); + List findByConnectorName(String connectorName); + JobSchedulerConfiguration findByJobGroupAndJobName(String jobGroup, String jobName); } diff --git a/libreplan-business/src/main/java/org/libreplan/business/common/daos/JobSchedulerConfigurationDAO.java b/libreplan-business/src/main/java/org/libreplan/business/common/daos/JobSchedulerConfigurationDAO.java index aa812251f..b07db6648 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/common/daos/JobSchedulerConfigurationDAO.java +++ b/libreplan-business/src/main/java/org/libreplan/business/common/daos/JobSchedulerConfigurationDAO.java @@ -21,6 +21,7 @@ package org.libreplan.business.common.daos; import java.util.List; +import org.hibernate.Criteria; import org.hibernate.criterion.Restrictions; import org.libreplan.business.common.entities.JobSchedulerConfiguration; import org.springframework.beans.factory.config.BeanDefinition; @@ -55,4 +56,14 @@ public class JobSchedulerConfigurationDAO extends .add(Restrictions.eq("jobName", jobName)).uniqueResult(); } + @Override + @Transactional(readOnly = true) + public List findByConnectorName( + String connectorName) { + Criteria c = getSession().createCriteria( + JobSchedulerConfiguration.class).add( + Restrictions.eq("connectorName", connectorName)); + return ((List) c.list()); + } + } diff --git a/libreplan-business/src/main/java/org/libreplan/business/common/entities/Connector.java b/libreplan-business/src/main/java/org/libreplan/business/common/entities/Connector.java index d7a6aaf57..b1a621618 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/common/entities/Connector.java +++ b/libreplan-business/src/main/java/org/libreplan/business/common/entities/Connector.java @@ -112,4 +112,10 @@ public class Connector extends BaseEntity { } + public boolean isActivated() { + return getPropertiesAsMap() + .get(PredefinedConnectorProperties.ACTIVATED).equalsIgnoreCase( + "Y"); + } + } diff --git a/libreplan-business/src/main/java/org/libreplan/business/common/entities/JobClassNameEnum.java b/libreplan-business/src/main/java/org/libreplan/business/common/entities/JobClassNameEnum.java new file mode 100644 index 000000000..a7f2f3a77 --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/common/entities/JobClassNameEnum.java @@ -0,0 +1,52 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2013 St. Antoniusziekenhuis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.business.common.entities; + + +/** + * Defines the job class package and name to be used as data type in + * {@link JobSchedulerConfiguration} + * + * @author Miciele Ghiorghis + */ +public enum JobClassNameEnum { + + IMPORT_ROSTER_FROM_TIM_JOB("org.libreplan.importers", + "ImportRosterFromTimJob"), EXPORT_TIMESHEET_TO_TIM_JOB( + "org.libreplan.importers", + "ExportTimesheetToTimJob"); + + private String packageName; + private String name; + + private JobClassNameEnum(String packageName, String name) { + this.packageName = packageName; + this.name = name; + } + + public String getPackageName() { + return packageName; + } + + public String getName() { + return name; + } + +} diff --git a/libreplan-business/src/main/java/org/libreplan/business/common/entities/JobSchedulerConfiguration.java b/libreplan-business/src/main/java/org/libreplan/business/common/entities/JobSchedulerConfiguration.java index ccee32606..c801225d2 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/common/entities/JobSchedulerConfiguration.java +++ b/libreplan-business/src/main/java/org/libreplan/business/common/entities/JobSchedulerConfiguration.java @@ -21,6 +21,7 @@ package org.libreplan.business.common.entities; import org.hibernate.validator.NotNull; import org.libreplan.business.common.BaseEntity; +import org.libreplan.business.common.IHumanIdentifiable; /** * JobSchedulerConfiguration entity, represents parameters for the jobs to be @@ -33,7 +34,8 @@ import org.libreplan.business.common.BaseEntity; * * @author Miciele Ghiorghis */ -public class JobSchedulerConfiguration extends BaseEntity { +public class JobSchedulerConfiguration extends BaseEntity implements + IHumanIdentifiable { public static JobSchedulerConfiguration create() { return create(new JobSchedulerConfiguration()); @@ -49,13 +51,13 @@ public class JobSchedulerConfiguration extends BaseEntity { private String jobName; - private String triggerGroup; - - private String triggerName; - private String cronExpression; - private String jobClassName; + private JobClassNameEnum jobClassName; + + private boolean schedule; + + private String connectorName; @NotNull(message = "job group not specified") public String getJobGroup() { @@ -75,24 +77,6 @@ public class JobSchedulerConfiguration extends BaseEntity { this.jobName = jobName; } - @NotNull(message = "trigger group not specified") - public String getTriggerGroup() { - return triggerGroup; - } - - public void setTriggerGroup(String triggerGroup) { - this.triggerGroup = triggerGroup; - } - - @NotNull(message = "trigger name not specified") - public String getTriggerName() { - return triggerName; - } - - public void setTriggerName(String triggerName) { - this.triggerName = triggerName; - } - @NotNull(message = "cron expression not specified") public String getCronExpression() { return cronExpression; @@ -103,11 +87,32 @@ public class JobSchedulerConfiguration extends BaseEntity { } @NotNull(message = "job class name not specified") - public String getJobClassName() { + public JobClassNameEnum getJobClassName() { return jobClassName; } - public void setJobClassName(String jobClassName) { + public void setJobClassName(JobClassNameEnum jobClassName) { this.jobClassName = jobClassName; } + + public boolean isSchedule() { + return schedule; + } + + public void setSchedule(boolean schedule) { + this.schedule = schedule; + } + + public String getConnectorName() { + return connectorName; + } + + public void setConnectorName(String connectorName) { + this.connectorName = connectorName; + } + + @Override + public String getHumanId() { + return jobGroup == null ? "" : jobGroup; + } } diff --git a/libreplan-business/src/main/resources/db.changelog-1.3.xml b/libreplan-business/src/main/resources/db.changelog-1.3.xml index 8f5f7c66c..cb055b51c 100644 --- a/libreplan-business/src/main/resources/db.changelog-1.3.xml +++ b/libreplan-business/src/main/resources/db.changelog-1.3.xml @@ -378,7 +378,6 @@ referencedTableName="connector" referencesUniqueColumn="false"/> - Create new table job_scheduler_configuration @@ -395,39 +394,14 @@ - - - - - - - + + + - - Insert jobs to be scheduled with default cron-expression - - - - - - - - - - - - - - - - - - - diff --git a/libreplan-business/src/main/resources/org/libreplan/business/common/entities/JobSchedulerConfiguration.hbm.xml b/libreplan-business/src/main/resources/org/libreplan/business/common/entities/JobSchedulerConfiguration.hbm.xml index 881a4aa8d..822636ef6 100644 --- a/libreplan-business/src/main/resources/org/libreplan/business/common/entities/JobSchedulerConfiguration.hbm.xml +++ b/libreplan-business/src/main/resources/org/libreplan/business/common/entities/JobSchedulerConfiguration.hbm.xml @@ -14,10 +14,14 @@ - - - - - + + + + org.libreplan.business.common.entities.JobClassNameEnum + + + + + diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/ExportTimesheetToTimJob.java b/libreplan-webapp/src/main/java/org/libreplan/importers/ExportTimesheetToTimJob.java index ce7863ff2..20d45268e 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/importers/ExportTimesheetToTimJob.java +++ b/libreplan-webapp/src/main/java/org/libreplan/importers/ExportTimesheetToTimJob.java @@ -18,36 +18,28 @@ */ package org.libreplan.importers; + import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.context.annotation.Scope; +import org.springframework.context.ApplicationContext; import org.springframework.scheduling.quartz.QuartzJobBean; -import org.springframework.stereotype.Component; /** * A job that exports time sheets to Tim SOAP server * * @author Miciele Ghiorghis */ -@Component -@Scope(BeanDefinition.SCOPE_SINGLETON) public class ExportTimesheetToTimJob extends QuartzJobBean { - private IExportTimesheetsToTim exportTimesheetsToTim; - - public IExportTimesheetsToTim getExportTimesheetsToTim() { - return exportTimesheetsToTim; - } - - public void setExportTimesheetsToTim( - IExportTimesheetsToTim exportTimesheetsToTim) { - this.exportTimesheetsToTim = exportTimesheetsToTim; - } - @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { + ApplicationContext applicationContext = (ApplicationContext) context + .getJobDetail().getJobDataMap().get("applicationContext"); + + IExportTimesheetsToTim exportTimesheetsToTim = (IExportTimesheetsToTim) applicationContext + .getBean("exportTimesheetsToTim"); + exportTimesheetsToTim.exportTimesheets(); } diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/ISchedulerManager.java b/libreplan-webapp/src/main/java/org/libreplan/importers/ISchedulerManager.java index c5913488e..745ef51a3 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/importers/ISchedulerManager.java +++ b/libreplan-webapp/src/main/java/org/libreplan/importers/ISchedulerManager.java @@ -19,9 +19,8 @@ package org.libreplan.importers; -import java.util.List; - import org.libreplan.business.common.entities.JobSchedulerConfiguration; +import org.quartz.SchedulerException; import org.springframework.scheduling.quartz.CronTriggerBean; import org.springframework.scheduling.quartz.JobDetailBean; @@ -31,51 +30,73 @@ import org.springframework.scheduling.quartz.JobDetailBean; * * The start and destroy of the scheduler itself is managed by the Spring * framework. The scheduler starts automatically when the application starts and - * destroyed when the application stops. The sole purpose of this manager is to - * create jobs {@link JobDetailBean} and cron-triggers {@link CronTriggerBean} - * when the scheduler is started. It links the triggers with the jobs and add - * them to the scheduler. + * destroyed when the application stops. * - * The SchedulerManager reads the jobs to be scheduled and the cron-triggers to - * fire the jobs form the {@link JobSchedulerConfiguration} entity. Hence the - * {@link JobSchedulerConfiguration} entity must exist with predefined jobs and - * valid cron-triggers + * This manager (un)schedules the jobs based on the configuration + * {@link JobSchedulerConfiguration} entity once the scheduler starts. * - * This manager also supports the rescheduling of jobs. + *
    + *
  • Schedule job:create job {@link JobDetailBean} and cron-trigger + * {@link CronTriggerBean}, associated the trigger with the job and add it to + * the scheduler. + *
  • + *
  • Delete job: search the job in the scheduler and if found + * unschedule(delete) the job
  • + *
* * @author Miciele Ghiorghis */ public interface ISchedulerManager { /** - * Reads job configuration from the {@link JobSchedulerConfiguration} and - * schedules the jobs as defined in the configuration + * Reads the jobs to be scheduled from the {@link JobSchedulerConfiguration} + * and schedules the jobs based on the cron expression defined for each job + * in the {@link JobSchedulerConfiguration} */ void scheduleJobs(); /** - * Reschedule the job. + * Reads the jobs to be scheduled from the specified + * {@link JobSchedulerConfiguration} and (un)schedule it + * accordingly * - * Reads the job to be rescheduled from the specified parameter - * {@link JobSchedulerConfiguration} and reschedule the job accordingly + * In the specified {@link JobSchedulerConfiguration} + * + *
    + *
  • {@link JobSchedulerConfiguration#getConnectorName()} + * check if job has a connector and the connector is activated
  • + *
  • {@link JobSchedulerConfiguration#isSchedule()} if true + * the job would be scheduled, if not job deleted
  • + *
* * @param jobSchedulerConfiguration - * the job scheduler configuration + * configuration for job to be (un)scheduled + * @throws SchedulerException + * if unable to (un)schedule */ - void rescheduleJob(JobSchedulerConfiguration jobSchedulerConfiguration); + void scheduleOrUnscheduleJob(JobSchedulerConfiguration jobSchedulerConfiguration) throws SchedulerException; /** - * returns the scheduler info list. Can be useful to display in UI + * Deletes the job from the scheduler for the specified job by + * {@link JobSchedulerConfiguration}, if the job is already in + * the scheduler * - * @return list of scheduler info + * @param jobSchedulerConfiguration + * configuration for job to be deleted + * @throws SchedulerException + * if unable to delete */ - List getSchedulerInfos(); + void deleteJob(JobSchedulerConfiguration jobSchedulerConfiguration) throws SchedulerException; /** - * To manually execute the job specified by jobName + * gets the next fire time for the specified job from + * {@link JobSchedulerConfiguration} if job is already scheduled. This is + * only neede for UI * - * @param jobName - * the name of the job to be executed + * @param jobSchedulerConfiguration + * configuration to check for next fire time + * @return next fire time or empty string */ - void doManual(String jobName); + String getNextFireTime(JobSchedulerConfiguration jobSchedulerConfiguration); + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/ImportRosterFromTimJob.java b/libreplan-webapp/src/main/java/org/libreplan/importers/ImportRosterFromTimJob.java index 5278a2e46..fe8cc12b7 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/importers/ImportRosterFromTimJob.java +++ b/libreplan-webapp/src/main/java/org/libreplan/importers/ImportRosterFromTimJob.java @@ -22,10 +22,10 @@ package org.libreplan.importers; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Scope; import org.springframework.scheduling.quartz.QuartzJobBean; import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; /** * A job that import rosters from Tim SOAP server @@ -36,21 +36,17 @@ import org.springframework.transaction.annotation.Transactional; @Scope(BeanDefinition.SCOPE_SINGLETON) public class ImportRosterFromTimJob extends QuartzJobBean { - private IImportRosterFromTim importRosterFromTim; - - public IImportRosterFromTim getImportRosterFromTim() { - return importRosterFromTim; - } - - public void setImportRosterFromTim(IImportRosterFromTim importRosterFromTim) { - this.importRosterFromTim = importRosterFromTim; - } - @Override - @Transactional protected void executeInternal(JobExecutionContext context) throws JobExecutionException { + ApplicationContext applicationContext = (ApplicationContext) context + .getJobDetail().getJobDataMap().get("applicationContext"); + + IImportRosterFromTim importRosterFromTim = (IImportRosterFromTim) applicationContext + .getBean("importRosterFromTim"); + importRosterFromTim.importRosters(); + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/SchedulerManager.java b/libreplan-webapp/src/main/java/org/libreplan/importers/SchedulerManager.java index 302290f2d..e3b8ac9de 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/importers/SchedulerManager.java +++ b/libreplan-webapp/src/main/java/org/libreplan/importers/SchedulerManager.java @@ -20,26 +20,30 @@ package org.libreplan.importers; import java.text.ParseException; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.libreplan.business.common.daos.IJobSchedulerConfigurationDAO; +import org.libreplan.business.common.entities.Connector; +import org.libreplan.business.common.entities.JobClassNameEnum; import org.libreplan.business.common.entities.JobSchedulerConfiguration; +import org.libreplan.web.common.IConfigurationModel; import org.quartz.CronExpression; import org.quartz.CronTrigger; +import org.quartz.JobExecutionContext; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Scope; import org.springframework.scheduling.quartz.CronTriggerBean; import org.springframework.scheduling.quartz.JobDetailBean; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; /** * Implementation of scheduler manager @@ -55,15 +59,21 @@ public class SchedulerManager implements ISchedulerManager { @Autowired private Scheduler scheduler; - @Autowired - private IImportRosterFromTim importRosterFromTim; - - @Autowired - private IExportTimesheetsToTim exportTimesheetsToTim; - @Autowired private IJobSchedulerConfigurationDAO jobSchedulerConfigurationDAO; + @Autowired + private ApplicationContext applicationContext; + + @Autowired + private IConfigurationModel configurationModel; + + + /** + * suffix for trigger -group and -name + */ + private static final String TRIGGER_SUFFIX = "-TRIGGER"; + public Scheduler getScheduler() { return scheduler; } @@ -77,40 +87,163 @@ public class SchedulerManager implements ISchedulerManager { List jobSchedulerConfigurations = jobSchedulerConfigurationDAO .getAll(); for (JobSchedulerConfiguration conf : jobSchedulerConfigurations) { - CronTriggerBean cronTriggerBean = createCronTriggerBean( - conf.getTriggerGroup(), conf.getTriggerName(), - conf.getCronExpression()); - if (cronTriggerBean != null) { - cronTriggerBean.setJobName(conf.getJobName()); - cronTriggerBean.setJobGroup(conf.getJobGroup()); - JobDetailBean jobDetailBean = createJobDetailBean( - conf.getJobName(), conf.getJobGroup(), - conf.getJobClassName()); - if (jobDetailBean != null) { - scheduleJob(jobDetailBean, cronTriggerBean); - } + try { + scheduleOrUnscheduleJob(conf); + } catch (SchedulerException e) { + LOG.error("Unable to schedule", e); } } } + @Override + public void scheduleOrUnscheduleJob( + JobSchedulerConfiguration jobSchedulerConfiguration) throws SchedulerException { + + if (hasConnector(jobSchedulerConfiguration.getConnectorName())) { + if (isConnectorActivated(jobSchedulerConfiguration + .getConnectorName())) { + if (jobSchedulerConfiguration.isSchedule()) { + scheduleNewJob(jobSchedulerConfiguration); + return; + } + } + deleteJob(jobSchedulerConfiguration); + return; + } + if (!jobSchedulerConfiguration.isSchedule()) { + deleteJob(jobSchedulerConfiguration); + return; + } + scheduleNewJob(jobSchedulerConfiguration); + } + /** - * Creates and returns {@link CronTriggerBean} + * Check if {@link JobSchedulerConfiguration} has a connector * - * @param triggerGroup - * the trigger group - * @param triggerName - * the trigger name - * @param cronExpression - * the cron expression string + * @param connectorName + * the connector to check for + * @return true if connector is not null or empty */ - private CronTriggerBean createCronTriggerBean(String triggerGroup, - String triggerName, String cronExpression) { - CronTriggerBean cronTriggerBean = new CronTriggerBean(); - cronTriggerBean.setGroup(triggerGroup); - cronTriggerBean.setName(triggerName); + private boolean hasConnector(String connectorName) { + return !StringUtils.isBlank(connectorName); + } + + /** + * Check if the specified {@link Connector} is activated + * + * @param connectorName + * the connector to check for activated + * @return true if activated + */ + private boolean isConnectorActivated(String connectorName) { + configurationModel.initConnectorConfiguration(); + + Connector connector = configurationModel + .getConnectorByName(connectorName); + if (connector == null) { + return false; + } + return connector.isActivated(); + } + + @Override + public void deleteJob(JobSchedulerConfiguration jobSchedulerConfiguration) throws SchedulerException { + String triggerName = jobSchedulerConfiguration.getJobName() + + TRIGGER_SUFFIX; + String triggerGroup = jobSchedulerConfiguration.getJobGroup() + + TRIGGER_SUFFIX; + + CronTriggerBean trigger = getTriggerBean(triggerName, triggerGroup); + if (trigger == null) { + LOG.warn("Trigger not found"); + return; + } + + if (isJobCurrentlyExecuting(triggerName, triggerGroup)) { + LOG.warn("Job is currently executing..."); + return; + } + + // deleteJob doesn't work using unscheduleJob + this.scheduler.unscheduleJob(trigger.getName(), trigger.getGroup()); + } + + /** + * Checks if job is currently running for the specified + * triggerName and triggerGroup + * + * @param triggerName + * the triggerName + * @param triggerGroup + * the triggerGroup + * @return true if job is currently running, otherwise false + */ + @SuppressWarnings("unchecked") + private boolean isJobCurrentlyExecuting(String triggerName, + String triggerGroup) { try { + List currentExecutingJobs = this.scheduler + .getCurrentlyExecutingJobs(); + for (JobExecutionContext jobExecutionContext : currentExecutingJobs) { + String name = jobExecutionContext.getTrigger().getName(); + String group = jobExecutionContext.getTrigger().getGroup(); + if (triggerName.equals(name) && triggerGroup.equals(group)) { + return true; + } + } + } catch (SchedulerException e) { + LOG.error("Unable to get currently executing jobs", e); + } + return false; + } + + /** + * Creates {@link CronTriggerBean} and {@link JobDetailBean} based on the + * specified {@link JobSchedulerConfiguration}. First delete + * job if exist and then schedule it + * + * @param jobSchedulerConfiguration + * where to reade jobs to be scheduled + * @throws SchedulerException + * if unable to delete and/or schedule job + */ + private void scheduleNewJob( + JobSchedulerConfiguration jobSchedulerConfiguration) throws SchedulerException { + CronTriggerBean cronTriggerBean = createCronTriggerBean(jobSchedulerConfiguration); + if (cronTriggerBean == null) { + return; + } + + JobDetailBean jobDetailBean = createJobDetailBean(jobSchedulerConfiguration); + if (jobDetailBean == null) { + return; + } + deleteJob(jobSchedulerConfiguration); + this.scheduler.scheduleJob(jobDetailBean, cronTriggerBean); + } + + /** + * Creates {@link CronTriggerBean} from the specified + * {@link JobSchedulerConfiguration} + * + * @param jobSchedulerConfiguration + * configuration to create CronTriggerBean + * @return the created CronTriggerBean or null if unable to + * create it + */ + private CronTriggerBean createCronTriggerBean( + JobSchedulerConfiguration jobSchedulerConfiguration) { + CronTriggerBean cronTriggerBean = new CronTriggerBean(); + cronTriggerBean.setName(jobSchedulerConfiguration.getJobName() + TRIGGER_SUFFIX); + cronTriggerBean.setGroup(jobSchedulerConfiguration.getJobGroup() + + TRIGGER_SUFFIX); + + try { + cronTriggerBean.setCronExpression(new CronExpression( + jobSchedulerConfiguration.getCronExpression())); + cronTriggerBean.setJobName(jobSchedulerConfiguration.getJobName()); cronTriggerBean - .setCronExpression(new CronExpression(cronExpression)); + .setJobGroup(jobSchedulerConfiguration.getJobGroup()); return cronTriggerBean; } catch (ParseException e) { LOG.error("Unable to parse cron expression", e); @@ -119,56 +252,33 @@ public class SchedulerManager implements ISchedulerManager { } /** - * Creates and returns {@link JobDetailBean} + * Creates {@link JobDetailBean} from the specified + * {@link JobSchedulerConfiguration} * - * @param jobName - * the job name - * @param jobGroup - * the job group - * @param jobClassName - * the job classname + * @param jobSchedulerConfiguration + * configuration to create JobDetailBean + * @return the created JobDetailBean or null if unable to it */ - private JobDetailBean createJobDetailBean(String jobName, String jobGroup, - String jobClassName) { + private JobDetailBean createJobDetailBean( + JobSchedulerConfiguration jobSchedulerConfiguration) { JobDetailBean jobDetailBean = new JobDetailBean(); - Class jobClass = getJobClass(jobClassName); + Class jobClass = getJobClass(jobSchedulerConfiguration + .getJobClassName()); if (jobClass == null) { - LOG.error("JobClass '" + jobClassName + "' not found"); return null; } - jobDetailBean.setGroup(jobGroup); - jobDetailBean.setName(jobName); + + jobDetailBean.setName(jobSchedulerConfiguration.getJobName()); + jobDetailBean.setGroup(jobSchedulerConfiguration.getJobGroup()); jobDetailBean.setJobClass(jobClass); Map jobDataAsMap = new HashMap(); - if (jobDetailBean.getJobClass().getSimpleName() - .equals("ImportRosterFromTimJob")) { - jobDataAsMap.put("importRosterFromTim", importRosterFromTim); - } else { - jobDataAsMap.put("exportTimesheetsToTim", exportTimesheetsToTim); - } + jobDataAsMap.put("applicationContext", applicationContext); jobDetailBean.setJobDataAsMap(jobDataAsMap); return jobDetailBean; } - /** - * Schedules the job specified by {@link JobDetailBean} and - * link it with the specified {@link CronTriggerBean} - * - * @param jobDetailBean - * the jobDetailBean - * @param cronTriggerBean - * the cronTriggerBean - */ - private void scheduleJob(JobDetailBean jobDetailBean, - CronTriggerBean cronTriggerBean) { - try { - this.scheduler.scheduleJob(jobDetailBean, cronTriggerBean); - } catch (SchedulerException e) { - LOG.error("unable to schedule job", e); - } - } /** * returns jobClass based on jobClassName parameter @@ -176,79 +286,52 @@ public class SchedulerManager implements ISchedulerManager { * @param jobClassName * job className */ - private Class getJobClass(String jobClassName) { - if (jobClassName.equals("ImportRosterFromTimJob")) { - return org.libreplan.importers.ImportRosterFromTimJob.class; + private Class getJobClass(JobClassNameEnum jobClassName) { + try { + return Class.forName(jobClassName.getPackageName() + "." + + jobClassName.getName()); + } catch (ClassNotFoundException e) { + LOG.error("Unable to get class object '" + jobClassName + "'", e); } - - if (jobClassName.equals("ExportTimesheetToTimJob")) { - return org.libreplan.importers.ExportTimesheetToTimJob.class; - } - return null; } @Override - public void rescheduleJob(JobSchedulerConfiguration jobSchedulerConfiguration) { - CronTriggerBean cronTriggerBean = createCronTriggerBean( - jobSchedulerConfiguration.getTriggerGroup(), - jobSchedulerConfiguration.getTriggerName(), - jobSchedulerConfiguration.getCronExpression()); - cronTriggerBean.setName(jobSchedulerConfiguration.getTriggerName()); - cronTriggerBean.setGroup(jobSchedulerConfiguration.getTriggerGroup()); + public String getNextFireTime( + JobSchedulerConfiguration jobSchedulerConfiguration) { try { - cronTriggerBean.setCronExpression(jobSchedulerConfiguration - .getCronExpression()); - } catch (ParseException e) { - throw new RuntimeException("Invalid cron expression"); - } - cronTriggerBean.setJobName(jobSchedulerConfiguration.getJobName()); - cronTriggerBean.setJobGroup(jobSchedulerConfiguration.getJobGroup()); - try { - scheduler.rescheduleJob(jobSchedulerConfiguration.getTriggerName(), - jobSchedulerConfiguration.getTriggerGroup(), - cronTriggerBean); - } catch (SchedulerException e) { - throw new RuntimeException("Unable to reschedule the job"); - } - } - - @Override - @Transactional(readOnly = true) - public List getSchedulerInfos() { - List jobSchedulerConfigurations = jobSchedulerConfigurationDAO - .getAll(); - List results = new ArrayList(); - for (JobSchedulerConfiguration jobSchedulerConfiguration : jobSchedulerConfigurations) { - SchedulerInfo schedulerInfo = new SchedulerInfo(); - schedulerInfo - .setJobSchedulerConfiguration(jobSchedulerConfiguration); - try { - CronTrigger trigger = (CronTrigger) scheduler.getTrigger( - jobSchedulerConfiguration.getTriggerName(), - jobSchedulerConfiguration.getTriggerGroup()); - if (trigger != null) { - schedulerInfo.setNextFireTime(trigger.getNextFireTime() - .toString()); - } - results.add(schedulerInfo); - } catch (SchedulerException e) { - LOG.error("unable to get the trigger"); + CronTrigger trigger = (CronTrigger) this.scheduler.getTrigger( + jobSchedulerConfiguration.getJobName() + TRIGGER_SUFFIX, + jobSchedulerConfiguration.getJobGroup() + + TRIGGER_SUFFIX); + if (trigger != null) { + return trigger.getNextFireTime().toString(); } + } catch (SchedulerException e) { + LOG.error("unable to get the trigger", e); } - return results; + return ""; } - @Override - public void doManual(String jobName) { - if (jobName.equals("Import roster from Tim")) { - importRosterFromTim.importRosters(); - return; - } - - if (jobName.equals("Export timesheet to Tim")) { - exportTimesheetsToTim.exportTimesheets(); - return; + /** + * gets the {@link CronTriggerBean} for the specified + * triggerName and tirggerGroup + * + * @param triggerName + * the trigger name + * @param triggerGroup + * the trigger group + * @return CronTriggerBean if found, otherwise null + */ + private CronTriggerBean getTriggerBean(String triggerName, + String triggerGroup) { + try { + return (CronTriggerBean) this.scheduler.getTrigger(triggerName, + triggerGroup); + } catch (SchedulerException e) { + LOG.error("Unable to get job trigger", e); } + return null; } + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/ConfigurationController.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/ConfigurationController.java index 7c4ca0c52..e9c4a2a93 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/common/ConfigurationController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/ConfigurationController.java @@ -228,6 +228,12 @@ public class ConfigurationController extends GenericForwardComposer { configurationModel.confirm(); configurationModel.init(); messages.showMessage(Level.INFO, _("Changes saved")); + if (!configurationModel + .scheduleOrUnscheduleJobs(getSelectedConnector())) { + messages.showMessage( + Level.ERROR, + _("Scheduling or unscheduling of jobs for this connector is not completed")); + } reloadWindow(); reloadEntitySequences(); reloadConnectors(); diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/ConfigurationModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/ConfigurationModel.java index a89ba5b11..d8594ea3c 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/common/ConfigurationModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/ConfigurationModel.java @@ -96,6 +96,9 @@ public class ConfigurationModel implements IConfigurationModel { @Autowired private IConnectorDAO connectorDAO; + @Autowired + private IJobSchedulerModel jobSchedulerModel; + @Override @Transactional(readOnly = true) public List getCalendars() { @@ -136,7 +139,9 @@ public class ConfigurationModel implements IConfigurationModel { } } - private void initConnectorConfiguration() { + @Override + @Transactional(readOnly = true) + public void initConnectorConfiguration() { connectors = connectorDAO.getAll(); forceLoadConnectors(); } @@ -748,4 +753,9 @@ public class ConfigurationModel implements IConfigurationModel { return null; } + @Override + public boolean scheduleOrUnscheduleJobs(Connector connector) { + return jobSchedulerModel.scheduleOrUnscheduleJobs(connector); + } + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/IConfigurationModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/IConfigurationModel.java index 1ce2889f8..837590d12 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/common/IConfigurationModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/IConfigurationModel.java @@ -195,8 +195,12 @@ public interface IConfigurationModel { void setJiraConnectorTypeOfWorkHours(TypeOfWorkHours typeOfWorkHours); + void initConnectorConfiguration(); + List getConnectors(); Connector getConnectorByName(String name); + boolean scheduleOrUnscheduleJobs(Connector connector); + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/IJobSchedulerModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/IJobSchedulerModel.java index b89178e9a..6ce07428b 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/common/IJobSchedulerModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/IJobSchedulerModel.java @@ -21,20 +21,120 @@ package org.libreplan.web.common; import java.util.List; -import org.libreplan.importers.SchedulerInfo; +import org.libreplan.business.common.entities.Connector; +import org.libreplan.business.common.entities.JobSchedulerConfiguration; +import org.libreplan.business.common.entities.PredefinedConnectorProperties; +import org.libreplan.business.common.exceptions.ValidationException; /** * Contract for {@link JobSchedulerModel}. * * @author Manuel Rego Casasnovas + * @author Miciele Ghiorghis */ public interface IJobSchedulerModel { - List getSchedulerInfos(); + /** + * returns all job scheduler configurations + * + * @return list of JobSchedulerConfiguration + */ + List getJobSchedulerConfigurations(); - void doManual(SchedulerInfo schedulerInfo); + /** + * returns next fire time for the specified job from + * {@link JobSchedulerConfiguration} + * + * @param jobSchedulerConfiguration + * the job scheduler configuration + */ + String getNextFireTime(JobSchedulerConfiguration jobSchedulerConfiguration); - void saveJobConfigurationAndReschedule(String jobGroup, String jobName, - String cronExp); + /** + * Do manual action(replacement of scheduling) + * + * @param jobSchedulerConfiguration + * the job configuration + */ + void doManual(JobSchedulerConfiguration jobSchedulerConfiguration); + + + /** + * Prepares for create a new {@link JobSchedulerConfiguration}. + */ + void initCreate(); + + /** + * Prepares for edit {@link JobSchedulerConfiguration} + * + * @param jobSchedulerConfiguration + * object to be edited + */ + void initEdit(JobSchedulerConfiguration jobSchedulerConfiguration); + + /** + * Gets the current {@link JobSchedulerConfiguration}. + * + * @return A {@link JobSchedulerConfiguration} + */ + JobSchedulerConfiguration getJobSchedulerConfiguration(); + + /** + * Saves the current {@link JobSchedulerConfiguration} + * + * @throws ValidationException + * if validation fails + */ + void confirmSave() throws ValidationException; + + /** + * Cancels the current {@link JobSchedulerConfiguration} + */ + void cancel(); + + /** + * Removes the current {@link JobSchedulerConfiguration} + * + * @param jobSchedulerConfiguration + * object to be removed + */ + void remove(JobSchedulerConfiguration jobSchedulerConfiguration); + + /** + * returns list of connectors + */ + List getConnectors(); + + /** + * Schedule or unschedule jobs for the specified connector + * + * schedule all jobs of the specified connector's property + * {@link PredefinedConnectorProperties#ACTIVATED} is 'Y', otherwise + * unschedule the jobs + * + * @param connector + * where to check if property is changed + * @return true if (un)scheduling is successful, false otherwise + */ + boolean scheduleOrUnscheduleJobs(Connector connector); + + /** + * schedule or unschedule job for the specified job in + * {@link JobSchedulerConfiguration} + * + * @return true if scheduling is succeeded, false otherwise + */ + boolean scheduleOrUnscheduleJob(); + + /** + * Delete job specified in {@link JobSchedulerConfiguration} + * + * @param jobSchedulerConfiguration + * configuration for the job to be deleted + * @return true if job is successfully deleted from the scheduler, false + * otherwise + */ + boolean deleteScheduledJob( + JobSchedulerConfiguration jobSchedulerConfiguration); } \ No newline at end of file diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/JobSchedulerController.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/JobSchedulerController.java index 5e9ee8ef6..37e5755e1 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/common/JobSchedulerController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/JobSchedulerController.java @@ -22,20 +22,23 @@ package org.libreplan.web.common; import static org.libreplan.web.I18nHelper._; import java.text.ParseException; +import java.util.ArrayList; import java.util.List; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.libreplan.business.common.entities.Connector; +import org.libreplan.business.common.entities.JobClassNameEnum; import org.libreplan.business.common.entities.JobSchedulerConfiguration; -import org.libreplan.importers.SchedulerInfo; +import org.libreplan.business.common.exceptions.InstanceNotFoundException; +import org.libreplan.business.common.exceptions.ValidationException; import org.quartz.CronExpression; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.WrongValueException; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.Events; -import org.zkoss.zk.ui.util.GenericForwardComposer; import org.zkoss.zul.Button; import org.zkoss.zul.Grid; import org.zkoss.zul.Hbox; @@ -50,19 +53,21 @@ import org.zkoss.zul.api.Textbox; * * @author Miciele Ghiorghis */ -public class JobSchedulerController extends GenericForwardComposer { +public class JobSchedulerController extends + BaseCRUDController { private static final Log LOG = LogFactory .getLog(JobSchedulerController.class); - private Grid jobSchedulerGrid; + private Grid listJobSchedulings; + private Grid cronExpressionGrid; private Popup cronExpressionInputPopup; private Label jobGroup; - private Label jobName; + private Textbox cronExpressionTextBox; private Textbox cronExpressionSeconds; private Textbox cronExpressionMinutes; private Textbox cronExpressionHours; @@ -76,89 +81,191 @@ public class JobSchedulerController extends GenericForwardComposer { @Override public void doAfterCompose(Component comp) throws Exception { super.doAfterCompose(comp); - comp.setAttribute("jobSchedulerController", this); + listJobSchedulings = (Grid) listWindow + .getFellowIfAny("listJobSchedulings"); + listJobSchedulings.getModel(); + initCronExpressionPopup(); } - public List getSchedulerInfos() { - return jobSchedulerModel.getSchedulerInfos(); + /** + * initializes cron expressions for popup + */ + private void initCronExpressionPopup() { + cronExpressionTextBox = (Textbox) editWindow + .getFellow("cronExpressionTextBox"); + + cronExpressionInputPopup = (Popup) editWindow + .getFellow("cronExpressionInputPopup"); + + jobGroup = (Label) cronExpressionInputPopup.getFellow("jobGroup"); + jobName = (Label) cronExpressionInputPopup.getFellow("jobName"); + + cronExpressionGrid = (Grid) cronExpressionInputPopup + .getFellow("cronExpressionGrid"); + + cronExpressionSeconds = (Textbox) cronExpressionGrid + .getFellow("cronExpressionSeconds"); + cronExpressionMinutes = (Textbox) cronExpressionGrid + .getFellow("cronExpressionMinutes"); + cronExpressionHours = (Textbox) cronExpressionGrid + .getFellow("cronExpressionHours"); + cronExpressionDayOfMonth = (Textbox) cronExpressionGrid + .getFellow("cronExpressionDayOfMonth"); + cronExpressionMonth = (Textbox) cronExpressionGrid + .getFellow("cronExpressionMonth"); + cronExpressionDayOfWeek = (Textbox) cronExpressionGrid + .getFellow("cronExpressionDayOfWeek"); + cronExpressionYear = (Textbox) cronExpressionGrid + .getFellow("cronExpressionYear"); } + /** + * returns a list of {@link JobSchedulerConfiguration} + */ + public List getJobSchedulerConfigurations() { + return jobSchedulerModel.getJobSchedulerConfigurations(); + } + + /** + * returns {@link JobSchedulerConfiguration} + */ + public JobSchedulerConfiguration getJobSchedulerConfiguration() { + return jobSchedulerModel.getJobSchedulerConfiguration(); + } + + /** + * returns all predefined jobs + */ + public JobClassNameEnum[] getJobNames() { + return JobClassNameEnum.values(); + } + + /** + * return list of connectorNames + */ + public List getConnectorNames() { + List connectors = jobSchedulerModel.getConnectors(); + List connectorNames = new ArrayList(); + for (Connector connector : connectors) { + connectorNames.add(connector.getName()); + } + return connectorNames; + } + + /** + * renders job scheduling and returns {@link RowRenderer} + */ public RowRenderer getJobSchedulingRenderer() { return new RowRenderer() { @Override public void render(Row row, Object data) { - SchedulerInfo schedulerInfo = (SchedulerInfo) data; + final JobSchedulerConfiguration jobSchedulerConfiguration = (JobSchedulerConfiguration) data; row.setValue(data); - Util.appendLabel(row, schedulerInfo - .getJobSchedulerConfiguration().getJobGroup()); - Util.appendLabel(row, schedulerInfo - .getJobSchedulerConfiguration().getJobName()); - appendCronExpressionAndButton(row, schedulerInfo); - Util.appendLabel(row, schedulerInfo.getNextFireTime()); - appendManualStart(row, schedulerInfo); + Util.appendLabel(row, jobSchedulerConfiguration.getJobGroup()); + Util.appendLabel(row, jobSchedulerConfiguration.getJobName()); + Util.appendLabel(row, + jobSchedulerConfiguration.getCronExpression()); + Util.appendLabel(row, + getNextFireTime(jobSchedulerConfiguration)); + Hbox hbox = new Hbox(); + hbox.appendChild(createManualButton(new EventListener() { + + @Override + public void onEvent(Event event) throws Exception { + jobSchedulerModel.doManual(jobSchedulerConfiguration); + } + })); + hbox.appendChild(Util.createEditButton(new EventListener() { + @Override + public void onEvent(Event event) { + goToEditForm(jobSchedulerConfiguration); + } + })); + hbox.appendChild(Util.createRemoveButton(new EventListener() { + @Override + public void onEvent(Event event) { + confirmDelete(jobSchedulerConfiguration); + } + })); + row.appendChild(hbox); + } }; } - private void appendCronExpressionAndButton(final Row row, - final SchedulerInfo schedulerInfo) { - final Hbox hBox = new Hbox(); - hBox.setWidth("170px"); - - Label label = new Label(schedulerInfo.getJobSchedulerConfiguration() - .getCronExpression()); - label.setHflex("1"); - hBox.appendChild(label); - - Button button = Util.createEditButton(new EventListener() { - @Override - public void onEvent(Event event) throws Exception { - setupCronExpressionPopup(schedulerInfo); - cronExpressionInputPopup.open(hBox); - } - }); - hBox.appendChild(button); - - row.appendChild(hBox); + /** + * returns the next fire time for the specified job in + * {@link JobSchedulerConfiguration} + * + * @param jobSchedulerConfiguration + * the job scheduler configuration + */ + private String getNextFireTime( + JobSchedulerConfiguration jobSchedulerConfiguration) { + return jobSchedulerModel.getNextFireTime(jobSchedulerConfiguration); } + /** + * creates and returns a button + * + * @param eventListener + * Event listener for this button + */ + private static Button createManualButton(EventListener eventListener) { + Button button = new Button(_("Manual")); + button.setTooltiptext(_("Manual")); + button.addEventListener(Events.ON_CLICK, eventListener); + return button; + } - private void setupCronExpressionPopup(final SchedulerInfo schedulerInfo) { - JobSchedulerConfiguration jobSchedulerConfiguration = schedulerInfo.getJobSchedulerConfiguration(); - jobGroup.setValue(jobSchedulerConfiguration.getJobGroup()); - jobName.setValue(jobSchedulerConfiguration.getJobName()); + /** + * Opens the cronExpressionInputPopup + */ + public void openPopup() { + setupCronExpressionPopup(getJobSchedulerConfiguration()); + cronExpressionInputPopup.open(cronExpressionTextBox, "after_start"); + } - String cronExpression = jobSchedulerConfiguration.getCronExpression(); - String[] cronExpressionArray = StringUtils.split(cronExpression); + /** + * Sets the cronExpression values for cronExpressionInputPopup + * + * @param jobSchedulerConfiguration + * where to read the values + */ + private void setupCronExpressionPopup( + final JobSchedulerConfiguration jobSchedulerConfiguration) { + if (jobSchedulerConfiguration != null) { + jobGroup.setValue(jobSchedulerConfiguration.getJobGroup()); + jobName.setValue(jobSchedulerConfiguration.getJobName()); - cronExpressionSeconds.setValue(cronExpressionArray[0]); - cronExpressionMinutes.setValue(cronExpressionArray[1]); - cronExpressionHours.setValue(cronExpressionArray[2]); - cronExpressionDayOfMonth.setValue(cronExpressionArray[3]); - cronExpressionMonth.setValue(cronExpressionArray[4]); - cronExpressionDayOfWeek.setValue(cronExpressionArray[5]); + String cronExpression = jobSchedulerConfiguration + .getCronExpression(); + if (cronExpression == null || cronExpression.isEmpty()) { + return; + } - if (cronExpressionArray.length == 7) { - cronExpressionYear.setValue(cronExpressionArray[6]); + String[] cronExpressionArray = StringUtils.split(cronExpression); + + cronExpressionSeconds.setValue(cronExpressionArray[0]); + cronExpressionMinutes.setValue(cronExpressionArray[1]); + cronExpressionHours.setValue(cronExpressionArray[2]); + cronExpressionDayOfMonth.setValue(cronExpressionArray[3]); + cronExpressionMonth.setValue(cronExpressionArray[4]); + cronExpressionDayOfWeek.setValue(cronExpressionArray[5]); + + if (cronExpressionArray.length == 7) { + cronExpressionYear.setValue(cronExpressionArray[6]); + } } } - private void appendManualStart(final Row row, - final SchedulerInfo schedulerInfo) { - final Button rescheduleButton = new Button(_("Manual")); - rescheduleButton.addEventListener(Events.ON_CLICK, new EventListener() { - - @Override - public void onEvent(Event event) throws Exception { - jobSchedulerModel.doManual(schedulerInfo); - } - }); - row.appendChild(rescheduleButton); - } - - public void reschedule() { + /** + * sets the cronExpressionTextBox value from the + * cronExpressionInputPopup + */ + public void updateCronExpression() { String cronExpression = getCronExpressionString(); try { // Check cron expression format @@ -169,13 +276,16 @@ public class JobSchedulerController extends GenericForwardComposer { _("Unable to parse cron expression") + ":\n" + e.getMessage()); } - - jobSchedulerModel.saveJobConfigurationAndReschedule( - jobGroup.getValue(), jobName.getValue(), cronExpression); + cronExpressionTextBox.setValue(cronExpression); cronExpressionInputPopup.close(); - Util.reloadBindings(jobSchedulerGrid); + Util.saveBindings(cronExpressionTextBox); } + /** + * Concatenating the cronExpression values + * + * @return cronExpression string + */ private String getCronExpressionString() { String cronExpression = ""; cronExpression += StringUtils.trimToEmpty(cronExpressionSeconds.getValue()) + " "; @@ -193,8 +303,61 @@ public class JobSchedulerController extends GenericForwardComposer { return cronExpression; } - public void cancel() { + /** + * closes the popup + */ + public void cancelPopup() { cronExpressionInputPopup.close(); } + @Override + protected String getEntityType() { + return _("Job scheduling"); + } + + @Override + protected String getPluralEntityType() { + return _("Job scheduling"); + } + + @Override + protected void initCreate() { + jobSchedulerModel.initCreate(); + + } + + @Override + protected void initEdit(JobSchedulerConfiguration entity) { + jobSchedulerModel.initEdit(entity); + } + + @Override + protected void save() throws ValidationException { + jobSchedulerModel.confirmSave(); + if (jobSchedulerModel.scheduleOrUnscheduleJob()) { + messagesForUser.showMessage(Level.INFO, + _("Job is scheduled/unscheduled")); + } + } + + @Override + protected void cancel() { + jobSchedulerModel.cancel(); + } + + @Override + protected JobSchedulerConfiguration getEntityBeingEdited() { + return jobSchedulerModel.getJobSchedulerConfiguration(); + } + + @Override + protected void delete(JobSchedulerConfiguration entity) + throws InstanceNotFoundException { + jobSchedulerModel.remove(entity); + if (jobSchedulerModel.deleteScheduledJob(entity)) { + messagesForUser.showMessage(Level.INFO, + _("Job is deleted from scheduler")); + } + } + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/JobSchedulerModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/JobSchedulerModel.java index 0a78e62f8..06e483d88 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/common/JobSchedulerModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/JobSchedulerModel.java @@ -19,15 +19,20 @@ package org.libreplan.web.common; -import java.util.Collections; -import java.util.Comparator; import java.util.List; +import org.libreplan.business.common.daos.IConnectorDAO; import org.libreplan.business.common.daos.IJobSchedulerConfigurationDAO; +import org.libreplan.business.common.entities.Connector; +import org.libreplan.business.common.entities.JobClassNameEnum; import org.libreplan.business.common.entities.JobSchedulerConfiguration; +import org.libreplan.business.common.exceptions.InstanceNotFoundException; +import org.libreplan.business.common.exceptions.ValidationException; +import org.libreplan.importers.IExportTimesheetsToTim; +import org.libreplan.importers.IImportRosterFromTim; import org.libreplan.importers.ISchedulerManager; -import org.libreplan.importers.SchedulerInfo; import org.libreplan.web.common.concurrentdetection.OnConcurrentModification; +import org.quartz.SchedulerException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; @@ -38,60 +43,132 @@ import org.springframework.transaction.annotation.Transactional; * Model for UI operations related to {@link JobSchedulerConfiguration}. * * @author Manuel Rego Casasnovas + * @author Miciele Ghiorghis */ @Service @Scope(BeanDefinition.SCOPE_PROTOTYPE) @OnConcurrentModification(goToPage = "/common/job_scheduling.zul") public class JobSchedulerModel implements IJobSchedulerModel { + private JobSchedulerConfiguration jobSchedulerConfiguration; + @Autowired private ISchedulerManager schedulerManager; @Autowired private IJobSchedulerConfigurationDAO jobSchedulerConfigurationDAO; - @Override - public List getSchedulerInfos() { - List schedulerInfoList = schedulerManager - .getSchedulerInfos(); - Collections.sort(schedulerInfoList, new Comparator() { + @Autowired + private IConnectorDAO connectorDAO; - @Override - public int compare(SchedulerInfo o1, SchedulerInfo o2) { - int result = o1 - .getJobSchedulerConfiguration() - .getJobGroup() - .compareTo( - o2.getJobSchedulerConfiguration().getJobGroup()); - if (result == 0) { - result = o1 - .getJobSchedulerConfiguration() - .getJobName() - .compareTo( - o2.getJobSchedulerConfiguration() - .getJobName()); - } - return result; - } - }); - return schedulerInfoList; + @Autowired + private IImportRosterFromTim importRosterFromTim; + + @Autowired + private IExportTimesheetsToTim exportTimesheetsToTim; + + @Override + @Transactional(readOnly = true) + public List getJobSchedulerConfigurations() { + return jobSchedulerConfigurationDAO.getAll(); } @Override - public void doManual(SchedulerInfo schedulerInfo) { - schedulerManager.doManual(schedulerInfo.getJobSchedulerConfiguration() - .getJobName()); + public String getNextFireTime( + JobSchedulerConfiguration jobSchedulerConfiguration) { + return schedulerManager.getNextFireTime(jobSchedulerConfiguration); + } + + @Override + public void doManual(JobSchedulerConfiguration jobSchedulerConfiguration) { + String name = jobSchedulerConfiguration.getJobClassName().getName(); + if (name.equals(JobClassNameEnum.IMPORT_ROSTER_FROM_TIM_JOB.getName())) { + importRosterFromTim.importRosters(); + return; + } + if (name.equals(JobClassNameEnum.EXPORT_TIMESHEET_TO_TIM_JOB.getName())) { + exportTimesheetsToTim.exportTimesheets(); + return; + } + } + + @Override + public void initCreate() { + this.jobSchedulerConfiguration = JobSchedulerConfiguration.create(); + } + + @Override + public void initEdit(JobSchedulerConfiguration jobSchedulerConfiguration) { + this.jobSchedulerConfiguration = jobSchedulerConfiguration; + } + + @Override + public JobSchedulerConfiguration getJobSchedulerConfiguration() { + return this.jobSchedulerConfiguration; } @Override @Transactional - public void saveJobConfigurationAndReschedule(String jobGroup, - String jobName, String cronExp) { - JobSchedulerConfiguration jobSchedulerConfiguration = jobSchedulerConfigurationDAO - .findByJobGroupAndJobName(jobGroup, jobName); - jobSchedulerConfiguration.setCronExpression(cronExp); + public void confirmSave() throws ValidationException { jobSchedulerConfigurationDAO.save(jobSchedulerConfiguration); - schedulerManager.rescheduleJob(jobSchedulerConfiguration); + } + + @Override + public void cancel() { + jobSchedulerConfiguration = null; + } + + @Override + @Transactional + public void remove(JobSchedulerConfiguration jobSchedulerConfiguration) { + try { + jobSchedulerConfigurationDAO.remove(jobSchedulerConfiguration + .getId()); + } catch (InstanceNotFoundException e) { + throw new RuntimeException(e); + } + } + + @Override + @Transactional(readOnly = true) + public List getConnectors() { + return connectorDAO.getAll(); + } + + @Override + public boolean scheduleOrUnscheduleJobs(Connector connector) { + List jobSchedulerConfigurations = jobSchedulerConfigurationDAO + .findByConnectorName(connector.getName()); + + for (JobSchedulerConfiguration jobSchedulerConfiguration : jobSchedulerConfigurations) { + try { + schedulerManager.scheduleOrUnscheduleJob(jobSchedulerConfiguration); + } catch (SchedulerException e) { + return false; + } + } + return true; + } + + @Override + public boolean scheduleOrUnscheduleJob() { + try { + schedulerManager.scheduleOrUnscheduleJob(jobSchedulerConfiguration); + } catch (SchedulerException e) { + throw new RuntimeException("Failed to schedule job", e); + } + return true; + } + + @Override + public boolean deleteScheduledJob( + JobSchedulerConfiguration jobSchedulerConfiguration) { + try { + schedulerManager.deleteJob(jobSchedulerConfiguration); + } catch (SchedulerException e) { + throw new RuntimeException("Failed to delete job", e); + } + return true; } } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/orders/TimSynchronizationController.java b/libreplan-webapp/src/main/java/org/libreplan/web/orders/TimSynchronizationController.java index c632a0c04..b39188e81 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/orders/TimSynchronizationController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/orders/TimSynchronizationController.java @@ -24,7 +24,6 @@ import static org.libreplan.web.I18nHelper._; import org.apache.commons.logging.LogFactory; import org.libreplan.business.common.daos.IConnectorDAO; import org.libreplan.business.common.entities.Connector; -import org.libreplan.business.common.entities.PredefinedConnectorProperties; import org.libreplan.business.common.entities.PredefinedConnectors; import org.libreplan.business.orders.entities.OrderSyncInfo; import org.libreplan.importers.IExportTimesheetsToTim; @@ -108,8 +107,6 @@ public class TimSynchronizationController extends GenericForwardComposer { if (connector == null) { return false; } - return connector.getPropertiesAsMap() - .get(PredefinedConnectorProperties.ACTIVATED) - .equalsIgnoreCase("Y"); + return connector.isActivated(); } } diff --git a/libreplan-webapp/src/main/webapp/common/_editJobScheduling.zul b/libreplan-webapp/src/main/webapp/common/_editJobScheduling.zul new file mode 100644 index 000000000..fba176462 --- /dev/null +++ b/libreplan-webapp/src/main/webapp/common/_editJobScheduling.zul @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + +