org.apache.olingo.ext.pojogen.AbstractPOJOGenMojo.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.olingo.ext.pojogen.AbstractPOJOGenMojo.java

Source

/*
 * 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.olingo.ext.pojogen;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.GZIPOutputStream;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.tuple.ImmutableTriple;
import org.apache.commons.lang3.tuple.Triple;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.olingo.client.api.ODataClient;
import org.apache.olingo.client.api.communication.request.retrieve.EdmMetadataRequest;
import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse;
import org.apache.olingo.client.api.edm.xml.XMLMetadata;
import org.apache.olingo.commons.api.edm.Edm;
import org.apache.olingo.commons.api.edm.EdmComplexType;
import org.apache.olingo.commons.api.edm.EdmEntityContainer;
import org.apache.olingo.commons.api.edm.EdmEntitySet;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.edm.EdmEnumType;
import org.apache.olingo.commons.api.edm.EdmSchema;
import org.apache.olingo.commons.api.edm.EdmTerm;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
import org.codehaus.plexus.util.FileUtils;

public abstract class AbstractPOJOGenMojo extends AbstractMojo {

    /**
     * Generated files base root.
     */
    @Parameter(property = "outputDirectory", required = true)
    protected String outputDirectory;

    /**
     * OData service root URL.
     */
    @Parameter(property = "serviceRootURL", required = false)
    protected String serviceRootURL;

    /**
     * Local file from which Edm information can be loaded.
     */
    @Parameter(property = "localEdm", required = false)
    protected String localEdm;

    /**
     * Base package.
     */
    @Parameter(property = "basePackage", required = false)
    protected String basePackage;

    protected final Set<String> namespaces = new HashSet<String>();

    protected static String TOOL_DIR = "ojc-plugin";

    protected AbstractUtility utility;

    protected abstract String getVersion();

    protected File mkdir(final String path) {
        final File dir = new File(outputDirectory + File.separator + TOOL_DIR + File.separator + path);

        if (dir.exists()) {
            if (!dir.isDirectory()) {
                throw new IllegalArgumentException("Invalid path '" + path + "': it is not a directory");
            }
        } else {
            dir.mkdirs();
        }

        return dir;
    }

    protected File mkPkgDir(final String path) {
        return StringUtils.isBlank(basePackage) ? mkdir(path)
                : mkdir(basePackage.replace('.', File.separatorChar) + File.separator + path);
    }

    protected void writeFile(final String name, final File path, final VelocityContext ctx, final Template template,
            final boolean append) throws MojoExecutionException {

        if (!path.exists()) {
            throw new IllegalArgumentException("Invalid base path '" + path.getAbsolutePath() + "'");
        }

        FileWriter writer = null;
        try {
            final File toBeWritten = new File(path, name);
            if (!append && toBeWritten.exists()) {
                throw new IllegalStateException("File '" + toBeWritten.getAbsolutePath() + "' already exists");
            }
            writer = new FileWriter(toBeWritten, append);
            template.merge(ctx, writer);
        } catch (IOException e) {
            throw new MojoExecutionException("Error creating file '" + name + "'", e);
        } finally {
            IOUtils.closeQuietly(writer);
        }
    }

    protected VelocityContext newContext() {
        final VelocityContext ctx = new VelocityContext();

        ctx.put("utility", getUtility());
        ctx.put("basePackage", basePackage);
        ctx.put("schemaName", getUtility().getSchemaName());
        ctx.put("namespace", getUtility().getNamespace());
        ctx.put("namespaces", namespaces);
        ctx.put("odataVersion", getVersion());

        return ctx;
    }

    protected void parseObj(final File base, final String pkg, final String name, final String out)
            throws MojoExecutionException {

        parseObj(base, false, pkg, name, out, Collections.<String, Object>emptyMap());
    }

    protected void parseObj(final File base, final String pkg, final String name, final String out,
            final Map<String, Object> objs) throws MojoExecutionException {

        parseObj(base, false, pkg, name, out, objs);
    }

    protected void parseObj(final File base, final boolean append, final String pkg, final String name,
            final String out, final Map<String, Object> objs) throws MojoExecutionException {

        final VelocityContext ctx = newContext();
        ctx.put("package", pkg);

        if (objs != null) {
            for (Map.Entry<String, Object> obj : objs.entrySet()) {
                if (StringUtils.isNotBlank(obj.getKey()) && obj.getValue() != null) {
                    ctx.put(obj.getKey(), obj.getValue());
                }
            }
        }

        final Template template = Velocity.getTemplate(name + ".vm");
        writeFile(out, base, ctx, template, append);
    }

    protected abstract void createUtility(Edm edm, EdmSchema schema, String basePackage);

    protected abstract AbstractUtility getUtility();

    protected abstract ODataClient getClient();

    private Triple<XMLMetadata, String, Edm> getMetadata() throws FileNotFoundException {
        if (StringUtils.isEmpty(serviceRootURL) && StringUtils.isEmpty(localEdm)) {
            throw new IllegalArgumentException("Must provide either serviceRootURL or localEdm");
        }
        if (StringUtils.isNotEmpty(serviceRootURL) && StringUtils.isNotEmpty(localEdm)) {
            throw new IllegalArgumentException("Must provide either serviceRootURL or localEdm, not both");
        }

        XMLMetadata metadata = null;
        String metadataETag = null;
        Edm edm = null;
        if (StringUtils.isNotEmpty(serviceRootURL)) {
            final EdmMetadataRequest req = getClient().getRetrieveRequestFactory()
                    .getMetadataRequest(serviceRootURL);
            metadata = req.getXMLMetadata();
            final ODataRetrieveResponse<Edm> res = req.execute();
            metadataETag = res.getETag();
            edm = res.getBody();
        } else if (StringUtils.isNotEmpty(localEdm)) {
            final FileInputStream fis = new FileInputStream(FileUtils.getFile(localEdm));
            try {
                metadata = getClient().getDeserializer(ContentType.APPLICATION_XML).toMetadata(fis);
                edm = getClient().getReader().readMetadata(metadata.getSchemaByNsOrAlias());
            } finally {
                IOUtils.closeQuietly(fis);
            }
        }

        if (metadata == null || edm == null) {
            throw new IllegalStateException("Metadata not found");
        }
        return new ImmutableTriple<XMLMetadata, String, Edm>(metadata, metadataETag, edm);
    }

    @Override
    public void execute() throws MojoExecutionException, MojoFailureException {
        if (new File(outputDirectory + File.separator + TOOL_DIR).exists()) {
            getLog().info("Nothing to do because " + TOOL_DIR + " directory already exists. Clean to update.");
            return;
        }

        Velocity.addProperty(Velocity.RESOURCE_LOADER, "class");
        Velocity.addProperty("class.resource.loader.class", ClasspathResourceLoader.class.getName());

        try {
            final Triple<XMLMetadata, String, Edm> metadata = getMetadata();

            for (EdmSchema schema : metadata.getRight().getSchemas()) {
                namespaces.add(schema.getNamespace().toLowerCase());
            }

            final Map<String, String> entityTypeNames = new HashMap<String, String>();
            final Map<String, String> complexTypeNames = new HashMap<String, String>();
            final Map<String, String> enumTypeNames = new HashMap<String, String>();
            final Map<String, String> termNames = new HashMap<String, String>();

            final Map<String, Object> objs = new HashMap<String, Object>();

            for (EdmSchema schema : metadata.getRight().getSchemas()) {
                createUtility(metadata.getRight(), schema, basePackage);

                // write package-info for the base package
                final String schemaPath = utility.getNamespace().toLowerCase().replace('.', File.separatorChar);
                final File base = mkPkgDir(schemaPath);
                final String pkg = StringUtils.isBlank(basePackage) ? utility.getNamespace().toLowerCase()
                        : basePackage + "." + utility.getNamespace().toLowerCase();
                parseObj(base, pkg, "package-info", "package-info.java");

                // write package-info for types package
                final File typesBaseDir = mkPkgDir(schemaPath + "/types");
                final String typesPkg = pkg + ".types";
                parseObj(typesBaseDir, typesPkg, "package-info", "package-info.java");

                for (EdmTerm term : schema.getTerms()) {
                    final String className = utility.capitalize(term.getName());
                    termNames.put(term.getFullQualifiedName().toString(), typesPkg + "." + className);
                    objs.clear();
                    objs.put("term", term);
                    parseObj(typesBaseDir, typesPkg, "term", className + ".java", objs);
                }

                for (EdmEnumType enumType : schema.getEnumTypes()) {
                    final String className = utility.capitalize(enumType.getName());
                    enumTypeNames.put(enumType.getFullQualifiedName().toString(), typesPkg + "." + className);
                    objs.clear();
                    objs.put("enumType", enumType);
                    parseObj(typesBaseDir, typesPkg, "enumType", className + ".java", objs);
                }

                final List<EdmComplexType> complexes = new ArrayList<EdmComplexType>();

                for (EdmComplexType complex : schema.getComplexTypes()) {
                    complexes.add(complex);
                    final String className = utility.capitalize(complex.getName());
                    complexTypeNames.put(complex.getFullQualifiedName().toString(), typesPkg + "." + className);
                    objs.clear();
                    objs.put("complexType", complex);

                    parseObj(typesBaseDir, typesPkg, "complexType", className + ".java", objs);
                    parseObj(typesBaseDir, typesPkg, "complexTypeComposableInvoker",
                            className + "ComposableInvoker.java", objs);
                    parseObj(typesBaseDir, typesPkg, "complexCollection", className + "Collection.java", objs);
                    parseObj(typesBaseDir, typesPkg, "complexCollectionComposableInvoker",
                            className + "CollectionComposableInvoker.java", objs);
                }

                for (EdmEntityType entity : schema.getEntityTypes()) {
                    final String className = utility.capitalize(entity.getName());
                    entityTypeNames.put(entity.getFullQualifiedName().toString(), typesPkg + "." + className);

                    objs.clear();
                    objs.put("entityType", entity);

                    final Map<String, String> keys;

                    EdmEntityType baseType = null;
                    if (entity.getBaseType() == null) {
                        keys = getUtility().getEntityKeyType(entity);
                    } else {
                        baseType = entity.getBaseType();
                        objs.put("baseType", getUtility().getJavaType(baseType.getFullQualifiedName().toString()));
                        while (baseType.getBaseType() != null) {
                            baseType = baseType.getBaseType();
                        }
                        keys = getUtility().getEntityKeyType(baseType);
                    }

                    if (keys.size() > 1) {
                        // create compound key class
                        final String keyClassName = utility
                                .capitalize(baseType == null ? entity.getName() : baseType.getName()) + "Key";
                        objs.put("keyRef", keyClassName);

                        if (entity.getBaseType() == null) {
                            objs.put("keys", keys);
                            parseObj(typesBaseDir, typesPkg, "entityTypeKey", keyClassName + ".java", objs);
                        }
                    }

                    parseObj(typesBaseDir, typesPkg, "entityType", className + ".java", objs);
                    parseObj(typesBaseDir, typesPkg, "entityComposableInvoker",
                            className + "ComposableInvoker.java", objs);
                    parseObj(typesBaseDir, typesPkg, "entityCollection", className + "Collection.java", objs);
                    parseObj(typesBaseDir, typesPkg, "entityCollectionComposableInvoker",
                            className + "CollectionComposableInvoker.java", objs);
                }

                // write container and top entity sets into the base package
                EdmEntityContainer container = schema.getEntityContainer();
                if (container != null) {
                    objs.clear();
                    objs.put("container", container);
                    objs.put("namespace", schema.getNamespace());
                    objs.put("complexes", complexes);

                    parseObj(base, pkg, "container", utility.capitalize(container.getName()) + ".java", objs);

                    for (EdmEntitySet entitySet : container.getEntitySets()) {
                        objs.clear();
                        objs.put("entitySet", entitySet);
                        objs.put("container", container);
                        parseObj(base, pkg, "entitySet", utility.capitalize(entitySet.getName()) + ".java", objs);
                    }
                }
            }

            final ByteArrayOutputStream baos = new ByteArrayOutputStream();
            final GZIPOutputStream gzos = new GZIPOutputStream(baos);
            final ObjectOutputStream oos = new ObjectOutputStream(gzos);
            try {
                oos.writeObject(metadata.getLeft());
            } finally {
                oos.close();
                gzos.close();
                baos.close();
            }

            objs.clear();
            objs.put("metadata", new String(Base64.encodeBase64(baos.toByteArray()), "UTF-8"));
            objs.put("metadataETag", metadata.getMiddle());
            objs.put("entityTypes", entityTypeNames);
            objs.put("complexTypes", complexTypeNames);
            objs.put("enumTypes", enumTypeNames);
            objs.put("terms", termNames);
            final String actualBP = StringUtils.isBlank(basePackage) ? StringUtils.EMPTY : basePackage;
            parseObj(mkdir(actualBP.replace('.', File.separatorChar)), actualBP, "service", "Service.java", objs);
        } catch (Exception t) {
            getLog().error(t);

            throw (t instanceof MojoExecutionException) ? (MojoExecutionException) t
                    : new MojoExecutionException("While executin mojo", t);
        }
    }
}