org.forgerock.openicf.maven.ConnectorDocBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.forgerock.openicf.maven.ConnectorDocBuilder.java

Source

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2012-2014 ForgeRock AS. All Rights Reserved
 *
 * The contents of this file are subject to the terms
 * of the Common Development and Distribution License
 * (the License). You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 * http://forgerock.org/license/CDDLv1.0.html
 * See the License for the specific language governing
 * permission and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL
 * Header Notice in each file and include the License file
 * at http://forgerock.org/license/CDDLv1.0.html
 * If applicable, add the following below the CDDL Header,
 * with the fields enclosed by brackets [] replaced by
 * your own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 */

package org.forgerock.openicf.maven;

import java.io.Writer;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;

import org.apache.commons.lang.StringUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.DefaultArtifact;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.shared.artifact.filter.PatternExcludesArtifactFilter;
import org.apache.maven.shared.artifact.filter.PatternIncludesArtifactFilter;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.context.Context;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.exception.VelocityException;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.PathTool;
import org.codehaus.plexus.util.ReaderFactory;
import org.identityconnectors.common.CollectionUtil;
import org.identityconnectors.common.Pair;
import org.identityconnectors.framework.api.APIConfiguration;
import org.identityconnectors.framework.api.ConfigurationProperty;
import org.identityconnectors.framework.api.ConnectorFacadeFactory;
import org.identityconnectors.framework.api.ConnectorInfo;
import org.identityconnectors.framework.api.ConnectorInfoManager;
import org.identityconnectors.framework.api.ConnectorInfoManagerFactory;
import org.identityconnectors.framework.api.operations.APIOperation;
import org.identityconnectors.framework.api.operations.AuthenticationApiOp;
import org.identityconnectors.framework.api.operations.CreateApiOp;
import org.identityconnectors.framework.api.operations.DeleteApiOp;
import org.identityconnectors.framework.api.operations.GetApiOp;
import org.identityconnectors.framework.api.operations.ResolveUsernameApiOp;
import org.identityconnectors.framework.api.operations.SchemaApiOp;
import org.identityconnectors.framework.api.operations.ScriptOnConnectorApiOp;
import org.identityconnectors.framework.api.operations.ScriptOnResourceApiOp;
import org.identityconnectors.framework.api.operations.SearchApiOp;
import org.identityconnectors.framework.api.operations.SyncApiOp;
import org.identityconnectors.framework.api.operations.TestApiOp;
import org.identityconnectors.framework.api.operations.UpdateApiOp;
import org.identityconnectors.framework.common.objects.ObjectClass;
import org.identityconnectors.framework.common.objects.ObjectClassInfo;
import org.identityconnectors.framework.common.objects.OperationOptionInfo;
import org.identityconnectors.framework.common.objects.Schema;
import org.identityconnectors.framework.impl.api.local.LocalConnectorInfoImpl;
import org.identityconnectors.framework.spi.Connector;
import org.identityconnectors.framework.spi.operations.SchemaOp;

/**
 * A ConnectorDocBuilder .
 *
 * @author Laszlo Hordos
 */
public class ConnectorDocBuilder {

    private final ConnectorMojoBridge handler;

    public ConnectorDocBuilder(ConnectorMojoBridge bridge) {
        this.handler = bridge;
    }

    protected static final List<Class<? extends APIOperation>> OBJECTCLASS_OPERATIONS = CollectionUtil.newList(
            AuthenticationApiOp.class, CreateApiOp.class, DeleteApiOp.class, ResolveUsernameApiOp.class,
            SearchApiOp.class, SyncApiOp.class, UpdateApiOp.class);

    protected static final List<Class<? extends APIOperation>> OPERATIONS = CollectionUtil.newList(
            AuthenticationApiOp.class, CreateApiOp.class, DeleteApiOp.class, ResolveUsernameApiOp.class,
            SchemaApiOp.class, ScriptOnConnectorApiOp.class, ScriptOnResourceApiOp.class, SearchApiOp.class,
            SyncApiOp.class, TestApiOp.class, UpdateApiOp.class);

    protected static final Map<Class<? extends APIOperation>, Pair<String, String>> OP_DICTIONARY;

