2015-06-02 78 views
0

我在服务器启动时出现以下异常。石英不识别石英jar文件中的模式job_scheduling_data_2_0.xsd

我使用石英2.2.21与弹簧3.2。 我已启用石英插件(org.quartz.plugins.xml.XMLSchedulingDataProcessorPlugin)。

请找我们的XML文件的开始标记下面:

在服务器启动期间我们得到了如下的日志信息和堆栈跟踪:

错误消息:

Unable to load local schema packaged in quartz distribution jar. Utilizing schema online at http://www.quartz-scheduler.org/xml/job_scheduling_data_2_0.xsd 

例外:

Caused by: org.xml.sax.SAXParseException; systemId: file:///quartz_job_data.xml; lineNumber: 5; columnNumber: 104; 
schema_reference.4: Failed to read schema document 'http://www.quartz-scheduler.org/xml/job_scheduling_data_2_0.xsd', because 1) could not find the document; 2) the document could not be read; 3) the root element of the document is not <xsd:schema>. 
+0

哪个应用程序服务器您使用的? –

+0

我正在使用wildfly 8.2 – Shan

回答

0

我有同样的亲blem。我正在使用Jboss的7.1.1版本,并且当您没有连接到互联网时出现问题。这很容易,因为在主机中放置一个无法访问的虚假地址。

我试图强制本地复制,但它不起作用。

我最终做的是部分覆盖功能,直到这个问题得到解决。关注:https://jira.spring.io/browse/SPR-13706

public class CustomXMLSchedulingDataProcessor extends org.quartz.xml.XMLSchedulingDataProcessor { 
    public static final String QUARTZ_XSD_PATH_IN_JAR_CLASSPATH = "classpath:org/quartz/xml/job_scheduling_data_2_0.xsd"; 

    public CustomXMLSchedulingDataProcessor(ClassLoadHelper clh) throws ParserConfigurationException { 
     super(clh); 
    } 

    @Override 
    protected Object resolveSchemaSource() { 
     InputSource inputSource; 
     InputStream is = null; 
      try { 
       is = classLoadHelper.getResourceAsStream(QUARTZ_XSD_PATH_IN_JAR_CLASSPATH); 
      } finally { 
       if (is != null) { 
        inputSource = new InputSource(is); 
        inputSource.setSystemId(QUARTZ_SCHEMA_WEB_URL); 
       } 
       else { 
        return QUARTZ_SCHEMA_WEB_URL; 
       } 

      } 

      return inputSource; 
    } 
} 

