/******************************************************************************* * Copyright (c) 2009 the CHISEL group and contributors. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Del Myers - initial API and implementation *******************************************************************************/ package ca.uvic.chisel.hsqldb.server; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.PrintStream; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.HashMap; import java.util.Properties; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; /** * @author Del Myers * */ public class DefaultDataPortal implements IDataPortal { private Connection connection; private IPath alias; private HashMap<String, PreparedStatement> cachedStatements; private HashMap<String, CallableStatement> cachedCalls; boolean isReadOnly; /** * @param append * @throws CoreException */ protected DefaultDataPortal(IPath databaseAlias) throws IOException { checkDatabase(); isReadOnly = true; this.alias = databaseAlias; cachedStatements = new HashMap<String, PreparedStatement>(); cachedCalls = new HashMap<String, CallableStatement>(); } /* (non-Javadoc) * @see ca.uvic.chisel.hsqldb.server.IDataPortal#close() */ @Override public synchronized void close() { try { cachedStatements.clear(); cachedCalls.clear(); if (connection != null) { connection.createStatement().execute("SHUTDOWN"); connection.close(); connection = null; } } catch (SQLException e) { } } /* (non-Javadoc) * @see ca.uvic.chisel.hsqldb.server.IDataPortal#getDBName() */ @Override public String getDBName() { try { return connection.getMetaData().getURL().toString(); } catch (SQLException e) { } return null; } /* (non-Javadoc) * @see ca.uvic.chisel.hsqldb.server.IDataPortal#getDefaultConnection() */ @Override public synchronized Connection getDefaultConnection(boolean readOnly) { try { if (!readOnly) { //make sure to open a new connection setWritable(true); return connection; } else if (connection == null) { this.connection = openConnection(alias, true); } } catch (IOException e) { DBPlugin.getDefault().log(e); return null; } catch (SQLException e) { DBPlugin.getDefault().log(e); return null; } return connection; } public Connection getDefaultConnection() { return getDefaultConnection(true); } public synchronized void setWritable(boolean writable) throws IOException, SQLException { if (!writable == isReadOnly) { return; } else { close(); connection = openConnection(alias, !writable); isReadOnly = !writable; cachedStatements.clear(); } } /* (non-Javadoc) * @see ca.uvic.chisel.hsqldb.server.IDataPortal#prepareStatement(java.lang.String) */ @Override public synchronized PreparedStatement prepareStatement(String sql) throws SQLException { if (connection == null) { getDefaultConnection(); } PreparedStatement statement = cachedStatements.get(sql); if (statement == null) { statement = connection.prepareStatement(sql); cachedStatements.put(sql, statement); } return statement; } /** * Closes the connection to the database, and deletes all of the database files. * @return true if the database could be cleared. * @throws SQLException */ public boolean clear() throws SQLException { //reset it isReadOnly = true; if (connection != null) { close(); connection = null; } File aliasFile = alias.toFile(); File parentDirectory = aliasFile.getParentFile(); boolean success = true; if (parentDirectory.isDirectory()) { File[] databaseFiles = parentDirectory.listFiles(); for (File file :databaseFiles) { if (file.getName().startsWith(aliasFile.getName() + ".")) { success |= file.delete(); } } } return success; } /** * Opens a new connection to a database on the specified path. Creates the * database if it doesn't already exist. It is up to the caller to close the * connection when finished. * * @param databaseAlias * the path to the database. * @param readOnly * @return a newly open connection. * @throws CoreException */ private Connection openConnection(IPath databaseAlias, boolean readOnly) throws SQLException, IOException { checkDatabase(); cachedStatements.clear(); cachedCalls.clear(); IPath propsPath = new Path(databaseAlias.toPortableString() + ".properties"); Properties props = new Properties(); props.setProperty("hsqldb.script_format", "0"); props.setProperty("sql.enforce_strict_size", "false"); props.setProperty("hsqldb.cache_size_scale", "10"); props.setProperty("hsqldb.cache_file_scale", "8"); props.setProperty("readonly", "false"); props.setProperty("hsqldb.nio_data_file", "true"); props.setProperty("hsqldb.cache_scale", "11"); props.setProperty("version", "1.8.0"); props.setProperty("hsqldb.default_table_type", "cached"); props.setProperty("hsqldb.log_size", "20"); props.setProperty("modified", "yes"); props.setProperty("hsqldb.cache_version", "1.7.0"); props.setProperty("hsqldb.original_version", "1.8.0"); props.setProperty("hsqldb.compatible_version", "1.8.0"); props.setProperty("runtime.gc_interval", "10000"); if (!propsPath.toFile().exists()) { FileWriter w = new FileWriter(propsPath.toFile()); props.store(w, null); w.close(); // create a log to set the user IPath logPath = new Path(databaseAlias.toPortableString() + ".log"); PrintStream ps = new PrintStream(logPath.toFile()); ps.println("CREATE USER SA PASSWORD \"\" ADMIN"); ps.close(); } else { FileReader r = new FileReader(propsPath.toFile()); props.load(r); r.close(); props.setProperty("readonly", (readOnly) ? "true" : "false"); } Connection c = null; props.setProperty("user", "SA"); props.setProperty("password", ""); c = DriverManager.getConnection("jdbc:hsqldb:file:" + databaseAlias.toPortableString(), props); return c; } private void checkDatabase() throws IOException { try { Class.forName("org.hsqldb.jdbcDriver"); } catch (Exception e) { throw new IOException("could not load HSQLDB JDBC driver", e); } } /* (non-Javadoc) * @see ca.uvic.chisel.hsqldb.server.IDataPortal#prepareCall(java.lang.String) */ @Override public synchronized CallableStatement prepareCall(String sql) throws SQLException { if (connection == null) { getDefaultConnection(); } CallableStatement statement = cachedCalls.get(sql); if (statement == null) { statement = connection.prepareCall(sql); cachedCalls.put(sql, statement); } return statement; } }