    static {
        Map<Class<? extends APIOperation>, Pair<String, String>> dictionary = new HashMap<Class<? extends APIOperation>, Pair<String, String>>();
        dictionary.put(AuthenticationApiOp.class,
                Pair.of(AuthenticationApiOp.class.getSimpleName(), "Authenticate"));
        dictionary.put(CreateApiOp.class, Pair.of(CreateApiOp.class.getSimpleName(), "Create"));
        dictionary.put(DeleteApiOp.class, Pair.of(DeleteApiOp.class.getSimpleName(), "Delete"));
        dictionary.put(ResolveUsernameApiOp.class,
                Pair.of(ResolveUsernameApiOp.class.getSimpleName(), "Resolve Username"));
        dictionary.put(SchemaApiOp.class, Pair.of(SchemaApiOp.class.getSimpleName(), "Schema"));
        dictionary.put(ScriptOnConnectorApiOp.class,
                Pair.of(ScriptOnConnectorApiOp.class.getSimpleName(), "Script on Connector"));
        dictionary.put(ScriptOnResourceApiOp.class,
                Pair.of(ScriptOnResourceApiOp.class.getSimpleName(), "Script on Resource"));
        dictionary.put(SearchApiOp.class, Pair.of(SearchApiOp.class.getSimpleName(), "Search"));
        dictionary.put(GetApiOp.class, Pair.of(GetApiOp.class.getSimpleName(), "Read"));
        dictionary.put(SyncApiOp.class, Pair.of(SyncApiOp.class.getSimpleName(), "Sync"));
        dictionary.put(TestApiOp.class, Pair.of(TestApiOp.class.getSimpleName(), "Test"));
        dictionary.put(UpdateApiOp.class, Pair.of(UpdateApiOp.class.getSimpleName(), "Update"));
        OP_DICTIONARY = CollectionUtil.newReadOnlyMap(dictionary);
    }

