Package org.apache.ojb.broker.util.dbhandling

Source Code of org.apache.ojb.broker.util.dbhandling.TorqueDBHandling

package org.apache.ojb.broker.util.dbhandling;

/* Copyright 2004-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.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.StringTokenizer;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
import org.apache.ojb.broker.platforms.PlatformException;
import org.apache.ojb.broker.util.logging.LoggerFactory;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.SQLExec;
import org.apache.tools.ant.types.FileSet;
import org.apache.torque.task.TorqueDataModelTask;
import org.apache.torque.task.TorqueSQLExec;
import org.apache.torque.task.TorqueSQLTask;

/**
* Provides basic database handling (drop, create, init) via torque.
*
* @author Thomas Dudziak
*/
public class TorqueDBHandling implements DBHandling
{
    /** Torque db platforms */
    protected static final String TORQUE_PLATFORM_DB2        = "db2";
    protected static final String TORQUE_PLATFORM_HYPERSONIC = "hypersonic";
    protected static final String TORQUE_PLATFORM_INTERBASE  = "interbase";
    protected static final String TORQUE_PLATFORM_MSSQL      = "mssql";
    protected static final String TORQUE_PLATFORM_MYSQL      = "mysql";
    protected static final String TORQUE_PLATFORM_ORACLE     = "oracle";
    protected static final String TORQUE_PLATFORM_POSTGRESQL = "postgresql";
    protected static final String TORQUE_PLATFORM_SAPDB      = "sapdb";
    protected static final String TORQUE_PLATFORM_SYBASE     = "sybase";

    /** The name of the db-creation script */
    private static final String CREATION_SCRIPT_NAME = "create-db.sql";
    /** The name of the torque database mapping file */
    private static final String SQL_DB_MAP_NAME      = "sqldb.map";

    /** Mapping from ojb dbms to torque database setting */
    private static HashMap _dbmsToTorqueDb = new HashMap();
   
    static
    {
        _dbmsToTorqueDb.put("db2",         TORQUE_PLATFORM_DB2);
        _dbmsToTorqueDb.put("hsqldb",      TORQUE_PLATFORM_HYPERSONIC);
        _dbmsToTorqueDb.put("firebird",    TORQUE_PLATFORM_INTERBASE);
        _dbmsToTorqueDb.put("mssqlserver", TORQUE_PLATFORM_MSSQL);
        _dbmsToTorqueDb.put("mysql",       TORQUE_PLATFORM_MYSQL);
        _dbmsToTorqueDb.put("oracle",      TORQUE_PLATFORM_ORACLE);
        _dbmsToTorqueDb.put("oracle9i",    TORQUE_PLATFORM_ORACLE);
        _dbmsToTorqueDb.put("postgresql",  TORQUE_PLATFORM_POSTGRESQL);
        _dbmsToTorqueDb.put("sapdb",       TORQUE_PLATFORM_SAPDB);
        _dbmsToTorqueDb.put("sybaseasa",   TORQUE_PLATFORM_SYBASE);
        _dbmsToTorqueDb.put("sybasease",   TORQUE_PLATFORM_SYBASE);
        _dbmsToTorqueDb.put("sybase",      TORQUE_PLATFORM_SYBASE);
    }
   
    /** The jdbc connection for communicating with the db */
    private JdbcConnectionDescriptor _jcd;
    /** The target database */
    private String _targetDatabase;
    /** The directory where we work in */
    private File _workDir;
    /** The compressed contents of the torque schemata */
    private HashMap _torqueSchemata = new HashMap();
    /** The compressed content of the creation script */
    private byte[] _creationScript;
    /** The compressed contents of the db initialization scripts */
    private HashMap _initScripts = new HashMap();

    /**
     * Creates a new handling object.
     */
    public TorqueDBHandling()
    {}

    /**
     * Sets the jdbc connection to use.
     *
     * @param jcd The connection to use
     * @throws PlatformException If the target database cannot be handled with torque
     */
    public void setConnection(JdbcConnectionDescriptor jcd) throws PlatformException
    {
        _jcd = jcd;

        String targetDatabase = (String)_dbmsToTorqueDb.get(_jcd.getDbms().toLowerCase());

        if (targetDatabase == null)
        {
            throw new PlatformException("Database "+_jcd.getDbms()+" is not supported by torque");
        }
        if (!targetDatabase.equals(_targetDatabase))
        {
            _targetDatabase = targetDatabase;
            _creationScript = null;
            _initScripts.clear();
        }
    }

    /**
     * Returns the connection descriptor used by this handling object.
     *
     * @return The connection descriptor
     */
    public JdbcConnectionDescriptor getConnection()
    {
        return _jcd;
    }

    /**
     * Returns the torque database platform used.
     *
     * @return The target db platform
     */
    public String getTargetTorquePlatform()
    {
        return _targetDatabase;
    }

    /**
     * Adds the input files (in our case torque schema files) to use.
     *
     * @param srcDir          The directory containing the files
     * @param listOfFilenames The filenames in a comma-separated list
     */
    public void addDBDefinitionFiles(String srcDir, String listOfFilenames) throws IOException
    {
        StringTokenizer tokenizer = new StringTokenizer(listOfFilenames, ",");
        File            dir       = new File(srcDir);
        String          filename;
       
        while (tokenizer.hasMoreTokens())
        {
            filename = tokenizer.nextToken().trim();
            if (filename.length() > 0)
            {   
                 _torqueSchemata.put("schema"+_torqueSchemata.size()+".xml",
                                     readTextCompressed(new File(dir, filename)));
            }
        }
    }

    /**
     * Adds an input stream of a db definition (in our case of a torque schema file).
     *
     * @param schemaStream The input stream
     */
    public void addDBDefinitionFile(InputStream schemaStream) throws IOException
    {
        _torqueSchemata.put("schema"+_torqueSchemata.size()+".xml",
                            readStreamCompressed(schemaStream));
    }

    /**
     * Writes the torque schemata to files in the given directory and returns
     * a comma-separated list of the filenames.
     *
     * @param dir The directory to write the files to
     * @return The list of filenames
     * @throws IOException If an error occurred
     */
    private String writeSchemata(File dir) throws IOException
    {
        writeCompressedTexts(dir, _torqueSchemata);

        StringBuffer includes = new StringBuffer();

        for (Iterator it = _torqueSchemata.keySet().iterator(); it.hasNext();)
        {
            includes.append((String)it.next());
            if (it.hasNext())
            {
                includes.append(",");
            }
        }
        return includes.toString();
    }
   
    /**
     * Creates the db-creation sql script (but does not perform it).
     *
     * @throws PlatformException If some error occurred
     */
    public void createCreationScript() throws PlatformException
    {
        Project             project    = new Project();
        TorqueDataModelTask modelTask  = new TorqueDataModelTask();
        File                tmpDir     = null;
        File                scriptFile = null;
       
        _creationScript = null;
        try
        {
            tmpDir = new File(getWorkDir(), "schemas");
            tmpDir.mkdir();

            String includes = writeSchemata(tmpDir);
           
            scriptFile = new File(tmpDir, CREATION_SCRIPT_NAME);

            project.setBasedir(tmpDir.getAbsolutePath());

            // populating with defaults
            modelTask.setProject(project);
            modelTask.setUseClasspath(true);
            modelTask.setControlTemplate("sql/db-init/Control.vm");
            modelTask.setOutputDirectory(tmpDir);
            modelTask.setOutputFile(CREATION_SCRIPT_NAME);
            modelTask.setTargetDatabase(_targetDatabase);

            FileSet files = new FileSet();

            files.setDir(tmpDir);
            files.setIncludes(includes);
            modelTask.addFileset(files);
            modelTask.execute();

            _creationScript = readTextCompressed(scriptFile);

            deleteDir(tmpDir);
        }
        catch (Exception ex)
        {
            // clean-up
            if ((tmpDir != null) && tmpDir.exists())
            {
                deleteDir(tmpDir);
            }
            throw new PlatformException(ex);
        }
    }
   
    /**
     * Creates the database.
     *
     * @throws PlatformException If some error occurred
     */
    public void createDB() throws PlatformException
    {
        if (_creationScript == null)
        {
            createCreationScript();
        }

        Project             project    = new Project();
        TorqueDataModelTask modelTask  = new TorqueDataModelTask();
        File                tmpDir     = null;
        File                scriptFile = null;
       
        try
        {
            tmpDir = new File(getWorkDir(), "schemas");
            tmpDir.mkdir();

            scriptFile = new File(tmpDir, CREATION_SCRIPT_NAME);

            writeCompressedText(scriptFile, _creationScript);

            project.setBasedir(tmpDir.getAbsolutePath());

            // we use the ant task 'sql' to perform the creation script
          SQLExec         sqlTask = new SQLExec();
          SQLExec.OnError onError = new SQLExec.OnError();
 
          onError.setValue("continue");
          sqlTask.setProject(project);
          sqlTask.setAutocommit(true);
          sqlTask.setDriver(_jcd.getDriver());
          sqlTask.setOnerror(onError);
          sqlTask.setUserid(_jcd.getUserName());
          sqlTask.setPassword(_jcd.getPassWord() == null ? "" : _jcd.getPassWord());
          sqlTask.setUrl(getDBCreationUrl());
          sqlTask.setSrc(scriptFile);
          sqlTask.execute();

          deleteDir(tmpDir);
        }
        catch (Exception ex)
        {
            // clean-up
            if ((tmpDir != null) && tmpDir.exists())
            {
                try
                {
                    scriptFile.delete();
                }
                catch (NullPointerException e)
                {
                    LoggerFactory.getLogger(this.getClass()).error("NPE While deleting scriptFile [" + scriptFile.getName() + "]", e);
                }
            }
            throw new PlatformException(ex);
        }
    }

    /**
     * Creates the initialization scripts (creation of tables etc.) but does
     * not perform them.
     *
     * @throws PlatformException If some error occurred
     */
    public void createInitScripts() throws PlatformException
    {
        Project       project   = new Project();
        TorqueSQLTask sqlTask   = new TorqueSQLTask();
        File          schemaDir = null;
        File          sqlDir    = null;
       
        _initScripts.clear();
        try
        {
            File tmpDir = getWorkDir();

            schemaDir = new File(tmpDir, "schemas");
            sqlDir    = new File(tmpDir, "sql");
            schemaDir.mkdir();
            sqlDir.mkdir();

            String includes     = writeSchemata(schemaDir);
            File   sqlDbMapFile = new File(sqlDir, SQL_DB_MAP_NAME);

            sqlDbMapFile.createNewFile();
            project.setBasedir(sqlDir.getAbsolutePath());
           
            // populating with defaults
            sqlTask.setProject(project);
            sqlTask.setUseClasspath(true);
            sqlTask.setBasePathToDbProps("sql/base/");
            sqlTask.setControlTemplate("sql/base/Control.vm");
            sqlTask.setOutputDirectory(sqlDir);
            // we put the report in the parent directory as we don't want
            // to read it in later on
            sqlTask.setOutputFile("../report.sql.generation");
            sqlTask.setSqlDbMap(SQL_DB_MAP_NAME);
            sqlTask.setTargetDatabase(_targetDatabase);

            FileSet files = new FileSet();
           
            files.setDir(schemaDir);
            files.setIncludes(includes);
            sqlTask.addFileset(files);
            sqlTask.execute();

            readTextsCompressed(sqlDir, _initScripts);
            deleteDir(schemaDir);
            deleteDir(sqlDir);
        }
        catch (Exception ex)
        {
            // clean-up
            if ((schemaDir != null) && schemaDir.exists())
            {
                deleteDir(schemaDir);
            }
            if ((sqlDir != null) && sqlDir.exists())
            {
                deleteDir(sqlDir);
            }
            throw new PlatformException(ex);
        }
    }

    /**
     * Creates the tables according to the schema files.
     *
     * @throws PlatformException If some error occurred
     */
    public void initDB() throws PlatformException
    {
        if (_initScripts.isEmpty())
        {
            createInitScripts();
        }

        Project       project   = new Project();
        TorqueSQLTask sqlTask   = new TorqueSQLTask();
        File          outputDir = null;
       
        try
        {
            outputDir = new File(getWorkDir(), "sql");

            outputDir.mkdir();
            writeCompressedTexts(outputDir, _initScripts);

            project.setBasedir(outputDir.getAbsolutePath());

            // executing the generated sql, but this time with a torque task
            TorqueSQLExec         sqlExec = new TorqueSQLExec();
            TorqueSQLExec.OnError onError = new TorqueSQLExec.OnError();

            sqlExec.setProject(project);
            onError.setValue("continue");
            sqlExec.setAutocommit(true);
            sqlExec.setDriver(_jcd.getDriver());
            sqlExec.setOnerror(onError);
            sqlExec.setUserid(_jcd.getUserName());
            sqlExec.setPassword(_jcd.getPassWord() == null ? "" : _jcd.getPassWord());
            sqlExec.setUrl(getDBManipulationUrl());
            sqlExec.setSrcDir(outputDir.getAbsolutePath());
            sqlExec.setSqlDbMap(SQL_DB_MAP_NAME);
            sqlExec.execute();
           
            deleteDir(outputDir);
        }
        catch (Exception ex)
        {
            // clean-up
            if (outputDir != null)
            {
                deleteDir(outputDir);
            }
            throw new PlatformException(ex);
        }
    }

    /**
     * Template-and-Hook method for generating the url required by the jdbc driver
     * to allow for creating a database (as opposed to accessing an already-existing
     * database).
     *
     */
    protected String getDBCreationUrl()
    {
        JdbcConnectionDescriptor jcd = getConnection();

        // currently I only know about specifics for mysql
        if (TORQUE_PLATFORM_MYSQL.equals(getTargetTorquePlatform()))
        {
            // we have to remove the db name as the jdbc driver would try to connect to
            // a non-existing db
            // the db-alias has this form: [host&port]/[dbname]?[options]
            String dbAliasPrefix = jcd.getDbAlias();
            String dbAliasSuffix = "";
            int    questionPos   = dbAliasPrefix.indexOf('?');

            if (questionPos > 0)
            {
                dbAliasSuffix = dbAliasPrefix.substring(questionPos);
                dbAliasPrefix = dbAliasPrefix.substring(0, questionPos);
            }

            int slashPos = dbAliasPrefix.lastIndexOf('/');

            if (slashPos > 0)
            {
                // it is important that the slash at the end is present
                dbAliasPrefix = dbAliasPrefix.substring(0, slashPos + 1);
            }
            return jcd.getProtocol()+":"+jcd.getSubProtocol()+":"+dbAliasPrefix+dbAliasSuffix;
        }
        else if (TORQUE_PLATFORM_POSTGRESQL.equals(getTargetTorquePlatform()))
        {
            // we have to replace the db name with 'template1'
            // the db-alias has this form: [host&port]/[dbname]?[options]
            String dbAliasPrefix = jcd.getDbAlias();
            String dbAliasSuffix = "";
            int    questionPos   = dbAliasPrefix.indexOf('?');

            if (questionPos > 0)
            {
                dbAliasSuffix = dbAliasPrefix.substring(questionPos);
                dbAliasPrefix = dbAliasPrefix.substring(0, questionPos);
            }

            int slashPos = dbAliasPrefix.lastIndexOf('/');

            if (slashPos > 0)
            {
                // it is important that the slash at the end is present
                dbAliasPrefix = dbAliasPrefix.substring(0, slashPos + 1);
            }
            else
            {
                dbAliasPrefix += "/";
            }
            dbAliasPrefix += "template1";
            if (dbAliasSuffix.length() > 0)
            {
                dbAliasPrefix += "/";
            }
            return jcd.getProtocol()+":"+jcd.getSubProtocol()+":"+dbAliasPrefix+dbAliasSuffix;
           
        }
        else
        {
            return jcd.getProtocol()+":"+jcd.getSubProtocol()+":"+jcd.getDbAlias();
        }
    }

    /**
     * Template-and-Hook method for generating the url required by the jdbc driver
     * to allow for modifying an existing database.
     *
     */
    protected String getDBManipulationUrl()
    {
        JdbcConnectionDescriptor jcd = getConnection();

        return jcd.getProtocol()+":"+jcd.getSubProtocol()+":"+jcd.getDbAlias();
    }

    /**
     * Reads the given text file and compressed its content.
     *
     * @param file The file
     * @return A byte array containing the GZIP-compressed content of the file
     * @throws IOException If an error ocurred
     */
    private byte[] readTextCompressed(File file) throws IOException
    {
        return readStreamCompressed(new FileInputStream(file));
    }

