From c5796d602b2a8dfd9e90823bd8fe64672aeab9bc Mon Sep 17 00:00:00 2001 From: Manuel Rego Casasnovas Date: Tue, 12 Feb 2013 14:24:35 +0100 Subject: [PATCH] tim-connector: Changes in JobSchedulerController and zul Lots of changes: * Created a new model to follow the standard structure of controllers in LibrePlan. * Marked several strings to be internationalized. * Improved .zul page, removing unneeded stuff and simplifying code. * Checked that cron expression is right, otherwise inform the user. FEA: ItEr77S16JiraAndTimConnectorContributionIntegration --- .../web/common/IJobSchedulerModel.java | 40 +++ .../web/common/JobSchedulerController.java | 324 +++++------------- .../web/common/JobSchedulerModel.java | 97 ++++++ .../libreplan-webapp-spring-config.xml | 9 +- .../common/job_scheduler_configuration.zul | 163 ++++----- 5 files changed, 313 insertions(+), 320 deletions(-) create mode 100644 libreplan-webapp/src/main/java/org/libreplan/web/common/IJobSchedulerModel.java create mode 100644 libreplan-webapp/src/main/java/org/libreplan/web/common/JobSchedulerModel.java 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 new file mode 100644 index 000000000..b89178e9a --- /dev/null +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/IJobSchedulerModel.java @@ -0,0 +1,40 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2013 Igalia, S.L. + * + * 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.web.common; + +import java.util.List; + +import org.libreplan.importers.SchedulerInfo; + +/** + * Contract for {@link JobSchedulerModel}. + * + * @author Manuel Rego Casasnovas + */ +public interface IJobSchedulerModel { + + List getSchedulerInfos(); + + void doManual(SchedulerInfo schedulerInfo); + + void saveJobConfigurationAndReschedule(String jobGroup, String jobName, + String cronExp); + +} \ 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 89ce2467f..5696c9b5d 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 @@ -19,38 +19,31 @@ package org.libreplan.web.common; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.StringTokenizer; +import static org.libreplan.web.I18nHelper._; -import org.apache.commons.lang.Validate; +import java.text.ParseException; +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.IAdHocTransactionService; -import org.libreplan.business.common.IOnTransaction; -import org.libreplan.business.common.daos.IJobSchedulerConfigurationDAO; import org.libreplan.business.common.entities.JobSchedulerConfiguration; -import org.libreplan.importers.ISchedulerManager; import org.libreplan.importers.SchedulerInfo; -import org.springframework.beans.factory.annotation.Autowired; +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.Cell; import org.zkoss.zul.Grid; import org.zkoss.zul.Hbox; import org.zkoss.zul.Label; import org.zkoss.zul.Popup; import org.zkoss.zul.Row; import org.zkoss.zul.RowRenderer; -import org.zkoss.zul.Rows; -import org.zkoss.zul.SimpleListModel; -import org.zkoss.zul.Toolbarbutton; +import org.zkoss.zul.api.Textbox; /** * Controller for job scheduler manager @@ -58,289 +51,148 @@ import org.zkoss.zul.Toolbarbutton; * @author Miciele Ghiorghis */ public class JobSchedulerController extends GenericForwardComposer { + private static final Log LOG = LogFactory .getLog(JobSchedulerController.class); - private Grid jobSchedulerGrid, cronExpressionGrid; + private Grid jobSchedulerGrid; private Popup cronExpressionInputPopup; - private Label jobGroupLabel, jobNameLable; + private Label jobGroup; - @Autowired - ISchedulerManager schedulerManager; + private Label jobName; - @Autowired - IJobSchedulerConfigurationDAO jobSchedulerConfigurationDAO; + private Textbox cronExpressionSeconds; + private Textbox cronExpressionMinutes; + private Textbox cronExpressionHours; + private Textbox cronExpressionDayOfMonth; + private Textbox cronExpressionMonth; + private Textbox cronExpressionDayOfWeek; + private Textbox cronExpressionYear; + + private IJobSchedulerModel jobSchedulerModel; @Override public void doAfterCompose(Component comp) throws Exception { super.doAfterCompose(comp); - comp.setVariable("jobSchedulerController", this, true); - reloadSchedulerJobs(); + comp.setAttribute("jobSchedulerController", this); } - public List getSchedulerInfo() { - List schedulerInfoList = schedulerManager - .getSchedulerInfos(); - Collections.sort(schedulerInfoList, new Comparator() { + public List getSchedulerInfos() { + return jobSchedulerModel.getSchedulerInfos(); + } + + public RowRenderer getJobSchedulingRenderer() { + return new RowRenderer() { @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; + public void render(Row row, Object data) { + SchedulerInfo schedulerInfo = (SchedulerInfo) 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); } - }); - return schedulerInfoList; - } - - private void reloadSchedulerJobs() { - jobSchedulerGrid.setModel(new SimpleListModel(getSchedulerInfo())); - jobSchedulerGrid.invalidate(); - } - - public JobSchedulingRenderer getJobSchedulingRenderer() { - return new JobSchedulingRenderer(); - } - - public class JobSchedulingRenderer implements RowRenderer { - @Override - public void render(Row row, Object data) { - - SchedulerInfo schedulerInfo = (SchedulerInfo) data; - Util.appendLabel(row, schedulerInfo.getJobSchedulerConfiguration() - .getJobGroup()); - Util.appendLabel(row, schedulerInfo.getJobSchedulerConfiguration() - .getJobName()); - appendCronExpressionAndButton(row, schedulerInfo); - Util.appendLabel(row, schedulerInfo.getNextFireTime()); - appendManualStart(row, schedulerInfo); - } + }; } private void appendCronExpressionAndButton(final Row row, final SchedulerInfo schedulerInfo) { final Hbox hBox = new Hbox(); - hBox.setWidth("100%"); - Cell cell = new Cell(); - cell.setHflex("2"); - cell.setAlign("left"); Label label = new Label(schedulerInfo.getJobSchedulerConfiguration() .getCronExpression()); - cell.appendChild(label); - - Cell cell2 = new Cell(); - cell2.setHflex("1"); - cell2.setWidth("10px"); - final Toolbarbutton button = new Toolbarbutton(); - button.setImage("/common/img/ico_editar.png"); - cell2.appendChild(button); - hBox.appendChild(cell); - hBox.appendChild(cell2); - - button.addEventListener(Events.ON_CLICK, new EventListener() { + hBox.appendChild(label); + Button button = Util.createEditButton(new EventListener() { @Override public void onEvent(Event event) throws Exception { setupCronExpressionPopup(schedulerInfo); - cronExpressionInputPopup.open(jobSchedulerGrid, "at_pointer"); + cronExpressionInputPopup.open(hBox); } }); + hBox.appendChild(button); + row.appendChild(hBox); } private void setupCronExpressionPopup(final SchedulerInfo schedulerInfo) { - List list = new ArrayList(); - list.add(getCronExpression(schedulerInfo.getJobSchedulerConfiguration() - .getCronExpression())); - cronExpressionGrid.setModel(new SimpleListModel(list)); + JobSchedulerConfiguration jobSchedulerConfiguration = schedulerInfo.getJobSchedulerConfiguration(); + jobGroup.setValue(jobSchedulerConfiguration.getJobGroup()); + jobName.setValue(jobSchedulerConfiguration.getJobName()); - jobGroupLabel = (Label) cronExpressionInputPopup - .getFellowIfAny("jobGroup"); - jobNameLable = (Label) cronExpressionInputPopup - .getFellowIfAny("jobName"); - jobGroupLabel.setValue(schedulerInfo.getJobSchedulerConfiguration() - .getJobGroup()); - jobNameLable.setValue(schedulerInfo.getJobSchedulerConfiguration() - .getJobName()); - } + String cronExpression = jobSchedulerConfiguration.getCronExpression(); + String[] cronExpressionArray = StringUtils.split(cronExpression); - @Autowired - private IAdHocTransactionService adHocTransactionService; + cronExpressionSeconds.setValue(cronExpressionArray[0]); + cronExpressionMinutes.setValue(cronExpressionArray[1]); + cronExpressionHours.setValue(cronExpressionArray[2]); + cronExpressionDayOfMonth.setValue(cronExpressionArray[3]); + cronExpressionMonth.setValue(cronExpressionArray[4]); + cronExpressionDayOfWeek.setValue(cronExpressionArray[5]); - private void saveJobConfigurationAndReschedule( - final String jobGroup, final String jobName, final String cronExp) { - adHocTransactionService - .runOnAnotherTransaction(new IOnTransaction() { - @Override - public Void execute() { - JobSchedulerConfiguration jobSchedulerConfiguration = jobSchedulerConfigurationDAO - .findByJobGroupAndJobName(jobGroup, jobName); - jobSchedulerConfiguration.setCronExpression(cronExp); - jobSchedulerConfigurationDAO - .save(jobSchedulerConfiguration); - schedulerManager.rescheduleJob(jobSchedulerConfiguration); - reloadSchedulerJobs(); - return null; - } - }); - } - - private CronExpression getCronExpression(String cronExpressionStr) { - CronExpression cronExpression = new CronExpression(); - StringTokenizer st = new StringTokenizer(cronExpressionStr); - int countTokens = st.countTokens(); - if (countTokens < 6) { - throw new IllegalArgumentException("Cron expression is not valid"); + if (cronExpressionArray.length == 7) { + cronExpressionYear.setValue(cronExpressionArray[6]); } - cronExpression.setSeconds(getNextToken(st.nextToken())); - cronExpression.setMinutes(getNextToken(st.nextToken())); - cronExpression.setHours(getNextToken(st.nextToken())); - cronExpression.setDayOfMonth(getNextToken(st.nextToken())); - cronExpression.setMonth(getNextToken(st.nextToken())); - cronExpression.setDayOfWeek(getNextToken(st.nextToken())); - if (countTokens > 6) { // optional - cronExpression.setYear(getNextToken(st.nextToken())); - } else { - cronExpression.setYear(""); - } - - return cronExpression; - } - - private String getNextToken(String token) { - return token.isEmpty() ? "" : token.trim(); } private void appendManualStart(final Row row, final SchedulerInfo schedulerInfo) { - final Button rescheduleButton = new Button("Manual"); + final Button rescheduleButton = new Button(_("Manual")); rescheduleButton.addEventListener(Events.ON_CLICK, new EventListener() { @Override public void onEvent(Event event) throws Exception { - schedulerManager.doManual(schedulerInfo - .getJobSchedulerConfiguration().getJobName()); + jobSchedulerModel.doManual(schedulerInfo); } }); row.appendChild(rescheduleButton); } public void reschedule() { - jobGroupLabel = (Label) cronExpressionInputPopup - .getFellowIfAny("jobGroup"); - jobNameLable = (Label) cronExpressionInputPopup - .getFellowIfAny("jobName"); + String cronExpression = getCronExpressionString(); + try { + // Check cron expression format + new CronExpression(cronExpression); + } catch (ParseException e) { + LOG.info("Unable to parse cron expression", e); + throw new WrongValueException(cronExpressionInputPopup, + _("Unable to parse cron expression") + ":\n" + + e.getMessage()); + } - Rows rows = cronExpressionGrid.getRows(); - Row row = (Row) rows.getChildren().get(0); - CronExpression cronExp = (CronExpression) row.getValue(); - saveJobConfigurationAndReschedule(jobGroupLabel.getValue(), - jobNameLable.getValue(), convertToCronExpressionStr(cronExp)); + jobSchedulerModel.saveJobConfigurationAndReschedule( + jobGroup.getValue(), jobName.getValue(), cronExpression); cronExpressionInputPopup.close(); - getJobSchedulingRenderer(); + Util.reloadBindings(jobSchedulerGrid); } - private String convertToCronExpressionStr(CronExpression cronExp) { - return String.format("%1$s %2$s %3$s %4$s %5$s %6$s %7$s", - cronExp.getSeconds(), cronExp.getMinutes(), cronExp.getHours(), - cronExp.getDayOfMonth(), cronExp.getMonth(), - cronExp.getDayOfWeek(), cronExp.getYear()).trim(); + private String getCronExpressionString() { + String cronExpression = ""; + cronExpression += StringUtils.trimToEmpty(cronExpressionSeconds.getValue()) + " "; + cronExpression += StringUtils.trimToEmpty(cronExpressionMinutes.getValue()) + " "; + cronExpression += StringUtils.trimToEmpty(cronExpressionHours.getValue()) + " "; + cronExpression += StringUtils.trimToEmpty(cronExpressionDayOfMonth.getValue()) + " "; + cronExpression += StringUtils.trimToEmpty(cronExpressionMonth.getValue()) + " "; + cronExpression += StringUtils.trimToEmpty(cronExpressionDayOfWeek.getValue()); + + String year = StringUtils.trimToEmpty(cronExpressionYear.getValue()); + if (!year.isEmpty()) { + cronExpression += " " + year; + } + + return cronExpression; } public void cancel() { - cronExpressionInputPopup.invalidate(); cronExpressionInputPopup.close(); } - /** - * Class representing cron expression - */ - public class CronExpression { - private String seconds; - private String minutes; - private String hours; - private String dayOfMonth; - private String month; - private String dayOfWeek; - private String year; - - public String getSeconds() { - return seconds; - } - - public void setSeconds(String seconds) { - Validate.notEmpty(seconds, "Seconds is mandatory"); - this.seconds = seconds; - } - - public String getMinutes() { - return minutes; - } - - public void setMinutes(String minutes) { - Validate.notEmpty(minutes, "Minutes is mandatory"); - this.minutes = minutes; - } - - public String getHours() { - return hours; - } - - public void setHours(String hours) { - Validate.notEmpty(hours, "Hours is mandatory"); - this.hours = hours; - } - - public String getDayOfMonth() { - return dayOfMonth; - } - - public void setDayOfMonth(String dayOfMonth) { - Validate.notEmpty(dayOfMonth, "day of month is mandatory"); - this.dayOfMonth = dayOfMonth; - } - - public String getMonth() { - return month; - } - - public void setMonth(String month) { - Validate.notEmpty(month, "month is mandatory"); - this.month = month; - } - - public String getDayOfWeek() { - return dayOfWeek; - } - - public void setDayOfWeek(String dayOfWeek) { - Validate.notEmpty(dayOfWeek, "day of week is mandatory"); - this.dayOfWeek = dayOfWeek; - } - - public String getYear() { - return year; - } - - public void setYear(String year) { - this.year = year; - } - - } - } 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 new file mode 100644 index 000000000..42fa8544f --- /dev/null +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/JobSchedulerModel.java @@ -0,0 +1,97 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2013 Igalia, S.L. + * + * 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.web.common; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import org.libreplan.business.common.daos.IJobSchedulerConfigurationDAO; +import org.libreplan.business.common.entities.JobSchedulerConfiguration; +import org.libreplan.importers.ISchedulerManager; +import org.libreplan.importers.SchedulerInfo; +import org.libreplan.web.common.concurrentdetection.OnConcurrentModification; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * Model for UI operations related to {@link JobSchedulerConfiguration}. + * + * @author Manuel Rego Casasnovas + */ +@Service +@Scope(BeanDefinition.SCOPE_PROTOTYPE) +@OnConcurrentModification(goToPage = "/common/job_scheduler_configuration.zul") +public class JobSchedulerModel implements IJobSchedulerModel { + + @Autowired + private ISchedulerManager schedulerManager; + + @Autowired + private IJobSchedulerConfigurationDAO jobSchedulerConfigurationDAO; + + @Override + public List getSchedulerInfos() { + List schedulerInfoList = schedulerManager + .getSchedulerInfos(); + Collections.sort(schedulerInfoList, new Comparator() { + + @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; + } + + @Override + public void doManual(SchedulerInfo schedulerInfo) { + schedulerManager.doManual(schedulerInfo.getJobSchedulerConfiguration() + .getJobName()); + } + + @Override + @Transactional + public void saveJobConfigurationAndReschedule(String jobGroup, + String jobName, String cronExp) { + JobSchedulerConfiguration jobSchedulerConfiguration = jobSchedulerConfigurationDAO + .findByJobGroupAndJobName(jobGroup, jobName); + jobSchedulerConfiguration.setCronExpression(cronExp); + jobSchedulerConfigurationDAO.save(jobSchedulerConfiguration); + schedulerManager.rescheduleJob(jobSchedulerConfiguration); + } + +} diff --git a/libreplan-webapp/src/main/resources/libreplan-webapp-spring-config.xml b/libreplan-webapp/src/main/resources/libreplan-webapp-spring-config.xml index aff4e6005..a602dfd15 100644 --- a/libreplan-webapp/src/main/resources/libreplan-webapp-spring-config.xml +++ b/libreplan-webapp/src/main/resources/libreplan-webapp-spring-config.xml @@ -52,9 +52,12 @@ - - - + + + diff --git a/libreplan-webapp/src/main/webapp/common/job_scheduler_configuration.zul b/libreplan-webapp/src/main/webapp/common/job_scheduler_configuration.zul index f86ca9af0..b3ef91bf2 100644 --- a/libreplan-webapp/src/main/webapp/common/job_scheduler_configuration.zul +++ b/libreplan-webapp/src/main/webapp/common/job_scheduler_configuration.zul @@ -17,107 +17,108 @@ along with this program. If not, see . --> - - + - - + - - - - + + + + + + + - + - + - +
- - - - - - - - - - - - - - - - - - - - - - - -
- ${i18n:_("For more details on cron expression click")} - ${i18n:_('this')} -
+ + + + + + + + + + + + + + + + + + + + + + + +
+ ${i18n:_("For more details on cron expression click")} + ${i18n:_('here')}. +
- - -