    /**
     * Execute the generation of the report.
     *
     * @throws org.apache.maven.reporting.MavenReportException
     *             if any
     */
    protected void executeReport() throws MojoExecutionException {
        List<ConnectorInfo> infoList = null;
        try {
            infoList = listAllConnectorInfo();
        } catch (Exception e) {
            handler.getLog().error("Failed to get the ConnectorInfoManager", e);
            return;
        }

        for (ConnectorInfo info : infoList) {
            handler.getLog().debug("Processing ConnectorInfo: " + info.toString());
            try {
                Context context = new VelocityContext();
                context.put("connectorInfo", info);
                context.put("connectorDisplayName", info.getConnectorDisplayName());
                context.put("connectorCategory", info.getConnectorCategory());
                context.put("bookName",
                        handler.getMavenProject().getArtifactId() + "-" + handler.getMavenProject().getVersion());

                String connectorName = info.getConnectorKey().getConnectorName()
                        .substring(info.getConnectorKey().getConnectorName().lastIndexOf('.') + 1).toLowerCase();
                if (connectorName.endsWith("connector")) {
                    connectorName = connectorName.substring(0, connectorName.length() - 9) + "-connector";
                }
                context.put("connectorName", connectorName);
                context.put("uniqueConnectorName", info.getConnectorKey().getConnectorName().replaceAll("\\.", "-")
                        + "-" + info.getConnectorKey().getBundleVersion());

                APIConfiguration config = info.createDefaultAPIConfiguration();
                context.put("APIConfiguration", config);
                try {
                    if (config.getSupportedOperations().contains(SchemaApiOp.class)) {
                        Schema schema = null;
                        try {
                            APIConfiguration facadeConfig = info.createDefaultAPIConfiguration();
                            if (null != handler.getConfigurationProperties()) {
                                handler.getConfigurationProperties()
                                        .mergeConfigurationProperties(facadeConfig.getConfigurationProperties());
                            }
                            schema = ConnectorFacadeFactory.getInstance().newInstance(facadeConfig).schema();
                        } catch (Throwable t) {
                            handler.getLog().debug("Getting Schema with ConnectorFacade", t);
                        }

                        if (null == schema && info instanceof LocalConnectorInfoImpl) {
                            Class<? extends Connector> connectorClass = ((LocalConnectorInfoImpl) info)
                                    .getConnectorClass();
                            try {
                                SchemaOp connector = (SchemaOp) connectorClass.newInstance();
                                schema = connector.schema();
                            } catch (Throwable t) {
                                handler.getLog().debug("Getting Schema with Connector Instance", t);
                            }
                        }

                        if (null != schema) {

                            SortedMap<Pair<String, String>, List<Map<String, Object>>> operationOptionsMap = new TreeMap<Pair<String, String>, List<Map<String, Object>>>(
                                    PAIR_COMPARATOR);

                            for (Class<? extends APIOperation> op : OPERATIONS) {
                                if (SchemaApiOp.class.equals(op) || TestApiOp.class.equals(op)) {
                                    continue;
                                }
                                List<Map<String, Object>> optionList = null;
                                for (OperationOptionInfo optionInfo : schema.getSupportedOptionsByOperation(op)) {
                                    Map<String, Object> optionInfoMap = new HashMap<String, Object>();
                                    optionInfoMap.put("name", optionInfo.getName());
                                    optionInfoMap.put("type", optionInfo.getType().getSimpleName());
                                    optionInfoMap.put("description",
                                            info.getMessages().format(optionInfo.getName() + ".help",
                                                    "Additional description is not available"));
                                    if (null == optionList) {
                                        optionList = new ArrayList<Map<String, Object>>();
                                    }
                                    optionList.add(optionInfoMap);
                                }
                                if (null != optionList) {
                                    operationOptionsMap.put(OP_DICTIONARY.get(op), optionList);
                                }
                            }

                            List<Map<String, Object>> objectClasses = new ArrayList<Map<String, Object>>();

                            Set<Class<? extends APIOperation>> operationSet = new TreeSet<Class<? extends APIOperation>>(
                                    new Comparator<Class<? extends APIOperation>>() {
                                        public int compare(Class<? extends APIOperation> o1,
                                                Class<? extends APIOperation> o2) {
                                            return String.CASE_INSENSITIVE_ORDER.compare(o1.getCanonicalName(),
                                                    o2.getCanonicalName());
                                        }
                                    });
                            operationSet.addAll(config.getSupportedOperations());
                            operationSet.retainAll(OBJECTCLASS_OPERATIONS);

                            for (ObjectClassInfo objectClassInfo : schema.getObjectClassInfo()) {
                                Map<String, Object> objectClassInfoMap = new HashMap<String, Object>();
                                ObjectClass oc = new ObjectClass(objectClassInfo.getType());
                                objectClassInfoMap.put("name", objectClassInfo.getType());
                                objectClassInfoMap.put("displayName", info.getMessages()
                                        .format(oc.getDisplayNameKey(), oc.getObjectClassValue()));
                                objectClassInfoMap.put("attributes", objectClassInfo.getAttributeInfo());

                                boolean limited = false;
                                List<Pair<String, String>> operations = new ArrayList<Pair<String, String>>();

                                for (Class<? extends APIOperation> op : operationSet) {
                                    if (schema.getSupportedObjectClassesByOperation(op).contains(objectClassInfo)) {
                                        operations.add(OP_DICTIONARY.get(op));
                                    } else {
                                        limited = true;
                                    }
                                }

                                objectClassInfoMap.put("operations", limited ? operations : null);
                                objectClasses.add(objectClassInfoMap);
                            }

                            context.put("schema", schema);
                            context.put("operationOptions", operationOptionsMap);
                            context.put("objectClasses", objectClasses);
                        }
                    }

                } catch (Throwable e) {
                    if (handler.getLog().isDebugEnabled()) {
                        handler.getLog().debug("Getting the default Schema.", e);
                    }
                }

                try {
                    Set<Pair<String, String>> interfaces = new TreeSet<Pair<String, String>>(PAIR_COMPARATOR);
                    for (Class<? extends APIOperation> clazz : config.getSupportedOperations()) {
                        if (OP_DICTIONARY.containsKey(clazz)) {
                            interfaces.add(OP_DICTIONARY.get(clazz));
                        }
                    }
                    context.put("connectorInterfaces", interfaces);
                } catch (Throwable e) {
                    handler.getLog().error("Getting the connector interfaces.", e);
                }

                Map<String, List<Map<String, Object>>> configurationTable = new LinkedHashMap<String, List<Map<String, Object>>>();

                for (String propertyName : config.getConfigurationProperties().getPropertyNames()) {
                    ConfigurationProperty property = config.getConfigurationProperties().getProperty(propertyName);

                    String groupKey = property.getGroup("Configuration");

                    List<Map<String, Object>> configurationGroup = configurationTable.get(groupKey);
                    if (configurationGroup == null) {
                        configurationGroup = new ArrayList<Map<String, Object>>();
                        configurationTable.put(groupKey, configurationGroup);
                    }

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

                    propertyMap.put("name", propertyName);
                    propertyMap.put("type", property.getType().getSimpleName());
                    propertyMap.put("property", property);
                    propertyMap.put("required", property.isRequired());
                    propertyMap.put("operations", convertOperations(property.getOperations()));
                    propertyMap.put("confidential", property.isConfidential());
                    propertyMap.put("description",
                            convertHTMLtoDocBook(property.getHelpMessage("Description is not available")));

                    configurationGroup.add(propertyMap);
                }

                context.put("configurationProperties", configurationTable);

                context.put("connectorPoolingSupported", config.isConnectorPoolingSupported());

                context.put("PathTool", new PathTool());

                context.put("FileUtils", new FileUtils());

                context.put("StringUtils", new org.codehaus.plexus.util.StringUtils());

                context.put("ConnectorUtils", new ConnectorUtils());

                context.put("i18n", handler.getI18N());

                context.put("project", handler.getMavenProject());

                handler.generate(this, context, connectorName);

            } catch (ResourceNotFoundException e) {
                throw new MojoExecutionException("Resource not found.", e);
            } catch (VelocityException e) {
                throw new MojoExecutionException(e.toString(), e);
            }
        }
    }

