databaseadapter.GenerateMojo.java Source code

Java tutorial

Introduction

Here is the source code for databaseadapter.GenerateMojo.java

Source

package databaseadapter;

/*
 * Copyright 2001-2005 The Apache Software Foundation.
 *
 * 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.
 */

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.StringWriter;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.apache.commons.lang.text.StrSubstitutor;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;

/**
 * Echos an object string to the output screen.
 * 
 * @goal generate
 * @phase generate-sources
 * 
 * @requiresDependencyResolution runtime
 * @requiresProject false
 */
public class GenerateMojo extends AbstractMojo {

    /**
     * @parameter default-value="${project}"
     * @required
     * @readonly
     */
    private MavenProject project;

    /**
     * Represents the JDBC driver class name to load.
     * 
     * @parameter expression="${generate.driverClass}"
     * @required
     */
    private String driverClass;

    /**
     * Represents the URL of the database.
     * 
     * @parameter expression="${generate.url}"
     * @required
     */
    private String url;

    /**
     * Represents the username to use in order to connect to the database..
     * 
     * @parameter expression="${generate.username}"
     */
    private String username;

    /**
     * Represents the password to use in order to connect to the database..
     * 
     * @parameter expression="${generate.password}"
     */
    private String password;

    /**
     * Represents other connection properties to use.
     * <p>
     * They dependes strictly on JDBC driver.
     * </p>
     * 
     * @parameter
     */
    private Properties connectionProperties;

    /**
     * Represents the pattern of the schema(s) to read (e.g. {@code MYSCH%}).
     * 
     * @parameter expression="${generate.schemaPattern}" default-value="%"
     */
    private String schemaPattern;

    /**
     * Represents the patterns to use in order to include tables in the process.
     * 
     * @parameter alias="includes"
     */
    private String[] includes;

    /**
     * Represents the patterns to use in order to exclude tables in the process.
     * 
     * @parameter alias="excludes"
     */
    private String[] excludes;

    /**
     * Represents the templates to use to generate code.
     * 
     * @parameter alias="templates"
     */
    private Template[] templates;

    private Connection connection;

    public GenerateMojo() {
        super();
    }

    GenerateMojo(String driverClass, String url, String username, String password, String schemaPattern,
            String[] includes, String[] excludes, Template[] templates) throws MojoExecutionException {
        super();
        this.driverClass = driverClass;
        this.url = url;
        this.username = username;
        this.password = password;
        this.schemaPattern = schemaPattern;
        this.includes = includes;
        this.excludes = excludes;
        this.templates = templates;

        initialize();
    }

    public void execute() throws MojoExecutionException, MojoFailureException {
        initialize();

        Collection<Table> tables = collectTables();
        Map<Table, List<Column>> tablesColumns = collectColumns(tables);

        for (Map.Entry<Table, List<Column>> entry : tablesColumns.entrySet()) {
            Table table = entry.getKey();
            List<Column> columns = entry.getValue();
            table.setColumns(columns);

            getLog().debug("... table " + table + ":");
            for (Column col : columns)
                getLog().debug("    \\--> column " + col);
        }

        for (Template template : templates) {
            if (template.isForeach())
                for (Table table : tables) {
                    String code = generateCode(table, template);
                    File outputDirectory = template.getOutputDirectory();
                    if (outputDirectory == null) {
                        outputDirectory = new File(project.getBuild().getDirectory(), "generated-sources");
                        outputDirectory = new File(outputDirectory, "databaseadapter");
                    }

                    String[] subfolders = StringUtils.split(template.getPackage(), '.');
                    for (String subfolder : subfolders)
                        outputDirectory = new File(outputDirectory, subfolder);

                    if (!outputDirectory.exists())
                        outputDirectory.mkdirs();
                    File outputFile = new File(outputDirectory, tableToClassName(table, template) + ".java");
                    try {
                        FileUtils.writeStringToFile(outputFile, code);
                    } catch (IOException e) {
                        throw new RuntimeException("Unable to write generated code into file '" + outputFile
                                + "' due to a " + e.getClass().getName() + " with message '" + e.getMessage() + "'",
                                e);
                    }
                }
            else {
                String code = generateCode(tables, template);
                File outputDirectory = template.getOutputDirectory();
                if (outputDirectory == null) {
                    outputDirectory = new File(project.getBuild().getDirectory(), "generated-sources");
                    outputDirectory = new File(outputDirectory, "databaseadapter");
                }

                String[] subfolders = StringUtils.split(template.getPackage(), '.');
                for (String subfolder : subfolders)
                    outputDirectory = new File(outputDirectory, subfolder);

                if (!outputDirectory.exists())
                    outputDirectory.mkdirs();
                File outputFile = new File(outputDirectory, template.getClassName() + ".java");
                try {
                    FileUtils.writeStringToFile(outputFile, code);
                } catch (IOException e) {
                    throw new RuntimeException("Unable to write generated code into file '" + outputFile
                            + "' due to a " + e.getClass().getName() + " with message '" + e.getMessage() + "'", e);
                }
            }
        }
    }

