/* * 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 <http://www.gnu.org/licenses/>. */ package org.libreplan.web.common; import static org.libreplan.web.I18nHelper._; import java.text.ParseException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang3.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.ConnectorException; import org.libreplan.business.common.entities.JobClassNameEnum; import org.libreplan.business.common.entities.JobSchedulerConfiguration; import org.libreplan.business.common.exceptions.InstanceNotFoundException; import org.libreplan.importers.SynchronizationInfo; import org.quartz.CronExpression; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.Executions; import org.zkoss.zk.ui.SuspendNotAllowedException; import org.zkoss.zk.ui.WrongValueException; import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.Events; import org.zkoss.zkplus.spring.SpringUtil; import org.zkoss.zul.Button; import org.zkoss.zul.Grid; import org.zkoss.zul.Groupbox; import org.zkoss.zul.Hbox; import org.zkoss.zul.Label; import org.zkoss.zul.Listbox; import org.zkoss.zul.Popup; import org.zkoss.zul.RowRenderer; import org.zkoss.zul.SimpleListModel; import org.zkoss.zul.Caption; import org.zkoss.zul.Textbox; import org.zkoss.zul.Window; /** * Controller for job scheduler manager. * * @author Miciele Ghiorghis <m.ghiorghis@antoniusziekenhuis.nl> */ public class JobSchedulerController extends BaseCRUDController<JobSchedulerConfiguration> { private static final Log LOG = LogFactory.getLog(JobSchedulerController.class); private Popup cronExpressionInputPopup; private Label jobGroup; private Label jobName; private Textbox cronExpressionTextBox; private Textbox cronExpressionSeconds; private Textbox cronExpressionMinutes; private Textbox cronExpressionHours; private Textbox cronExpressionDayOfMonth; private Textbox cronExpressionMonth; private Textbox cronExpressionDayOfWeek; private Textbox cronExpressionYear; private IJobSchedulerModel jobSchedulerModel; public JobSchedulerController() { jobSchedulerModel = (IJobSchedulerModel) SpringUtil.getBean("jobSchedulerModel"); } @Override public void doAfterCompose(Component comp) throws Exception { super.doAfterCompose(comp); Grid listJobSchedulings = (Grid) listWindow.getFellowIfAny("listJobSchedulings"); listJobSchedulings.getModel(); initCronExpressionPopup(); } /** * 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"); Grid 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<JobSchedulerConfiguration> getJobSchedulerConfigurations() { return jobSchedulerModel.getJobSchedulerConfigurations(); } /** * Returns {@link JobSchedulerConfiguration}. */ public JobSchedulerConfiguration getJobSchedulerConfiguration() { return jobSchedulerModel.getJobSchedulerConfiguration(); } /** * Returns all predefined jobs. */ public JobClassNameEnum[] getJobNames() { return JobClassNameEnum.values(); } /** * Returns list of connectorNames. */ public List<String> getConnectorNames() { List<Connector> connectors = jobSchedulerModel.getConnectors(); List<String> connectorNames = new ArrayList<>(); for (Connector connector : connectors) { connectorNames.add(connector.getName()); } return connectorNames; } /** * Renders job scheduling and returns {@link RowRenderer}. */ public RowRenderer getJobSchedulingRenderer() { return (row, o, i) -> { final JobSchedulerConfiguration jobSchedulerConfiguration = (JobSchedulerConfiguration) o; row.setValue(o); 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(event -> { try { jobSchedulerModel.doManual(jobSchedulerConfiguration); showSynchronizationInfo(); } catch (ConnectorException e) { messagesForUser.showMessage(Level.ERROR, e.getMessage()); } })); hbox.appendChild(Util.createEditButton(event -> goToEditForm(jobSchedulerConfiguration))); hbox.appendChild(Util.createRemoveButton(event -> confirmDelete(jobSchedulerConfiguration))); row.appendChild(hbox); }; } private RowRenderer getSynchronizationInfoRenderer() { return (row, o, i) -> { final SynchronizationInfo synchronizationInfo = (SynchronizationInfo) o; row.setValue(o); Groupbox groupbox = new Groupbox(); groupbox.setClosable(true); Caption caption = new Caption(); caption.setLabel(synchronizationInfo.getAction()); groupbox.appendChild(caption); row.appendChild(groupbox); if ( synchronizationInfo.isSuccessful() ) { groupbox.appendChild(new Label(_("Completed"))); } else { Listbox listbox = new Listbox(); listbox.setModel(new SimpleListModel<>(synchronizationInfo.getFailedReasons())); groupbox.appendChild(listbox); } }; } private List<SynchronizationInfo> getSynchronizationInfos() { return jobSchedulerModel.getSynchronizationInfos(); } private void showSynchronizationInfo() { Map<String, Object> args = new HashMap<>(); Window win = (Window) Executions.createComponents("/orders/_synchronizationInfo.zul", null, args); Window syncInfoWin = (Window) win.getFellowIfAny("syncInfoWin"); Grid syncInfoGrid = (Grid) syncInfoWin.getFellowIfAny("syncInfoGrid"); syncInfoGrid.setModel(new SimpleListModel<>(getSynchronizationInfos())); syncInfoGrid.setRowRenderer(getSynchronizationInfoRenderer()); try { win.doModal(); } catch (SuspendNotAllowedException e) { throw new RuntimeException(e); } } /** * 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.setSclass("add-button"); button.addEventListener(Events.ON_CLICK, eventListener); return button; } /** * Opens the <code>cronExpressionInputPopup</code>. */ public void openPopup() { setupCronExpressionPopup(getJobSchedulerConfiguration()); cronExpressionInputPopup.open(cronExpressionTextBox, "after_start"); } /** * Sets the cronExpression values for <code>cronExpressionInputPopup</code>. * * @param jobSchedulerConfiguration * where to read the values */ private void setupCronExpressionPopup(final JobSchedulerConfiguration jobSchedulerConfiguration) { if ( jobSchedulerConfiguration != null ) { jobGroup.setValue(jobSchedulerConfiguration.getJobGroup()); jobName.setValue(jobSchedulerConfiguration.getJobName()); String cronExpression = jobSchedulerConfiguration.getCronExpression(); if ( cronExpression == null || cronExpression.isEmpty() ) { return; } 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]); } } } /** * Sets the <code>cronExpressionTextBox</code> value from the <code>cronExpressionInputPopup</code>. */ public void updateCronExpression() { 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()); } cronExpressionTextBox.setValue(cronExpression); cronExpressionInputPopup.close(); Util.saveBindings(cronExpressionTextBox); } /** * Concatenating the cronExpression values. * * @return cronExpression string */ 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()); return !year.isEmpty() ? cronExpression + " " + year : cronExpression; } /** * 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() { 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")); } } }