Quartz没有识别quartz jar文件中存在的模式job_scheduling_data_2_0.xsd

时间:2015-06-02 21:56:04

标签: spring quartz-scheduler

我在服务器启动时遇到异常。

我使用的是带有弹簧3.2的石英2.2.21。 我启用了石英插件(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>.

1 个答案:

答案 0 :(得分:0)

我有同样的问题。我正在使用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,只是覆盖了上面类的实例化。

                    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