    private void initialize() throws MojoExecutionException {
        Validate.notEmpty(driverClass, "'driverClass' parameter is required");
        Validate.notEmpty(url, "'url' parameter is required");
        Validate.notEmpty(templates, "'templates' parameter is required");

        if (templates != null) {
            for (Template template : templates) {
                getLog().info(template.toString());
            }
        }

        getLog().info("Generating database adapter classes against database at URL '" + url + "'...");
        getLog().debug("... connecting using driver '" + driverClass + "' and credentials [" + username + "/"
                + password + "]...");

        loadJDBCDriver();
        connection = newConnection();
    }

    protected Collection<Table> collectTables() throws MojoExecutionException {
        ResultSet rstables = null;
        try {
            List<Table> tables = new LinkedList<Table>();
            DatabaseMetaData metaData = connection.getMetaData();
            rstables = metaData.getTables(null, schemaPattern, "%", new String[] { "TABLE" });
            while (rstables.next()) {
                String schema = rstables.getString("TABLE_SCHEM");
                String table = rstables.getString("TABLE_NAME");
                String type = rstables.getString("TABLE_TYPE");
                String remarks = rstables.getString("REMARKS");
                getLog().debug("... found table " + schema + "." + table + " (" + type + ")...");
                if (isToInclude(table)) {
                    if (!isToExclude(table)) {
                        getLog().debug("... processing...");
                        Table t = new Table(table, remarks);
                        tables.add(t);

                    } else {
                        getLog().debug("... skipping due to 'excludes' pattern(s)");
                    }
                } else {
                    getLog().debug("... skipping due to 'includes' pattern(s)");
                }
            }

            getLog().info("... found to process " + tables.size() + " tables: " + tables);
            return tables;
        } catch (SQLException e) {
            throw new MojoExecutionException("Unable to generate database adapter due to a '"
                    + e.getClass().getName() + "' with message '" + e.getMessage() + "'", e);
        } finally {
            if (rstables != null) {
                try {
                    rstables.close();
                } catch (SQLException ignore) {
                }
            }
        }
    }

    protected Map<Table, List<Column>> collectColumns(Collection<Table> tables) throws MojoExecutionException {
        ResultSet rscolumns = null;
        try {
            Map<Table, List<Column>> map = new LinkedHashMap<Table, List<Column>>();
            DatabaseMetaData metaData = connection.getMetaData();
            rscolumns = metaData.getColumns(null, schemaPattern, "%", "%");
            while (rscolumns.next()) {
                String tableName = rscolumns.getString("TABLE_NAME");
                Table table = find(tables, tableName);
                if (table == null)
                    continue;

                List<Column> columns = map.get(table);
                if (columns == null) {
                    columns = new LinkedList<Column>();
                    map.put(table, columns);
                }

                String columnName = rscolumns.getString("COLUMN_NAME");
                int dataType = rscolumns.getInt("DATA_TYPE");
                int columnSize = rscolumns.getInt("COLUMN_SIZE");
                int decimalDigits = rscolumns.getInt("DECIMAL_DIGITS");
                int nullable = rscolumns.getInt("NULLABLE");
                String remarks = rscolumns.getString("REMARKS");

                Column column = new Column(columnName, dataType, columnSize, decimalDigits,
                        nullable == DatabaseMetaData.columnNullable, remarks);
                columns.add(column);
            }

            return map;
        } catch (SQLException e) {
            throw new MojoExecutionException("Unable to generate database adapter due to a '"
                    + e.getClass().getName() + "' with message '" + e.getMessage() + "'", e);
        } finally {
            if (rscolumns != null) {
                try {
                    rscolumns.close();
                } catch (SQLException ignore) {
                }
            }
        }
    }

