/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ro.nextreports.server.web.schedule; import java.sql.Time; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Map; import org.apache.wicket.AttributeModifier; import org.apache.wicket.Component; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.extensions.wizard.IWizard; import org.apache.wicket.extensions.wizard.Wizard; import org.apache.wicket.extensions.wizard.WizardModel; import org.apache.wicket.extensions.wizard.WizardStep; import org.apache.wicket.extensions.wizard.WizardModel.ICondition; import org.apache.wicket.feedback.FeedbackMessage; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.RequiredTextField; import org.apache.wicket.markup.html.image.ContextImage; import org.apache.wicket.markup.html.panel.FeedbackPanel; import org.apache.wicket.model.CompoundPropertyModel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.LoadableDetachableModel; import org.apache.wicket.model.Model; import org.apache.wicket.spring.injection.annot.SpringBean; import org.odlabs.wiquery.core.events.Event; import org.odlabs.wiquery.core.events.MouseEvent; import org.odlabs.wiquery.core.events.WiQueryEventBehavior; import org.odlabs.wiquery.core.javascript.JsScope; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.context.SecurityContextHolder; import ro.nextreports.server.StorageConstants; import ro.nextreports.server.distribution.Destination; import ro.nextreports.server.domain.MailServer; import ro.nextreports.server.domain.Report; import ro.nextreports.server.domain.ReportRuntimeParameterModel; import ro.nextreports.server.domain.ReportRuntimeTemplate; import ro.nextreports.server.domain.SchedulerJob; import ro.nextreports.server.domain.Settings; import ro.nextreports.server.domain.ShortcutType; import ro.nextreports.server.licence.ModuleLicence; import ro.nextreports.server.licence.NextServerModuleLicence; import ro.nextreports.server.report.AbstractReportRuntimeParameterModel; import ro.nextreports.server.report.ReportConstants; import ro.nextreports.server.report.next.NextUtil; import ro.nextreports.server.service.AnalysisService; import ro.nextreports.server.service.DataSourceService; import ro.nextreports.server.service.ReportService; import ro.nextreports.server.service.StorageService; import ro.nextreports.server.util.SchedulerUtil; import ro.nextreports.server.util.StorageUtil; import ro.nextreports.server.web.common.event.AjaxUpdateEvent; import ro.nextreports.server.web.common.event.AjaxUpdateListener; import ro.nextreports.server.web.common.event.ChangeShortcutTemplateEvent; import ro.nextreports.server.web.common.event.ChangeValuesTemplateEvent; import ro.nextreports.server.web.common.form.AdvancedForm; import ro.nextreports.server.web.common.jgrowl.JGrowlAjaxBehavior; import ro.nextreports.server.web.common.misc.AjaxWizardButtonBar; import ro.nextreports.server.web.common.panel.NextFeedbackPanel; import ro.nextreports.server.web.core.EntityBrowserPanel; import ro.nextreports.server.web.core.HomePage; import ro.nextreports.server.web.core.section.SectionManager; import ro.nextreports.server.web.core.validation.DuplicationEntityValidator; import ro.nextreports.server.web.core.validation.JcrNameValidator; import ro.nextreports.server.web.integration.ReportsPage; import ro.nextreports.server.web.report.NextRuntimePanel; import ro.nextreports.server.web.report.ReportRuntimeModel; import ro.nextreports.server.web.report.jasper.JasperRuntimePanel; import ro.nextreports.server.web.schedule.batch.BatchDefinitionPanel; import ro.nextreports.server.web.schedule.destination.DestinationsPanel; import ro.nextreports.server.web.schedule.time.JobPanel; import ro.nextreports.engine.queryexec.QueryParameter; import ro.nextreports.engine.util.ParameterUtil; public class ScheduleWizard extends Wizard { private static final Logger LOG = LoggerFactory.getLogger(ScheduleWizard.class); @SpringBean protected StorageService storageService; @SpringBean protected DataSourceService dataSourceService; @SpringBean protected ReportService reportService; @SpringBean protected AnalysisService analysisService; @SpringBean protected SectionManager sectionManager; @SpringBean private ModuleLicence moduleLicence; private SchedulerJob schedulerJob; private ReportRuntimeModel runtimeModel; private boolean edit = false; private boolean runNow = false; private ICondition notRunNowCondition = new NotRunNowCondition(); private ICondition editNotRunNowCondition = new EditNotRunNowCondition(); private ICondition batchCondition = new BatchCondition(); private ICondition mailServerDefinedCondition = new MailServerDefinedCondition(); private AjaxWizardButtonBar buttonBar; private int[] finishSteps = new int[0]; private TemplatePanel templatePanel; //private String INFO = "After 'Finish' is clicked the running process is started and it can be seen in the Monitor section"; private String INFO = getString("ActionContributor.Run.info"); public ScheduleWizard(String id, Report report, boolean runNow) { super(id); schedulerJob = new SchedulerJob(); schedulerJob.setReport(report); this.runNow = runNow; initWizard(); add(AttributeModifier.replace("class", "wizardScheduler")); } public ScheduleWizard(String id, SchedulerJob job) { super(id); schedulerJob = job; edit = true; initWizard(); } private void initWizard() { setDefaultModel(new CompoundPropertyModel<ScheduleWizard>(this)); WizardModel model = new WizardModel(); model.add(new ScheduleNameStep(), editNotRunNowCondition); model.add(new ScheduleRuntimeStep()); model.add(new ScheduleJobStep(), notRunNowCondition); if (moduleLicence.isValid(NextServerModuleLicence.BATCH_MODULE)) { model.add(new ScheduleBatchStep(), batchCondition); } model.add(new ScheduleDestinationsStep()); if (runNow) { finishSteps = new int[] {1,2,3}; } else { finishSteps = new int[] {2,3,4}; } // initialize the wizard with the wizard model we just built init(model); } // Job step and Destination step are allowed to finish protected Component newButtonBar(String s) { buttonBar = new AjaxWizardButtonBar(s, this) { public void onCancel(AjaxRequestTarget target) { EntityBrowserPanel panel = findParent(EntityBrowserPanel.class); panel.backwardWorkspace(target); } }; buttonBar.setFinishSteps(finishSteps); return buttonBar; } public SchedulerJob getSchedulerJob() { return schedulerJob; } @Override public void onFinish() { // a scheduled alarm / indicator / display must have at least an alert if (schedulerJob.getReport().isAlarmType() || schedulerJob.getReport().isIndicatorType() || schedulerJob.getReport().isDisplayType()) { if (schedulerJob.getDestinations().isEmpty()) { error(getString("ActionContributor.Run.destination.error.alert")); return; } } String globalMessage; if (runNow) { globalMessage = getString("ActionContributor.Run.running"); } else { globalMessage = getString("ActionContributor.Run.scheduledMessage"); } schedulerJob.setCreator(SecurityContextHolder.getContext().getAuthentication().getName()); if (ReportConstants.ETL_FORMAT.equals(runtimeModel.getExportType())) { // test to create user node under analysis analysisService.checkAnalysisPath(); } schedulerJob.setRuntimeModel(runtimeModel); if (schedulerJob.getBatchDefinition() != null) { String batchParameter = schedulerJob.getBatchDefinition().getParameter(); if (batchParameter != null) { // batch parameter must not be set as dynamic, if it is dynamic we reset it. schedulerJob.getReportRuntime().resetDynamic(batchParameter); // all children dependent parameters to batch parameter must be dynamic ro.nextreports.engine.Report nextReport = NextUtil.getNextReport(storageService.getSettings(), schedulerJob.getReport()); Map<String, QueryParameter> map = ParameterUtil.getChildDependentParameters(nextReport, batchParameter); schedulerJob.getReportRuntime().setDynamic(new ArrayList<String>(map.keySet())); } } if (!runNow) { schedulerJob.setPath(StorageConstants.SCHEDULER_ROOT + "/" + schedulerJob.getName()); SchedulerUtil.updateSchedulerTime(schedulerJob.getTime()); schedulerJob.getTime().setPath(StorageUtil.createPath(schedulerJob, schedulerJob.getTime().getName())); schedulerJob.getReportRuntime().setPath(StorageUtil.createPath(schedulerJob, schedulerJob.getReportRuntime().getName())); schedulerJob.setRunNow(false); for (Destination destination : schedulerJob.getDestinations()) { // temporary path created with an UUID if (destination.getPath().indexOf(StorageConstants.PATH_SEPARATOR) == -1) { // add new destination destination.setPath(schedulerJob.getPath() + StorageConstants.PATH_SEPARATOR + StorageConstants.DESTINATIONS + StorageConstants.PATH_SEPARATOR + destination.getName()); } } } if (LOG.isDebugEnabled()) { LOG.debug("job = " + schedulerJob); } try { if (runtimeModel.isSaveTemplate()) { //System.out.println("**** save template : " + runtimeModel.getTemplateName()); // save values template Report report = schedulerJob.getReport(); ReportRuntimeTemplate template = new ReportRuntimeTemplate(); template.setName(runtimeModel.getTemplateName()); template.setReportRuntime(schedulerJob.getReportRuntime()); template.setShortcutType(runtimeModel.getShortcutType()); String path = null; try { if (!StorageUtil.isVersion(report)) { path = storageService.getEntityById(report.getId()).getPath(); } else { String versionId = StorageUtil.getVersionableId(report); path = storageService.getEntityById(versionId).getPath(); } } catch (Exception e) { e.printStackTrace(); LOG.error(e.getMessage(), e); } path += StorageConstants.PATH_SEPARATOR + "templates" + StorageConstants.PATH_SEPARATOR + template.getName(); template.setPath(path); if (LOG.isDebugEnabled()) { LOG.debug("Create values template '" + template.getPath() + "'"); } try { String id = storageService.addEntity(template); schedulerJob.setTemplate((ReportRuntimeTemplate)storageService.getEntityById(id)); } catch (Exception e) { e.printStackTrace(); LOG.error(e.getMessage(), e); } } else { ReportRuntimeTemplate template = templatePanel.getTemplate(); schedulerJob.setTemplate(templatePanel.getTemplate()); } if (edit) { // IMPORTANT : see SchedulerJobModifieddvice storageService.modifyEntity(schedulerJob); } else if (runNow) { schedulerJob.setRunNow(true); reportService.runReport(schedulerJob); //sectionManager.setSelectedSectionId(MonitorSection.ID); } else { // IMPORTANT : see SchedulerJobAddedAdvice storageService.addEntity(schedulerJob); sectionManager.setSelectedSectionId(SchedulerSection.ID); } } catch (Exception e) { e.printStackTrace(); LOG.error(e.getMessage(), e); error(e.getMessage()); } finally { // some values for jasper parameters are updated in run method, so we log here if (LOG.isDebugEnabled()) { LOG.debug(schedulerJob.getReportRuntime().toString()); } } // just put a feedback message to be shown by JGrowl getSession().getFeedbackMessages().add(new FeedbackMessage(null, globalMessage, JGrowlAjaxBehavior.INFO_FADE)); if (getPage() instanceof ReportsPage) { setResponsePage(ReportsPage.class); } else { setResponsePage(HomePage.class); } } private final class ScheduleNameStep extends WizardStep { public ScheduleNameStep() { super(); RequiredTextField<String> textField = new RequiredTextField<String>("schedulerJob.name"); textField.setLabel(new Model<String>(getString("name"))); textField.add(new JcrNameValidator()); add(textField); add(new DuplicationEntityValidator(textField, StorageConstants.SCHEDULER_ROOT)); } @Override public String getTitle() { return getString("ActionContributor.Run.schedule") + " : " + schedulerJob.getReport().getName(); } @Override public String getSummary() { return getString("ActionContributor.Run.schedulerName"); } @Override public Component getHeader(final String id, final Component parent, final IWizard wizard) { Component c = super.getHeader(id, parent, wizard); c.get("summary").setEscapeModelStrings(false); return c; } public String getName() { return schedulerJob.getName(); } } private final class ScheduleRuntimeStep extends WizardStep implements AjaxUpdateListener { private WebMarkupContainer container; public ScheduleRuntimeStep() { super(); container = new WebMarkupContainer("container"); container.setOutputMarkupId(true); IModel<String> toggleImageModel = new LoadableDetachableModel<String>() { private static final long serialVersionUID = 1L; @Override protected String load() { String imagePath = "images/down-gray.png"; if (runtimeModel.isCollapsed()) { imagePath = "images/up-gray.png"; } return imagePath; } }; ContextImage toggle = new ContextImage("toggle", toggleImageModel); container.add(toggle); toggle.add(new WiQueryEventBehavior(new Event(MouseEvent.CLICK) { private static final long serialVersionUID = 1L; @Override public JsScope callback() { return JsScope.quickScope(getJsCode()); } String collapse = getString("collapse"); String expand = getString("expand"); private CharSequence getJsCode() { StringBuilder buffer = new StringBuilder(); buffer.append("var content = $(this).siblings('.runtimePanel').find('tr.parameters');"); buffer.append("if (content.css('display') == 'none') {"); buffer.append("content.slideDown(400);"); buffer.append("$(this).attr('src', \"../images/down-gray.png\");"); buffer.append("$(this).attr('title', \"" + collapse + "\");"); buffer.append("} else {"); buffer.append("content.slideUp(200);"); buffer.append("$(this).attr('src', \"../images/up-gray.png\");"); buffer.append("$(this).attr('title', \"" + expand + "\");"); buffer.append("}"); return buffer.toString(); } })); if (edit) { runtimeModel = SchedulerUtil.getStoredRuntimeModel(storageService, schedulerJob, reportService, dataSourceService); } else { runtimeModel = schedulerJob.createRuntimeModel(); } String type = schedulerJob.getReport().getType(); if (ReportConstants.JASPER.equals(type)) { container.add(new JasperRuntimePanel("runtimePanel", schedulerJob.getReport(), runtimeModel).setOutputMarkupPlaceholderTag(true)); } else { container.add(new NextRuntimePanel("runtimePanel", schedulerJob.getReport(), runtimeModel, runNow).setOutputMarkupPlaceholderTag(true)); } templatePanel = new TemplatePanel("templatePanel", schedulerJob.getReport(), runtimeModel); if (edit) { templatePanel.setTemplate(schedulerJob.getTemplate()); } templatePanel.setOutputMarkupId(true); add(templatePanel); add(container); } @Override public String getTitle() { String action = runNow ? getString("run") : getString("schedule"); return action + " " + getString("Report") + " : " + schedulerJob.getReport().getName(); } @Override public String getSummary() { String action = runNow ? "" : getString("Section.Scheduler.name"); String result = getString("select") + " " + action + " " + getString("ActionContributor.Run.parameters"); if (runNow) { result = result + "<br>" + INFO; } return result; } @Override public Component getHeader(final String id, final Component parent, final IWizard wizard) { Component c = super.getHeader(id, parent, wizard); c.get("summary").setEscapeModelStrings(false); return c; } // replace entire panel to have all dependent parameters compute their values public void onAjaxUpdate(AjaxUpdateEvent event) { boolean changed = false; if (event instanceof ChangeValuesTemplateEvent) { ChangeValuesTemplateEvent changeEvent = (ChangeValuesTemplateEvent) event; runtimeModel = SchedulerUtil.getStoredRuntimeModel(storageService, changeEvent.getReport(), changeEvent.getReportRuntime(), reportService, dataSourceService); runtimeModel.setShortcutType(changeEvent.getShortcutType()); if (templatePanel != null) { templatePanel.setRuntimeModel(runtimeModel); } String type = schedulerJob.getReport().getType(); if (ReportConstants.JASPER.equals(type)) { container.replace(new JasperRuntimePanel("runtimePanel", schedulerJob.getReport(), runtimeModel)); } else { container.replace(new NextRuntimePanel("runtimePanel", schedulerJob.getReport(), runtimeModel, runNow)); } changed = true; } else if (event instanceof ChangeShortcutTemplateEvent) { ChangeShortcutTemplateEvent shortcutEvent = (ChangeShortcutTemplateEvent)event; if (shortcutEvent.getShortcutType().equals(ShortcutType.NONE)) { // no selection return; } Date[] dates = shortcutEvent.getShortcutType().getTimeShortcutType().getDates(); HashMap<String, ReportRuntimeParameterModel> params = runtimeModel.getParameters(); AbstractReportRuntimeParameterModel startModel = (AbstractReportRuntimeParameterModel)params.get(QueryParameter.INTERVAL_START_DATE_NAME); if (startModel != null) { Object start = startModel.getRawValue(); if (start instanceof Date) { startModel.setRawValue(dates[0]); } else if (start instanceof Timestamp) { startModel.setRawValue(new Timestamp(dates[0].getTime())); } else if (start instanceof Time) { startModel.setRawValue(new Time(dates[0].getTime())); } } AbstractReportRuntimeParameterModel endModel = (AbstractReportRuntimeParameterModel)params.get(QueryParameter.INTERVAL_END_DATE_NAME); if (endModel != null) { Object end = endModel.getRawValue(); if (end instanceof Date) { endModel.setRawValue(dates[1]); } else if (end instanceof Timestamp) { endModel.setRawValue(new Timestamp(dates[1].getTime())); } else if (end instanceof Time) { endModel.setRawValue(new Time(dates[1].getTime())); } } changed = true; schedulerJob.setTemplate(templatePanel.getTemplate()); } runtimeModel.setCollapsed(changed); event.getTarget().add(container); if (runtimeModel.isCollapsed()) { event.getTarget().appendJavaScript("jQuery('.runtimePanel').find('tr.parameters').css({'display' : 'none' });"); } } } private final class ScheduleJobStep extends WizardStep { public ScheduleJobStep() { super(); if (edit) { SchedulerUtil.restoreSchedulerTime(schedulerJob.getTime()); } add(new JobPanel("jobPanel", schedulerJob)); } @Override public String getTitle() { return getString("ActionContributor.Run.schedule") + " : " + schedulerJob.getReport().getName(); } @Override public String getSummary() { return getString("ActionContributor.Run.time"); } @Override public Component getHeader(final String id, final Component parent, final IWizard wizard) { Component c = super.getHeader(id, parent, wizard); c.get("summary").setEscapeModelStrings(false); return c; } } private final class ScheduleBatchStep extends WizardStep { public ScheduleBatchStep() { super(); add(new BatchDefinitionPanel("batchPanel", schedulerJob)); } @Override public String getTitle() { return getString("ActionContributor.Run.schedule") + " : " + schedulerJob.getReport().getName(); } @Override public String getSummary() { return getString("ActionContributor.Run.batch"); } @Override public Component getHeader(final String id, final Component parent, final IWizard wizard) { Component c = super.getHeader(id, parent, wizard); c.get("summary").setEscapeModelStrings(false); return c; } } private final class ScheduleDestinationsStep extends WizardStep { public ScheduleDestinationsStep() { super(); add(new DestinationsPanel("destinationsPanel", schedulerJob)); } @Override public String getTitle() { String action = runNow ? getString("run") : getString("schedule"); return action + getString("Report") + " : " + schedulerJob.getReport().getName(); } @Override public String getSummary() { String action = runNow ? "" : getString("Section.Scheduler.name"); String result = getString("select") + " " + action + " " + getString("ActionContributor.Run.destinations"); if (runNow) { result = result + "<br>" + INFO; } return result; } @Override public Component getHeader(final String id, final Component parent, final IWizard wizard) { Component c = super.getHeader(id, parent, wizard); c.get("summary").setEscapeModelStrings(false); return c; } } private final class NotRunNowCondition implements ICondition { public boolean evaluate() { return !runNow; } } private final class EditNotRunNowCondition implements ICondition { public boolean evaluate() { return !runNow && !edit; } } private final class BatchCondition implements ICondition { public boolean evaluate() { String type = schedulerJob.getReport().getType(); return ReportConstants.NEXT.equals(type); } } private final class MailServerDefinedCondition implements ICondition { public boolean evaluate() { Settings settings = storageService.getSettings(); MailServer mailServer = settings.getMailServer(); return (mailServer != null) && !"".equals(mailServer.getIp()); } } // create form for scheduler (to validate components) protected <E> Form<E> newForm(String s) { return new AdvancedForm<E>(s); } // overwrite feedback panel so we will show to it only the messages that do not come from validation // (like errors, or information) protected FeedbackPanel newFeedbackPanel(String s) { return new NextFeedbackPanel(s, getForm()); } }