而且我做了一个新的插件XMLSchedulingDataProcessorPlugin overwritting以上只是类的instanciation。

    public class XMLSchedulingDataProcessorPlugin 
    extends SchedulerPluginWithUserTransactionSupport 
    implements FileScanListener { 

    /* 
    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    * 
    * Data members. 
    * 
    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    */ 
    private static final int MAX_JOB_TRIGGER_NAME_LEN = 80; 
    private static final String JOB_INITIALIZATION_PLUGIN_NAME = "JobSchedulingDataLoaderPlugin"; 
    private static final String FILE_NAME_DELIMITERS = ","; 

    private boolean failOnFileNotFound = true; 

    private String fileNames = CustomXMLSchedulingDataProcessor.QUARTZ_XML_DEFAULT_FILE_NAME; 

    // Populated by initialization 
    private Map<String, JobFile> jobFiles = new LinkedHashMap<String, JobFile>(); 

    private long scanInterval = 0; 

    boolean started = false; 

    protected ClassLoadHelper classLoadHelper = null; 

    private Set<String> jobTriggerNameSet = new HashSet<String>(); 

    /* 
    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    * 
    * Constructors. 
    * 
    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    */ 

    public XMLSchedulingDataProcessorPlugin() { 
    } 

    /* 
    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    * 
    * Interface. 
    * 
    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    */ 

    /** 
    * Comma separated list of file names (with paths) to the XML files that should be read. 
    */ 
    public String getFileNames() { 
     return fileNames; 
    } 

    /** 
    * The file name (and path) to the XML file that should be read. 
    */ 
    public void setFileNames(String fileNames) { 
     this.fileNames = fileNames; 
    } 

    /** 
    * The interval (in seconds) at which to scan for changes to the file. 
    * If the file has been changed, it is re-loaded and parsed. The default 
    * value for the interval is 0, which disables scanning. 
    * 
    * @return Returns the scanInterval. 
    */ 
    public long getScanInterval() { 
     return scanInterval/1000; 
    } 

    /** 
    * The interval (in seconds) at which to scan for changes to the file. 
    * If the file has been changed, it is re-loaded and parsed. The default 
    * value for the interval is 0, which disables scanning. 
    * 
    * @param scanInterval The scanInterval to set. 
    */ 
    public void setScanInterval(long scanInterval) { 
     this.scanInterval = scanInterval * 1000; 
    } 

    /** 
    * Whether or not initialization of the plugin should fail (throw an 
    * exception) if the file cannot be found. Default is <code>true</code>. 
    */ 
    public boolean isFailOnFileNotFound() { 
     return failOnFileNotFound; 
    } 

    /** 
    * Whether or not initialization of the plugin should fail (throw an 
    * exception) if the file cannot be found. Default is <code>true</code>. 
    */ 
    public void setFailOnFileNotFound(boolean failOnFileNotFound) { 
     this.failOnFileNotFound = failOnFileNotFound; 
    } 

    /* 
    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    * 
    * SchedulerPlugin Interface. 
    * 
    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    */ 

    /** 
    * <p> 
    * Called during creation of the <code>Scheduler</code> in order to give 
    * the <code>SchedulerPlugin</code> a chance to initialize. 
    * </p> 
    * 
    * @throws org.quartz.SchedulerConfigException 
    *   if there is an error initializing. 
    */ 
    public void initialize(String name, final Scheduler scheduler, ClassLoadHelper schedulerFactoryClassLoadHelper) 
     throws SchedulerException { 
     super.initialize(name, scheduler); 
     this.classLoadHelper = schedulerFactoryClassLoadHelper; 

     getLog().info("Registering Quartz Job Initialization Plug-in."); 

     // Create JobFile objects 
     StringTokenizer stok = new StringTokenizer(fileNames, FILE_NAME_DELIMITERS); 
     while (stok.hasMoreTokens()) { 
      final String fileName = stok.nextToken(); 
      final JobFile jobFile = new JobFile(fileName); 
      jobFiles.put(fileName, jobFile);   
     } 
    } 


    @Override 
    public void start(UserTransaction userTransaction) { 
     try { 
      if (jobFiles.isEmpty() == false) { 

       if (scanInterval > 0) { 
        getScheduler().getContext().put(JOB_INITIALIZATION_PLUGIN_NAME + '_' + getName(), this); 
       } 

       Iterator<JobFile> iterator = jobFiles.values().iterator(); 
       while (iterator.hasNext()) { 
        JobFile jobFile = iterator.next(); 

        if (scanInterval > 0) { 
         String jobTriggerName = buildJobTriggerName(jobFile.getFileBasename()); 
         TriggerKey tKey = new TriggerKey(jobTriggerName, JOB_INITIALIZATION_PLUGIN_NAME); 

         // remove pre-existing job/trigger, if any 
         getScheduler().unscheduleJob(tKey); 

         JobDetail job = newJob().withIdentity(jobTriggerName, JOB_INITIALIZATION_PLUGIN_NAME).ofType(FileScanJob.class) 
          .usingJobData(FileScanJob.FILE_NAME, jobFile.getFileName()) 
          .usingJobData(FileScanJob.FILE_SCAN_LISTENER_NAME, JOB_INITIALIZATION_PLUGIN_NAME + '_' + getName()) 
          .build(); 

         SimpleTrigger trig = newTrigger().withIdentity(tKey).withSchedule(
           simpleSchedule().repeatForever().withIntervalInMilliseconds(scanInterval)) 
           .forJob(job) 
           .build(); 

         getScheduler().scheduleJob(job, trig); 
         getLog().debug("Scheduled file scan job for data file: {}, at interval: {}", jobFile.getFileName(), scanInterval); 
        } 

        processFile(jobFile); 
       } 
      } 
     } catch(SchedulerException se) { 
      getLog().error("Error starting background-task for watching jobs file.", se); 
     } finally { 
      started = true; 
     } 
    } 

    /** 
    * Helper method for generating unique job/trigger name for the 
    * file scanning jobs (one per FileJob). The unique names are saved 
    * in jobTriggerNameSet. 
    */ 
    private String buildJobTriggerName(
      String fileBasename) { 
     // Name w/o collisions will be prefix + _ + filename (with '.' of filename replaced with '_') 
     // For example: JobInitializationPlugin_jobInitializer_myjobs_xml 
     String jobTriggerName = JOB_INITIALIZATION_PLUGIN_NAME + '_' + getName() + '_' + fileBasename.replace('.', '_'); 

     // If name is too long (DB column is 80 chars), then truncate to max length 
     if (jobTriggerName.length() > MAX_JOB_TRIGGER_NAME_LEN) { 
      jobTriggerName = jobTriggerName.substring(0, MAX_JOB_TRIGGER_NAME_LEN); 
     } 

     // Make sure this name is unique in case the same file name under different 
     // directories is being checked, or had a naming collision due to length truncation. 
     // If there is a conflict, keep incrementing a _# suffix on the name (being sure 
     // not to get too long), until we find a unique name. 
     int currentIndex = 1; 
     while (jobTriggerNameSet.add(jobTriggerName) == false) { 
      // If not our first time through, then strip off old numeric suffix 
      if (currentIndex > 1) { 
       jobTriggerName = jobTriggerName.substring(0, jobTriggerName.lastIndexOf('_')); 
      } 

      String numericSuffix = "_" + currentIndex++; 

      // If the numeric suffix would make the name too long, then make room for it. 
      if (jobTriggerName.length() > (MAX_JOB_TRIGGER_NAME_LEN - numericSuffix.length())) { 
       jobTriggerName = jobTriggerName.substring(0, (MAX_JOB_TRIGGER_NAME_LEN - numericSuffix.length())); 
      } 

      jobTriggerName += numericSuffix; 
     } 

     return jobTriggerName; 
    } 

    /** 
    * Overriden to ignore <em>wrapInUserTransaction</em> because shutdown() 
    * does not interact with the <code>Scheduler</code>. 
    */ 
    @Override 
    public void shutdown() { 
     // Since we have nothing to do, override base shutdown so don't 
     // get extranious UserTransactions. 
    } 

    private void processFile(JobFile jobFile) { 
     if (jobFile == null || !jobFile.getFileFound()) { 
      return; 
     } 


     try { 
      CustomXMLSchedulingDataProcessor processor = 
       new CustomXMLSchedulingDataProcessor(this.classLoadHelper); 

      processor.addJobGroupToNeverDelete(JOB_INITIALIZATION_PLUGIN_NAME); 
      processor.addTriggerGroupToNeverDelete(JOB_INITIALIZATION_PLUGIN_NAME); 

      processor.processFileAndScheduleJobs(
        jobFile.getFileName(), 
        jobFile.getFileName(), // systemId 
        getScheduler()); 
     } catch (Exception e) { 
      getLog().error("Error scheduling jobs: " + e.getMessage(), e); 
     } 
    } 

    public void processFile(String filePath) { 
     processFile((JobFile)jobFiles.get(filePath)); 
    } 

    /** 
    * @see org.quartz.jobs.FileScanListener#fileUpdated(java.lang.String) 
    */ 
    public void fileUpdated(String fileName) { 
     if (started) { 
      processFile(fileName); 
     } 
    } 

    class JobFile { 
     private String fileName; 

     // These are set by initialize() 
     private String filePath; 
     private String fileBasename; 
     private boolean fileFound; 

     protected JobFile(String fileName) throws SchedulerException { 
      this.fileName = fileName; 
      initialize(); 
     } 

     protected String getFileName() { 
      return fileName; 
     } 

     protected boolean getFileFound() { 
      return fileFound; 
     } 

     protected String getFilePath() { 
      return filePath; 
     } 

     protected String getFileBasename() { 
      return fileBasename; 
     } 

     private void initialize() throws SchedulerException { 
      InputStream f = null; 
      try { 
       String furl = null; 

       File file = new File(getFileName()); // files in filesystem 
       if (!file.exists()) { 
        URL url = classLoadHelper.getResource(getFileName()); 
        if(url != null) { 
         try { 
          furl = URLDecoder.decode(url.getPath(), "UTF-8"); 
         } catch (UnsupportedEncodingException e) { 
          furl = url.getPath(); 
         } 
         file = new File(furl); 
         try { 
          f = url.openStream(); 
         } catch (IOException ignor) { 
          // Swallow the exception 
         } 
        }   
       } else { 
        try {    
         f = new java.io.FileInputStream(file); 
        }catch (FileNotFoundException e) { 
         // ignore 
        } 
       } 

       if (f == null) { 
        if (isFailOnFileNotFound()) { 
         throw new SchedulerException(
          "File named '" + getFileName() + "' does not exist."); 
        } else { 
         getLog().warn("File named '" + getFileName() + "' does not exist."); 
        } 
       } else { 
        fileFound = true; 
       } 
       filePath = (furl != null) ? furl : file.getAbsolutePath(); 
       fileBasename = file.getName(); 
      } finally { 
       try { 
        if (f != null) { 
         f.close(); 
        } 
       } catch (IOException ioe) { 
        getLog().warn("Error closing jobs file " + getFileName(), ioe); 
       } 
      } 
     } 
    } 
} 

这样,你只需要在配置中使用这个插件,一切都将默认工作。

org.quartz.plugin.jobInitializer.class = com.level2.quartz.processor.plugin.XMLSchedulingDataProcessorPlugin