    protected String generateCode(Collection<Table> tables, Template template) {
        try {
            VelocityContext context = new VelocityContext();
            context.put("package", template.getPackage());
            context.put("className", template.getClassName());
            context.put("tables", tables);
            context.put("stringUtils", new databaseadapter.StringUtils());

            StringWriter writer = new StringWriter();
            Velocity.evaluate(context, writer, "databaseadapter-" + template.getClassName(),
                    new FileReader(template.getSource()));
            return writer.toString();
        } catch (ParseErrorException e) {
            throw new RuntimeException("unexpected error", e);
        } catch (MethodInvocationException e) {
            throw new RuntimeException("unexpected error", e);
        } catch (ResourceNotFoundException e) {
            throw new RuntimeException("unexpected error", e);
        } catch (FileNotFoundException e) {
            throw new RuntimeException("unexpected error", e);
        }
    }

    protected String generateCode(Table table, Template template) {
        try {
            VelocityContext context = new VelocityContext();
            context.put("package", template.getPackage());
            context.put("table", table);
            context.put("stringUtils", new databaseadapter.StringUtils());

            String className = tableToClassName(table, template);
            context.put("className", className);

            StringWriter writer = new StringWriter();
            Velocity.evaluate(context, writer, "databaseadapter-" + className,
                    new FileReader(template.getSource()));
            return writer.toString();
        } catch (ParseErrorException e) {
            throw new RuntimeException("unexpected error", e);
        } catch (MethodInvocationException e) {
            throw new RuntimeException("unexpected error", e);
        } catch (ResourceNotFoundException e) {
            throw new RuntimeException("unexpected error", e);
        } catch (FileNotFoundException e) {
            throw new RuntimeException("unexpected error", e);
        }
    }

    private Table find(Collection<Table> tables, String tableName) {
        for (Table table : tables)
            if (table.named(tableName))
                return table;
        return null;
    }

    private void loadJDBCDriver() throws MojoExecutionException {
        try {
            Class.forName(driverClass);
        } catch (ClassNotFoundException e) {
            throw new MojoExecutionException("Unable to find a JDBC driver class named '" + driverClass
                    + "'. Please check configuration of databaseadapter-maven-plugin", e);
        }
    }

    @SuppressWarnings("unchecked")
    private Connection newConnection() throws MojoExecutionException {
        try {
            Properties props = new Properties();
            props.setProperty("user", username);
            props.setProperty("password", password);
            if (connectionProperties != null) {
                Enumeration<String> propertyNames = (Enumeration<String>) connectionProperties.propertyNames();
                while (propertyNames.hasMoreElements()) {
                    String property = propertyNames.nextElement();
                    props.setProperty(property, connectionProperties.getProperty(property));
                }
            }
            return DriverManager.getConnection(url, props);
        } catch (SQLException e) {
            throw new MojoExecutionException(
                    "Unable to get a database connection using URL '" + url + "' and credentials [" + username + "/"
                            + password + "]. Please check configuration of databaseadapter-maven-plugin",
                    e);
        }
    }

    private boolean isToInclude(String table) {
        if (includes == null || includes.length == 0)
            return true;

        for (String regex : includes) {
            getLog().debug("... checking table '" + table + "' against regex '" + regex
                    + "' in order to check if it is to be included...");

            Matcher matcher = Pattern.compile(regex, Pattern.CASE_INSENSITIVE).matcher(table);
            if (matcher.matches())
                return true;
        }

        return false;
    }

    private boolean isToExclude(String table) {
        if (excludes == null || excludes.length == 0)
            return false;

        for (String regex : excludes) {
            getLog().debug("... checking table '" + table + "' against regex '" + regex
                    + "' in order to check if it is to be excluded...");

            Matcher matcher = Pattern.compile(regex, Pattern.CASE_INSENSITIVE).matcher(table);
            if (matcher.matches())
                return true;
        }

        return false;
    }

    private String tableToClassName(Table table, Template template) {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("tableName", table.getName());
        map.put("tableNameAsCamelCase", databaseadapter.StringUtils.toCamelCase(table.getName()));
        String className = StrSubstitutor.replace(template.getClassName(), map);
        return className;
    }

}