alsutton/enterprisepasswordsafe

View on GitHub
src/main/java/com/enterprisepasswordsafe/database/vendorspecific/AbstractDAL.java

Summary

Maintainability
A
0 mins
Test Coverage
C
71%
/*
 * Copyright (c) 2017 Carbon Security Ltd. <opensource@carbonsecurity.co.uk>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package com.enterprisepasswordsafe.database.vendorspecific;

import com.enterprisepasswordsafe.database.BOMFactory;
import com.enterprisepasswordsafe.database.schema.ColumnSpecification;
import com.enterprisepasswordsafe.database.schema.IndexSpecification;
import com.enterprisepasswordsafe.database.schema.TableSpecification;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Interface implemented by all Database Abstraction Layer classes.
 */

public abstract class AbstractDAL
    implements DALInterface
{
    /**
     * Type translation map, maps the specified type to a database specific
     * type.
     */

    protected final Map<Integer,String> translationMap = new HashMap<>();

    /**
     * Whether or not the database expects the word column in the add clause.
     */

    private  boolean usesColumnOnAdd = true;

    /**
     * Whether or not the database supports the unique constraint.
     */

    protected boolean supportsUnique = true;

    /**
     * The connection to the database
     */
    protected Connection connection;

    @Override
    public String addCreationURLParameters(final String url) {
        return url;
    }

    /**
     * Set the database connection.
     *
     * @param theConnection The connection to the database.
     */

    @Override
    public void setConnection( final Connection theConnection ) {
        connection = theConnection;
    }

    /**
     * Gets the current database connection.
     *
     * @return A java.sql.Connection object.
     */

    @Override
    public Connection getConnection()
        throws SQLException {
        if(connection == null) {
            connection = BOMFactory.getCurrentConntection();
        }

        return connection;
    }

    /**
     * Places the code to start a table creation into a StringBuffer
     *
     * @param buffer The buffer to use.
     * @param spec The specification holding the table definition.
     */

    protected void startTableDefinition( final StringBuilder buffer, final TableSpecification spec )
    {
        buffer.append( "create table " );
        buffer.append( spec.getName() );
        buffer.append( '(' );
    }

    /**
     * Places the code to end a table creation into a StringBuffer
     *
     * @param buffer The buffer to use.
     */

    protected void endTableDefinition( final StringBuilder buffer )
    {
        buffer.append( ')' );
    }

    /**
     * Places the code to add a column to a table definition.
     *
     * @param buffer The buffer to use.
     * @param spec The specifications for the columns in the table.
     */

    protected void addColumnDefinition( final StringBuilder buffer, final ColumnSpecification spec )
    {
        buffer.append( spec.getName() );
        buffer.append( ' ' );
        buffer.append( translationMap.get(spec.getType()) );

        if( spec.getRejectNulls() )
        {
            buffer.append( " NOT NULL" );
        }

        if( spec.getUniqueOnly() && supportsUnique )
        {
            buffer.append( " UNIQUE" );
        }
    }

    /**
     * Creates a table in the database.
     *
     * @param spec The database specification.
     *
     * @throws SQLException If there is problem with the database.
     */

    @Override
    public void createTable( final TableSpecification spec )
        throws SQLException
    {
        StringBuilder sqlBuffer = new StringBuilder("select * from ");
        sqlBuffer.append(spec.getName());
        try (Statement stmt = getConnection().createStatement()) {
            try (ResultSet rs = stmt.executeQuery(sqlBuffer.toString())) {
                if (rs.next()) {
                    Logger.getAnonymousLogger().log(Level.WARNING, "Warning; Attempt to create " + spec.getName() + " blocked, table already exists.");
                    return;
                }
            }
        } catch (SQLException sqle) {
            // SQL Exception is good, it means the table doesn't exist.
        }

        StringBuilder creationCommand = new StringBuilder( 1024 );

        startTableDefinition( creationCommand, spec );

        for(ColumnSpecification thisSpec : spec.getColumnSpecifications()) {
            addColumnDefinition( creationCommand, thisSpec );
            creationCommand.append( ',' );
        }
        creationCommand.deleteCharAt(creationCommand.length()-1);


        endTableDefinition( creationCommand );

        runSQLUpdate(creationCommand.toString());

        // Create all of the indexes.
        for(IndexSpecification indexSpec : spec.getIndexSpecifications()) {
            addIndex(indexSpec );
        }
    }

    /**
     * Adds a column to a given table
     *
     * @param tableName The name of the table to alter.
     * @param spec The specification of the column to add.
     *
     * @throws SQLException Thrown if there is a problem adding the column.
     */

    @Override
    public void addColumn( final String tableName,
            final ColumnSpecification spec )
        throws SQLException
    {
        StringBuilder commandBuffer = new StringBuilder( 512 );

        commandBuffer.append( "ALTER TABLE " );
        commandBuffer.append( tableName );
        commandBuffer.append( " ADD " );

        if( getUsesColumnOnAdd() )
        {
            commandBuffer.append( "COLUMN " );
        }

        addColumnDefinition( commandBuffer, spec );

        runSQLUpdate(commandBuffer.toString());
    }

    /**
     * Creates an index of a set of columns.
     *
     * @param specification The index specification.
     *
     * @throws SQLException Thrown if there is a problem with the database.
     */

    @Override
    public void addIndex( final IndexSpecification specification)
        throws SQLException
    {
        StringBuilder commandBuffer = new StringBuilder( 512 );

        commandBuffer.append( "CREATE ");
        if( specification.isUnique() )
        {
            commandBuffer.append( "UNIQUE ");
        }
        commandBuffer.append( "INDEX ");
        commandBuffer.append( specification.getIndexName() );
        commandBuffer.append( " ON ");
        commandBuffer.append( specification.getTableName() );
        commandBuffer.append( " (" );

        for(String columnName : specification.getColumns() ) {
            commandBuffer.append( columnName );
            commandBuffer.append( ',' );
        }

        commandBuffer.deleteCharAt(commandBuffer.length()-1);
        commandBuffer.append( ')' );

        runSQLUpdate(commandBuffer.toString());
    }

    /**
     * Send a query to the database
     *
     * @param sql The SQL to run.
     *
     * @return true if there were any results.
     *
     * @throws SQLException Thrown if there is a problem talking to the database.
     */

    @Override
    public boolean runSQLQuery( final String sql )
        throws SQLException {
        try(Statement stmt = getConnection().createStatement()) {
            stmt.setMaxRows(1);
            try(ResultSet rs = stmt.executeQuery( sql )) {
                return rs.next();
            }
        }
    }

    /**
     * Send a command to the database
     *
     * @param sql The SQL to run.
     *
     * @throws SQLException Thrown if there is a problem talking to the database.
     */

    @Override
    public void runSQLUpdate( String sql )
        throws SQLException {
        try(Statement stmt = getConnection().createStatement()) {
            stmt.executeUpdate( sql );
        }
    }

    /**
     * Get whether or not the DAL needs add column or not.
     *
     * @return true if the DAL needs "add column", false if not.
     */
    public boolean getUsesColumnOnAdd() {
        return usesColumnOnAdd;
    }

    /**
     * Set whether or not the DAL needs add column or not.
     *
     * @param newUsesColumnOnAdd true if the DAL needs "add column", false if not.
     */
    public void setUsesColumnOnAdd(boolean newUsesColumnOnAdd) {
        usesColumnOnAdd = newUsesColumnOnAdd;
    }

}