protected void schedule(
      Scheduler scheduler,
      StorageType storageType,
      Trigger trigger,
      String description,
      String destinationName,
      Message message)
      throws Exception {

    if (_jsonFactory == null) {
      throw new IllegalStateException("JSON factory not initialized");
    }

    try {
      JobBuilder jobBuilder = JobBuilder.newJob(MessageSenderJob.class);

      jobBuilder.withIdentity(trigger.getJobKey());

      jobBuilder.storeDurably();

      JobDetail jobDetail = jobBuilder.build();

      JobDataMap jobDataMap = jobDetail.getJobDataMap();

      jobDataMap.put(SchedulerEngine.DESCRIPTION, description);
      jobDataMap.put(SchedulerEngine.DESTINATION_NAME, destinationName);
      jobDataMap.put(SchedulerEngine.MESSAGE, _jsonFactory.serialize(message));
      jobDataMap.put(SchedulerEngine.STORAGE_TYPE, storageType.toString());

      JobState jobState =
          new JobState(
              TriggerState.NORMAL, message.getInteger(SchedulerEngine.EXCEPTIONS_MAX_SIZE));

      jobDataMap.put(SchedulerEngine.JOB_STATE, JobStateSerializeUtil.serialize(jobState));

      unregisterMessageListener(scheduler, trigger.getJobKey());

      synchronized (this) {
        scheduler.deleteJob(trigger.getJobKey());
        scheduler.scheduleJob(jobDetail, trigger);
      }
    } catch (ObjectAlreadyExistsException oaee) {
      if (_log.isInfoEnabled()) {
        _log.info("Message is already scheduled");
      }
    }
  }
  private void registerJob(
      final String jobName,
      final Job jobInstance,
      final String cronExpression,
      final Date startAt,
      final Map<String, Object> jobMap)
      throws SchedulerException, ParseException {

    synchronized (scheduler.getScheduler()) {
      boolean jobAlreadyRunning = false;
      for (JobExecutionContext jobCtx : scheduler.getScheduler().getCurrentlyExecutingJobs()) {
        if (jobName.equals(jobCtx.getJobDetail().getKey().getName())
            && Scheduler.DEFAULT_GROUP.equals(jobCtx.getJobDetail().getKey().getGroup())) {

          jobAlreadyRunning = true;

          LOG.debug("Job {} already running, cancel", jobCtx.getJobDetail().getKey());
        }
      }

      if (jobAlreadyRunning) {
        return;
      }
    }

    // 0. unregister job
    unregisterJob(jobName);

    // 1. Job bean
    ApplicationContextProvider.getBeanFactory().registerSingleton(jobName, jobInstance);

    // 2. JobDetail bean
    JobBuilder jobDetailBuilder =
        JobBuilder.newJob(jobInstance.getClass())
            .withIdentity(jobName)
            .usingJobData(new JobDataMap(jobMap));

    // 3. Trigger
    if (cronExpression == null && startAt == null) {
      // Jobs added with no trigger must be durable
      scheduler.getScheduler().addJob(jobDetailBuilder.storeDurably().build(), true);
    } else {
      TriggerBuilder<?> triggerBuilder;

      if (cronExpression == null) {
        triggerBuilder =
            TriggerBuilder.newTrigger()
                .withIdentity(JobNamer.getTriggerName(jobName))
                .startAt(startAt);
      } else {
        triggerBuilder =
            TriggerBuilder.newTrigger()
                .withIdentity(JobNamer.getTriggerName(jobName))
                .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression));

        if (startAt == null) {
          triggerBuilder = triggerBuilder.startNow();
        } else {
          triggerBuilder = triggerBuilder.startAt(startAt);
        }
      }

      scheduler.getScheduler().scheduleJob(jobDetailBuilder.build(), triggerBuilder.build());
    }
  }