    /**
     * Reads the given text stream and compressed its content.
     *
     * @param stream The input stream
     * @return A byte array containing the GZIP-compressed content of the stream
     * @throws IOException If an error ocurred
     */
    private byte[] readStreamCompressed(InputStream stream) throws IOException
    {
        ByteArrayOutputStream bao    = new ByteArrayOutputStream();
        GZIPOutputStream      gos    = new GZIPOutputStream(bao);
        OutputStreamWriter    output = new OutputStreamWriter(gos);
        BufferedReader        input  = new BufferedReader(new InputStreamReader(stream));
        String                line;

        while ((line = input.readLine()) != null)
        {
            output.write(line);
            output.write('\n');
        }
        input.close();
        stream.close();
        output.close();
        gos.close();
        bao.close();
        return bao.toByteArray();
    }

    /**
     * Reads the text files in the given directory and puts their content
     * in the given map after compressing it. Note that this method does not
     * traverse recursivly into sub-directories.
     *
     * @param dir     The directory to process
     * @param results Map that will receive the contents (indexed by the relative filenames)
     * @throws IOException If an error ocurred
     */
    private void readTextsCompressed(File dir, HashMap results) throws IOException
    {
        if (dir.exists() && dir.isDirectory())
        {
            File[] files = dir.listFiles();

            for (int idx = 0; idx < files.length; idx++)
            {
                if (files[idx].isDirectory())
                {
                    continue;
                }
                results.put(files[idx].getName(), readTextCompressed(files[idx]));
            }
        }
    }

    /**
     * Uncompresses the given textual content and writes it to the given file.
     *
     * @param file              The file to write to
     * @param compressedContent The content
     * @throws IOException If an error occurred
     */
    private void writeCompressedText(File file, byte[] compressedContent) throws IOException
    {
        ByteArrayInputStream bais   = new ByteArrayInputStream(compressedContent);
        GZIPInputStream      gis    = new GZIPInputStream(bais);
        BufferedReader       input  = new BufferedReader(new InputStreamReader(gis));
        BufferedWriter       output = new BufferedWriter(new FileWriter(file));
        String               line;

        while ((line = input.readLine()) != null)
        {
            output.write(line);
            output.write('\n');
        }
        input.close();
        gis.close();
        bais.close();
        output.close();
    }
   
    /**
     * Uncompresses the textual contents in the given map and and writes them to the files
     * denoted by the keys of the map.
     *
     * @param dir      The base directory into which the files will be written
     * @param contents The map containing the contents indexed by the filename
     * @throws IOException If an error occurred
     */
    private void writeCompressedTexts(File dir, HashMap contents) throws IOException
    {
        String filename;

        for (Iterator nameIt = contents.keySet().iterator(); nameIt.hasNext();)
        {
            filename = (String)nameIt.next();
            writeCompressedText(new File(dir, filename), (byte[])contents.get(filename));
        }
    }
   
    /**
     * Sets the working directory.
     *
     * @param dir The directory
     * @throws IOException If the directory does not exist or cannot be written/read
     */
    public void setWorkDir(String dir) throws IOException
    {
        File workDir = new File(dir);

        if (!workDir.exists() || !workDir.canWrite() || !workDir.canRead())
        {
            throw new IOException("Cannot access directory "+dir);
        }
        _workDir = workDir;
    }

    /**
     * Returns the temporary directory used by java.
     *
     * @return The temporary directory
     * @throws IOException If an io error occurred
     */
    private File getWorkDir() throws IOException
    {
        if (_workDir == null)
        {   
            File   dummy   = File.createTempFile("dummy", ".log");
            String workDir = dummy.getPath().substring(0, dummy.getPath().lastIndexOf(File.separatorChar));
   
            if ((workDir == null) || (workDir.length() == 0))
            {
                workDir = ".";
            }
            dummy.delete();
            _workDir = new File(workDir);
        }
        return _workDir;
    }

    /**
     * Little helper function that recursivly deletes a directory.
     *
     * @param dir The directory
     */
    private void deleteDir(File dir)
    {
        if (dir.exists() && dir.isDirectory())
        {
            File[] files = dir.listFiles();

            for (int idx = 0; idx < files.length; idx++)
            {
                if (!files[idx].exists())
                {
                    continue;
                }
                if (files[idx].isDirectory())
                {
                    deleteDir(files[idx]);
                }
                else
                {
                    files[idx].delete();
                }
            }
            dir.delete();
        }
    }
}
TOP

Related Classes of org.apache.ojb.broker.util.dbhandling.TorqueDBHandling

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.