All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bridgedb.rdb.construct.DataDerby Maven / Gradle / Ivy

The newest version!
// BridgeDb,
// An abstraction layer for identifier mapping services, both local and online.
// Copyright 2006-2009 BridgeDb developers
//
// 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.
//
package org.bridgedb.rdb.construct;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.bridgedb.IDMapperException;


//import org.pathvisio.debug.Logger;
//import org.pathvisio.debug.StopWatch;

/**
   DBConnector implementation using the Derby driver, with the database in a
   single, uncompressed zip archive.
   While creating, the data is stored in a temporary directory. 
   This directory will be cleaned up when the database is finalized.
*/
//TODO: make sure the temp directory is cleaned up also when this is not finalized
public class DataDerby extends DBConnector
{
	public static final String DB_FILE_EXT_GDB = "bridge";
	public static final String DB_FILE_EXT_GEX = "pgex";

	String getDbExt() {
		switch(getDbType()) {
		case TYPE_GDB: return DB_FILE_EXT_GDB;
		case TYPE_GEX: return DB_FILE_EXT_GEX;
		default: return "";
		}
	}
	
	private static final String DB_NAME_IN_ZIP = "database";
	
	// name of db, what it will be when the db is finalized.
	private String finalDbName;
	
	// while making a database, it is created in a temporary directory,
	private File tempDbSubdir = null;
	private File tempDbParentdir = null;
	
	private boolean finalized;
	
	/**
	 * Generates a correct JDBC connection string.
	 * @return the JDBC connection string. 
	 */
	private String getDbUrl()
	{
		String url = "jdbc:derby:";
		if (finalized)
		{
			url += "jar:(" + finalDbName + ")" + DB_NAME_IN_ZIP;
		} 
		else 
		{
			url += tempDbSubdir;
		}
		return url;
	}
	
	/**
	 * @param props one of PROP_NONE or PROP_RECREATE. PROP_NONE will
	 *   lead to a normal connection, PROP_RECREATE will lead to destroying
	 *   the old database and creating a clean new one.
	 * @param dbName is the file that will be produced finally.
	 * If dbName doesn't end with the right extension, the right extension will be added.
	 * @return the JDBC database Connection.
	 * @throws IDMapperException if the database connection could not be made,
	 * 	or if the Derby driver could not be loaded.
	 */
	public Connection createConnection(String dbName, int props) throws IDMapperException 
	{
		boolean recreate = (props & PROP_RECREATE) != 0;

		// make sure the final Db name ends with the right extension.
		finalDbName = dbName;
		finalized = !recreate;
		
		if(recreate) 
		{
			finalDbName = dbName.endsWith(getDbExt()) ? dbName : dbName + "." + getDbExt();
			try
			{
				tempDbParentdir = FileUtilsGdb.createTempDir("derby", ".tmp");
				tempDbSubdir = new File (tempDbParentdir, "database");
			}
			catch (IOException e)
			{
				throw new IDMapperException (e);
			}
		}
		
		Properties sysprop = System.getProperties();
		
		try
		{
			sysprop.setProperty("derby.storage.tempDirectory", System.getProperty("java.io.tmpdir"));
			sysprop.setProperty("derby.stream.error.file", File.createTempFile("derby",".log").toString());
			Class.forName("org.apache.derby.jdbc.ClientDriver");
		}
		catch (ClassNotFoundException e)
		{
			throw new IDMapperException (e);
		}
		catch (IOException f)
		{
			throw new IDMapperException (f);
		}
		Properties prop = new Properties();
		prop.setProperty("create", Boolean.toString(recreate));
		
//		StopWatch timer = new StopWatch();
//		timer.start();
		
		String url = getDbUrl();

		Connection con;
		try
		{
			con = DriverManager.getConnection(url, prop);
		}
		catch (SQLException e)
		{
			throw new IDMapperException (e);
		}
		
//		Logger.log.info("Connecting with derby to " + url + ":\t" + timer.stop());		
		return con;
	}
	
	public Connection createNewDatabaseConnection(String dbName) throws IDMapperException 
	{
		return createConnection(FileUtilsGdb.removeExtension(dbName), PROP_RECREATE);
	}
	
	public String finalizeNewDatabase(String dbName) throws IDMapperException 
	{
		if (finalized) return finalDbName; // already finalized.
		
		//Transfer db to zip and clear old dbfiles
		toZip (new File (finalDbName), tempDbSubdir);
		
		FileUtilsGdb.deleteRecursive(tempDbParentdir);
		
		//Return new database file
		return finalDbName;
	}
	
	public void closeConnection(Connection con) throws IDMapperException 
	{
		closeConnection(con, PROP_NONE);
	}
	
	/**
	 * Close the connection to this database.
	 * @param con JDBC Connection object
	 * @param props Passing PROP_FINALIZE for props will cause a full shutdown, which is necessary
	 * after creating a fresh database.
	 * @throws IDMapperException if the database could not be closed.
	 */
	public void closeConnection(Connection con, int props) throws IDMapperException 
	{
		if(con != null) 
		{
			if (props == PROP_FINALIZE)
			{
				//shutdown only necessary after modifying database!!!
				//Otherwise causes problems when loading same database twice.
				try
				{
					DriverManager.getConnection(getDbUrl() + ";shutdown=true");
				}
				catch (SQLException se)  
				{	
					/*
					 In this case a thrown exception signals success. See:
				      http://db.apache.org/derby/docs/10.3/getstart/rwwdactivity3.html
				      if (e.getSQLState().equals("XJ015")) shutdownSuccess= true; //Derby engine
					 */
					if ( se.getSQLState().equals("08006") ) // single file
					{		
//						Logger.log.info ("Database " + getDbUrl() + " shutdown cleanly");
					}
					else throw new IDMapperException (se);
				}
			}
			try
			{
				con.close();
				con = null;
			}
			catch (SQLException se)
			{
				throw new IDMapperException (se);
			}
		}
	}
	
