Wednesday, February 20, 2019

REST and Payload

I have seen many ways REST webservices has been implemented and some are up to the standard and some looks more specific with their implementation.
Once we got a REST service code where all the calls were implemented using only POST.
In REST there are no hard rules, nevertheless it is nice if we can follow the conventions. Looking at various implementations, i was little confused with the what was the real way to do so sometimes.
So i did a few research on the internet and read what people had written.

Basic confusion with GET and DELETE where whether we can include a payload or body with the request call.
As there are no hard rules, REST does not force to not to do so, and still you can include a body for GET and DELETE.
Specially we do to make code looks more better or for better implementation. eg: create Search criteria, Filer criteria , most send inside body. This looks more object oriented. In my experience we did a search implementation for Apache SOLR. There i send all the search related info as params (search parms ) with the URL.

Conclusion:
REST --> GET and DELETE avoid using a payload or body (avoid @RequestBody use @PathVariable or URI with URI builder )



https://stackoverflow.com/questions/978061/http-get-with-request-body
https://stackoverflow.com/questions/299628/is-an-entity-body-allowed-for-an-http-delete-request

Wednesday, February 13, 2019

Create quartz scheduler as REST service and persist scheduler data to Database

Objective of this to create Scheduler as

1. REST service.
2. Persist scheduler data to a database

Here i use spring boot , quartz, mysql

Below is the link to sql script to create necessary table structure

https://cgenit.blogspot.com/2019/02/quartz-sql-script.html

Use Spring Initializer to create the project

https://start.spring.io/

Below is the POM file

https://cgenit.blogspot.com/2019/02/create-quartz-scheduler-as-rest-service_13.html

Few Annotations to be noted:
1. @EnableScheduling   - use in Springboot main class along with @SpringBootApplication
2. @PersistJobDataAfterExecution - used in Quartz job class eg: SyncJob


Below is the code for the Service

1. Controller class - with few selected service calls

package com.com.saap.scheduler.controller;

import java.util.List;

import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.com.saap.scheduler.model.SaapJob;
import com.com.saap.scheduler.model.SaapJob1;
import com.com.saap.scheduler.model.SaapJobKey;
import com.com.saap.scheduler.model.SaapTrigger;
import com.com.saap.scheduler.service.SchedulerService;

@RestController
@RequestMapping("sheduler")
public class ShedulerController {

@Autowired
SchedulerService schedulerService;

/**
* Returns list of schedule jobs.
*
* @return List list of jobs.
* @throws SchedulerException
*/
@GetMapping("/jobs")
public ResponseEntity> getJobList() throws SchedulerException {
List jobList = schedulerService.getJobList();

return new ResponseEntity>(jobList, HttpStatus.OK);
}

/**
* Filter list of jobs with give state.
*
* @param state state of the job.
* @return list of jobs.
* @throws SchedulerException
*/
@GetMapping("/jobs/{state}")
public ResponseEntity> getJobList(@PathVariable("state") String state) throws SchedulerException {
List jobList = schedulerService.getJobList(state);

return new ResponseEntity>(jobList, HttpStatus.OK);
}

/**
* Schedule a job. A new Trigger will be created and will be assign to same job.
*
* @param jobDetail {@link SaapJob} Information needs to create and schedule a
*                  job.
* @return
* @throws ClassNotFoundException
* @throws SchedulerException
*/
@PostMapping("job")
public ResponseEntity createSheduler(@RequestBody SaapJob1 jobDetail)
throws ClassNotFoundException, SchedulerException {

// pass the full object
schedulerService.sheduleJob(jobDetail.getJobClass(), jobDetail.getJobName(), jobDetail.getJobGroup(),
jobDetail.getTriggerName(), jobDetail.getTriggerGroup(), jobDetail.getCronExpression(),
jobDetail.getSourceURL(), jobDetail.getDestinationURL());

HttpHeaders headers = new HttpHeaders();
return new ResponseEntity(headers, HttpStatus.OK);
}

/**
* Delete a job with given {@link SaapJobKey}.
*
* @param jobkey combination of job name and corresponding group.
* @return
* @throws SchedulerException
*/
@PostMapping("job/del")
public ResponseEntity deleteJob(@RequestBody SaapJobKey jobkey) throws SchedulerException {
schedulerService.deleteJob(jobkey.getJobName(), jobkey.getJobGroup());
HttpHeaders headers = new HttpHeaders();
return new ResponseEntity(headers, HttpStatus.OK);
}

/**
* Pause a job with given {@link SaapJobKey}.
*
* @param jobkey combination of job name and corresponding group.
* @return
* @throws SchedulerException
*/
@PostMapping("job/pause")
public ResponseEntity pauseJob(@RequestBody SaapJobKey jobkey) throws SchedulerException {
schedulerService.pauseJob(jobkey.getJobName(), jobkey.getJobGroup());
HttpHeaders headers = new HttpHeaders();
return new ResponseEntity(headers, HttpStatus.OK);
}

/**
* Resume a job with given {@link SaapJobKey}.
*
* @param jobkey combination of job name and corresponding group.
* @return
* @throws SchedulerException
*/
@PostMapping("job/resume")
public ResponseEntity resumeJob(@RequestBody SaapJobKey jobkey) throws SchedulerException {
schedulerService.resumeJob(jobkey.getJobName(), jobkey.getJobGroup());
HttpHeaders headers = new HttpHeaders();
return new ResponseEntity(headers, HttpStatus.OK);
}

/**
* Interrupt a job with given {@link SaapJobKey}.
*
* @param jobkey combination of job name and corresponding group.
* @return
* @throws SchedulerException
*/
@PostMapping("job/interrupt")
public ResponseEntity interruptJob(@RequestBody SaapJobKey jobkey) throws SchedulerException {
schedulerService.interruptJob(jobkey.getJobName(), jobkey.getJobGroup());
HttpHeaders headers = new HttpHeaders();
return new ResponseEntity(headers, HttpStatus.OK);
}

/**
* Trigger a job with given {@link SaapJobKey}.
*
* @param jobkey combination of job name and corresponding group.
* @return
* @throws SchedulerException
*/
@PostMapping("job/trigger")
public ResponseEntity triggerJob(@RequestBody SaapJobKey jobkey) throws SchedulerException {
// TODO Auto-generated method stub
schedulerService.triggerJob(jobkey.getJobName(), jobkey.getJobGroup());
return null;
}

/**
* Change Cron expression of a trigger.
*
* @param triggerDetail Trigger details.
* @return
* @throws SchedulerException
*/
@PutMapping("job/trigger")
public ResponseEntity changeTrigger(@RequestBody SaapTrigger triggerDetail) throws SchedulerException {

schedulerService.changeTriggerForJob(triggerDetail.getTriggerKey().getName(),
triggerDetail.getTriggerKey().getGroup(), triggerDetail.getCronExpression());
HttpHeaders headers = new HttpHeaders();
return new ResponseEntity(headers, HttpStatus.OK);
}

/**
*
* @return
* @throws SchedulerException
*/
@GetMapping("start")
public ResponseEntity start() throws SchedulerException {
schedulerService.start();
return new ResponseEntity(HttpStatus.OK);
}

/**
*
* @return
* @throws SchedulerException
*/
@GetMapping("shutdown")
public ResponseEntity shutdown() throws SchedulerException {
schedulerService.shutdown();
return new ResponseEntity(HttpStatus.OK);
}

}



2. Singleton class to create Scheduler object - not used as we use Spring bean

package com.com.saap.scheduler.core;

import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;

public class SaapScheduler {

private static volatile Scheduler scheduler = null;

// make this singleton
public Scheduler getScheduler() throws SchedulerException {
if (scheduler == null) {
scheduler = new StdSchedulerFactory().getScheduler();
}
return scheduler;
}

public void start(Scheduler scheduler) throws SchedulerException {
if (scheduler != null) {
scheduler.start();
}
}

public void shutDown(Scheduler scheduler) throws SchedulerException {
if (scheduler != null) {
scheduler.shutdown();
}
}
}


3. Sample Job classes

3. 1 HelloJob.java  -- simple job to print a line

package com.com.saap.scheduler.job;

import java.util.Date;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class HelloJob implements Job {

@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// TODO Auto-generated method stub
System.out.println(new Date() + "Hello Quartz!");
}

}


3.2 SyncJob.java  --> do a Sync job. take data from one REST API call and POST to anther REST API

Note : @PersistJobDataAfterExecution

package com.com.saap.scheduler.job;

import java.util.Date;
import java.util.logging.Logger;

import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.quartz.PersistJobDataAfterExecution;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

@PersistJobDataAfterExecution
public class SyncJob implements Job {

private static Logger LOG = Logger.getLogger(SyncJob.class.getName());

public static final String SOURCE_URL = "sourceURL";
public static final String DESTINATION_URL = "destinationURL";

@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// TODO Auto-generated method stub
System.out.println(new Date() + "SyncJob Quartz!");
JobKey jobKey = context.getJobDetail().getKey();

// Grab and print passed parameters
JobDataMap data = context.getJobDetail().getJobDataMap();
String sourceURL = data.getString(SOURCE_URL);
String destinationURL = data.getString(DESTINATION_URL);

LOG.info("************  stat start SyncJob ***************");
LOG.info("jobKey.getName() =" + jobKey.getName());
LOG.info("sourceURL =" + sourceURL);
LOG.info("destinationURL=" + destinationURL);
LOG.info("************  stat end SyncJob ***************");

/**
* Implementing Job Here
*
*/

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
RestTemplate restTemplate = new RestTemplate();
String url = data.getString(SOURCE_URL);
HttpEntity requestEntity = new HttpEntity(headers);

try {
ResponseEntity responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity,
SyncJobResponse.class, 1);
LOG.info(responseEntity.toString());
} catch (Exception ex) {
System.out.println(ex.getLocalizedMessage());
}
}
}


4. Model classes use to transfer data 
4.1 SaapJob.java

package com.com.saap.scheduler.model;

import java.io.Serializable;

/**
 * Combination of the job functionality and the Trigger details relates to job.
 * 
 *
 */
public class SaapJob implements Serializable {

/**
*/
private static final long serialVersionUID = 688729605535527366L;

/**
* Details of the job functionality..
*/
private SaapJobDetail jobDetail;
/**
* Details of the Trigger.
*/
private SaapTrigger trigger;

public SaapJob(SaapJobDetail jobDetail, SaapTrigger trigger) {
this.jobDetail = jobDetail;
this.trigger = trigger;
}

public SaapJob() {

}

public SaapJobDetail getJobDetail() {
return jobDetail;
}

public void setJobDetail(SaapJobDetail jobDetail) {
this.jobDetail = jobDetail;
}

public SaapTrigger getTrigger() {
return trigger;
}

public void setTrigger(SaapTrigger trigger) {
this.trigger = trigger;
}

}


4.2 SaapJob1.java  - old class with all data

package com.com.saap.scheduler.model;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class SaapJob1 implements Serializable {

/**
*/
private static final long serialVersionUID = 2734719600011908418L;
private String jobClass;
private String jobName;
private String jobGroup;
private String triggerName;
private String triggerGroup;
private String cronExpression;
private String triggerState;
Date nextFireTime;

///
private String sourceURL;
private String destinationURL;
List jobDataList = new ArrayList();

public SaapJob1() {

}

public SaapJob1(String jobClass, String jobName, String jobGroup, String triggerName, String triggerGroup,
String cronExpression, Date nextFireTime, String triggerState) {
this.jobClass = jobClass;
this.jobName = jobName;
this.jobGroup = jobGroup;
this.triggerName = triggerName;
this.triggerGroup = triggerGroup;
this.cronExpression = cronExpression;
this.nextFireTime = nextFireTime != null ? (Date) nextFireTime.clone() : null;
this.triggerState = triggerState;
}

public String getJobClass() {
return jobClass;
}

public void setJobClass(String jobClass) {
this.jobClass = jobClass;
}

public String getJobName() {
return jobName;
}

public void setJobName(String jobName) {
this.jobName = jobName;
}

public String getJobGroup() {
return jobGroup;
}

public void setJobGroup(String jobGroup) {
this.jobGroup = jobGroup;
}

public String getTriggerName() {
return triggerName;
}

public void setTriggerName(String triggerName) {
this.triggerName = triggerName;
}

public String getTriggerGroup() {
return triggerGroup;
}

public void setTriggerGroup(String triggerGroup) {
this.triggerGroup = triggerGroup;
}

public String getCronExpression() {
return cronExpression;
}

public void setCronExpression(String cronExpression) {
this.cronExpression = cronExpression;
}

public String getTriggerState() {
return triggerState;
}

public void setTriggerState(String triggerState) {
this.triggerState = triggerState;
}

public Date getNextFireTime() {
return nextFireTime != null ? (Date) nextFireTime.clone() : null;
}

public void setNextFireTime(Date nextFireTime) {
this.nextFireTime = nextFireTime != null ? (Date) nextFireTime.clone() : null;
}

public String getSourceURL() {
return sourceURL;
}

public void setSourceURL(String sourceURL) {
this.sourceURL = sourceURL;
}

public String getDestinationURL() {
return destinationURL;
}

public void setDestinationURL(String destinationURL) {
this.destinationURL = destinationURL;
}

public List getJobDataList() {
return jobDataList;
}

public void setJobDataList(List jobDataList) {
this.jobDataList = jobDataList;
}

}

4.3 SaapJobData

package com.com.saap.scheduler.model;

import java.io.Serializable;


public class SaapJobData implements Serializable {

/**
*
*/
private static final long serialVersionUID = 6234077751506588604L;
private String key;
private String value;

public SaapJobData() {

}

SaapJobData(String key, String value) {
this.key = key;
this.value = value;
}

public String getKey() {
return key;
}

public void setKey(String key) {
this.key = key;
}

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}

}


4.4 SaapJobDetail

package com.com.saap.scheduler.model;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * Conveys the detail properties of a given SaapJob instance.
 * 
 *
 * SaapobDetail contains
 *
     *


  1. jobClass
  2.  *


  3. jobDescription
  4.  *


  5. jobKey
  6.  *


  7. jobDataList
  8.  *
     *
     * 
     * 
     *
     */
    public class SaapJobDetail implements Serializable {

    /**
    */
    private static final long serialVersionUID = 3059373659998965232L;

    /**
    * name of the job class which logic is encapsulated.
    */
    private String jobClass;
    /**
    * Abstract description of the job.
    */
    private String jobDescription;
    /**
    * Unique job name and job group combination to identify the job.
    */
    private SaapJobKey jobKey;
    /**
    * List of {@link SaapJobData} which use as meta data for the job.
    */
    List jobDataList = new ArrayList();

    public SaapJobDetail() {

    }

    public SaapJobDetail(String jobClass, String jobDescription, SaapJobKey jobKey, List jobDataList) {
    this.jobClass = jobClass;
    this.jobDescription = jobDescription;
    this.jobKey = jobKey;
    this.jobDataList = jobDataList;
    }

    public String getJobClass() {
    return jobClass;
    }

    public void setJobClass(String jobClass) {
    this.jobClass = jobClass;
    }

    public String getJobDescription() {
    return jobDescription;
    }

    public void setJobDescription(String jobDescription) {
    this.jobDescription = jobDescription;
    }

    public SaapJobKey getJobKey() {
    return jobKey;
    }

    public void setJobKey(SaapJobKey jobKey) {
    this.jobKey = jobKey;
    }

    public List getJobDataList() {
    return jobDataList;
    }

    public void setJobDataList(List jobDataList) {
    this.jobDataList = jobDataList;
    }

    }


    4.5  SaapJobKey

    package com.com.saap.scheduler.model;

    import java.io.Serializable;

    /**
     * Job is identified uniquely using jobkey comprise of name and group. The
     * combination is unique.
     * 
     * 
     *
     */
    public class SaapJobKey implements Serializable {

    /**
    */
    private static final long serialVersionUID = 5196501106111618671L;
    /**
    * Name for particular job.
    */
    private String jobName;
    /**
    * Group name for particular job.
    */
    private String jobGroup;

    public SaapJobKey() {

    }

    public SaapJobKey(String jobName, String jobGroup) {
    this.jobName = jobName;
    this.jobGroup = jobGroup;
    }

    public String getJobName() {
    return jobName;
    }

    public void setJobName(String jobName) {
    this.jobName = jobName;
    }

    public String getJobGroup() {
    return jobGroup;
    }

    public void setJobGroup(String jobGroup) {
    this.jobGroup = jobGroup;
    }

    }


    4.6 SaapTrigger

    package com.com.saap.scheduler.model;

    import java.io.Serializable;
    import java.util.Date;
    import java.util.TimeZone;

    import org.quartz.Scheduler;
    import org.quartz.Trigger;

    /**
     *
     * SaapTriggerss have a {@link SaapTriggerKey} associated with
     * them, which should uniquely identify them within a single
     * {@link Scheduler}.
     *
     * 
     *
     * SaapTriggers are the 'mechanism' by which SaapJobs
     * are scheduled. Many SaapTriggers can point to the same
     * SaapJob, but a single SaapTrigger can only point to
     * one SaapJob.
     *
     * 
     *
     * SaapTriggers can 'send' parameters/data to SaapJobs by placing
     * contents into the List on the
     * SaapTrigger.
     *
     * 
     * 
     *
     */
    public class SaapTrigger implements Serializable {

    /**
    */
    private static final long serialVersionUID = 4507610386211548655L;
    private SaapTriggerKey triggerKey;
    private String description;
    private String cronExpression;
    private int priority = Trigger.DEFAULT_PRIORITY;
    private String triggerState;
    private Date startTime = null;
    private Date endTime = null;
    private Date nextFireTime = null;
    private Date previousFireTime = null;
    private transient TimeZone timeZone = null;

    public SaapTrigger() {

    }

    public SaapTrigger(SaapTriggerKey triggerKey, String description, String cronExpression, int priority) {
    this.triggerKey = triggerKey;
    this.description = description;
    this.cronExpression = cronExpression;
    this.priority = priority;
    }

    public SaapTriggerKey getTriggerKey() {
    return triggerKey;
    }

    public void setTriggerKey(SaapTriggerKey triggerKey) {
    this.triggerKey = triggerKey;
    }

    public String getDescription() {
    return description;
    }

    public void setDescription(String description) {
    this.description = description;
    }

    public String getCronExpression() {
    return cronExpression;
    }

    public void setCronExpression(String cronExpression) {
    this.cronExpression = cronExpression;
    }

    public int getPriority() {
    return priority;
    }

    public void setPriority(int priority) {
    this.priority = priority;
    }

    public String getTriggerState() {
    return triggerState;
    }

    public void setTriggerState(String triggerState) {
    this.triggerState = triggerState;
    }

    public Date getStartTime() {
    return startTime != null ? (Date) startTime.clone() : null;
    }

    public void setStartTime(Date startTime) {
    this.startTime = startTime != null ? (Date) startTime.clone() : null;
    }

    public Date getEndTime() {
    return endTime != null ? (Date) endTime.clone() : null;
    }

    public void setEndTime(Date endTime) {
    this.endTime = endTime != null ? (Date) endTime.clone() : null;
    }

    public Date getNextFireTime() {
    return nextFireTime != null ? (Date) nextFireTime.clone() : null;
    }

    public void setNextFireTime(Date nextFireTime) {
    this.nextFireTime = nextFireTime != null ? (Date) nextFireTime.clone() : null;
    }

    public Date getPreviousFireTime() {
    return previousFireTime != null ? (Date) previousFireTime.clone() : null;
    }

    public void setPreviousFireTime(Date previousFireTime) {
    this.previousFireTime = previousFireTime != null ? (Date) previousFireTime.clone() : null;
    }

    public TimeZone getTimeZone() {
    return timeZone;
    }

    public void setTimeZone(TimeZone timeZone) {
    this.timeZone = timeZone;
    }

    }


    4.7 SaapTriggerKey

    package com.com.saap.scheduler.model;

    import java.io.Serializable;

    /**
     * Uniquely identifies a {@link SaapTriggerKey}.
     * 
     *
     * Keys are composed of both a name and group, and the name must be unique
     * within the group. If only a name is specified then the default group name
     * will be used.
     *
     * 
     * 
     *
     */
    public class SaapTriggerKey implements Serializable {

    /**
    */
    private static final long serialVersionUID = 2271019390246303775L;
    /**
    * The default group for scheduling entities, with the value "DEFAULT".
    */
    public static final String DEFAULT_GROUP = "DEFAULT";

    /**
    * name of the Trigger.
    */
    private String name;
    /**
    * group of the Trigger.
    */
    private String group;

    public SaapTriggerKey() {

    }

    public SaapTriggerKey(String name) {
    this(name, null);
    }

    public SaapTriggerKey(String name, String group) {

    if (name == null)
    throw new IllegalArgumentException("Name cannot be null.");
    this.name = name;
    if (group != null)
    this.group = group;
    else
    this.group = DEFAULT_GROUP;
    }

    public String getName() {
    return name;
    }

    public void setName(String name) {
    this.name = name;
    }

    public String getGroup() {
    return group;
    }

    public void setGroup(String group) {
    this.group = group;
    }

    @Override
    public String toString() {
    return "SaapTriggerKey [name=" + name + ", group=" + group + "]";
    }

    @Override
    public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((group == null) ? 0 : group.hashCode());
    result = prime * result + ((name == null) ? 0 : name.hashCode());
    return result;
    }

    @Override
    public boolean equals(Object obj) {
    if (this == obj)
    return true;
    if (obj == null)
    return false;
    if (getClass() != obj.getClass())
    return false;
    equalsTriggerKey(obj);
    return true;
    }

    private boolean equalsTriggerKey(Object obj) {
    SaapTriggerKey other = (SaapTriggerKey) obj;
    if (group == null) {
    if (other.group != null)
    return false;
    } else if (!group.equals(other.group))
    return false;
    if (name == null) {
    if (other.name != null)
    return false;
    } else if (!name.equals(other.name))
    return false;

    return false;
    }

    }


    4.8 SaapTriggerState

    package com.com.saap.scheduler.model;

    /**
     * Trigger states {@link SaapTriggerState} for the schedule job.State helps to
     * find the actual state of the job as trigger is associated with a job.
     * 
     *
     * five states of the trigger are as follows
     *
       *


    1. NONE
    2.  *


    3. NORMAL
    4.  *


    5. PAUSED
    6.  *


    7. COMPLETE
    8.  *


    9. ERROR
    10.  *


    11. BLOCKED
    12.  *
       *
       * 
       * 
       *
       */
      public enum SaapTriggerState {
      NONE, NORMAL, PAUSED, COMPLETE, ERROR, BLOCKED
      }

      5. Service classes

      5.1 JobService

      package com.com.saap.scheduler.service;

      import org.quartz.Job;
      import org.quartz.JobDetail;
      import org.quartz.JobKey;

      public interface JobService {

      JobKey createJobKey(final String name);

      JobKey createJobKey(final String name, final String group);

      JobDetail createJobDetail(Class jobClass, final JobKey jobKey);

      }


      5.2 JobServiceImpl

      package com.com.saap.scheduler.service;

      import org.quartz.Job;
      import org.quartz.JobBuilder;
      import org.quartz.JobDetail;
      import org.quartz.JobKey;
      import org.springframework.stereotype.Service;

      @Service
      public class JobServiceImpl implements JobService {
      @Override
      public JobKey createJobKey(String name) {
      // TODO Auto-generated method stub
      return new JobKey(name);
      }

      @Override
      public JobKey createJobKey(String name, String group) {
      // TODO Auto-generated method stub
      return new JobKey(name, group);
      }

      ////
      @Override
      public JobDetail createJobDetail(Class jobClass, JobKey jobKey) {
      // TODO Auto-generated method stub
      return JobBuilder.newJob(jobClass).withIdentity(jobKey).build();
      }
      }


      5.3 SchedulerService

      package com.com.saap.scheduler.service;

      import java.util.List;

      import org.quartz.SchedulerException;
      import org.springframework.stereotype.Service;

      import com.com.saap.scheduler.model.SaapJob;

      @Service
      public interface SchedulerService {

      /**
      * Start scheduler.
      * @throws SchedulerException
      */
      void start() throws SchedulerException;

      /**
      * Shutdown scheduler.
      * @throws SchedulerException
      */
      void shutdown() throws SchedulerException;

      //// jobs

      /**
      * Returns all the jobs in the scheduler.
      * @return
      * @throws SchedulerException
      */
      List getJobList() throws SchedulerException;

      /**
      * Filter and return the list of jobs in the scheduler.
      * @param triggerState (NONE, NORMAL, PAUSED, COMPLETE, ERROR, BLOCKED)
      * @return
      * @throws SchedulerException
      */
      List getJobList(String triggerState) throws SchedulerException;

      /**
      * Trigger a selected job.
      * @param jobName
      * @param jobGroup
      * @throws SchedulerException
      */
      void triggerJob(String jobName, String jobGroup) throws SchedulerException;

      /**
      * Create Job and Trigger. Schedule the Job with created Trigger.
      * @param JobClass
      * @param jobName
      * @param jobGroup
      * @param triggerName
      * @param triggerGroup
      * @param cronExpression
      * @throws SchedulerException
      * @throws ClassNotFoundException
      */
      public void sheduleJob(final String JobClass, final String jobName, String jobGroup, final String triggerName,
      final String triggerGroup, final String cronExpression, final String sourceURL, final String destURL)
      throws SchedulerException, ClassNotFoundException;

      /**
      * Change the trigger value: cron Expression for the given job.
      * @param triggerName
      * @param triggerGroup
      * @param newCronExp
      * @throws SchedulerException
      */
      void changeTriggerForJob(String triggerName, String triggerGroup, String newCronExp) throws SchedulerException;

      /**
      * Delete a job.
      * @param jobName
      * @param jobGroup
      * @throws SchedulerException
      */
      void deleteJob(final String jobName, String jobGroup) throws SchedulerException;

      /**
      * Pause schedule job.
      * @param jobName
      * @param jobGroup
      * @throws SchedulerException
      */
      void pauseJob(String jobName, String jobGroup) throws SchedulerException;

      /**
      * Resume a given job.
      * @param jobName
      * @param jobGroup
      * @throws SchedulerException
      */
      void resumeJob(String jobName, String jobGroup) throws SchedulerException;

      /**
      * Interrupt a given job.
      * @param jobName
      * @param jobGroup
      * @throws SchedulerException
      */
      void interruptJob(String jobName, String jobGroup) throws SchedulerException;

      /**
      * Get the Job details for given job name with corresponding group.
      * @param jobName
      * @param jobGroup
      * @return
      * @throws SchedulerException
      */
      SaapJob getJobDetail(String jobName, String jobGroup) throws SchedulerException;

      }


      5.4 SchedulerServiceImpl

      package com.com.saap.scheduler.service;

      import java.util.ArrayList;
      import java.util.List;
      import java.util.Map;
      import java.util.function.Predicate;
      import java.util.logging.Logger;
      import java.util.stream.Collectors;

      import org.quartz.CronScheduleBuilder;
      import org.quartz.CronTrigger;
      import org.quartz.Job;
      import org.quartz.JobDataMap;
      import org.quartz.JobDetail;
      import org.quartz.JobExecutionContext;
      import org.quartz.JobKey;
      import org.quartz.Scheduler;
      import org.quartz.SchedulerException;
      import org.quartz.Trigger;
      import org.quartz.TriggerBuilder;
      import org.quartz.TriggerKey;
      import org.quartz.impl.matchers.GroupMatcher;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Service;

      import com.com.saap.scheduler.model.SaapJob;
      import com.com.saap.scheduler.model.SaapJobData;
      import com.com.saap.scheduler.model.SaapJobDetail;
      import com.com.saap.scheduler.model.SaapJobKey;
      import com.com.saap.scheduler.model.SaapTrigger;
      import com.com.saap.scheduler.model.SaapTriggerKey;

      @Service
      public class SchedulerServiceImpl implements SchedulerService {

      private static Logger LOG = Logger.getLogger(SchedulerServiceImpl.class.getName());

      @Autowired
      Scheduler scheduler;

      @Autowired
      TriggerServiceImpl triggerService;

      @Autowired
      JobServiceImpl jobService;

      @Override
      public void start() throws SchedulerException {
      scheduler.start();
      }

      @Override
      public void shutdown() throws SchedulerException {
      scheduler.shutdown();
      }

      public void addJob(final String JobClass, final String jobName, String jobGroup)
      throws SchedulerException, ClassNotFoundException {
      JobKey jobKey = jobService.createJobKey(jobName, jobGroup);
      JobDetail jobDetail = jobService.createJobDetail((Class) Class.forName(JobClass), jobKey);
      scheduler.addJob(jobDetail, true);
      }

      @Override
      public void sheduleJob(final String JobClass, final String jobName, String jobGroup, final String triggerName,
      final String triggerGroup, final String cronExpression, final String sourceURL, final String destURL)
      throws SchedulerException, ClassNotFoundException {

      JobKey jobKey = jobService.createJobKey(jobName, jobGroup);
      JobDetail jobDetail = jobService.createJobDetail((Class) Class.forName(JobClass), jobKey);
      jobDetail.getJobDataMap().put("sourceURL", sourceURL);
      jobDetail.getJobDataMap().put("destinationURL", destURL);
      TriggerKey triggerKey = triggerService.createTriggerKey(triggerName, triggerGroup);
      Trigger trigger = triggerService.createTrigger(triggerKey, cronExpression);
      scheduler.scheduleJob(jobDetail, trigger);
      }

      @Override
      public void deleteJob(final String jobName, String jobGroup) throws SchedulerException {
      JobKey jobKey = jobService.createJobKey(jobName, jobGroup);
      scheduler.deleteJob(jobKey);
      }

      @Override
      public List getJobList() throws SchedulerException {

      List saapJobList = new ArrayList();

      for (String groupName : scheduler.getJobGroupNames()) {
      for (JobKey jobKey : scheduler.getJobKeys(GroupMatcher.jobGroupEquals(groupName))) {

      String jobName = jobKey.getName();
      String jobGroup = jobKey.getGroup();
      String jobDescription = scheduler.getJobDetail(jobKey).getDescription();
      String jobClass = scheduler.getJobDetail(jobKey).getJobClass().getName();

      List jobDataList = new ArrayList();
      JobDetail jobDetail = scheduler.getJobDetail(jobKey);

      if (jobDetail.getJobDataMap() != null && jobDetail.getJobDataMap().size() > 0) {
      JobDataMap jobDataMap = jobDetail.getJobDataMap();

      // check for null pointer
      for (Map.Entry entry : jobDataMap.entrySet()) {

      SaapJobData jobData = new SaapJobData();
      jobData.setKey(entry.getKey());
      jobData.setValue(entry.getValue().toString());
      jobDataList.add(jobData);
      }
      }

      List triggers = (List) scheduler.getTriggersOfJob(jobKey);
      Trigger trigger = triggers.get(0);
      CronTrigger cronTrigger = (CronTrigger) trigger;
      String cronExpression = cronTrigger.getCronExpression();

      // create saapJobDetail
      SaapJobDetail saapJobDetail = new SaapJobDetail();
      saapJobDetail.setJobClass(jobClass);
      saapJobDetail.setJobDataList(jobDataList);
      saapJobDetail.setJobDescription(jobDescription);
      saapJobDetail.setJobKey(new SaapJobKey(jobName, jobGroup));
      // create Trigger details
      SaapTrigger saapTrigger = new SaapTrigger();
      saapTrigger.setCronExpression(cronExpression);
      saapTrigger.setDescription(trigger.getDescription());
      saapTrigger.setEndTime(trigger.getEndTime());
      saapTrigger.setNextFireTime(trigger.getNextFireTime());
      saapTrigger.setPreviousFireTime(trigger.getPreviousFireTime());
      saapTrigger.setPriority(trigger.getPriority());
      saapTrigger.setStartTime(trigger.getStartTime());
      saapTrigger.setTriggerKey(new SaapTriggerKey(trigger.getKey().getName(), trigger.getKey().getGroup()));
      saapTrigger.setTriggerState(scheduler.getTriggerState(trigger.getKey()).toString());

      SaapJob saapJob = new SaapJob(saapJobDetail, saapTrigger);
      saapJobList.add(saapJob);
      }
      }

      return saapJobList;
      }

      @Override
      public List getJobList(String triggerState) throws SchedulerException {
      // SaapTriggerState.valueOf(triggerState)
      Predicate jobPricate = job -> job.getTrigger().getTriggerState().equals(triggerState);

      List jobs = getJobList().stream().parallel().filter(jobPricate).collect(Collectors.toList());

      return jobs;
      }

      @Override
      public SaapJob getJobDetail(String jobName, String jobGroup) throws SchedulerException {
      JobKey jobKey = new JobKey(jobName, jobGroup);
      JobDetail jobDetail = scheduler.getJobDetail(jobKey);

      // create saapJobDetail
      SaapJobDetail saapJobDetail = new SaapJobDetail();
      saapJobDetail.setJobClass(jobDetail.getClass().getName());

      List jobDataList = new ArrayList();
      if (jobDetail.getJobDataMap() != null && jobDetail.getJobDataMap().size() > 0) {
      JobDataMap jobDataMap = jobDetail.getJobDataMap();

      // check for null pointer
      for (Map.Entry entry : jobDataMap.entrySet()) {

      SaapJobData jobData = new SaapJobData();
      jobData.setKey(entry.getKey());
      jobData.setValue(entry.getValue().toString());
      jobDataList.add(jobData);
      }
      }
      saapJobDetail.setJobDataList(jobDataList);

      saapJobDetail.setJobDescription(jobDetail.getDescription());
      saapJobDetail.setJobKey(new SaapJobKey(jobName, jobGroup));
      // create Trigger details
      List triggers = (List) scheduler.getTriggersOfJob(jobKey);
      CronTrigger trigger = (CronTrigger) triggers.get(0);

      SaapTrigger saapTrigger = new SaapTrigger();
      saapTrigger.setCronExpression(trigger.getCronExpression());
      saapTrigger.setDescription(trigger.getDescription());
      saapTrigger.setEndTime(trigger.getEndTime());
      saapTrigger.setNextFireTime(trigger.getNextFireTime());
      saapTrigger.setPreviousFireTime(trigger.getPreviousFireTime());
      saapTrigger.setPriority(trigger.getPriority());
      saapTrigger.setStartTime(trigger.getStartTime());
      saapTrigger.setTriggerKey(new SaapTriggerKey(trigger.getKey().getName(), trigger.getKey().getGroup()));
      saapTrigger.setTriggerState(scheduler.getTriggerState(trigger.getKey()).toString());

      SaapJob saapJob = new SaapJob(saapJobDetail, saapTrigger);

      return saapJob;
      }

      public void getJobDetails(String jobGroupName) throws SchedulerException {
      GroupMatcher.jobGroupEquals(jobGroupName);
      List jobGroupList = scheduler.getJobGroupNames();
      LOG.info(jobGroupList.toString());
      }

      public void getCurrentlyRunningJobs() throws SchedulerException {
      List list = scheduler.getCurrentlyExecutingJobs();
      LOG.info(list.toString());
      }

      @Override
      public void triggerJob(String jobName, String jobGroup) throws SchedulerException {
      // create when group is null - for default group
      JobKey jobKey = new JobKey(jobName, jobGroup);
      scheduler.triggerJob(jobKey);
      }

      @Override
      public void pauseJob(String jobName, String jobGroup) throws SchedulerException {
      JobKey jobKey = new JobKey(jobName, jobGroup);
      scheduler.pauseJob(jobKey);
      }

      @Override
      public void resumeJob(String jobName, String jobGroup) throws SchedulerException {
      JobKey jobKey = new JobKey(jobName, jobGroup);
      scheduler.resumeJob(jobKey);
      }

      @Override
      public void interruptJob(String jobName, String jobGroup) throws SchedulerException {
      JobKey jobKey = new JobKey(jobName, jobGroup);
      scheduler.interrupt(jobKey);
      }

      @Override
      public void changeTriggerForJob(String triggerName, String triggerGroup, String newCronExp)
      throws SchedulerException {
      TriggerKey oldTriggerKey = new TriggerKey(triggerName, triggerGroup);
      Trigger newTrigger = TriggerBuilder.newTrigger().withIdentity(oldTriggerKey)
      .withSchedule(CronScheduleBuilder.cronSchedule(newCronExp)).build();
      scheduler.rescheduleJob(oldTriggerKey, newTrigger);
      }
      }


      5.5 TriggerService

      package com.com.saap.scheduler.service;

      import org.quartz.Trigger;
      import org.quartz.TriggerKey;

      public interface TriggerService {
      // trigger
      TriggerKey createTriggerKey(final String name);

      TriggerKey createTriggerKey(final String name, final String group);

      Trigger createTrigger(final TriggerKey triggerKey, final String cronExpression);
      }


      5.6 TriggerServiceImpl

      package com.com.saap.scheduler.service;

      import org.quartz.CronScheduleBuilder;
      import org.quartz.Trigger;
      import org.quartz.TriggerBuilder;
      import org.quartz.TriggerKey;
      import org.springframework.stereotype.Service;

      @Service
      public class TriggerServiceImpl implements TriggerService {
      @Override
      public TriggerKey createTriggerKey(String name) {
      // TODO Auto-generated method stub
      return new TriggerKey(name);
      }

      @Override
      public TriggerKey createTriggerKey(String name, String group) {
      // TODO Auto-generated method stub
      return new TriggerKey(name, group);
      }

      ////
      @Override
      public Trigger createTrigger(TriggerKey triggerKey, String cronExpression) {
      // TODO Auto-generated method stub triggerDescription triggerPriority
      return TriggerBuilder.newTrigger().withDescription(null).withIdentity(triggerKey)
      .withPriority(Trigger.DEFAULT_PRIORITY).withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
      .build();
      }
      }


      6. SaapSchedulerApplication  - main class for SpringBoot application
      Note: @EnableScheduling

      package com.saap.scheduler;

      import org.springframework.boot.SpringApplication;
      import org.springframework.boot.autoconfigure.SpringBootApplication;
      import org.springframework.scheduling.annotation.EnableScheduling;

      @SpringBootApplication
      @EnableScheduling
      public class SaapSchedulerApplication {

      public static void main(String[] args) {
      SpringApplication.run(SaapSchedulerApplication.class, args);
      }
      }


      7. application.properties

      # Spring properties
      spring.application.name= sheduler-service

      #map error
      server.error.path: /error

      # HTTP Server
      server.port=8889

      # Discovery Server Access
      #  1. DEV ONLY: Reduce the lease renewal interval to speed up registration
      #  2. Define URL of registration server (defaultZone)
      eureka.client.serviceUrl.defaultZone= http://localhost:1111/eureka/
      eureka.instance.leaseRenewalIntervalInSeconds=5

      using.spring.schedulerFactory=true

      ## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
      spring.datasource.url = jdbc:mysql://localhost:3306/qzDS
      spring.datasource.username = root
      spring.datasource.password = 123

      ## Hibernate Properties
      # The SQL dialect makes Hibernate generate better SQL for the chosen database
      spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
      # Hibernate ddl auto (create, create-drop, validate, update)
      spring.jpa.hibernate.ddl-auto = update

      ## QuartzProperties
      spring.quartz.job-store-type=jdbc
      #spring.quartz.jdbc.schema=classpath:org/quartz/impl/jdbcjobstore/tables_mysql_innodb.sql
      spring.quartz.properties.org.quartz.threadPool.threadCount=5


      8. quartz.properties - not needed as we are using Springboot

      ##scheduler
      #quartz.scheduler.instanceName = MyScheduler
      #
      ## thread-pool
      #org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
      #org.quartz.threadPool.threadCount=2
      #org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
      #
      ## job-store
      ##org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
      #
      ## others
      #org.quartz.jobStore.misfireThreshold = 60000
      #
      ## Using JobStoreTX
      ### Be sure to run the appropriate script(under docs/dbTables) first to create database/tables
      #org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
      #
      ## Configuring JDBCJobStore with the Table Prefix
      #org.quartz.jobStore.tablePrefix = QRTZ_
      #
      ## Using DriverDelegate
      #org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
      #
      ## Using datasource
      #org.quartz.jobStore.dataSource = qzDS
      #
      ## Define the datasource to use
      ##org.quartz.dataSource.qzDS.driver = com.ibm.db2.jcc.DB2Driver
      ##org.quartz.dataSource.qzDS.URL = jdbc:db2://localhost:50000/QZ_SMPL
      ##org.quartz.dataSource.qzDS.user = wiki_rw
      ##org.quartz.dataSource.qzDS.password = db24local
      ##org.quartz.dataSource.qzDS.maxConnections = 30
      #org.quartz.dataSource.qzDS.driver = com.mysql.jdbc.Driver
      #org.quartz.dataSource.qzDS.URL = jdbc:mysql://localhost:3306/qzDS
      #org.quartz.dataSource.qzDS.user = root
      #org.quartz.dataSource.qzDS.password = 123
      ##org.quartz.dataSource.qzDS.maxConnections = 30



      Create quartz scheduler as REST service and persist scheduler data to Database dependencies

      This is the POM file file




      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>

      <groupId>com.saap.scheduler</groupId>
      <artifactId>saap-scheduler</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      <packaging>jar</packaging>

      <name>saap-scheduler</name>
      <description>Demo project for Spring Boot</description>

      <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.1.0.RELEASE</version>
      <relativePath /> <!-- lookup parent from repository -->
      </parent>

      <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
      <java.version>1.8</java.version>
      <spring-cloud.version>Greenwich.M1</spring-cloud.version>
      </properties>

      <dependencies>
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-quartz</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
      </dependency>

      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <scope>runtime</scope>
      </dependency>
      <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <scope>runtime</scope>
      </dependency>
      <dependency>
      <groupId>com.mchange</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.5.2</version>
      </dependency>

      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
      </dependency>
      </dependencies>

      <dependencyManagement>
      <dependencies>
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-dependencies</artifactId>
      <version>${spring-cloud.version}</version>
      <type>pom</type>
      <scope>import</scope>
      </dependency>
      </dependencies>
      </dependencyManagement>

      <build>
      <plugins>
      <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
      </plugins>
      </build>

      <repositories>
      <repository>
      <id>spring-milestones</id>
      <name>Spring Milestones</name>
      <url>https://repo.spring.io/milestone</url>
      <snapshots>
      <enabled>false</enabled>
      </snapshots>
      </repository>
      </repositories>


      </project>