/*
* Copyright 2001-2009 Terracotta, Inc.
*
* 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 org.quartz.impl;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
import org.quartz.PersistJobDataAfterExecution;
import org.quartz.Scheduler;
import org.quartz.StatefulJob;
import org.quartz.Trigger;
import org.quartz.utils.ClassUtils;
/**
* <p>
* Conveys the detail properties of a given <code>Job</code> instance.
* </p>
*
* <p>
* Quartz does not store an actual instance of a <code>Job</code> class, but
* instead allows you to define an instance of one, through the use of a <code>JobDetail</code>.
* </p>
*
* <p>
* <code>Job</code>s have a name and group associated with them, which
* should uniquely identify them within a single <code>{@link Scheduler}</code>.
* </p>
*
* <p>
* <code>Trigger</code>s are the 'mechanism' by which <code>Job</code>s
* are scheduled. Many <code>Trigger</code>s can point to the same <code>Job</code>,
* but a single <code>Trigger</code> can only point to one <code>Job</code>.
* </p>
*
* @see Job
* @see StatefulJob
* @see JobDataMap
* @see Trigger
*
* @author James House
* @author Sharada Jambula
*/
@SuppressWarnings("deprecation")
public class JobDetailImpl implements Cloneable, java.io.Serializable, JobDetail {
private static final long serialVersionUID = -6069784757781506897L;
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Data members.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
private String name;
private String group = Scheduler.DEFAULT_GROUP;
private String description;
private Class<? extends Job> jobClass;
private JobDataMap jobDataMap;
private boolean durability = false;
private boolean shouldRecover = false;
private transient JobKey key = null;
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Constructors.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
/**
* <p>
* Create a <code>JobDetail</code> with no specified name or group, and
* the default settings of all the other properties.
* </p>
*
* <p>
* Note that the {@link #setName(String)},{@link #setGroup(String)}and
* {@link #setJobClass(Class)}methods must be called before the job can be
* placed into a {@link Scheduler}
* </p>
*/
public JobDetailImpl() {
// do nothing...
}
/**
* <p>
* Create a <code>JobDetail</code> with the given name, given class, default group,
* and the default settings of all the other properties.
* </p>
*
* @exception IllegalArgumentException
* if name is null or empty, or the group is an empty string.
*
* @deprecated use {@link JobBuilder}
*/
public JobDetailImpl(String name, Class<? extends Job> jobClass) {
this(name, null, jobClass);
}
/**
* <p>
* Create a <code>JobDetail</code> with the given name, group and class,
* and the default settings of all the other properties.
* </p>
*
* @param group if <code>null</code>, Scheduler.DEFAULT_GROUP will be used.
*
* @exception IllegalArgumentException
* if name is null or empty, or the group is an empty string.
*
* @deprecated use {@link JobBuilder}
*/
public JobDetailImpl(String name, String group, Class<? extends Job> jobClass) {
setName(name);
setGroup(group);
setJobClass(jobClass);
}
/**
* <p>
* Create a <code>JobDetail</code> with the given name, and group, and
* the given settings of all the other properties.
* </p>
*
* @param group if <code>null</code>, Scheduler.DEFAULT_GROUP will be used.
*
* @exception IllegalArgumentException
* if name is null or empty, or the group is an empty string.
*
* @deprecated use {@link JobBuilder}
*/
public JobDetailImpl(String name, String group, Class<? extends Job> jobClass,
boolean durability, boolean recover) {
setName(name);
setGroup(group);
setJobClass(jobClass);
setDurability(durability);
setRequestsRecovery(recover);
}
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Interface.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
/**
* <p>
* Get the name of this <code>Job</code>.
* </p>
*/
public String getName() {
return name;
}
/**
* <p>
* Set the name of this <code>Job</code>.
* </p>
*
* @exception IllegalArgumentException
* if name is null or empty.
*/
public void setName(String name) {
if (name == null || name.trim().length() == 0) {
throw new IllegalArgumentException("Job name cannot be empty.");
}
this.name = name;
this.key = null;
}
/**
* <p>
* Get the group of this <code>Job</code>.
* </p>
*/
public String getGroup() {
return group;
}
/**
* <p>
* Set the group of this <code>Job</code>.
* </p>
*
* @param group if <code>null</code>, Scheduler.DEFAULT_GROUP will be used.
*
* @exception IllegalArgumentException
* if the group is an empty string.
*/
public void setGroup(String group) {
if (group != null && group.trim().length() == 0) {
throw new IllegalArgumentException(
"Group name cannot be empty.");
}
if (group == null) {
group = Scheduler.DEFAULT_GROUP;
}
this.group = group;
this.key = null;
}
/**
* <p>
* Returns the 'full name' of the <code>JobDetail</code> in the format
* "group.name".
* </p>
*/
public String getFullName() {
return group + "." + name;
}
/* (non-Javadoc)
* @see org.quartz.JobDetailI#getKey()
*/
public JobKey getKey() {
if(key == null) {
if(getName() == null)
return null;
key = new JobKey(getName(), getGroup());
}
return key;
}
public void setKey(JobKey key) {
if(key == null)
throw new IllegalArgumentException("Key cannot be null!");
setName(key.getName());
setGroup(key.getGroup());
this.key = key;
}
/* (non-Javadoc)
* @see org.quartz.JobDetailI#getDescription()
*/
public String getDescription() {
return description;
}
/**
* <p>
* Set a description for the <code>Job</code> instance - may be useful
* for remembering/displaying the purpose of the job, though the
* description has no meaning to Quartz.
* </p>
*/
public void setDescription(String description) {
this.description = description;
}
/* (non-Javadoc)
* @see org.quartz.JobDetailI#getJobClass()
*/
public Class<? extends Job> getJobClass() {
return jobClass;
}
/**
* <p>
* Set the instance of <code>Job</code> that will be executed.
* </p>
*
* @exception IllegalArgumentException
* if jobClass is null or the class is not a <code>Job</code>.
*/
public void setJobClass(Class<? extends Job> jobClass) {
if (jobClass == null) {
throw new IllegalArgumentException("Job class cannot be null.");
}
if (!Job.class.isAssignableFrom(jobClass)) {
throw new IllegalArgumentException(
"Job class must implement the Job interface.");
}
this.jobClass = jobClass;
}
/* (non-Javadoc)
* @see org.quartz.JobDetailI#getJobDataMap()
*/
public JobDataMap getJobDataMap() {
if (jobDataMap == null) {
jobDataMap = new JobDataMap();
}
return jobDataMap;
}
/**
* <p>
* Set the <code>JobDataMap</code> to be associated with the <code>Job</code>.
* </p>
*/
public void setJobDataMap(JobDataMap jobDataMap) {
this.jobDataMap = jobDataMap;
}
/**
* <p>
* Set whether or not the <code>Job</code> should remain stored after it
* is orphaned (no <code>{@link Trigger}s</code> point to it).
* </p>
*
* <p>
* If not explicitly set, the default value is <code>false</code>.
* </p>
*/
public void setDurability(boolean durability) {
this.durability = durability;
}
/**
* <p>
* Set whether or not the the <code>Scheduler</code> should re-execute
* the <code>Job</code> if a 'recovery' or 'fail-over' situation is
* encountered.
* </p>
*
* <p>
* If not explicitly set, the default value is <code>false</code>.
* </p>
*
* @see JobExecutionContext#isRecovering()
*/
public void setRequestsRecovery(boolean shouldRecover) {
this.shouldRecover = shouldRecover;
}
/* (non-Javadoc)
* @see org.quartz.JobDetailI#isDurable()
*/
public boolean isDurable() {
return durability;
}
/**
* @return whether the associated Job class carries the {@link PersistJobDataAfterExecution} annotation.
*/
public boolean isPersistJobDataAfterExecution() {
return ClassUtils.isAnnotationPresent(jobClass, PersistJobDataAfterExecution.class);
}
/**
* @return whether the associated Job class carries the {@link DisallowConcurrentExecution} annotation.
*/
public boolean isConcurrentExectionDisallowed() {
return ClassUtils.isAnnotationPresent(jobClass, DisallowConcurrentExecution.class);
}
/* (non-Javadoc)
* @see org.quartz.JobDetailI#requestsRecovery()
*/
public boolean requestsRecovery() {
return shouldRecover;
}
/**
* <p>
* Return a simple string representation of this object.
* </p>
*/
@Override
public String toString() {
return "JobDetail '" + getFullName() + "': jobClass: '"
+ ((getJobClass() == null) ? null : getJobClass().getName())
+ " concurrentExectionDisallowed: " + isConcurrentExectionDisallowed()
+ " persistJobDataAfterExecution: " + isPersistJobDataAfterExecution()
+ " isDurable: " + isDurable() + " requestsRecovers: " + requestsRecovery();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof JobDetail)) {
return false;
}
JobDetail other = (JobDetail) obj;
if(other.getKey() == null || getKey() == null)
return false;
if (!other.getKey().equals(getKey())) {
return false;
}
return true;
}
@Override
public int hashCode() {
return getKey().hashCode();
}
@Override
public Object clone() {
JobDetailImpl copy;
try {
copy = (JobDetailImpl) super.clone();
if (jobDataMap != null) {
copy.jobDataMap = (JobDataMap) jobDataMap.clone();
}
} catch (CloneNotSupportedException ex) {
throw new IncompatibleClassChangeError("Not Cloneable.");
}
return copy;
}
public JobBuilder getJobBuilder() {
JobBuilder b = JobBuilder.newJob()
.ofType(getJobClass())
.requestRecovery(requestsRecovery())
.storeDurably(isDurable())
.usingJobData(getJobDataMap())
.withDescription(getDescription())
.withIdentity(getKey());
return b;
}
}