	//TODO: I wonder if this is possible for zipped databases...
	// if not, this can be done together with finalizing.
	public void compact(Connection con) throws IDMapperException 
	{
		try
		{
			con.commit();
			con.setAutoCommit(true);
	
			if (getDbType() == DBConnector.TYPE_GDB)
			{
				CallableStatement cs = con.prepareCall
				("CALL SYSCS_UTIL.SYSCS_COMPRESS_TABLE(?, ?, ?)");
				//Gene table
				cs.setString(1, "APP");
				cs.setString(2, "DATANODE");
				cs.setShort(3, (short) 1);
				cs.execute();
				
				//Link table
				cs.setString(1, "APP");
				cs.setString(2, "LINK");
				cs.setShort(3, (short) 1);
				cs.execute();

				cs.setString(1, "APP");
				cs.setString(2, "ATTRIBUTE");
				cs.setShort(3, (short) 1);
				cs.execute();

			}
			else if (getDbType() == DBConnector.TYPE_GEX)
			{
				CallableStatement cs = con.prepareCall
				("CALL SYSCS_UTIL.SYSCS_COMPRESS_TABLE(?, ?, ?)");
				//Expression table
				cs.setString(1, "APP");
				cs.setString(2, "EXPRESSION");
				cs.setShort(3, (short) 1);
				cs.execute();
			}
			con.commit(); //Just to be sure...
		}
		catch (SQLException e)
		{
			throw new IDMapperException (e); 
		}
	}
		
	/**
	 * create a zip file from a directory.
	 * @param zipFile output file
	 * @param dbDir input dir
	 */
	private void toZip(File zipFile, File dbDir) 
	{
		try {			
			if(zipFile.exists()) zipFile.delete();
			
			ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFile));
			out.setMethod(ZipOutputStream.STORED);
			for(File f : dbDir.listFiles()) addFiles(f, DB_NAME_IN_ZIP + '/', out);
			out.closeEntry();
			out.close();
			
			String zipPath = zipFile.getAbsolutePath().replace(File.separatorChar, '/');
			String url = "jdbc:derby:jar:(" + zipPath + ")" + DB_NAME_IN_ZIP;

			DriverManager.getConnection(url);
		
		} catch(IOException e) {
			e.printStackTrace();
		}
		catch(SQLException e) {
			e.printStackTrace();
		}
	}
	
	private byte[] buf = new byte[1024];
	
	/**
	 * recursively add files or directory to ZipOutputStream. Skips directories
	 * named "tmp" or files ending with "lck".
	 * 
	 * @param file file or directory to add.
	 * @param dir base directory inside zip
	 * @param out target zip output stream.
	 * @throws IOException on IO error
	 */
	private void addFiles(File file, String dir, ZipOutputStream out) throws IOException 
	{
		if(file.isDirectory()) 
		{
			if(file.getName().equals("tmp")) return; //Skip 'tmp' directory
			
			String newDir = dir + file.getName() + '/';
			ZipEntry add = new ZipEntry(newDir);
			setZipEntryAttributes(file, add);
			out.putNextEntry(add);
			
			for(File f : file.listFiles()) addFiles(f, newDir,out);
		} 
		else 
		{
			if(file.getName().endsWith(".lck")) return; //Skip '*.lck' files
			ZipEntry add = new ZipEntry(dir + file.getName());
			
			setZipEntryAttributes(file, add);
			
			// real clever: putNextEntry has to be called before writing to out,
			// and CRC has to be calculated before putNextEntry
			// so we end up reading all files twice.
			out.putNextEntry(add);
				        
			FileInputStream in = new FileInputStream(file);
			int len;
			while ((len = in.read(buf)) > 0) 
			{
				out.write(buf, 0, len);
			}
			in.close();
		}
	}
	
	/**
	 * Calculates required attributes for ZipEntry.
	 * 
    *
  • method: ZipEntry.STORED (meaning uncompressed) *
  • crc: calculated *
  • size: file size *
  • compressedSize: also file size, since uncompressed *
* @param z the ZipEntry to set attributes on * @param f the file to calculate the attributes for * @throws IOException when the file couldn't be read */ private void setZipEntryAttributes(File f, ZipEntry z) throws IOException { z.setTime(f.lastModified()); z.setMethod(ZipEntry.STORED); if(f.isDirectory()) { z.setCrc(0); z.setSize(0); z.setCompressedSize(0); } else { z.setSize(f.length()); z.setCompressedSize(f.length()); z.setCrc(computeCheckSum(f)); } } /** * Calculate the 32-bit CRC code for a file, * Suitable for ZipEntry.setCrc. * @param f file to check * @throws IOException on file read error * @return CRC code */ private long computeCheckSum(File f) throws IOException { CheckedInputStream cis = new CheckedInputStream( new FileInputStream(f), new CRC32()); byte[] tempBuf = new byte[128]; while (cis.read(tempBuf) >= 0) { } long result = cis.getChecksum().getValue(); cis.close(); return result; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy