/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.cxf.maven_plugin; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.DependencyResolutionRequiredException; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.artifact.resolver.ArtifactResolutionRequest; import org.apache.maven.artifact.resolver.ArtifactResolutionResult; import org.apache.maven.execution.MavenSession; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; import org.apache.maven.repository.RepositorySystem; import org.codehaus.plexus.archiver.jar.JarArchiver; import org.codehaus.plexus.archiver.jar.Manifest; import org.codehaus.plexus.archiver.jar.Manifest.Attribute; import org.codehaus.plexus.util.cli.CommandLineException; import org.codehaus.plexus.util.cli.CommandLineUtils; import org.codehaus.plexus.util.cli.Commandline; import org.codehaus.plexus.util.cli.StreamConsumer; import org.sonatype.plexus.build.incremental.BuildContext; /** * @description CXF XSD To Java Tool */ public abstract class AbstractXSDToJavaMojo extends AbstractMojo { @Component protected MavenProject project; @Parameter XsdOption xsdOptions[]; /** * Directory in which the "DONE" markers are saved that */ @Parameter(defaultValue = "${project.build.directory}/cxf-xsd-plugin-markers", property = "cxf.markerDirectory") File markerDirectory; /** * The extension artifacts that will be retrieved and added to the classpath. */ @Parameter private List<String> extensions; @Component private BuildContext buildContext; @Component private RepositorySystem repository; @Component private MavenSession session; /** * Allows running in a separate process. */ @Parameter(defaultValue = "false") private boolean fork; /** * Sets the Java executable to use when fork parameter is <code>true</code>. */ @Parameter(defaultValue = "${java.home}/bin/java") private String javaExecutable; /** * Sets the JVM arguments (i.e. <code>-Xms128m -Xmx128m</code>) if fork is set to <code>true</code>. */ @Parameter(property = "cxf.xjc.jvmArgs") private String additionalJvmArgs; /** * The plugin dependencies, needed for the fork mode. */ @Parameter(property = "plugin.artifacts", readonly = true, required = true) private List<Artifact> pluginArtifacts; abstract String getOutputDir(); private URI mapLocation(String s) throws MojoExecutionException { try { File file = new File(s).getAbsoluteFile(); URI uri; if (file.exists()) { uri = file.toURI(); } else { file = new File(project.getBasedir(), s).getAbsoluteFile(); if (file.exists()) { uri = file.toURI(); } else { try { uri = new URI(s); } catch (URISyntaxException use) { file = new File(s).getAbsoluteFile(); if (file.getParentFile().exists()) { return file.toURI(); } else { throw use; } } } } if ("classpath".equals(uri.getScheme())) { URL url = Thread.currentThread().getContextClassLoader() .getResource(s.substring(10)); if (url == null) { url = Thread.currentThread().getContextClassLoader() .getResource(s.substring(11)); } if (url != null) { uri = url.toURI(); } } return uri; } catch (URISyntaxException e1) { throw new MojoExecutionException("Could not map " + s, e1); } } public void execute() throws MojoExecutionException { String outputDir = getOutputDir(); File outputDirFile = new File(outputDir); outputDirFile.mkdirs(); markerDirectory.mkdirs(); boolean result = true; if (xsdOptions == null) { throw new MojoExecutionException("Must specify xsdOptions"); } for (int x = 0; x < xsdOptions.length; x++) { ClassLoader origLoader = Thread.currentThread().getContextClassLoader(); final String[] xsdFiles = getXsdFiles(xsdOptions[x].getXsdDir(), xsdOptions[x].getXsd()); for (String xsdFile : xsdFiles) { try { URI xsdURI = mapLocation(xsdFile); URI basedir = project.getBasedir().toURI(); String doneFileName = xsdURI.toString(); if (doneFileName.startsWith(basedir.toString())) { doneFileName = doneFileName.substring(basedir.toString().length()); } doneFileName = doneFileName.replace('?', '_') .replace('&', '_').replace('/', '_').replace('\\', '_') .replace(':', '_').replace('!', '_'); // If URL to WSDL, replace ? and & since they're invalid chars for file names File doneFile = new File(markerDirectory, "." + doneFileName + ".DONE"); long srctimestamp = 0; if ("file".equals(xsdURI.getScheme())) { srctimestamp = new File(xsdURI).lastModified(); } else { try { srctimestamp = xsdURI.toURL().openConnection().getDate(); } catch (Exception e) { //ignore } } if (xsdOptions[x].getBindingFile() != null) { URI bindingURI = mapLocation(xsdOptions[x].getBindingFile()); if ("file".equals(bindingURI.getScheme())) { long bts = new File(bindingURI).lastModified(); if (bts > srctimestamp) { srctimestamp = bts; } } } boolean doWork = false; if (!doneFile.exists()) { doWork = true; } else if (srctimestamp > doneFile.lastModified()) { doWork = true; } else { File files[] = xsdOptions[x].getDependencies(); if (files != null) { for (int z = 0; z < files.length; ++z) { if (files[z].lastModified() > doneFile.lastModified()) { doWork = true; } } } } if (doWork) { try { File files[] = xsdOptions[x].getDependencies(); if (files != null) { for (int z = 0; z < files.length; ++z) { if (files[z].lastModified() > doneFile.lastModified()) { buildContext.removeMessages(files[z]); } } } removeMessages(xsdFile); removeMessages(xsdOptions[x].getBindingFile()); int i = run(xsdOptions[x], xsdFile, outputDir); if (i == 0) { doneFile.delete(); doneFile.createNewFile(); } File dirs[] = xsdOptions[x].getDeleteDirs(); if (dirs != null) { for (int idx = 0; idx < dirs.length; ++idx) { result = result && deleteDir(dirs[idx]); } } buildContext.refresh(outputDirFile); } catch (Exception e) { throw new MojoExecutionException(e.getMessage(), e); } } if (!result) { throw new MojoExecutionException("Could not delete redundant dirs"); } } finally { Thread.currentThread().setContextClassLoader(origLoader); } } } } private String[] getXsdFiles(String xsdDir, String xsd) throws MojoExecutionException { final String[] xsdFiles; if (xsdDir != null && !xsdDir.isEmpty()) { File dir = new File(xsdDir); if (!dir.isDirectory()) { throw new MojoExecutionException("Error, xsdDir \"" + xsdDir + "\" does not exist."); } String[] fileList = dir.list(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.endsWith(".xsd"); } }); if (fileList == null || fileList.length == 0) { throw new MojoExecutionException("Error, xsdDir \"" + xsdDir + "\" does not contain any *.xsd files."); } xsdFiles = new String[fileList.length]; for (int i = 0; i < fileList.length; ++i) { xsdFiles[i] = xsdDir + (xsdDir.endsWith(File.separator) ? "" : File.separator) + fileList[i]; } } else { xsdFiles = new String[1]; xsdFiles[0] = xsd; } return xsdFiles; } private List<File> resolve(String artifactDescriptor) throws MojoExecutionException { String[] s = artifactDescriptor.split(":"); String type = s.length >= 4 ? s[3] : "jar"; Artifact artifact = repository.createArtifact(s[0], s[1], s[2], type); ArtifactResolutionRequest request = new ArtifactResolutionRequest(); request.setArtifact(artifact); request.setResolveRoot(true).setResolveTransitively(true); request.setServers(session.getRequest().getServers()); request.setMirrors(session.getRequest().getMirrors()); request.setProxies(session.getRequest().getProxies()); request.setLocalRepository(session.getLocalRepository()); List<ArtifactRepository> r = new ArrayList<ArtifactRepository>(); r.addAll(project.getPluginArtifactRepositories()); r.addAll(project.getRemoteArtifactRepositories()); r.addAll(session.getRequest().getRemoteRepositories()); r.addAll(session.getRequest().getPluginArtifactRepositories()); request.setRemoteRepositories(r); ArtifactResolutionResult result = repository.resolve(request); List<File> files = new ArrayList<File>(); for (Artifact a : result.getArtifacts()) { if (a.getFile() == null) { throw new MojoExecutionException("Unable to resolve " + a.toString() + " while resolving " + artifactDescriptor); } files.add(a.getFile()); } if (!files.contains(artifact.getFile())) { files.add(artifact.getFile()); } return files; } protected List<String> getClasspathElements() throws DependencyResolutionRequiredException { return project.getCompileClasspathElements(); } private int run(XsdOption option, String xsdFile, String outputDir) throws Exception { if (!fork) { String[] args = getArguments(option, outputDir); this.getLog().debug("Args: " + Arrays.asList(args)); XJCErrorListener listener = new XJCErrorListener(buildContext); int i = new XSDToJavaRunner(args, listener, new File(xsdFile), getClasspathElements()).run(); if (i != 0 && listener.getFirstError() != null) { throw listener.getFirstError(); } return i; } return runForked(option, outputDir); } private void removeMessages(String file) throws MojoExecutionException { if (file == null) { return; } URI location = mapLocation(file); if ("file".equals(location.getScheme())) { File f = new File(location); if (f.exists()) { buildContext.removeMessages(f); } } } private String[] getArguments(XsdOption option, String outputDir) throws MojoExecutionException, MalformedURLException { List<URL> newCp = new ArrayList<URL>(); List<String> list = new ArrayList<String>(); if (extensions != null && extensions.size() > 0) { try { for (String ext : extensions) { for (File file : resolve(ext)) { list.add("-classpath"); list.add(file.toURI().toURL().toExternalForm()); newCp.add(file.toURI().toURL()); } } } catch (Exception ex) { throw new MojoExecutionException("Could not download extension artifact", ex); } } if (!newCp.isEmpty()) { Thread.currentThread() .setContextClassLoader(new URLClassLoader(newCp.toArray(new URL[newCp.size()]), Thread.currentThread().getContextClassLoader())); } if (option.getPackagename() != null) { list.add("-p"); list.add(option.getPackagename()); } if (option.getBindingFile() != null) { list.add("-b"); list.add(mapLocation(option.getBindingFile()).toString()); } if (option.getCatalog() != null) { list.add("-catalog"); list.add(option.getCatalog()); } if (option.isExtension()) { list.add("-extension"); } if (option.getExtensionArgs() != null) { list.addAll(option.getExtensionArgs()); } if (getLog().isDebugEnabled()) { list.add("-verbose"); } String[] xsdFiles = getXsdFiles(option.getXsdDir(), option.getXsd()); for (String xsdFile : xsdFiles) { list.add("-d"); list.add(outputDir); list.add(mapLocation(xsdFile).toString()); } return list.toArray(new String[list.size()]); } private boolean deleteDir(File f) { if (f.isDirectory()) { File files[] = f.listFiles(); for (int idx = 0; idx < files.length; ++idx) { deleteDir(files[idx]); } } if (f.exists()) { return f.delete(); } return true; } private File getJavaExecutable() throws IOException { String exe = isWindows() && !javaExecutable.endsWith(".exe") ? ".exe" : ""; File javaExe = new File(javaExecutable + exe); if (!javaExe.isFile()) { throw new IOException( "The java executable '" + javaExe + "' doesn't exist or is not a file." + "Verify the <javaExecutable/> parameter."); } return javaExe; } private boolean isWindows() { String osName = System.getProperty("os.name"); if (osName == null) { return false; } return osName.startsWith("Windows"); } private int runForked(XsdOption option, String outputDir) throws Exception { String[] args = getArguments(option, outputDir); Commandline cmd = new Commandline(); cmd.getShell().setQuotedArgumentsEnabled(true); // for JVM args cmd.setWorkingDirectory(project.getBuild().getDirectory()); try { cmd.setExecutable(getJavaExecutable().getAbsolutePath()); } catch (IOException e) { getLog().debug(e); throw new MojoExecutionException(e.getMessage(), e); } cmd.createArg().setLine(additionalJvmArgs); File file = null; try { //file = new File("Y:\\Users\\dkulp\\tmp\\test.jar"); file = File.createTempFile("cxf-xjc-plugin", ".jar"); file.deleteOnExit(); JarArchiver jar = new JarArchiver(); jar.setDestFile(file.getAbsoluteFile()); Manifest manifest = new Manifest(); Attribute attr = new Attribute(); attr.setName("Class-Path"); StringBuilder b = new StringBuilder(8000); for (String cp : getClasspathElements()) { URI uri = mapLocation(cp); if (uri != null) { b.append(uri.toString()).append(' '); } } for (Artifact a : pluginArtifacts) { b.append(a.getFile().toURI().toURL().toExternalForm()).append(' '); } attr.setValue(b.toString()); manifest.getMainSection().addConfiguredAttribute(attr); attr = new Attribute(); attr.setName("Main-Class"); attr.setValue(XSDToJavaRunner.class.getName()); manifest.getMainSection().addConfiguredAttribute(attr); if (getLog().isDebugEnabled()) { getLog().debug("Manifest: " + manifest); } jar.addConfiguredManifest(manifest); jar.createArchive(); cmd.createArg().setValue("-jar"); String tmpFilePath = file.getAbsolutePath(); if (tmpFilePath.contains(" ")) { //ensure the path is in double quotation marks if the path contain space tmpFilePath = "\"" + tmpFilePath + "\""; } cmd.createArg().setValue(tmpFilePath); } catch (Exception e1) { throw new MojoExecutionException("Could not create runtime jar", e1); } cmd.addArguments(args); StreamConsumer out = new StreamConsumer() { File file; int severity; int linenum; int column; StringBuilder message = new StringBuilder(); public void consumeLine(String line) { if (getLog().isDebugEnabled()) { getLog().debug(line); } if (line.startsWith("DONE")) { buildContext.addMessage(file, linenum, column, message.toString(), severity, null); } else if (line.startsWith("MSG: ") || line.startsWith("ERROR: ") || line.startsWith("WARNING: ")) { file = new File(line.substring(line.indexOf(' ')).trim()); String type = line.substring(0, line.indexOf(':')); if (type.contains("ERROR")) { severity = BuildContext.SEVERITY_ERROR; } else if (type.contains("WARNING")) { severity = BuildContext.SEVERITY_WARNING; } else { severity = 0; } linenum = 0; column = 0; message.setLength(0); } else if (line.startsWith("Col: ")) { column = Integer.parseInt(line.substring(line.indexOf(' ')).trim()); } else if (line.startsWith("Line: ")) { linenum = Integer.parseInt(line.substring(line.indexOf(' ')).trim()); } else if (line.startsWith("Severity: ")) { severity = Integer.parseInt(line.substring(line.indexOf(' ')).trim()); } else { message.append(line).append('\n'); } } }; int exitCode; try { exitCode = CommandLineUtils.executeCommandLine(cmd, out, out); } catch (CommandLineException e) { getLog().debug(e); throw new MojoExecutionException(e.getMessage(), e); } String cmdLine = CommandLineUtils.toString(cmd.getCommandline()); if (exitCode != 0) { StringBuffer msg = new StringBuffer("\nExit code: "); msg.append(exitCode); msg.append('\n'); msg.append("Command line was: ").append(cmdLine).append('\n').append('\n'); throw new MojoExecutionException(msg.toString()); } file.delete(); return 0; } }