Java tutorial
/** * Copyright (C) 2016 Red Hat, Inc. * * Licensed 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 io.syndesis.project.converter; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; import java.util.Queue; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.stream.Collectors; import static java.nio.charset.StandardCharsets.UTF_8; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.mustachejava.DefaultMustacheFactory; import com.github.mustachejava.Mustache; import com.github.mustachejava.MustacheFactory; import io.syndesis.connector.catalog.ConnectorCatalog; import io.syndesis.core.MavenProperties; import io.syndesis.core.Names; import io.syndesis.integration.model.Flow; import io.syndesis.integration.model.SyndesisHelpers; import io.syndesis.integration.model.SyndesisModel; import io.syndesis.integration.model.steps.Endpoint; import io.syndesis.integration.support.Strings; import io.syndesis.model.integration.Integration; import io.syndesis.model.integration.Step; import io.syndesis.project.converter.ProjectGeneratorProperties.Templates; import io.syndesis.project.converter.visitor.GeneratorContext; import io.syndesis.project.converter.visitor.StepVisitor; import io.syndesis.project.converter.visitor.StepVisitorContext; import io.syndesis.project.converter.visitor.StepVisitorFactory; import io.syndesis.project.converter.visitor.StepVisitorFactoryRegistry; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DefaultProjectGenerator implements ProjectGenerator { private static final ObjectMapper YAML_OBJECT_MAPPER = SyndesisHelpers.createObjectMapper(); private final MustacheFactory mf = new DefaultMustacheFactory(); /* Not required for the moment, needed for local forking of a maven process private Mustache connectorPomMustache = mf.compile( new InputStreamReader(getClass().getResourceAsStream("templates/connector/pom.xml.mustache")), "pom.xml" ); */ private final ConnectorCatalog connectorCatalog; private final ProjectGeneratorProperties generatorProperties; private final StepVisitorFactoryRegistry registry; private final Mustache applicationJavaMustache; private final Mustache applicationPropertiesMustache; private final Mustache pomMustache; private static final Logger LOG = LoggerFactory.getLogger(DefaultProjectGenerator.class); public DefaultProjectGenerator(ConnectorCatalog connectorCatalog, ProjectGeneratorProperties generatorProperties, StepVisitorFactoryRegistry registry) throws IOException { this.connectorCatalog = connectorCatalog; this.generatorProperties = generatorProperties; this.registry = registry; this.applicationJavaMustache = compile(generatorProperties, "Application.java.mustache", "Application.java"); this.applicationPropertiesMustache = compile(generatorProperties, "application.properties.mustache", "application.properties"); this.pomMustache = compile(generatorProperties, "pom.xml.mustache", "pom.xml"); } private Mustache compile(ProjectGeneratorProperties generatorProperties, String template, String name) throws IOException { String overridePath = generatorProperties.getTemplates().getOverridePath(); URL resource = null; if (!Strings.isEmpty(overridePath)) { resource = getClass().getResource("templates/" + overridePath + "/" + template); } if (resource == null) { resource = getClass().getResource("templates/" + template); } if (resource == null) { throw new IllegalArgumentException(String.format( "Unable to find te required template (overridePath=%s, template=%s)", overridePath, template)); } try (InputStream stream = resource.openStream()) { return mf.compile(new InputStreamReader(stream, UTF_8), name); } } @Override public InputStream generate(GenerateProjectRequest request) throws IOException { Integration integration = request.getIntegration(); for (Step step : integration.getSteps()) { LOG.debug("Integration [{}]: Adding step {} ", Names.sanitize(integration.getName()), step.getId().orElse("")); step.getAction().ifPresent(action -> connectorCatalog.addConnector(action.getCamelConnectorGAV())); } return createTarInputStream(request); } private void addAdditionalResources(TarArchiveOutputStream tos) throws IOException { for (Templates.Resource additionalResource : generatorProperties.getTemplates().getAdditionalResources()) { String overridePath = generatorProperties.getTemplates().getOverridePath(); URL resource = null; if (!Strings.isEmpty(overridePath)) { resource = getClass() .getResource("templates/" + overridePath + "/" + additionalResource.getSource()); } if (resource == null) { resource = getClass().getResource("templates/" + additionalResource.getSource()); } if (resource == null) { throw new IllegalArgumentException( String.format("Unable to find te required additional resource (overridePath=%s, source=%s)", overridePath, additionalResource.getSource())); } try { addTarEntry(tos, additionalResource.getDestination(), Files.readAllBytes(Paths.get(resource.toURI()))); } catch (URISyntaxException e) { throw new IOException(e); } } } private InputStream createTarInputStream(GenerateProjectRequest request) throws IOException { PipedInputStream is = new PipedInputStream(); ExecutorService executor = Executors.newSingleThreadExecutor(); PipedOutputStream os = new PipedOutputStream(is); executor.submit(generateAddProjectTarEntries(request, os)); return is; } private Runnable generateAddProjectTarEntries(GenerateProjectRequest request, OutputStream os) { return () -> { try (TarArchiveOutputStream tos = new TarArchiveOutputStream(os)) { tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); addTarEntry(tos, "src/main/java/io/syndesis/example/Application.java", generateFromRequest(request, applicationJavaMustache)); addTarEntry(tos, "src/main/resources/application.properties", generateFromRequest(request, applicationPropertiesMustache)); addTarEntry(tos, "src/main/resources/syndesis.yml", generateFlowYaml(tos, request)); addTarEntry(tos, "pom.xml", generatePom(request.getIntegration())); addAdditionalResources(tos); LOG.info("Integration [{}]: Project files written to output stream", Names.sanitize(request.getIntegration().getName())); } catch (IOException e) { if (LOG.isErrorEnabled()) { LOG.error(String.format("Exception while creating runtime build tar for integration %s : %s", request.getIntegration().getName(), e.toString()), e); } } }; } private void addTarEntry(TarArchiveOutputStream tos, String path, byte[] content) throws IOException { TarArchiveEntry entry = new TarArchiveEntry(path); entry.setSize(content.length); tos.putArchiveEntry(entry); tos.write(content); tos.closeArchiveEntry(); } @Override public byte[] generatePom(Integration integration) throws IOException { Set<MavenGav> connectors = new LinkedHashSet<>(); Set<String> gavsSeen = new HashSet<>(); for (Step step : integration.getSteps()) { if (!step.getStepKind().equals(Endpoint.KIND)) { continue; } step.getAction().ifPresent(action -> { String gav = action.getCamelConnectorGAV(); if (gavsSeen.contains(gav)) { return; } String[] splitGav = gav.split(":"); if (splitGav.length == 3) { connectors.add(new MavenGav(splitGav[0], splitGav[1], splitGav[2])); } gavsSeen.add(gav); }); } return generateFromPomContext(new PomContext(integration.getId().orElse(""), integration.getName(), integration.getDescription().orElse(null), connectors, generatorProperties.getMavenProperties()), pomMustache); } @SuppressWarnings("PMD.UnusedPrivateMethod") // PMD false positive private byte[] generateFlowYaml(TarArchiveOutputStream tos, GenerateProjectRequest request) throws JsonProcessingException { final Map<Step, String> connectorIdMap = new HashMap<>(); final List<? extends Step> steps = request.getIntegration().getSteps(); final Flow flow = new Flow(); if (!steps.isEmpty()) { // Determine connector prefix request.getIntegration().getSteps().stream().filter(s -> s.getStepKind().equals(Endpoint.KIND)) .filter(s -> s.getAction().isPresent()).filter(s -> s.getConnection().isPresent()) .collect(Collectors.groupingBy(s -> s.getAction().get().getCamelConnectorPrefix())) .forEach((prefix, stepList) -> { if (stepList.size() > 1) { for (int i = 0; i < stepList.size(); i++) { connectorIdMap.put(stepList.get(i), Integer.toString(i + 1)); } } }); Queue<Step> remaining = new LinkedList<>(steps); Step first = remaining.remove(); if (first != null) { GeneratorContext generatorContext = new GeneratorContext.Builder() .connectorCatalog(connectorCatalog).generatorProperties(generatorProperties) .request(request).tarArchiveOutputStream(tos).flow(flow).visitorFactoryRegistry(registry) .build(); StepVisitorContext stepContext = new StepVisitorContext.Builder().index(1).step(first) .remaining(remaining) .connectorIdSupplier(step -> Optional.ofNullable(connectorIdMap.get(step))).build(); visitStep(generatorContext, stepContext); } } SyndesisModel syndesisModel = new SyndesisModel(); syndesisModel.addFlow(flow); return YAML_OBJECT_MAPPER.writeValueAsBytes(syndesisModel); } private void visitStep(GeneratorContext generatorContext, StepVisitorContext stepContext) { StepVisitorFactory<?> factory = registry.get(stepContext.getStep().getStepKind()); StepVisitor visitor = factory.create(generatorContext); generatorContext.getFlow().addStep(visitor.visit(stepContext)); if (stepContext.hasNext()) { visitStep(generatorContext, stepContext.next()); } } private byte[] generateFromRequest(GenerateProjectRequest request, Mustache template) throws IOException { return generate(request, template); } private byte[] generateFromPomContext(PomContext integration, Mustache template) throws IOException { return generate(integration, template); } private byte[] generate(Object scope, Mustache template) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); template.execute(new OutputStreamWriter(bos, UTF_8), scope).flush(); return bos.toByteArray(); } /* default */ static class MavenGav { private final String groupId; private final String artifactId; private final String version; /* default */ MavenGav(String groupId, String artifactId, String version) { this.groupId = groupId; this.artifactId = artifactId; this.version = version; } public String getGroupId() { return groupId; } public String getArtifactId() { return artifactId; } public String getVersion() { return version; } } /* default */ static class PomContext { private final String id; private final String name; private final String description; private final Set<MavenGav> connectors; private final MavenProperties mavenProperties; /* default */ PomContext(String id, String name, String description, Set<MavenGav> connectors, MavenProperties mavenProperties) { this.id = id; this.name = name; this.description = description; this.connectors = connectors; this.mavenProperties = mavenProperties; } public String getId() { return id; } public String getName() { return name; } public String getDescription() { return description; } public Set<MavenGav> getConnectors() { return connectors; } public Set<Entry<String, String>> getMavenRepositories() { return mavenProperties.getRepositories().entrySet(); } } }