开发者

How to recursively parse xsd files to generate a list of included schemas for incremental build in Maven?

I have a Maven project that uses the jaxb2-maven-plugin to compile some xsd files. It uses the staleFile to determine whether or not any of the ref开发者_Go百科erenced schemaFiles have been changed. Unfortunately, the xsd files in question use <xs:include schemaLocation="../relative/path.xsd"/> tags to include other schema files that are not listed in the schemaFile argument so the staleFile calculation in the plugin doesn't accurately detect when things need to be actually recompiled. This winds up breaking incremental builds as the included schemas evolve.

Obviously, one solution would be to list all the recursively referenced files in the execution's schemaFile. However, there are going to be cases where developers don't do this and break the build. I'd like instead to automate the generation of this list in some way.

One approach that comes to mind would be to somehow parse the top-level XSD files and then either sets a property or outputs a file that I can then pass into the schemaFile parameter or schemaFiles parameter. The Groovy gmaven plugin seems like it might be a natural way to embed that functionality right into the POM. But I'm not familiar enough with Groovy to get started.

Can anyone provide some sample code? Or offer an alternative implementation/solution?

Thanks!


Not sure how you'd integrate it into your Maven build -- Maven isn't really my thing :-(

However, if you have the path to an xsd file, you should be able to get the files it references by doing something like:

def rootXsd = new File( 'path/to/xsd' )
def refs = new XmlSlurper().parse( rootXsd ).depthFirst().findAll { it.name()=='include' }.@schemaLocation*.text()
println "$rootXsd references $refs"

So refs is a list of Strings which should be the paths to the included xsds


Based on tim_yates's answer, the following is a workable solution, which you may have to customize based on how you are configuring the jaxb2 plugin.

Configure a gmaven-plugin execution early in the lifecycle (e.g., in the initialize phase) that runs with the following configuration...

Start with a function to collect File objects of referenced schemas (this is a refinement of Tim's answer):

def findRefs { f ->
    def relPaths = new XmlSlurper().parse(f).depthFirst().findAll {
        it.name()=='include'
    }*.@schemaLocation*.text()
    relPaths.collect { new File(f.absoluteFile.parent + "/" + it).canonicalFile }
}

Wrap that in a function that iterates on the results until all children are found:

def recursiveFindRefs = { schemaFiles ->
    def outputs = [] as Set
    def inputs  = schemaFiles as Queue
    // Breadth-first examine all refs in all schema files
    while (xsd = inputs.poll()) {
        outputs << xsd
        findRefs(xsd).each {
            if (!outputs.contains(it)) inputs.add(it)
        }
    }
    outputs
}

The real magic then comes in when you parse the Maven project to determine what to do. First, find the JAXB plugin:

jaxb = project.build.plugins.find { it.artifactId == 'jaxb2-maven-plugin' }

Then, parse each execution of that plugin (if you have multiple). The code assumes that each execution sets schemaDirectory, schemaFiles and staleFile (i.e., does not use the defaults!) and that you are not using schemaListFileName:

jaxb.executions.each { ex ->
    log.info("Processing jaxb execution $ex")
    // Extract the schema locations; the configuration is an Xpp3Dom
    ex.configuration.children.each { conf ->
        switch (conf.name) {
            case "schemaDirectory":
                schemaDirectory = conf.value
                break
            case "schemaFiles":
                schemaFiles = conf.value.split(/,\s*/)
                break
            case "staleFile":
                staleFile = conf.value
                break
        }
    }

Finally, we can open the schemaFiles, parse them using the functions we've defined earlier:

    def schemaHandles = schemaFiles.collect { new File("${project.basedir}/${schemaDirectory}", it) }
    def allSchemaHandles = recursiveFindRefs(schemaHandles)

...and compare their last modified times against the stale file's modification time, unlinking the stale file if necessary.

    def maxLastModified = allSchemaHandles.collect {
            it.lastModified()
        }.max()
    def staleHandle = new File(staleFile)
    if (staleHandle.lastModified() < maxLastModified) {
        log.info("  New schemas detected; unlinking $staleFile.")
        staleHandle.delete()
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