io.fabric8.forge.camel.commands.CamelNewComponentsCommand.java Source code

Java tutorial

Introduction

Here is the source code for io.fabric8.forge.camel.commands.CamelNewComponentsCommand.java

Source

/**
 *  Copyright 2005-2014 Red Hat, Inc.
 *
 *  Red Hat 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 io.fabric8.forge.camel.commands;

import com.jcabi.xml.XML;
import com.jcabi.xml.XMLDocument;
import io.fabric8.forge.camel.api.CamelSupportedTechnologyEnum;
import org.apache.commons.io.FileUtils;
import org.jboss.forge.addon.dependencies.Dependency;
import org.jboss.forge.addon.dependencies.builder.DependencyBuilder;
import org.jboss.forge.addon.parser.java.facets.JavaSourceFacet;
import org.jboss.forge.addon.parser.java.resources.JavaResource;
import org.jboss.forge.addon.parser.java.resources.JavaResourceVisitor;
import org.jboss.forge.addon.projects.Project;
import org.jboss.forge.addon.projects.dependencies.DependencyInstaller;
import org.jboss.forge.addon.projects.facets.ResourcesFacet;
import org.jboss.forge.addon.resource.FileResource;
import org.jboss.forge.addon.resource.Resource;
import org.jboss.forge.addon.resource.ResourceFactory;
import org.jboss.forge.addon.resource.URLResource;
import org.jboss.forge.addon.resource.visit.VisitContext;
import org.jboss.forge.addon.rest.ClientFactory;
import org.jboss.forge.addon.templates.Template;
import org.jboss.forge.addon.templates.TemplateFactory;
import org.jboss.forge.addon.templates.freemarker.FreemarkerTemplate;
import org.jboss.forge.addon.ui.context.UIBuilder;
import org.jboss.forge.addon.ui.context.UIContext;
import org.jboss.forge.addon.ui.context.UIExecutionContext;
import org.jboss.forge.addon.ui.context.UIValidationContext;
import org.jboss.forge.addon.ui.input.InputComponent;
import org.jboss.forge.addon.ui.input.UICompleter;
import org.jboss.forge.addon.ui.input.UIInput;
import org.jboss.forge.addon.ui.input.UIPrompt;
import org.jboss.forge.addon.ui.metadata.UICommandMetadata;
import org.jboss.forge.addon.ui.metadata.WithAttributes;
import org.jboss.forge.addon.ui.result.Result;
import org.jboss.forge.addon.ui.result.Results;
import org.jboss.forge.addon.ui.util.Categories;
import org.jboss.forge.addon.ui.util.Metadata;
import org.jboss.forge.addon.ui.validate.UIValidator;
import org.jboss.forge.roaster.model.source.JavaClassSource;
import org.jboss.forge.roaster.model.source.JavaSource;

import javax.inject.Inject;
import javax.ws.rs.client.Client;
import javax.ws.rs.core.MediaType;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.net.URL;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

public class CamelNewComponentsCommand extends AbstractCamelCommand {

    //UIInput field names will automatically be the names for the command line flags

    @Inject
    @WithAttributes(label = "Camel components to add to the project", description = "Select the list of Camel component that you want to add to your project.", required = true)
    UIInput<String> named;

    @Inject
    @WithAttributes(label = "Version", description = "Version.", required = true)
    UIInput<String> version;

    @Inject
    private ClientFactory factory;

    @Inject
    TemplateFactory templateFactory;

    @Inject
    ResourceFactory resourceFactory;

    @Inject
    private DependencyInstaller dependencyInstaller;

    Client client;

    Set<String> availableComponents;

    @Override
    public UICommandMetadata getMetadata(UIContext context) {
        return Metadata.forCommand(CamelNewComponentsCommand.class).name("camel-new-component")
                .category(Categories.create(CATEGORY)).description("Add new Camel components to the project.");
    }

    @Override
    public void initializeUI(UIBuilder builder) throws Exception {
        super.initializeUI(builder);
        initializeAvailableCamelComponents();

        //populate autocompletion options
        named.setCompleter(new UICompleter<String>() {
            @Override
            public Iterable<String> getCompletionProposals(UIContext context, InputComponent<?, String> input,
                    String value) {

                // filter
                List<String> filteredList = new ArrayList<String>(availableComponents.size());
                for (String s : availableComponents) {
                    if (!camelDepsInUse.contains(s)) {
                        filteredList.add(s);
                    }
                }

                // rename key
                List<String> noPrefixList = new ArrayList<String>(filteredList.size());
                for (String s : filteredList) {
                    noPrefixList.add(s.split("-")[1]);
                }

                // keep only relevant
                List<String> componentsList = new ArrayList<>(noPrefixList.size());
                for (String s : noPrefixList) {
                    //adding to the proposals only entries relevant to the current partially typed string
                    if (s.startsWith(value))
                        componentsList.add(s);
                }

                //sort
                Collections.sort(componentsList);
                return componentsList;
            }
        });

        version.setCompleter(new UIVersionCompleter(this));

        named.addValidator(new UIValidator() {
            @Override
            public void validate(UIValidationContext context) {
                if (availableComponents == null) {
                    availableComponents = initializeAvailableCamelComponents();
                }

                if (!availableComponents.contains("camel-" + context.getCurrentInputComponent().getValue())) {
                    context.addValidationError(context.getCurrentInputComponent(),
                            "Selected Camel component is not available.");
                }
            }
        });

        builder.add(named).add(version);
    }

    @Override
    public Result execute(UIExecutionContext context) throws Exception {
        String component = named.getValue();
        component = "camel-" + component;
        String selectedVersion = version.getValue();

        Project project = getSelectedProject(context);

        installMavenDependency(component, selectedVersion, project);

        CamelSupportedTechnologyEnum tech = null;
        try {
            tech = detectTechnology(project);
        } catch (IllegalStateException e) {
            return Results.fail("Unable to identify project type.");
        }
        //add different samples based on technology
        switch (tech) {
        case BLUEPRINT: {
            ResourcesFacet facet = getSelectedProject(context).getFacet(ResourcesFacet.class);
            String outputFilePath = "OSGI-INF" + File.separator + "blueprint" + File.separator + "components.xml";
            FileResource<?> fileResource = facet.getResource(outputFilePath);
            Resource<URL> xml = resourceFactory.create(getClass().getResource("/templates/my_blueprint.ftl"))
                    .reify(URLResource.class);
            Template template = templateFactory.create(xml, FreemarkerTemplate.class);

            Map<String, Object> templateContext = new HashMap<>();
            String componentId = component.split("-")[1];
            templateContext.put("componentId", componentId);
            String componentClass = findComponentFQCN(component, selectedVersion);
            templateContext.put("componentClass", componentClass);

            UIPrompt prompt = context.getPrompt();
            if (!prompt
                    .promptBoolean("File '" + outputFilePath + "' already exists. Do you want to overwrite it?")) {
                return Results.fail("File '" + outputFilePath + "' already exists.");
            }

            fileResource.createNewFile();
            fileResource.setContents(template.process(templateContext));

            break;
        }
        case SPRING: {
            ResourcesFacet facet = getSelectedProject(context).getFacet(ResourcesFacet.class);
            String outputFilePath = "META-INF" + File.separator + "spring" + File.separator + "components.xml";
            FileResource<?> fileResource = facet.getResource(outputFilePath);
            Resource<URL> xml = resourceFactory.create(getClass().getResource("/templates/my_spring.ftl"))
                    .reify(URLResource.class);
            Template template = templateFactory.create(xml, FreemarkerTemplate.class);

            Map<String, Object> templateContext = new HashMap<>();
            String componentId = component.split("-")[1];
            templateContext.put("componentId", componentId);
            String componentClass = findComponentFQCN(component, selectedVersion);
            templateContext.put("componentClass", componentClass);

            UIPrompt prompt = context.getPrompt();
            if (!prompt
                    .promptBoolean("File '" + outputFilePath + "' already exists. Do you want to overwrite it?")) {
                return Results.fail("File '" + outputFilePath + "' already exists.");
            }

            fileResource.createNewFile();
            fileResource.setContents(template.process(templateContext));
            break;
        }
        case JAVA:
            break;
        }

        return Results.success(component + " component successfully installed!");

    }

    private String findComponentFQCN(String component, String selectedVersion) {
        String result = null;
        InputStream stream = null;
        try {
            File tmp = File.createTempFile("camel-dep", "jar");
            URL url = new URL(String.format("https://repo1.maven.org/maven2/org/apache/camel/%s/%s/%s-%s.jar",
                    component, selectedVersion, component, selectedVersion));

            FileUtils.copyURLToFile(url, tmp);

            ZipFile zipFile = new ZipFile(tmp);
            Enumeration<? extends ZipEntry> entries = zipFile.entries();

            while (entries.hasMoreElements()) {
                ZipEntry entry = entries.nextElement();
                if (entry.getName().startsWith("META-INF/services/org/apache/camel/component/")
                        && !entry.isDirectory()) {
                    stream = zipFile.getInputStream(entry);
                    Properties prop = new Properties();
                    prop.load(stream);
                    result = prop.getProperty("class");
                    break;
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("Unable to inspect added component", e);
        } finally {
            if (stream != null) {
                try {
                    stream.close();
                } catch (Exception e) {
                }
            }
        }
        return result;
    }

    private void installMavenDependency(String component, String selectedVersion, Project project) {
        Dependency dependency = DependencyBuilder.create().setGroupId(MVN_CAMEL_GROUPID).setVersion(selectedVersion)
                .setArtifactId(component);

        dependencyInstaller.install(project, dependency);
    }

    private synchronized Set<String> initializeAvailableCamelComponents() {
        if (availableComponents == null) {
            String camelComponentsListLocation = "https://raw.githubusercontent.com/fusesource/fuseide/master/core/plugins/org.fusesource.ide.camel.model/components/components-2.13.2.xml";
            Set<String> result = new LinkedHashSet<String>();
            String xmlString = null;

            //xmlString = IOUtils.toString(new URL(camelComponentsListLocation));
            client = factory.createClient();
            try {
                xmlString = client.target(camelComponentsListLocation).request(MediaType.WILDCARD_TYPE)
                        .get(String.class);
            } catch (Exception e) {
                throw new RuntimeException(
                        "Unable to fetch list of Camel components from: " + camelComponentsListLocation, e);
            }

            XML xml = new XMLDocument(xmlString);
            List<String> connectors = xml.xpath("/connectors/connector/@id");
            for (String id : connectors) {
                result.add("camel-" + id);
            }
            availableComponents = Collections.unmodifiableSet(result);
        }

        return availableComponents;
    }

    private CamelSupportedTechnologyEnum detectTechnology(Project project) {
        if (probeForCDI(project)) {
            return CamelSupportedTechnologyEnum.JAVA;
        } else if (probeForBlueprint(project)) {
            return CamelSupportedTechnologyEnum.BLUEPRINT;
        } else if (probeForSpring(project)) {
            return CamelSupportedTechnologyEnum.SPRING;
        } else {
            throw new IllegalStateException("We couldn't identify Camel Project technology");
        }
    }

    private boolean probeForSpring(Project project) {
        ResourcesFacet facet = project.getFacet(ResourcesFacet.class);
        FileResource<?> resource = facet.getResource("META-INF" + File.separator + "spring");
        if (resource.isDirectory()) {
            return true;
        } else {
            return false;
        }

    }

    private boolean probeForBlueprint(Project project) {
        ResourcesFacet facet = project.getFacet(ResourcesFacet.class);
        FileResource<?> resource = facet.getResource("OSGI-INF");
        if (resource.isDirectory()) {
            return true;
        } else {
            return false;
        }

    }

    private boolean probeForCDI(Project project) {
        final boolean matched[] = new boolean[1];
        matched[0] = false;
        project.getFacet(JavaSourceFacet.class).visitJavaSources(new JavaResourceVisitor() {
            @Override
            public void visit(VisitContext context, JavaResource javaResource) {
                try {
                    JavaSource<?> javaType = javaResource.getJavaType();
                    if (javaType.isClass()) {
                        JavaClassSource source = (JavaClassSource) javaType;
                        if (source.hasAnnotation(org.apache.camel.cdi.ContextName.class)
                                || source.hasAnnotation(org.apache.camel.cdi.Uri.class)) {
                            matched[0] = true;
                        }
                    }
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
            }
        });
        return matched[0];
    }

}

class UIVersionCompleter implements UICompleter {
    AbstractCamelCommand command;

    public UIVersionCompleter(AbstractCamelCommand command) {
        this.command = command;
    }

    @Override
    public Iterable getCompletionProposals(UIContext context, InputComponent input, String value) {
        List<String> result = new ArrayList<>(1);
        result.add(command.getCamelCoreVersion());
        return result;
    }
}