
org.opennms.web.svclayer.support.DefaultSchedulerService Maven / Gradle / Ivy
/*
* Licensed to The OpenNMS Group, Inc (TOG) under one or more
* contributor license agreements. See the LICENSE.md file
* distributed with this work for additional information
* regarding copyright ownership.
*
* TOG licenses this file to You under the GNU Affero General
* Public License Version 3 (the "License") or (at your option)
* any later version. You may not use this file except in
* compliance with the License. You may obtain a copy of the
* License at:
*
* https://www.gnu.org/licenses/agpl-3.0.txt
*
* 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 org.opennms.web.svclayer.support;
import static org.opennms.api.reporting.ReportParameterBuilder.Intervals;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.TimeZone;
import java.util.stream.Collectors;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import org.opennms.api.reporting.ReportMode;
import org.opennms.api.reporting.parameter.ReportDateParm;
import org.opennms.api.reporting.parameter.ReportDoubleParm;
import org.opennms.api.reporting.parameter.ReportFloatParm;
import org.opennms.api.reporting.parameter.ReportIntParm;
import org.opennms.api.reporting.parameter.ReportParameters;
import org.opennms.api.reporting.parameter.ReportParmVisitor;
import org.opennms.api.reporting.parameter.ReportStringParm;
import org.opennms.api.reporting.parameter.ReportTimezoneParm;
import org.opennms.reporting.core.DeliveryOptions;
import org.opennms.reporting.core.svclayer.DeliveryConfig;
import org.opennms.reporting.core.svclayer.ScheduleConfig;
import org.opennms.web.svclayer.SchedulerService;
import org.opennms.web.svclayer.model.TriggerDescription;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerKey;
import org.quartz.impl.matchers.GroupMatcher;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.quartz.impl.triggers.SimpleTriggerImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import com.google.common.base.Strings;
/**
* DefaultSchedulerService class.
*
* @author ranger
* @version $Id: $
* @since 1.8.1
*/
public class DefaultSchedulerService implements InitializingBean, SchedulerService {
private static final Logger LOG = LoggerFactory.getLogger(DefaultSchedulerService.class);
private static final String PROVIDE_A_VALUE_TEXT = "Please provide a value";
private static final String PROVIDED_VALUE_GREATER_ZERO_TEXT = "The provided value must be > 0";
private Scheduler m_scheduler;
private JobDetail m_jobDetail;
private String m_triggerGroup;
@Override
public void afterPropertiesSet() throws Exception {
LOG.debug("Adding job {} to scheduler", m_jobDetail.getKey().getName());
m_scheduler.addJob(m_jobDetail, true);
}
@Override
public List getTriggerDescriptions() {
final List triggerDescriptions = new ArrayList<>();
try {
final Set triggerKeys = m_scheduler.getTriggerKeys(GroupMatcher.groupEquals(m_triggerGroup));
for (TriggerKey triggerKey : triggerKeys) {
TriggerDescription description = new TriggerDescription();
Trigger trigger = m_scheduler.getTrigger(triggerKey);
description.setNextFireTime(trigger.getNextFireTime());
description.setTriggerName(triggerKey.getName());
description.setReportId((String)trigger.getJobDataMap().get("reportId"));
description.setDeliveryOptions((DeliveryOptions) trigger.getJobDataMap().get("deliveryOptions"));
description.setReportParameters(((ReportParameters) trigger.getJobDataMap().get("criteria")));
if (trigger instanceof CronTriggerImpl) {
description.setCronExpression(((CronTriggerImpl)trigger).getCronExpression());
}
triggerDescriptions.add(description);
}
} catch (SchedulerException e) {
LOG.error("exception retrieving trigger descriptions", e);
throw new org.opennms.web.svclayer.support.SchedulerException("Could not retrieve triggers: " + e.getMessage(), e);
}
return triggerDescriptions;
}
@Override
public Boolean exists(String triggerName) {
Objects.requireNonNull(triggerName);
try {
final Trigger trigger = m_scheduler.getTrigger(new TriggerKey(triggerName, m_triggerGroup));
if (trigger != null) {
return true;
}
} catch (SchedulerException e) {
LOG.error("exception looking up trigger name: {}", triggerName, e);
throw new org.opennms.web.svclayer.support.SchedulerException("Could not retrieve trigger '" + triggerName + " ': " + e.getMessage(), e);
}
return false;
}
@Override
public void removeTrigger(String triggerName) {
try {
m_scheduler.unscheduleJob(new TriggerKey(triggerName, m_triggerGroup));
} catch (SchedulerException e) {
LOG.error("exception when attempting to remove trigger {}", triggerName, e);
throw new org.opennms.web.svclayer.support.SchedulerException("Could not remove trigger '" + triggerName + " ': " + e.getMessage(), e);
}
}
@Override
public void removeTriggers(String[] triggerNames) {
for (String triggerName : triggerNames) {
removeTrigger(triggerName);
}
}
@Override
public void updateCronTrigger(String cronTrigger, ScheduleConfig scheduleConfig) {
Objects.requireNonNull(cronTrigger);
Objects.requireNonNull(scheduleConfig);
validate(scheduleConfig, ReportMode.SCHEDULED, true);
final TriggerKey triggerKey = new TriggerKey(cronTrigger, m_triggerGroup);
final ReportParameters parameters = scheduleConfig.getReportParameters();
final DeliveryOptions deliveryOptions = scheduleConfig.getDeliveryOptions();
final String cronExpression = scheduleConfig.getCronExpression();
TimeZone zone = getTimeZone(parameters);
try {
final Trigger trigger = m_scheduler.getTrigger(triggerKey);
trigger.getJobDataMap().put("criteria", parameters);
trigger.getJobDataMap().put("deliveryOptions", deliveryOptions);
trigger.getJobDataMap().put("cronExpression", cronExpression);
trigger.getJobDataMap().put("timeZone", zone.getID());
((CronTriggerImpl) trigger).setCronExpression(cronExpression);
((CronTriggerImpl) trigger).setTimeZone(zone);
m_scheduler.rescheduleJob(triggerKey, trigger);
} catch(SchedulerException e) {
LOG.error("Could not update cron trigger {}:{}", cronTrigger, e.getMessage(), e);
throw new org.opennms.web.svclayer.support.SchedulerException("An unexpected error occurred while updating cron trigger " + cronTrigger, e);
} catch (ParseException e) {
LOG.error("Provided cron expression '{}' could not be parsed", cronExpression, e);
throw new org.opennms.web.svclayer.support.InvalidCronExpressionException(e, cronExpression);
}
}
@Override
public void addCronTrigger(ScheduleConfig scheduleConfig) {
Objects.requireNonNull(scheduleConfig);
validate(scheduleConfig, ReportMode.SCHEDULED, false);
final CronTriggerImpl cronTrigger = new CronTriggerImpl();
final ReportParameters parameters = scheduleConfig.getReportParameters();
final DeliveryOptions deliveryOptions = scheduleConfig.getDeliveryOptions();
final String cronExpression = scheduleConfig.getCronExpression();
TimeZone zone = getTimeZone(parameters);
try {
cronTrigger.setGroup(m_triggerGroup);
cronTrigger.setName(deliveryOptions.getInstanceId());
cronTrigger.setJobName(m_jobDetail.getKey().getName());
cronTrigger.setCronExpression(cronExpression);
} catch (ParseException e) {
LOG.error("Provided cron expression '{}' could not be parsed", cronExpression, e);
throw new InvalidCronExpressionException(e, cronExpression);
}
cronTrigger.setJobName(m_jobDetail.getKey().getName());
cronTrigger.setTimeZone(zone);
cronTrigger.getJobDataMap().put("criteria", parameters);
cronTrigger.getJobDataMap().put("reportId", parameters.getReportId());
cronTrigger.getJobDataMap().put("deliveryOptions", deliveryOptions);
cronTrigger.getJobDataMap().put("timeZone", zone.getID());
cronTrigger.getJobDataMap().put("mode", ReportMode.SCHEDULED);
try {
m_scheduler.scheduleJob(cronTrigger);
} catch (SchedulerException e) {
throw new org.opennms.web.svclayer.support.SchedulerException(e);
}
}
private TimeZone getTimeZone(final ReportParameters parameters) {
TimeZone zone = TimeZone.getDefault();
if (parameters.getTimezoneParms() != null && parameters.getTimezoneParms().size() > 0) {
final String zoneString = parameters.getTimezoneParms().get(0).getValue();
try {
zone = TimeZone.getTimeZone(zoneString);
} catch (final Throwable t) {
LOG.warn("Failed to parse timezone '%s'", zoneString, t);
}
}
return zone;
}
@Override
public void execute(DeliveryConfig deliveryConfig) {
Objects.requireNonNull(deliveryConfig);
validate(deliveryConfig, ReportMode.IMMEDIATE, false);
final ReportParameters parameters = deliveryConfig.getReportParameters();
final DeliveryOptions deliveryOptions = deliveryConfig.getDeliveryOptions();
final SimpleTriggerImpl trigger = new SimpleTriggerImpl(deliveryOptions.getInstanceId(), m_triggerGroup, new Date(), null, 0, 0L);
trigger.setJobName(m_jobDetail.getKey().getName());
trigger.getJobDataMap().put("criteria", parameters);
trigger.getJobDataMap().put("reportId", parameters.getReportId());
trigger.getJobDataMap().put("deliveryOptions", deliveryOptions);
trigger.getJobDataMap().put("mode", ReportMode.IMMEDIATE);
try {
m_scheduler.scheduleJob(trigger);
} catch (SchedulerException e) {
throw new org.opennms.web.svclayer.support.SchedulerException(e);
}
}
public void setScheduler(Scheduler scheduler) {
m_scheduler = scheduler;
}
public void setJobDetail(JobDetail reportJob) {
m_jobDetail = reportJob;
}
public void setTriggerGroup(String triggerGroup) {
m_triggerGroup = triggerGroup;
}
private void validate(DeliveryConfig deliveryConfig, ReportMode reportMode, boolean update) {
Objects.requireNonNull(deliveryConfig);
Objects.requireNonNull(reportMode);
validate(deliveryConfig.getReportParameters(), reportMode);
validate(deliveryConfig.getDeliveryOptions(), update);
}
private void validate(DeliveryOptions deliveryOptions, boolean update) {
Objects.requireNonNull(deliveryOptions);
try {
if (!update) { // We skip the instanceId check if we update
final Set intanceIds = m_scheduler.getTriggerKeys(GroupMatcher.groupEquals(m_triggerGroup)).stream()
.map(tk -> tk.getName())
.collect(Collectors.toSet());
if (intanceIds.contains(deliveryOptions.getInstanceId())) {
throw new SchedulerContextException("instanceId", "The provided value already exists");
}
if (Strings.isNullOrEmpty(deliveryOptions.getInstanceId())) {
throw new SchedulerContextException("instanceId", PROVIDE_A_VALUE_TEXT);
}
}
if (!deliveryOptions.isSendMail() && !deliveryOptions.isPersist() && !deliveryOptions.isWebhook()) {
throw new SchedulerContextException("sendMail_persist_webhook", "Either sendMail, webhook or persist must be set");
}
if (deliveryOptions.getFormat() == null) {
throw new SchedulerContextException("format", PROVIDE_A_VALUE_TEXT);
}
if (deliveryOptions.isSendMail()) {
if (Strings.isNullOrEmpty(deliveryOptions.getMailTo())) {
throw new SchedulerContextException("mailTo", PROVIDE_A_VALUE_TEXT);
}
// Try parsing the input
try {
InternetAddress.parse(deliveryOptions.getMailTo(), false);
} catch (AddressException e) {
throw new SchedulerContextException("mailTo", "Provided recipients could not be parsed: {0}", e.getMessage(), e);
}
}
if (deliveryOptions.isWebhook()) {
if (Strings.isNullOrEmpty(deliveryOptions.getWebhookUrl())) {
throw new SchedulerContextException("webhookUrl", PROVIDE_A_VALUE_TEXT);
}
try {
new URL(deliveryOptions.getWebhookUrl());
} catch (MalformedURLException ex) {
throw new SchedulerContextException("webhookUrl", "The provided URL ''{0}'' is not valid: ''{1}''", deliveryOptions.getWebhookUrl(), ex.getMessage());
}
}
} catch (SchedulerException e) {
throw new org.opennms.web.svclayer.support.SchedulerException(e);
}
}
private void validate(ReportParameters reportParameters, ReportMode reportMode) {
Objects.requireNonNull(reportParameters);
Objects.requireNonNull(reportMode);
final ReportParmVisitor validator = new ParameterRequiredVisitor(reportMode);
if (reportParameters.getStringParms() != null) {
for (ReportStringParm eachParm : reportParameters.getStringParms()) {
validator.visit(eachParm);
}
}
if (reportParameters.getIntParms() != null) {
for (ReportIntParm eachParm : reportParameters.getIntParms()) {
validator.visit(eachParm);
}
}
if (reportParameters.getFloatParms() != null) {
for (ReportFloatParm eachParm : reportParameters.getFloatParms()) {
validator.visit(eachParm);
}
}
if (reportParameters.getDoubleParms() != null) {
for (ReportDoubleParm eachParm : reportParameters.getDoubleParms()) {
validator.visit(eachParm);
}
}
if (reportParameters.getDateParms() != null) {
for (ReportDateParm eachParm : reportParameters.getDateParms()) {
validator.visit(eachParm);
}
}
}
/**
* This visitor enforces that each value is actually set, as it is required by default.
*/
private static class ParameterRequiredVisitor implements ReportParmVisitor {
private final ReportMode mode;
public ParameterRequiredVisitor(ReportMode reportMode) {
this.mode = Objects.requireNonNull(reportMode);
}
@Override
public void visit(ReportStringParm parm) {
if (Strings.isNullOrEmpty(parm.getValue())) {
throw new SchedulerContextException(parm.getName(), PROVIDE_A_VALUE_TEXT);
}
}
@Override
public void visit(ReportIntParm parm) {
}
@Override
public void visit(ReportFloatParm parm) {
if (parm.getValue() == null) {
throw new SchedulerContextException(parm.getName(), PROVIDE_A_VALUE_TEXT);
}
}
@Override
public void visit(ReportDoubleParm parm) {
if (parm.getValue() == null) {
throw new SchedulerContextException(parm.getName(), PROVIDE_A_VALUE_TEXT);
}
}
@Override
public void visit(ReportTimezoneParm parm) {
if (parm.getValue() == null) {
throw new SchedulerContextException(parm.getName(), PROVIDE_A_VALUE_TEXT);
}
}
@Override
public void visit(ReportDateParm parm) {
if (parm.getUseAbsoluteDate() || mode == ReportMode.IMMEDIATE) {
if (parm.getDate() == null){
throw new SchedulerContextException(parm.getName() + "Date", PROVIDE_A_VALUE_TEXT);
}
} else {
if (parm.getInterval() == null || !Intervals.ALL.contains(parm.getInterval())) {
throw new SchedulerContextException(parm.getName() + "Interval", "The provided value must be any of the following {0}", Intervals.ALL);
}
if (parm.getCount() == null) {
throw new SchedulerContextException(parm.getName() + "Count", PROVIDED_VALUE_GREATER_ZERO_TEXT);
}
}
if (parm.getHours() == null) {
throw new SchedulerContextException(parm.getName() + "Hours", PROVIDE_A_VALUE_TEXT);
}
if (parm.getHours() < 0 || parm.getHours() > 23) {
throw new SchedulerContextException(parm.getName() + "Hours", "Please provide a value between 0 and 23");
}
if (parm.getMinutes() == null) {
throw new SchedulerContextException(parm.getName() + "Minutes", PROVIDE_A_VALUE_TEXT);
}
if (parm.getMinutes() < 0 || parm.getMinutes() > 59) {
throw new SchedulerContextException(parm.getName() + "Minutes", "Please provide a value between 0 and 59");
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy