org.mule.devkit.maven.MashapeGeneratorMojo.java Source code

Java tutorial

Introduction

Here is the source code for org.mule.devkit.maven.MashapeGeneratorMojo.java

Source

/**
 * Mule Development Kit
 * Copyright 2010-2011 (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
 *
 * 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 org.mule.devkit.maven;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProjectHelper;
import org.jfrog.maven.annomojo.annotations.MojoComponent;
import org.jfrog.maven.annomojo.annotations.MojoGoal;
import org.jfrog.maven.annomojo.annotations.MojoParameter;
import org.jfrog.maven.annomojo.annotations.MojoPhase;
import org.jfrog.maven.annomojo.annotations.MojoRequiresDependencyResolution;
import org.mule.api.annotations.Configurable;
import org.mule.api.annotations.Module;
import org.mule.api.annotations.Processor;
import org.mule.api.annotations.param.Optional;
import org.mule.api.annotations.rest.HttpMethod;
import org.mule.api.annotations.rest.RestCall;
import org.mule.api.annotations.rest.RestHeaderParam;
import org.mule.api.annotations.rest.RestQueryParam;
import org.mule.api.annotations.rest.RestUriParam;
import org.mule.devkit.model.code.AnnotationUse;
import org.mule.devkit.model.code.CatchBlock;
import org.mule.devkit.model.code.ClassAlreadyExistsException;
import org.mule.devkit.model.code.ClassType;
import org.mule.devkit.model.code.CodeModel;
import org.mule.devkit.model.code.CodeWriter;
import org.mule.devkit.model.code.DefinedClass;
import org.mule.devkit.model.code.ExpressionFactory;
import org.mule.devkit.model.code.FieldVariable;
import org.mule.devkit.model.code.Invocation;
import org.mule.devkit.model.code.Method;
import org.mule.devkit.model.code.Modifier;
import org.mule.devkit.model.code.Op;
import org.mule.devkit.model.code.TryStatement;
import org.mule.devkit.model.code.TypeReference;
import org.mule.devkit.model.code.Variable;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.annotation.PostConstruct;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;

@MojoPhase("generate-sources")
@MojoGoal("generate-mashape-client")
@MojoRequiresDependencyResolution("runtime")
public class MashapeGeneratorMojo extends AbstractMuleMojo {
    @MojoComponent
    private MavenProjectHelper projectHelper;

    /**
     * Directory containing the XML definitions for the services
     */
    @MojoParameter(expression = "${project.basedir}/src/main/resources", required = true)
    private File inputFolder;

    @MojoParameter(expression = "${project.build.directory}/generated-sources/mashape", required = true)
    private File defaultOutputDirectory;

    @MojoParameter(required = false)
    private String mashapeName;

    @MojoParameter(required = false)
    private String mashapeVersion;

    @MojoParameter(required = false)
    private String mashapeProxyHost;

    /**
     * Code Model
     */
    private CodeModel codeModel;

    public MashapeGeneratorMojo() {
        codeModel = new CodeModel(new CodeWriter() {
            @Override
            public OutputStream openBinary(org.mule.devkit.model.code.Package pkg, String fileName)
                    throws IOException {
                File file = new File(defaultOutputDirectory, pkg.name().replace(".", "/") + "/" + fileName);
                return FileUtils.openOutputStream(file);
            }

            @Override
            public void close() throws IOException {
            }
        });
    }

    public void execute() throws MojoExecutionException, MojoFailureException {
        if (inputFolder == null) {
            return;
        }
        getLog().info("Source directory: " + defaultOutputDirectory.getAbsolutePath() + " added");
        project.addCompileSourceRoot(defaultOutputDirectory.getAbsolutePath());
        try {
            traverse(inputFolder);
        } catch (IOException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        }
    }

    private void traverse(final File f) throws IOException, MojoExecutionException {
        final File[] childs = f.listFiles();
        if (childs != null) {
            for (File child : childs) {
                if (child.isDirectory()) {
                    traverse(child);
                } else if (FilenameUtils.isExtension(child.getAbsolutePath(), "mashape")) {
                    generateMashapeClient(child);
                }
            }
        }
    }

    private void generateMashapeClient(File inputFile) throws MojoExecutionException {
        if (mashapeName == null) {
            throw new MojoExecutionException("You must specify a mashapeName");
        }
        if (mashapeVersion == null) {
            throw new MojoExecutionException("You must specify a mashapeVersion");
        }
        if (mashapeProxyHost == null) {
            throw new MojoExecutionException("You must specify a mashapeProxyHost");
        }

        getLog().info("Generating POJO for " + inputFile.getAbsolutePath());

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

        DocumentBuilder db = null;
        try {
            db = factory.newDocumentBuilder();
            Document dom = db.parse(inputFile);

            DefinedClass clazz = codeModel._class(Modifier.ABSTRACT | Modifier.PUBLIC,
                    "org.mule.modules.tinypayme.TinyPayMeConnector", ClassType.CLASS);
            clazz.javadoc()
                    .add("Unkown\n\nAutomatically generated from Mashape XML file\n\n@author MuleSoft, Inc.");
            AnnotationUse moduleAnnotation = clazz.annotate(Module.class);
            moduleAnnotation.param("name", mashapeName);
            moduleAnnotation.param("schemaVersion", mashapeVersion);

            FieldVariable mashapePrivateKey = generateMashapePrivateKey(clazz);
            FieldVariable mashapePublicKey = generateMashapePublicKeyField(clazz);
            FieldVariable mashapeAuthorization = generateMashapeAuthorizationField(clazz);

            generateInit(clazz, mashapePrivateKey, mashapePublicKey, mashapeAuthorization);

            Node api = dom.getElementsByTagName("api").item(0);
            NodeList methods = api.getChildNodes();
            for (int i = 0; i < methods.getLength(); i++) {
                Node method = methods.item(i);

                if (!"method".equals(method.getNodeName())) {
                    continue;
                }

                Method restCallMethod = clazz.method(Modifier.PUBLIC | Modifier.ABSTRACT, ref(String.class),
                        method.getAttributes().getNamedItem("name").getNodeValue());
                restCallMethod._throws(ref(IOException.class));
                restCallMethod.annotate(Processor.class);
                AnnotationUse restCall = restCallMethod.annotate(RestCall.class);

                Node url = null;
                for (int j = 0; j < method.getChildNodes().getLength(); j++) {
                    if (!"url".equals(method.getChildNodes().item(j).getNodeName())) {
                        continue;
                    }

                    url = method.getChildNodes().item(j);
                    break;
                }

                String uri = url.getTextContent();
                if (uri.contains("?")) {
                    uri = uri.split("\\?")[0];
                }
                restCall.param("uri", "https://" + mashapeProxyHost + uri);

                if ("GET".equals(method.getAttributes().getNamedItem("http").getNodeValue())) {
                    restCall.param("method", ref(HttpMethod.class).staticRef("GET"));
                } else if ("PUT".equals(method.getAttributes().getNamedItem("http").getNodeValue())) {
                    restCall.param("method", ref(HttpMethod.class).staticRef("PUT"));
                } else if ("POST".equals(method.getAttributes().getNamedItem("http").getNodeValue())) {
                    restCall.param("method", ref(HttpMethod.class).staticRef("POST"));
                }

                Node parameters = null;
                for (int j = 0; j < method.getChildNodes().getLength(); j++) {
                    if (!"parameters".equals(method.getChildNodes().item(j).getNodeName())) {
                        continue;
                    }

                    parameters = method.getChildNodes().item(j);
                    break;
                }

                for (int j = 0; j < parameters.getChildNodes().getLength(); j++) {
                    if (!"parameter".equals(parameters.getChildNodes().item(j).getNodeName())) {
                        continue;
                    }

                    Node parameter = parameters.getChildNodes().item(j);

                    Variable restCallParameter = restCallMethod.param(ref(String.class),
                            StringUtils.uncapitalize(camel(parameter.getTextContent())));

                    if (uri.contains("{" + parameter.getTextContent() + "}")) {
                        AnnotationUse restUriParam = restCallParameter.annotate(ref(RestUriParam.class));
                        restUriParam.param("value", parameter.getTextContent());
                    } else {
                        AnnotationUse restQueryParam = restCallParameter.annotate(ref(RestQueryParam.class));
                        restQueryParam.param("value", parameter.getTextContent());
                    }

                    if (parameter.getAttributes().getNamedItem("optional") != null
                            && "true".equals(parameter.getAttributes().getNamedItem("optional").getNodeValue())) {
                        restCallParameter.annotate(ref(Optional.class));
                    }
                }

            }

            codeModel.build();
        } catch (ParserConfigurationException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        } catch (SAXException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        } catch (IOException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        } catch (ClassAlreadyExistsException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        }
    }

    private String camel(String uncamelCaseName) {
        String result = "";
        String[] parts = uncamelCaseName.split("_");

        for (int i = 0; i < parts.length; i++) {
            result += StringUtils.capitalize(parts[i].toLowerCase());
        }

        return result;
    }

    private FieldVariable generateMashapePrivateKey(DefinedClass clazz) {
        FieldVariable mashapePrivateKey = clazz.field(Modifier.PRIVATE, ref(String.class), "mashapePrivateKey");
        mashapePrivateKey.javadoc().add("Mashape Private Key");
        mashapePrivateKey.annotate(Configurable.class);

        Method setMashapePrivateKey = clazz.method(Modifier.PUBLIC, codeModel.VOID, "setMashapePrivateKey");
        Variable thatMashapePrivateKey = setMashapePrivateKey.param(ref(String.class), "newMashapePrivateKey");
        setMashapePrivateKey.body().assign(mashapePrivateKey, thatMashapePrivateKey);
        return mashapePrivateKey;
    }

    private FieldVariable generateMashapePublicKeyField(DefinedClass clazz) {
        FieldVariable mashapePublicKey = clazz.field(Modifier.PRIVATE, ref(String.class), "mashapePublicKey");
        mashapePublicKey.javadoc().add("Mashape Public Key");
        mashapePublicKey.annotate(Configurable.class);

        Method setMashapePublicKey = clazz.method(Modifier.PUBLIC, codeModel.VOID, "setMashapePublicKey");
        Variable thatMashapePublicKey = setMashapePublicKey.param(ref(String.class), "newMashapePublicKey");
        setMashapePublicKey.body().assign(mashapePublicKey, thatMashapePublicKey);
        return mashapePublicKey;
    }

    private FieldVariable generateMashapeAuthorizationField(DefinedClass clazz) {
        FieldVariable mashapeAuthorization = clazz.field(Modifier.PRIVATE, ref(String.class),
                "mashapeAuthorization");
        mashapeAuthorization.javadoc().add("Mashape Authorization");
        AnnotationUse mashapeAuthorizationRestHeaderParam = mashapeAuthorization
                .annotate(ref(RestHeaderParam.class));
        mashapeAuthorizationRestHeaderParam.param("value", "X-Mashape-Authorization");

        Method getMashapeAuthorization = clazz.method(Modifier.PUBLIC, ref(String.class),
                "getMashapeAuthorization");
        getMashapeAuthorization.body()._return(mashapeAuthorization);

        return mashapeAuthorization;
    }

    private void generateInit(DefinedClass clazz, FieldVariable mashapePrivateKey, FieldVariable mashapePublicKey,
            FieldVariable mashapeAuthorization) {
        Method init = clazz.method(Modifier.PUBLIC, codeModel.VOID, "init");
        init.annotate(ref(PostConstruct.class));

        TryStatement attempt = init.body()._try();

        Variable uuid = attempt.body().decl(ref(String.class), "uuid",
                ref(UUID.class).staticInvoke("randomUUID").invoke("toString"));
        Variable signingKey = attempt.body().decl(ref(SecretKeySpec.class), "signingKey", ExpressionFactory
                ._new(ref(SecretKeySpec.class)).arg(mashapePrivateKey.invoke("getBytes")).arg("HmacSHA1"));
        Variable mac = attempt.body().decl(ref(Mac.class), "mac",
                ref(Mac.class).staticInvoke("getInstance").arg("HmacSHA1"));
        attempt.body().add(mac.invoke("init").arg(signingKey));
        Variable rawHmac = attempt.body().decl(codeModel.BYTE.array(), "rawHmac",
                mac.invoke("doFinal").arg(uuid.invoke("getBytes")));
        Variable hex = attempt.body().decl(codeModel.BYTE.array(), "hex",
                ExpressionFactory._new(ref(Hex.class)).invoke("encode").arg(rawHmac));
        Variable headerValue = attempt.body().decl(ref(String.class), "headerValue",
                Op.plus(Op.plus(Op.plus(mashapePublicKey, ExpressionFactory.lit(":")),
                        ExpressionFactory._new(ref(String.class)).arg(hex).arg("UTF-8")), uuid));

        Invocation getHeaderValueBytes = ref(Base64.class).staticInvoke("encodeBase64String")
                .arg(headerValue.invoke("getBytes"));
        attempt.body().assign(mashapeAuthorization, getHeaderValueBytes.invoke("replace").arg("\r\n").arg(""));

        CatchBlock catchUnsupportedEncodingExpception = attempt._catch(ref(UnsupportedEncodingException.class));
        catchUnsupportedEncodingExpception.body()._throw(ExpressionFactory._new(ref(RuntimeException.class)));
        CatchBlock catchNoSuchAlgorithmException = attempt._catch(ref(NoSuchAlgorithmException.class));
        catchNoSuchAlgorithmException.body()._throw(ExpressionFactory._new(ref(RuntimeException.class)));
        CatchBlock catchInvalidKeyException = attempt._catch(ref(InvalidKeyException.class));
        catchInvalidKeyException.body()._throw(ExpressionFactory._new(ref(RuntimeException.class)));
    }

    private TypeReference ref(Class<?> clazz) {
        return codeModel.ref(clazz);
    }

}