| /* |
| * 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.quarkus.maven; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStreamReader; |
| import java.io.Reader; |
| import java.io.Writer; |
| import java.net.URL; |
| import java.nio.charset.StandardCharsets; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.EnumMap; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Optional; |
| import java.util.Properties; |
| import java.util.Set; |
| import java.util.TreeSet; |
| import java.util.stream.Collectors; |
| |
| import org.apache.maven.execution.MavenSession; |
| import org.apache.maven.model.Dependency; |
| import org.apache.maven.model.Model; |
| import org.apache.maven.model.io.xpp3.MavenXpp3Reader; |
| 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.plugins.annotations.ResolutionScope; |
| import org.apache.maven.project.MavenProject; |
| import org.apache.maven.project.MavenProjectHelper; |
| import org.apache.maven.project.ProjectBuilder; |
| import org.apache.maven.repository.RepositorySystem; |
| import org.codehaus.plexus.util.xml.pull.XmlPullParserException; |
| import org.mvel2.templates.TemplateRuntime; |
| |
| import com.google.gson.Gson; |
| import com.google.gson.GsonBuilder; |
| import com.google.gson.JsonObject; |
| import com.google.gson.JsonParser; |
| |
| import static org.apache.camel.maven.packaging.PackageHelper.loadText; |
| |
| /** |
| * Prepares the Quarkus provider camel catalog to include component it supports |
| */ |
| @Mojo(name = "prepare-catalog-quarkus", threadSafe = true, requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME) |
| public class PrepareCatalogQuarkusMojo extends AbstractMojo { |
| |
| private static final Set<String> EXCLUDE_EXTENSIONS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("http-common", "support"))); |
| |
| /** |
| * The maven project. |
| */ |
| @Parameter(property = "project", required = true, readonly = true) |
| protected MavenProject project; |
| |
| @Component |
| private RepositorySystem repositorySystem; |
| |
| @Component |
| private ProjectBuilder mavenProjectBuilder; |
| |
| @Parameter(defaultValue = "${session}", readonly = true) |
| private MavenSession session; |
| |
| @Component |
| private MavenProjectHelper projectHelper; |
| |
| /** |
| * The output directory for components catalog |
| */ |
| @Parameter(defaultValue = "${project.build.directory}/classes/org/apache/camel/catalog/quarkus/components") |
| protected File componentsOutDir; |
| |
| /** |
| * The output directory for dataformats catalog |
| */ |
| @Parameter(defaultValue = "${project.build.directory}/classes/org/apache/camel/catalog/quarkus/dataformats") |
| protected File dataFormatsOutDir; |
| |
| /** |
| * The output directory for languages catalog |
| */ |
| @Parameter(defaultValue = "${project.build.directory}/classes/org/apache/camel/catalog/quarkus/languages") |
| protected File languagesOutDir; |
| |
| /** |
| * The output directory for others catalog |
| */ |
| @Parameter(defaultValue = "${project.build.directory}/classes/org/apache/camel/catalog/quarkus/others") |
| protected File othersOutDir; |
| |
| /** |
| * The directory where all quarkus extension starters are |
| */ |
| @Parameter(defaultValue = "${project.build.directory}/../../../extensions") |
| protected File extensionsDir; |
| |
| /** |
| * 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 { |
| final List<CamelQuarkusExtension> extensions = findExtensionModules(); |
| final CamelCatalog camelCatalog = CamelCatalog.load(); |
| for (Kind kind : Kind.values()) { |
| doExecute(extensions, kind, camelCatalog); |
| } |
| appendOthers(extensions, camelCatalog); |
| } |
| |
| protected void doExecute(List<CamelQuarkusExtension> extensions, Kind kind, CamelCatalog catalog) throws MojoExecutionException { |
| |
| final Path outsDir = kind.getPath(this); |
| |
| // make sure to create out dir |
| try { |
| Files.createDirectories(outsDir); |
| } catch (IOException e) { |
| throw new MojoExecutionException("Could not create " + outsDir, e); |
| } |
| |
| final Gson gson = new GsonBuilder().enableComplexMapKeySerialization().setPrettyPrinting().create(); |
| |
| final Set<String> names = new TreeSet<>(); |
| |
| for (CamelQuarkusExtension ext : extensions) { |
| final String artifactId = ext.getCamelComponentArtifactId(); |
| for (JsonObject catalogEntry : catalog.getByArtifactId(kind, artifactId)) { |
| final JsonObject newCatalogEntry = catalogEntry.deepCopy(); |
| final JsonObject kindObject = newCatalogEntry.get(kind.getSingularName()).getAsJsonObject(); |
| final String firstVersion = ext.getFirstVersion().orElseThrow(() -> new MojoExecutionException( |
| "firstVersion property is missing in " + ext.getRuntimePomXmlPath())); |
| // lets use the camel-quarkus version as first version instead of Apache Camel version |
| kindObject.addProperty("firstVersion", firstVersion); |
| |
| // update json metadata to adapt to camel-quarkus-catalog |
| kindObject.addProperty("groupId", "org.apache.camel.quarkus"); |
| kindObject.addProperty("artifactId", ext.getRuntimeArtifactId()); |
| kindObject.addProperty("version", project.getVersion()); |
| |
| final String name = kind.getName(newCatalogEntry); |
| names.add(name); |
| final Path out = outsDir.resolve(name + ".json"); |
| try (Writer w = Files.newBufferedWriter(out, StandardCharsets.UTF_8)) { |
| gson.toJson(newCatalogEntry, w); |
| } catch (IOException e) { |
| throw new MojoExecutionException("Could not write to " + out); |
| } |
| } |
| } |
| |
| final Path newCatalog = outsDir.resolve("../" + kind + ".properties"); |
| try { |
| Files.write(newCatalog, names.stream().collect(Collectors.joining("\n")).getBytes(StandardCharsets.UTF_8)); |
| } catch (IOException e) { |
| throw new MojoExecutionException("Could not write to " + newCatalog); |
| } |
| } |
| |
| protected void appendOthers(List<CamelQuarkusExtension> extensions, CamelCatalog catalog) throws MojoExecutionException, MojoFailureException { |
| // make sure to create out dir |
| othersOutDir.mkdirs(); |
| final Path othersPropertiesPath = othersOutDir.toPath().resolve("../others.properties"); |
| |
| Set<String> names; |
| try { |
| names = Files.lines(othersPropertiesPath).collect(Collectors.toCollection(TreeSet::new)); |
| } catch (IOException e) { |
| throw new RuntimeException("Could not read " + othersPropertiesPath, e); |
| } |
| |
| for (CamelQuarkusExtension ext : extensions) { |
| // skip if the extension is already one of the following |
| if (ext.getCamelComponentArtifactId() == null || !catalog.getKind(ext.getCamelComponentArtifactId()).isPresent()) { |
| final Map<String, String> model = new HashMap<>(); |
| |
| String firstVersion = ext.getFirstVersion().orElseThrow(() -> new MojoExecutionException( |
| "firstVersion property is missing in " + ext.getRuntimePomXmlPath())); |
| model.put("firstVersion", firstVersion); |
| |
| final String name = ext.getRuntimeArtifactId().replace("camel-quarkus-", ""); |
| names.add(name); |
| model.put("name", name); |
| final String title = ext.getName().orElseThrow(() -> new MojoExecutionException( |
| "name is missing in " + ext.getRuntimePomXmlPath())); |
| model.put("title", title); |
| model.put("description", ext.getDescription().orElseThrow(() -> new MojoExecutionException( |
| "description is missing in " + ext.getRuntimePomXmlPath()))); |
| if (title.contains("(deprecated)")) { |
| model.put("deprecated", "true"); |
| } else { |
| model.put("deprecated", "false"); |
| } |
| model.put("label", ext.getLabel().orElse("quarkus")); |
| model.put("groupId", "org.apache.camel.quarkus"); |
| model.put("artifactId", ext.getRuntimeArtifactId()); |
| model.put("version", project.getVersion()); |
| |
| final String text = templateOther(model); |
| |
| // write new json file |
| Path to = othersOutDir.toPath().resolve(name + ".json"); |
| try { |
| Files.write(to, text.getBytes(StandardCharsets.UTF_8)); |
| } catch (IOException e) { |
| throw new RuntimeException("Could not write to " + to, e); |
| } |
| } |
| } |
| try { |
| Files.write(othersPropertiesPath, names.stream().collect(Collectors.joining("\n")).getBytes(StandardCharsets.UTF_8)); |
| } catch (IOException e) { |
| throw new RuntimeException("Could not write to " + othersPropertiesPath, e); |
| } |
| } |
| |
| private String templateOther(Map<?, ?> model) throws MojoExecutionException { |
| try { |
| String template = loadText(getClass().getClassLoader().getResourceAsStream("other-template.mvel")); |
| String out = (String) TemplateRuntime.eval(template, model); |
| return out; |
| } catch (Exception e) { |
| throw new MojoExecutionException("Error processing mvel template. Reason: " + e, e); |
| } |
| } |
| |
| private List<CamelQuarkusExtension> findExtensionModules() { |
| try { |
| return Files.list(extensionsDir.toPath()) |
| .filter(Files::isDirectory) |
| .filter(path -> !EXCLUDE_EXTENSIONS.contains(path.getFileName().toString())) |
| .map(path -> path.resolve("pom.xml")) |
| .filter(Files::exists) |
| .map(CamelQuarkusExtension::read) |
| .collect(Collectors.toList()); |
| } catch (IOException e) { |
| throw new RuntimeException("Could not list " + extensionsDir, e); |
| } |
| } |
| |
| enum Kind { |
| components() { |
| @Override |
| public String getName(JsonObject json) { |
| return json.get(getSingularName()).getAsJsonObject().get("scheme").getAsString(); |
| } |
| |
| @Override |
| public Path getPath(PrepareCatalogQuarkusMojo mojo) { |
| return mojo.componentsOutDir.toPath(); |
| } |
| |
| }, |
| languages() { |
| @Override |
| public String getName(JsonObject json) { |
| return json.get(getSingularName()).getAsJsonObject().get("name").getAsString(); |
| } |
| |
| @Override |
| public Path getPath(PrepareCatalogQuarkusMojo mojo) { |
| return mojo.languagesOutDir.toPath(); |
| } |
| }, |
| dataformats() { |
| @Override |
| public String getName(JsonObject json) { |
| return json.get(getSingularName()).getAsJsonObject().get("name").getAsString(); |
| } |
| |
| @Override |
| public Path getPath(PrepareCatalogQuarkusMojo mojo) { |
| return mojo.dataFormatsOutDir.toPath(); |
| } |
| }, |
| others() { |
| @Override |
| public String getName(JsonObject json) { |
| return json.get(getSingularName()).getAsJsonObject().get("name").getAsString(); |
| } |
| |
| @Override |
| public Path getPath(PrepareCatalogQuarkusMojo mojo) { |
| return mojo.othersOutDir.toPath(); |
| } |
| } |
| ; |
| |
| public abstract String getName(JsonObject json); |
| public abstract Path getPath(PrepareCatalogQuarkusMojo mojo); |
| public String getSingularName() { |
| return name().substring(0, name().length() - 1); |
| } |
| } |
| |
| static class CamelCatalog { |
| |
| public static CamelCatalog load() { |
| |
| Map<Kind, Map<String, List<JsonObject>>> entriesByKindByArtifactId = new EnumMap<>(Kind.class); |
| |
| for (Kind kind : Kind.values()) { |
| final String resourcePath = "org/apache/camel/catalog/" + kind + ".properties"; |
| final URL url = PrepareCatalogQuarkusMojo.class.getClassLoader().getResource(resourcePath); |
| try (BufferedReader propsReader = new BufferedReader( |
| new InputStreamReader( |
| url.openStream(), |
| StandardCharsets.UTF_8))) { |
| /* Load the catalog entries */ |
| |
| final JsonParser jsonParser = new JsonParser(); |
| final Map<String, List<JsonObject>> entries = new HashMap<>(); |
| propsReader.lines() |
| .map(name -> { |
| final String rPath = "org/apache/camel/catalog/" + kind + "/" + name + ".json"; |
| try (Reader r = new InputStreamReader(PrepareCatalogQuarkusMojo.class.getClassLoader() |
| .getResourceAsStream(rPath ), StandardCharsets.UTF_8)) { |
| return jsonParser.parse(r).getAsJsonObject(); |
| } catch (IOException e) { |
| throw new RuntimeException("Could not load resource " + rPath + " from class path", e); |
| } |
| }) |
| .forEach(json -> { |
| String aid = json.get(kind.getSingularName()).getAsJsonObject().get("artifactId").getAsString(); |
| List<JsonObject> jsons = entries.get(aid); |
| if (jsons == null) { |
| jsons = new ArrayList<JsonObject>(); |
| entries.put(aid, jsons); |
| } |
| jsons.add(json); |
| }); |
| |
| entriesByKindByArtifactId.put(kind, entries); |
| |
| } catch (IOException e) { |
| throw new RuntimeException("Could not load resource " + resourcePath + " from class path", e); |
| } |
| } |
| return new CamelCatalog(entriesByKindByArtifactId); |
| |
| } |
| |
| private final Map<Kind, Map<String, List<JsonObject>>> entriesByKindByArtifactId; |
| |
| public CamelCatalog(Map<Kind, Map<String, List<JsonObject>>> entriesByKindByArtifactId2) { |
| super(); |
| this.entriesByKindByArtifactId = entriesByKindByArtifactId2; |
| } |
| |
| public List<JsonObject> getByArtifactId(Kind kind, String artifactId) { |
| final Map<String, List<JsonObject>> kindEntries = entriesByKindByArtifactId.get(kind); |
| List<JsonObject> result = kindEntries != null ? kindEntries.get(artifactId) : null; |
| return result == null ? Collections.emptyList() : result; |
| } |
| |
| public Optional<Kind> getKind(String artifactId) { |
| return entriesByKindByArtifactId.entrySet().stream() |
| .filter(en -> en.getValue().containsKey(artifactId)) |
| .map(Entry::getKey) |
| .findFirst(); |
| } |
| } |
| |
| static class CamelQuarkusExtension { |
| |
| public static CamelQuarkusExtension read(Path parentPomXmlPath) { |
| final Path runtimePomXmlPath = parentPomXmlPath.getParent().resolve("runtime/pom.xml").toAbsolutePath().normalize(); |
| try (Reader parentReader = Files.newBufferedReader(parentPomXmlPath, StandardCharsets.UTF_8); |
| Reader runtimeReader = Files.newBufferedReader(runtimePomXmlPath, StandardCharsets.UTF_8)) { |
| final MavenXpp3Reader rxppReader = new MavenXpp3Reader(); |
| final Model parentPom = rxppReader.read(parentReader); |
| final Model runtimePom = rxppReader.read(runtimeReader); |
| final List<Dependency> deps = runtimePom.getDependencies(); |
| |
| final String aid = runtimePom.getArtifactId(); |
| String camelComponentArtifactId = null; |
| if (aid.equals("camel-quarkus-core")) { |
| camelComponentArtifactId = "camel-base"; |
| } else if (deps != null && !deps.isEmpty()) { |
| Optional<Dependency> artifact = deps.stream() |
| .filter(dep -> |
| |
| "org.apache.camel".equals(dep.getGroupId()) && |
| ("compile".equals(dep.getScope()) || dep.getScope() == null)) |
| .findFirst(); |
| if (artifact.isPresent()) { |
| camelComponentArtifactId = artifact.get().getArtifactId(); |
| } |
| } |
| final Properties props = runtimePom.getProperties() != null ? runtimePom.getProperties() : new Properties(); |
| |
| String name = props.getProperty("title"); |
| if (name == null) { |
| name = parentPom.getName().replace("Camel Quarkus :: ", ""); |
| } |
| |
| return new CamelQuarkusExtension( |
| parentPomXmlPath, |
| runtimePomXmlPath, |
| camelComponentArtifactId, |
| (String) props.get("firstVersion"), |
| aid, |
| name, |
| runtimePom.getDescription(), |
| props.getProperty("label") |
| ); |
| } catch (IOException | XmlPullParserException e) { |
| throw new RuntimeException("Could not read "+ parentPomXmlPath, e); |
| } |
| } |
| |
| private final String label; |
| |
| private final String description; |
| |
| private final String runtimeArtifactId; |
| |
| private final Path parentPomXmlPath; |
| private final Path runtimePomXmlPath; |
| private final String camelComponentArtifactId; |
| private final String firstVersion; |
| private final String name; |
| |
| public CamelQuarkusExtension( |
| Path pomXmlPath, |
| Path runtimePomXmlPath, |
| String camelComponentArtifactId, |
| String firstVersion, |
| String runtimeArtifactId, |
| String name, |
| String description, |
| String label) { |
| super(); |
| this.parentPomXmlPath = pomXmlPath; |
| this.runtimePomXmlPath = runtimePomXmlPath; |
| this.camelComponentArtifactId = camelComponentArtifactId; |
| this.firstVersion = firstVersion; |
| this.runtimeArtifactId = runtimeArtifactId; |
| this.name = name; |
| this.description = description; |
| this.label = label; |
| } |
| |
| public Path getParentPomXmlPath() { |
| return parentPomXmlPath; |
| } |
| |
| public Optional<String> getFirstVersion() { |
| return Optional.ofNullable(firstVersion); |
| } |
| |
| public Path getRuntimePomXmlPath() { |
| return runtimePomXmlPath; |
| } |
| |
| public Optional<String> getLabel() { |
| return Optional.ofNullable(label); |
| } |
| |
| public Optional<String> getDescription() { |
| return Optional.ofNullable(description); |
| } |
| |
| public String getRuntimeArtifactId() { |
| return runtimeArtifactId; |
| } |
| |
| public String getCamelComponentArtifactId() { |
| return camelComponentArtifactId; |
| } |
| |
| public Optional<String> getName() { |
| return Optional.ofNullable(name); |
| } |
| |
| } |
| |
| } |