blob: 3e306c6b8349eb955b59d46a6bf0fb32dd13ffce [file] [log] [blame]
/*
* 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.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;
import static org.apache.camel.maven.packaging.PackageHelper.loadText;
import static org.w3c.dom.Node.ELEMENT_NODE;
/**
* Prepares the Karaf provider camel catalog to include component it supports
*/
@Mojo(name = "prepare-catalog-karaf", threadSafe = true)
public class PrepareCatalogKarafMojo extends AbstractMojo {
public static final int BUFFER_SIZE = 128 * 1024;
/**
* The maven project.
*/
@Parameter(property = "project", required = true, readonly = true)
protected MavenProject project;
/**
* The output directory for components catalog
*/
@Parameter(defaultValue = "${project.build.directory}/classes/org/apache/camel/catalog/karaf/components")
protected File componentsOutDir;
/**
* The output directory for dataformats catalog
*/
@Parameter(defaultValue = "${project.build.directory}/classes/org/apache/camel/catalog/karaf/dataformats")
protected File dataFormatsOutDir;
/**
* The output directory for languages catalog
*/
@Parameter(defaultValue = "${project.build.directory}/classes/org/apache/camel/catalog/karaf/languages")
protected File languagesOutDir;
/**
* The output directory for others catalog
*/
@Parameter(defaultValue = "${project.build.directory}/classes/org/apache/camel/catalog/karaf/others")
protected File othersOutDir;
/**
* The karaf features directory
*/
@Parameter(defaultValue = "${project.build.directory}/../../../platforms/karaf/features/src/main/resources/")
protected File featuresDir;
/**
* The components directory where all the Apache Camel components are
*/
@Parameter(defaultValue = "${project.build.directory}/../../../components")
protected File componentsDir;
/**
* The camel-core directory
*/
@Parameter(defaultValue = "${project.build.directory}/../../../core/camel-core-engine")
protected File coreDir;
/**
* The camel-base directory
*/
@Parameter(defaultValue = "${project.build.directory}/../../../core/camel-base")
protected File baseDir;
/**
* Maven ProjectHelper.
*/
@Component
private MavenProjectHelper projectHelper;
/**
* Execute goal.
*
* @throws MojoExecutionException execution of the main class or one of the
* threads it generated failed.
* @throws MojoFailureException something bad happened...
*/
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
Set<String> features = findKarafFeatures();
executeComponents(features);
executeDataFormats(features);
executeLanguages(features);
executeOthers(features);
}
protected void executeComponents(Set<String> features) throws MojoExecutionException, MojoFailureException {
getLog().info("Copying all Camel component json descriptors");
// lets use sorted set/maps
Set<File> jsonFiles = new TreeSet<>();
Set<File> componentFiles = new TreeSet<>();
// find all json files in components and camel-core
if (componentsDir != null && componentsDir.isDirectory()) {
File[] components = componentsDir.listFiles();
if (components != null) {
for (File dir : components) {
if (dir.isDirectory() && !"target".equals(dir.getName())) {
File target = new File(dir, "target/classes");
// the directory must be in the list of known features
if (!features.contains(dir.getName())) {
continue;
}
// special for some components which is in a sub dir
if ("camel-as2".equals(dir.getName())) {
target = new File(dir, "camel-as2-component/target/classes");
} else if ("camel-box".equals(dir.getName())) {
target = new File(dir, "camel-box-component/target/classes");
} else if ("camel-salesforce".equals(dir.getName())) {
target = new File(dir, "camel-salesforce-component/target/classes");
} else if ("camel-servicenow".equals(dir.getName())) {
target = new File(dir, "camel-servicenow-component/target/classes");
} else {
// this module must be active with a source folder
File src = new File(dir, "src");
boolean active = src.isDirectory() && src.exists();
if (!active) {
continue;
}
}
findComponentFilesRecursive(target, jsonFiles, componentFiles, new CamelComponentsFileFilter());
}
}
}
}
if (coreDir != null && coreDir.isDirectory()) {
File target = new File(coreDir, "target/classes");
findComponentFilesRecursive(target, jsonFiles, componentFiles, new CamelComponentsFileFilter());
}
getLog().info("Found " + componentFiles.size() + " component.properties files");
getLog().info("Found " + jsonFiles.size() + " component json files");
// make sure to create out dir
componentsOutDir.mkdirs();
for (File file : jsonFiles) {
File to = new File(componentsOutDir, file.getName());
try {
copyFile(file, to);
} catch (IOException e) {
throw new MojoFailureException("Cannot copy file from " + file + " -> " + to, e);
}
}
File all = new File(componentsOutDir, "../components.properties");
try {
FileOutputStream fos = new FileOutputStream(all, false);
String[] names = componentsOutDir.list();
List<String> components = new ArrayList<>();
// sort the names
for (String name : names) {
if (name.endsWith(".json")) {
// strip out .json from the name
String componentName = name.substring(0, name.length() - 5);
components.add(componentName);
}
}
Collections.sort(components);
for (String name : components) {
fos.write(name.getBytes());
fos.write("\n".getBytes());
}
fos.close();
} catch (IOException e) {
throw new MojoFailureException("Error writing to file " + all);
}
}
protected void executeDataFormats(Set<String> features) throws MojoExecutionException, MojoFailureException {
getLog().info("Copying all Camel dataformat json descriptors");
// lets use sorted set/maps
Set<File> jsonFiles = new TreeSet<>();
Set<File> dataFormatFiles = new TreeSet<>();
// find all data formats from the components directory
if (componentsDir != null && componentsDir.isDirectory()) {
File[] dataFormats = componentsDir.listFiles();
if (dataFormats != null) {
for (File dir : dataFormats) {
if (dir.isDirectory() && !"target".equals(dir.getName())) {
// the directory must be in the list of known features
if (!features.contains(dir.getName())) {
continue;
}
// this module must be active with a source folder
File src = new File(dir, "src");
boolean active = src.isDirectory() && src.exists();
if (!active) {
continue;
}
File target = new File(dir, "target/classes");
findDataFormatFilesRecursive(target, jsonFiles, dataFormatFiles, new CamelDataFormatsFileFilter());
}
}
}
}
if (coreDir != null && coreDir.isDirectory()) {
File target = new File(coreDir, "target/classes");
findDataFormatFilesRecursive(target, jsonFiles, dataFormatFiles, new CamelDataFormatsFileFilter());
}
getLog().info("Found " + dataFormatFiles.size() + " dataformat.properties files");
getLog().info("Found " + jsonFiles.size() + " dataformat json files");
// make sure to create out dir
dataFormatsOutDir.mkdirs();
for (File file : jsonFiles) {
File to = new File(dataFormatsOutDir, file.getName());
try {
copyFile(file, to);
} catch (IOException e) {
throw new MojoFailureException("Cannot copy file from " + file + " -> " + to, e);
}
}
File all = new File(dataFormatsOutDir, "../dataformats.properties");
try {
FileOutputStream fos = new FileOutputStream(all, false);
String[] names = dataFormatsOutDir.list();
List<String> dataFormats = new ArrayList<>();
// sort the names
for (String name : names) {
if (name.endsWith(".json")) {
// strip out .json from the name
String dataFormatName = name.substring(0, name.length() - 5);
dataFormats.add(dataFormatName);
}
}
Collections.sort(dataFormats);
for (String name : dataFormats) {
fos.write(name.getBytes());
fos.write("\n".getBytes());
}
fos.close();
} catch (IOException e) {
throw new MojoFailureException("Error writing to file " + all);
}
}
protected void executeLanguages(Set<String> features) throws MojoExecutionException, MojoFailureException {
getLog().info("Copying all Camel language json descriptors");
// lets use sorted set/maps
Set<File> jsonFiles = new TreeSet<>();
Set<File> languageFiles = new TreeSet<>();
// find all languages from the components directory
if (componentsDir != null && componentsDir.isDirectory()) {
File[] languages = componentsDir.listFiles();
if (languages != null) {
for (File dir : languages) {
// the directory must be in the list of known features (or known languages)
if (!features.contains(dir.getName())
&& !dir.getName().equals("camel-bean")
&& !dir.getName().equals("camel-xpath")) {
continue;
}
// this module must be active with a source folder
File src = new File(dir, "src");
boolean active = src.isDirectory() && src.exists();
if (!active) {
continue;
}
if (dir.isDirectory() && !"target".equals(dir.getName())) {
File target = new File(dir, "target/classes");
findLanguageFilesRecursive(target, jsonFiles, languageFiles, new CamelLanguagesFileFilter());
}
}
}
}
if (baseDir != null && baseDir.isDirectory()) {
File target = new File(baseDir, "target/classes");
findLanguageFilesRecursive(target, jsonFiles, languageFiles, new CamelLanguagesFileFilter());
// also look in camel-jaxp
target = new File(baseDir, "../camel-jaxp/target/classes");
findLanguageFilesRecursive(target, jsonFiles, languageFiles, new CamelLanguagesFileFilter());
}
getLog().info("Found " + languageFiles.size() + " language.properties files");
getLog().info("Found " + jsonFiles.size() + " language json files");
// make sure to create out dir
languagesOutDir.mkdirs();
for (File file : jsonFiles) {
File to = new File(languagesOutDir, file.getName());
try {
copyFile(file, to);
} catch (IOException e) {
throw new MojoFailureException("Cannot copy file from " + file + " -> " + to, e);
}
}
File all = new File(languagesOutDir, "../languages.properties");
try {
FileOutputStream fos = new FileOutputStream(all, false);
String[] names = languagesOutDir.list();
List<String> languages = new ArrayList<>();
// sort the names
for (String name : names) {
if (name.endsWith(".json")) {
// strip out .json from the name
String languageName = name.substring(0, name.length() - 5);
languages.add(languageName);
}
}
Collections.sort(languages);
for (String name : languages) {
fos.write(name.getBytes());
fos.write("\n".getBytes());
}
fos.close();
} catch (IOException e) {
throw new MojoFailureException("Error writing to file " + all);
}
}
protected void executeOthers(Set<String> features) throws MojoExecutionException, MojoFailureException {
getLog().info("Copying all Camel other json descriptors");
// lets use sorted set/maps
Set<File> jsonFiles = new TreeSet<>();
Set<File> otherFiles = new TreeSet<>();
// find all languages from the components directory
if (componentsDir != null && componentsDir.isDirectory()) {
File[] others = componentsDir.listFiles();
if (others != null) {
for (File dir : others) {
// the directory must be in the list of known features
if (!features.contains(dir.getName())) {
continue;
}
// skip these special cases
boolean special = "camel-core-osgi".equals(dir.getName())
|| "camel-core-xml".equals(dir.getName())
|| "camel-http-common".equals(dir.getName())
|| "camel-jetty-common".equals(dir.getName());
boolean special2 = "camel-as2".equals(dir.getName())
|| "camel-box".equals(dir.getName())
|| "camel-olingo2".equals(dir.getName())
|| "camel-olingo4".equals(dir.getName())
|| "camel-servicenow".equals(dir.getName())
|| "camel-salesforce".equals(dir.getName());
boolean special3 = "camel-debezium-common".equals(dir.getName());
if (special || special2 || special3) {
continue;
}
// this module must be active with a source folder
File src = new File(dir, "src");
boolean active = src.isDirectory() && src.exists();
if (!active) {
continue;
}
if (dir.isDirectory() && !"target".equals(dir.getName())) {
File target = new File(dir, "target/classes");
findOtherFilesRecursive(target, jsonFiles, otherFiles, new CamelOthersFileFilter());
}
}
}
}
getLog().info("Found " + otherFiles.size() + " other.properties files");
getLog().info("Found " + jsonFiles.size() + " other json files");
// make sure to create out dir
othersOutDir.mkdirs();
for (File file : jsonFiles) {
File to = new File(othersOutDir, file.getName());
try {
copyFile(file, to);
} catch (IOException e) {
throw new MojoFailureException("Cannot copy file from " + file + " -> " + to, e);
}
}
File all = new File(othersOutDir, "../others.properties");
try {
FileOutputStream fos = new FileOutputStream(all, false);
String[] names = othersOutDir.list();
List<String> others = new ArrayList<>();
// sort the names
for (String name : names) {
if (name.endsWith(".json")) {
// strip out .json from the name
String otherName = name.substring(0, name.length() - 5);
others.add(otherName);
}
}
Collections.sort(others);
for (String name : others) {
fos.write(name.getBytes());
fos.write("\n".getBytes());
}
fos.close();
} catch (IOException e) {
throw new MojoFailureException("Error writing to file " + all);
}
}
private void findComponentFilesRecursive(File dir, Set<File> found, Set<File> components, FileFilter filter) {
File[] files = dir.listFiles(filter);
if (files != null) {
for (File file : files) {
// skip files in root dirs as Camel does not store information there but others may do
boolean rootDir = "classes".equals(dir.getName()) || "META-INF".equals(dir.getName());
boolean jsonFile = !rootDir && file.isFile() && file.getName().endsWith(".json");
boolean componentFile = !rootDir && file.isFile() && file.getName().equals("component.properties");
if (jsonFile) {
found.add(file);
} else if (componentFile) {
components.add(file);
} else if (file.isDirectory()) {
findComponentFilesRecursive(file, found, components, filter);
}
}
}
}
private void findDataFormatFilesRecursive(File dir, Set<File> found, Set<File> dataFormats, FileFilter filter) {
File[] files = dir.listFiles(filter);
if (files != null) {
for (File file : files) {
// skip files in root dirs as Camel does not store information there but others may do
boolean rootDir = "classes".equals(dir.getName()) || "META-INF".equals(dir.getName());
boolean jsonFile = !rootDir && file.isFile() && file.getName().endsWith(".json");
boolean dataFormatFile = !rootDir && file.isFile() && file.getName().equals("dataformat.properties");
if (jsonFile) {
found.add(file);
} else if (dataFormatFile) {
dataFormats.add(file);
} else if (file.isDirectory()) {
findDataFormatFilesRecursive(file, found, dataFormats, filter);
}
}
}
}
private void findLanguageFilesRecursive(File dir, Set<File> found, Set<File> languages, FileFilter filter) {
File[] files = dir.listFiles(filter);
if (files != null) {
for (File file : files) {
// skip files in root dirs as Camel does not store information there but others may do
boolean rootDir = "classes".equals(dir.getName()) || "META-INF".equals(dir.getName());
boolean jsonFile = !rootDir && file.isFile() && file.getName().endsWith(".json");
boolean languageFile = !rootDir && file.isFile() && file.getName().equals("language.properties");
if (jsonFile) {
found.add(file);
} else if (languageFile) {
languages.add(file);
} else if (file.isDirectory()) {
findLanguageFilesRecursive(file, found, languages, filter);
}
}
}
}
private void findOtherFilesRecursive(File dir, Set<File> found, Set<File> others, FileFilter filter) {
File[] files = dir.listFiles(filter);
if (files != null) {
for (File file : files) {
// skip files in root dirs as Camel does not store information there but others may do
boolean rootDir = "classes".equals(dir.getName()) || "META-INF".equals(dir.getName());
boolean jsonFile = rootDir && file.isFile() && file.getName().endsWith(".json");
boolean otherFile = !rootDir && file.isFile() && file.getName().equals("other.properties");
if (jsonFile) {
found.add(file);
} else if (otherFile) {
others.add(file);
} else if (file.isDirectory()) {
findOtherFilesRecursive(file, found, others, filter);
}
}
}
}
private class CamelComponentsFileFilter implements FileFilter {
@Override
public boolean accept(File pathname) {
if (pathname.isDirectory() && pathname.getName().equals("model")) {
// do not check the camel-core model packages as there is no components there
return false;
}
if (pathname.isFile() && pathname.getName().endsWith(".json")) {
// must be a components json file
try {
String json = loadText(new FileInputStream(pathname));
return json != null && json.contains("\"kind\": \"component\"");
} catch (IOException e) {
// ignore
}
}
return pathname.isDirectory() || (pathname.isFile() && pathname.getName().equals("component.properties"));
}
}
private class CamelDataFormatsFileFilter implements FileFilter {
@Override
public boolean accept(File pathname) {
if (pathname.isDirectory() && pathname.getName().equals("model")) {
// do not check the camel-core model packages as there is no components there
return false;
}
if (pathname.isFile() && pathname.getName().endsWith(".json")) {
// must be a dataformat json file
try {
String json = loadText(new FileInputStream(pathname));
return json != null && json.contains("\"kind\": \"dataformat\"");
} catch (IOException e) {
// ignore
}
}
return pathname.isDirectory() || (pathname.isFile() && pathname.getName().equals("dataformat.properties"));
}
}
private class CamelLanguagesFileFilter implements FileFilter {
@Override
public boolean accept(File pathname) {
if (pathname.isDirectory() && pathname.getName().equals("model")) {
// do not check the camel-core model packages as there is no components there
return false;
}
if (pathname.isFile() && pathname.getName().endsWith(".json")) {
// must be a language json file
try {
String json = loadText(new FileInputStream(pathname));
return json != null && json.contains("\"kind\": \"language\"");
} catch (IOException e) {
// ignore
}
}
return pathname.isDirectory() || (pathname.isFile() && pathname.getName().equals("language.properties"));
}
}
private class CamelOthersFileFilter implements FileFilter {
@Override
public boolean accept(File pathname) {
if (pathname.isFile() && pathname.getName().endsWith(".json")) {
// must be a language json file
try {
String json = loadText(new FileInputStream(pathname));
return json != null && json.contains("\"kind\": \"other\"");
} catch (IOException e) {
// ignore
}
}
return pathname.isDirectory() || (pathname.isFile() && pathname.getName().equals("other.properties"));
}
}
public static void copyFile(File from, File to) throws IOException {
FileChannel in = null;
FileChannel out = null;
try (FileInputStream fis = new FileInputStream(from); FileOutputStream fos = new FileOutputStream(to)) {
try {
in = fis.getChannel();
out = fos.getChannel();
long size = in.size();
long position = 0;
while (position < size) {
position += in.transferTo(position, BUFFER_SIZE, out);
}
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
}
private Set<String> findKarafFeatures() throws MojoExecutionException, MojoFailureException {
// load features.xml file and parse it
Set<String> answer = new LinkedHashSet<>();
try {
File file = new File(featuresDir, "features.xml");
InputStream is = new FileInputStream(file);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setIgnoringComments(true);
dbf.setIgnoringElementContentWhitespace(true);
dbf.setNamespaceAware(false);
dbf.setValidating(false);
dbf.setXIncludeAware(false);
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE);
Document dom = dbf.newDocumentBuilder().parse(is);
NodeList children = dom.getElementsByTagName("features");
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
if (child.getNodeType() == ELEMENT_NODE) {
NodeList children2 = child.getChildNodes();
for (int j = 0; j < children2.getLength(); j++) {
Node child2 = children2.item(j);
if ("feature".equals(child2.getNodeName())) {
String artifactId = child2.getAttributes().getNamedItem("name").getTextContent();
if (artifactId != null && artifactId.startsWith("camel-")) {
answer.add(artifactId);
}
}
}
}
}
getLog().info("Found " + answer.size() + " Camel features in file: " + file);
} catch (Exception e) {
throw new MojoExecutionException("Error reading features.xml file", e);
}
return answer;
}
}