/* * Copyright 2008-2017 by Emeric Vernat * * This file is part of Java Melody. * * Licensed 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 net.bull.javamelody; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.quartz.CronTrigger; import org.quartz.JobDetail; import org.quartz.JobExecutionContext; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.SimpleTrigger; import org.quartz.Trigger; import org.quartz.impl.SchedulerRepository; /** * Informations sur un job. * L'état d'une instance est initialisé à son instanciation et non mutable; * il est donc de fait thread-safe. * Cet état est celui d'un job à un instant t. * Les instances sont sérialisables pour pouvoir être transmises au serveur de collecte. * Pour l'instant seul quartz est géré. * @author Emeric Vernat */ class JobInformations implements Serializable { static final boolean QUARTZ_AVAILABLE = isQuartzAvailable(); private static final long serialVersionUID = -2826168112578815952L; private final String group; private final String name; private final String description; private final String jobClassName; private final Date previousFireTime; private final Date nextFireTime; private final long elapsedTime; private final long repeatInterval; private final String cronExpression; private final boolean paused; private final String globalJobId; JobInformations(JobDetail jobDetail, JobExecutionContext jobExecutionContext, Scheduler scheduler) throws SchedulerException { // pas throws SchedulerException ici sinon NoClassDefFoundError super(); assert jobDetail != null; assert scheduler != null; // rq: jobExecutionContext est non null si le job est en cours d'exécution ou null sinon final QuartzAdapter quartzAdapter = QuartzAdapter.getSingleton(); this.group = quartzAdapter.getJobGroup(jobDetail); this.name = quartzAdapter.getJobName(jobDetail); this.description = quartzAdapter.getJobDescription(jobDetail); this.jobClassName = quartzAdapter.getJobClass(jobDetail).getName(); if (jobExecutionContext == null) { elapsedTime = -1; } else { elapsedTime = System.currentTimeMillis() - quartzAdapter.getContextFireTime(jobExecutionContext).getTime(); } final List<Trigger> triggers = quartzAdapter.getTriggersOfJob(jobDetail, scheduler); this.nextFireTime = getNextFireTime(triggers); this.previousFireTime = getPreviousFireTime(triggers); String cronTriggerExpression = null; long simpleTriggerRepeatInterval = -1; boolean jobPaused = true; for (final Trigger trigger : triggers) { if (trigger instanceof CronTrigger) { cronTriggerExpression = quartzAdapter .getCronTriggerExpression((CronTrigger) trigger); } else if (trigger instanceof SimpleTrigger) { simpleTriggerRepeatInterval = quartzAdapter .getSimpleTriggerRepeatInterval((SimpleTrigger) trigger); } jobPaused = jobPaused && quartzAdapter.isTriggerPaused(trigger, scheduler); } this.repeatInterval = simpleTriggerRepeatInterval; this.cronExpression = cronTriggerExpression; this.paused = jobPaused; this.globalJobId = buildGlobalJobId(jobDetail); } private static boolean isQuartzAvailable() { try { Class.forName("org.quartz.Job"); Class.forName("org.quartz.impl.SchedulerRepository"); return true; } catch (final ClassNotFoundException e) { return false; } } @SuppressWarnings("unchecked") static List<JobInformations> buildJobInformationsList() { if (!QUARTZ_AVAILABLE) { return Collections.emptyList(); } final List<JobInformations> result = new ArrayList<JobInformations>(); try { for (final Scheduler scheduler : getAllSchedulers()) { final Map<String, JobExecutionContext> currentlyExecutingJobsByFullName = new LinkedHashMap<String, JobExecutionContext>(); for (final JobExecutionContext currentlyExecutingJob : (List<JobExecutionContext>) scheduler .getCurrentlyExecutingJobs()) { final JobDetail jobDetail = QuartzAdapter.getSingleton() .getContextJobDetail(currentlyExecutingJob); final String jobFullName = QuartzAdapter.getSingleton() .getJobFullName(jobDetail); currentlyExecutingJobsByFullName.put(jobFullName, currentlyExecutingJob); } for (final JobDetail jobDetail : getAllJobsOfScheduler(scheduler)) { final String jobFullName = QuartzAdapter.getSingleton() .getJobFullName(jobDetail); final JobExecutionContext jobExecutionContext = currentlyExecutingJobsByFullName .get(jobFullName); result.add(new JobInformations(jobDetail, jobExecutionContext, scheduler)); } } } catch (final Exception e) { throw new IllegalStateException(e); } return result; } @SuppressWarnings("unchecked") static List<Scheduler> getAllSchedulers() { return new ArrayList<Scheduler>(SchedulerRepository.getInstance().lookupAll()); } static List<JobDetail> getAllJobsOfScheduler(Scheduler scheduler) { try { return QuartzAdapter.getSingleton().getAllJobsOfScheduler(scheduler); } catch (final Exception e) { // si les jobs sont persistés en base de données, il peut y avoir une exception // dans scheduler.getJobGroupNames(), par exemple si la base est arrêtée LOG.warn(e.toString(), e); return Collections.emptyList(); } } private static Date getPreviousFireTime(List<Trigger> triggers) { Date previousFireTime = null; for (final Trigger trigger : triggers) { final Date triggerPreviousFireTime = QuartzAdapter.getSingleton() .getTriggerPreviousFireTime(trigger); if (previousFireTime == null || triggerPreviousFireTime != null && previousFireTime.before(triggerPreviousFireTime)) { previousFireTime = triggerPreviousFireTime; } } return previousFireTime; } private static Date getNextFireTime(List<Trigger> triggers) { Date nextFireTime = null; for (final Trigger trigger : triggers) { final Date triggerNextFireTime = QuartzAdapter.getSingleton() .getTriggerNextFireTime(trigger); if (nextFireTime == null || triggerNextFireTime != null && nextFireTime.after(triggerNextFireTime)) { nextFireTime = triggerNextFireTime; } } return nextFireTime; } String getGlobalJobId() { return globalJobId; } String getName() { return name; } String getGroup() { return group; } String getDescription() { return description; } String getJobClassName() { return jobClassName; } long getElapsedTime() { return elapsedTime; } boolean isCurrentlyExecuting() { return elapsedTime >= 0; } Date getNextFireTime() { return nextFireTime; } Date getPreviousFireTime() { return previousFireTime; } long getRepeatInterval() { return repeatInterval; } String getCronExpression() { return cronExpression; } boolean isPaused() { return paused; } private static String buildGlobalJobId(JobDetail jobDetail) { return PID.getPID() + '_' + Parameters.getHostAddress() + '_' + QuartzAdapter.getSingleton().getJobFullName(jobDetail).hashCode(); } /** {@inheritDoc} */ @Override public String toString() { return getClass().getSimpleName() + "[name=" + getName() + ", group=" + getGroup() + ']'; } }