/** * 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.camel.maven.packaging; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Collections; import java.util.Properties; import org.apache.maven.model.Resource; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugin.logging.Log; import org.apache.maven.project.MavenProject; import org.apache.maven.project.MavenProjectHelper; import org.sonatype.plexus.build.incremental.BuildContext; import static org.apache.camel.maven.packaging.StringHelper.camelDashToTitle; /** * Analyses the Camel plugins in a project and generates extra descriptor information for easier auto-discovery in Camel. * * @goal generate-others-list */ public class PackageOtherMojo extends AbstractMojo { /** * The maven project. * * @parameter property="project" * @required * @readonly */ protected MavenProject project; /** * The output directory for generated components file * * @parameter default-value="${project.build.directory}/generated/camel/others" */ protected File otherOutDir; /** * The output directory for generated languages file * * @parameter default-value="${project.build.directory}/classes" */ protected File schemaOutDir; /** * Maven ProjectHelper. * * @component * @readonly */ private MavenProjectHelper projectHelper; /** * build context to check changed files and mark them for refresh (used for * m2e compatibility) * * @component * @readonly */ private BuildContext buildContext; /** * Execute goal. * * @throws MojoExecutionException execution of the main class or one of the * threads it generated failed. * @throws MojoFailureException something bad happened... */ public void execute() throws MojoExecutionException, MojoFailureException { prepareOthers(getLog(), project, projectHelper, otherOutDir, schemaOutDir, buildContext); } public static void prepareOthers(Log log, MavenProject project, MavenProjectHelper projectHelper, File otherOutDir, File schemaOutDir, BuildContext buildContext) throws MojoExecutionException { // are there any components, data formats or languages? for (Resource r : project.getBuild().getResources()) { File f = new File(r.getDirectory()); if (!f.exists()) { f = new File(project.getBasedir(), r.getDirectory()); } File comp = new File(f, "META-INF/services/org/apache/camel/component"); if (comp.exists() && comp.isDirectory()) { return; } File df = new File(f, "META-INF/services/org/apache/camel/dataformat"); if (df.exists() && df.isDirectory()) { return; } File lan = new File(f, "META-INF/services/org/apache/camel/language"); if (lan.exists() && lan.isDirectory()) { return; } } // okay none of those then this is a other kind of artifact // first we need to setup the output directory because the next check // can stop the build before the end and eclipse always needs to know about that directory if (projectHelper != null) { projectHelper.addResource(project, otherOutDir.getPath(), Collections.singletonList("**/other.properties"), Collections.emptyList()); } if (!PackageHelper.haveResourcesChanged(log, project, buildContext, "META-INF/services/org/apache/camel/component") && !PackageHelper.haveResourcesChanged(log, project, buildContext, "META-INF/services/org/apache/camel/dataformat") && !PackageHelper.haveResourcesChanged(log, project, buildContext, "META-INF/services/org/apache/camel/language")) { return; } String name = project.getArtifactId(); // strip leading camel- if (name.startsWith("camel-")) { name = name.substring(6); } try { // create json model OtherModel otherModel = new OtherModel(); otherModel.setName(name); otherModel.setGroupId(project.getGroupId()); otherModel.setArtifactId(project.getArtifactId()); otherModel.setVersion(project.getVersion()); otherModel.setDescription(project.getDescription()); if (project.getName() != null && project.getName().contains("(deprecated)")) { otherModel.setDeprecated("true"); } else { otherModel.setDeprecated("false"); } otherModel.setFirstVersion(project.getProperties().getProperty("firstVersion")); otherModel.setLabel(project.getProperties().getProperty("label")); String title = project.getProperties().getProperty("title"); if (title == null) { title = camelDashToTitle(name); } otherModel.setTitle(title); log.debug("Model " + otherModel); // write this to the directory File dir = schemaOutDir; dir.mkdirs(); File out = new File(dir, name + ".json"); OutputStream fos = buildContext.newFileOutputStream(out); String json = createJsonSchema(otherModel); fos.write(json.getBytes()); fos.close(); buildContext.refresh(out); log.debug("Generated " + out + " containing JSon schema for " + name + " other"); } catch (Exception e) { throw new MojoExecutionException("Error loading language model from camel-core. Reason: " + e, e); } // now create properties file File camelMetaDir = new File(otherOutDir, "META-INF/services/org/apache/camel/"); Properties properties = new Properties(); properties.put("name", name); properties.put("groupId", project.getGroupId()); properties.put("artifactId", project.getArtifactId()); properties.put("version", project.getVersion()); properties.put("projectName", project.getName()); if (project.getDescription() != null) { properties.put("projectDescription", project.getDescription()); } camelMetaDir.mkdirs(); File outFile = new File(camelMetaDir, "other.properties"); // check if the existing file has the same content, and if so then leave it as is so we do not write any changes // which can cause a re-compile of all the source code if (outFile.exists()) { try { Properties existing = new Properties(); InputStream is = new FileInputStream(outFile); existing.load(is); is.close(); // are the content the same? if (existing.equals(properties)) { log.debug("No changes detected"); return; } } catch (IOException e) { // ignore } } try { OutputStream os = buildContext.newFileOutputStream(outFile); properties.store(os, "Generated by camel-package-maven-plugin"); os.close(); log.info("Generated " + outFile); } catch (IOException e) { throw new MojoExecutionException("Failed to write properties to " + outFile + ". Reason: " + e, e); } } private static String createJsonSchema(OtherModel otherModel) { StringBuilder buffer = new StringBuilder("{"); // language model buffer.append("\n \"other\": {"); buffer.append("\n \"name\": \"").append(otherModel.getName()).append("\","); buffer.append("\n \"kind\": \"").append("other").append("\","); if (otherModel.getTitle() != null) { buffer.append("\n \"title\": \"").append(otherModel.getTitle()).append("\","); } if (otherModel.getDescription() != null) { buffer.append("\n \"description\": \"").append(otherModel.getDescription()).append("\","); } buffer.append("\n \"deprecated\": \"").append(otherModel.getDeprecated()).append("\","); if (otherModel.getFirstVersion() != null) { buffer.append("\n \"firstVersion\": \"").append(otherModel.getFirstVersion()).append("\","); } if (otherModel.getLabel() != null) { buffer.append("\n \"label\": \"").append(otherModel.getLabel()).append("\","); } buffer.append("\n \"groupId\": \"").append(otherModel.getGroupId()).append("\","); buffer.append("\n \"artifactId\": \"").append(otherModel.getArtifactId()).append("\","); buffer.append("\n \"version\": \"").append(otherModel.getVersion()).append("\""); buffer.append("\n }"); buffer.append("\n}"); return buffer.toString(); } private static class OtherModel { private String name; private String title; private String description; private String deprecated; private String firstVersion; private String label; private String groupId; private String artifactId; private String version; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getDeprecated() { return deprecated; } public void setDeprecated(String deprecated) { this.deprecated = deprecated; } public String getFirstVersion() { return firstVersion; } public void setFirstVersion(String firstVersion) { this.firstVersion = firstVersion; } public String getLabel() { return label; } public void setLabel(String label) { this.label = label; } public String getGroupId() { return groupId; } public void setGroupId(String groupId) { this.groupId = groupId; } public String getArtifactId() { return artifactId; } public void setArtifactId(String artifactId) { this.artifactId = artifactId; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } @Override public String toString() { return "OtherModel[" + "name='" + name + '\'' + ", title='" + title + '\'' + ", description='" + description + '\'' + ", label='" + label + '\'' + ", groupId='" + groupId + '\'' + ", artifactId='" + artifactId + '\'' + ", version='" + version + '\'' + ']'; } } }