    private String convertHTMLtoDocBook(String source) {
        return source;
    }

    private Set<Pair<String, String>> convertOperations(Set<Class<? extends APIOperation>> operations) {
        if (null != operations) {
            Set<Pair<String, String>> operationNames = new TreeSet<Pair<String, String>>(PAIR_COMPARATOR);
            for (Class<? extends APIOperation> op : operations) {

                operationNames.add(OP_DICTIONARY.get(op));
            }
            return operationNames;
        }
        return null;
    }

    /**
     * Create the velocity template
     *
     * @param context
     *            velocity context that has the parameter values
     * @param templateName
     *            velocity template which will the context be merged
     * @throws org.apache.velocity.exception.ResourceNotFoundException
     *             , VelocityException, IOException
     */
    public void processTemplate(VelocityEngine engine, Context context, String templateName, Writer writer)
            throws MojoExecutionException {
        try {

            engine.setApplicationAttribute("baseDirectory", handler.getBasedir());

            String sourceEncoding = handler.getSourceEncoding();
            if (StringUtils.isEmpty(sourceEncoding)) {
                sourceEncoding = ReaderFactory.FILE_ENCODING;
                handler.getLog().warn("File encoding has not been set, using platform encoding "
                        + handler.getSourceEncoding() + ", i.e. build is platform dependent!");
            }

            Template velocityTemplate = engine.getTemplate(handler.getTemplateDirectory() + "/" + templateName,
                    sourceEncoding);

            velocityTemplate.merge(context, writer);

        } catch (ResourceNotFoundException e) {
            throw new ResourceNotFoundException(
                    "Template not found. ( " + handler.getTemplateDirectory() + "/" + templateName + " )");
        } catch (VelocityException e) {
            throw new VelocityException(e.toString());
        } catch (Exception e) {
            if (e.getCause() != null) {
                handler.getLog().warn(e.getCause());
            }
            throw new MojoExecutionException(e.toString(), e.getCause());
        }
    }

    public List<ConnectorInfo> listAllConnectorInfo() throws MojoExecutionException {
        List<ConnectorInfo> infos = new ArrayList<ConnectorInfo>();
        PatternIncludesArtifactFilter includesFilter = handler.getIncludes() == null ? null
                : new PatternIncludesArtifactFilter(Arrays.asList(handler.getIncludes()));
        PatternExcludesArtifactFilter excludesFilter = handler.getExcludes() == null ? null
                : new PatternExcludesArtifactFilter(Arrays.asList(handler.getExcludes()));

        for (ConnectorInfo info : getConnectorInfoManager().getConnectorInfos()) {
            Artifact artifact = new DefaultArtifact(info.getConnectorKey().getBundleName(),
                    info.getConnectorKey().getConnectorName(), info.getConnectorKey().getBundleVersion(), "compile",
                    info.getConnectorKey().getBundleVersion(), "", null);

            if (includesFilter != null && !includesFilter.include(artifact)) {
                handler.getLog().debug(
                        "Connector " + info.getConnectorKey() + " ignored as it does not match include rules.");
                continue;
            }

            if (excludesFilter != null && !excludesFilter.include(artifact)) {
                handler.getLog().debug(
                        "Connector " + info.getConnectorKey() + " ignored as it does matches exclude rules.");
                continue;
            }

            handler.getLog().info("Processing ConnectorInfo: " + info.getConnectorKey());
            infos.add(info);

        }
        return infos;
    }

    public ConnectorInfoManager getConnectorInfoManager() throws MojoExecutionException {
        if (null != handler.getRemoteFrameworkConnectionInfo()) {
            return ConnectorInfoManagerFactory.getInstance()
                    .getRemoteManager(handler.getRemoteFrameworkConnectionInfo());
        } else if (handler.getBuildOutputDirectory().exists()) {
            try {
                return ConnectorInfoManagerFactory.getInstance()
                        .getLocalManager(handler.getBuildOutputDirectory().toURI().toURL());
            } catch (MalformedURLException e) {
                throw new MojoExecutionException(e.getMessage(), e);
            }
        } else if ("pom".equalsIgnoreCase(handler.getMavenProject().getPackaging())) {
            throw new MojoExecutionException("The required remoteFrameworkConnectionInfo is missing");
        } else {
            throw new MojoExecutionException("The " + handler.getBuildOutputDirectory() + " is not exist");
        }
    }

    private static final Comparator<Pair<String, String>> PAIR_COMPARATOR = new Comparator<Pair<String, String>>() {
        public int compare(Pair<String, String> left, Pair<String, String> right) {
            if (left == null && right == null) {
                return 0;
            } else if (left == null) {
                return -1;
            } else if (right == null) {
                return 1;
            }
            return String.CASE_INSENSITIVE_ORDER.compare(left.first, right.first);
        }
    };

}