| /* |
| * Copyright (c) 2013, 2015 Eike Stepper (Berlin, Germany) and others. |
| * 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: |
| * Eike Stepper - initial API and implementation |
| */ |
| package org.eclipse.net4j.internal.db; |
| |
| import org.eclipse.net4j.db.DBException; |
| import org.eclipse.net4j.db.DBUtil; |
| import org.eclipse.net4j.db.DBUtil.RunnableWithConnection; |
| import org.eclipse.net4j.db.IDBConnection; |
| import org.eclipse.net4j.db.IDBConnectionProvider; |
| import org.eclipse.net4j.db.IDBDatabase; |
| import org.eclipse.net4j.db.ddl.IDBSchema; |
| import org.eclipse.net4j.db.ddl.delta.IDBSchemaDelta; |
| import org.eclipse.net4j.internal.db.ddl.delta.DBSchemaDelta; |
| import org.eclipse.net4j.spi.db.DBAdapter; |
| import org.eclipse.net4j.spi.db.ddl.InternalDBSchema; |
| import org.eclipse.net4j.util.WrappedException; |
| import org.eclipse.net4j.util.container.SetContainer; |
| import org.eclipse.net4j.util.event.Event; |
| import org.eclipse.net4j.util.security.IUserAware; |
| |
| import java.sql.Connection; |
| import java.sql.SQLException; |
| import java.util.LinkedList; |
| |
| /** |
| * @author Eike Stepper |
| */ |
| public final class DBDatabase extends SetContainer<IDBConnection>implements IDBDatabase |
| { |
| private DBAdapter adapter; |
| |
| private IDBConnectionProvider connectionProvider; |
| |
| private int statementCacheCapacity = DEFAULT_STATEMENT_CACHE_CAPACITY; |
| |
| private IDBSchema schema; |
| |
| private DBSchemaTransaction schemaTransaction; |
| |
| private final LinkedList<SchemaAccess> schemaAccessQueue = new LinkedList<SchemaAccess>(); |
| |
| public DBDatabase(final DBAdapter adapter, IDBConnectionProvider connectionProvider, final String schemaName, |
| final boolean fixNullableIndexColumns) |
| { |
| super(IDBConnection.class); |
| this.adapter = adapter; |
| this.connectionProvider = connectionProvider; |
| |
| schema = DBUtil.execute(DBDatabase.this, new RunnableWithConnection<IDBSchema>() |
| { |
| public IDBSchema run(Connection connection) throws SQLException |
| { |
| return DBUtil.readSchema(adapter, connection, schemaName, fixNullableIndexColumns); |
| } |
| }); |
| |
| ((InternalDBSchema)schema).lock(); |
| activate(); |
| } |
| |
| public String getUserID() |
| { |
| if (connectionProvider instanceof IUserAware) |
| { |
| return ((IUserAware)connectionProvider).getUserID(); |
| } |
| |
| return null; |
| } |
| |
| public DBAdapter getAdapter() |
| { |
| return adapter; |
| } |
| |
| public IDBSchema getSchema() |
| { |
| return schema; |
| } |
| |
| public DBSchemaTransaction openSchemaTransaction() |
| { |
| DBSchemaTransaction schemaTransaction = new DBSchemaTransaction(this); |
| this.schemaTransaction = schemaTransaction; |
| return schemaTransaction; |
| } |
| |
| public void closeSchemaTransaction(DBSchemaDelta delta) |
| { |
| try |
| { |
| beginSchemaAccess(true); |
| |
| for (IDBConnection transaction : getConnections()) |
| { |
| ((DBConnection)transaction).invalidateStatementCache(); |
| } |
| |
| fireEvent(new SchemaChangedEventImpl(delta)); |
| } |
| finally |
| { |
| schemaTransaction = null; |
| endSchemaAccess(); |
| } |
| } |
| |
| public DBSchemaTransaction getSchemaTransaction() |
| { |
| return schemaTransaction; |
| } |
| |
| public void updateSchema(RunnableWithSchema runnable) |
| { |
| DBSchemaTransaction schemaTransaction = openSchemaTransaction(); |
| |
| try |
| { |
| IDBSchema workingCopy = schemaTransaction.getWorkingCopy(); |
| runnable.run(workingCopy); |
| schemaTransaction.commit(); |
| } |
| finally |
| { |
| schemaTransaction.close(); |
| } |
| } |
| |
| public DBConnection getConnection() |
| { |
| Connection delegate = connectionProvider.getConnection(); |
| if (delegate == null) |
| { |
| throw new DBException("No connection from connection provider: " + connectionProvider); |
| } |
| |
| DBConnection connection = new DBConnection(this, delegate); |
| addElement(connection); |
| return connection; |
| } |
| |
| public void closeConnection(DBConnection connection) |
| { |
| removeElement(connection); |
| } |
| |
| public IDBConnection[] getConnections() |
| { |
| return getElements(); |
| } |
| |
| public int getStatementCacheCapacity() |
| { |
| return statementCacheCapacity; |
| } |
| |
| public void setStatementCacheCapacity(int statementCacheCapacity) |
| { |
| this.statementCacheCapacity = statementCacheCapacity; |
| } |
| |
| public boolean isClosed() |
| { |
| return !isActive(); |
| } |
| |
| public void close() |
| { |
| deactivate(); |
| } |
| |
| @Override |
| protected void doDeactivate() throws Exception |
| { |
| for (IDBConnection connection : getConnections()) |
| { |
| connection.close(); |
| } |
| |
| super.doDeactivate(); |
| } |
| |
| public void beginSchemaAccess(boolean write) |
| { |
| SchemaAccess schemaAccess = null; |
| synchronized (schemaAccessQueue) |
| { |
| if (write) |
| { |
| schemaAccess = new WriteSchemaAccess(); |
| schemaAccessQueue.addLast(schemaAccess); |
| } |
| else |
| { |
| if (!schemaAccessQueue.isEmpty()) |
| { |
| schemaAccess = schemaAccessQueue.getFirst(); |
| if (schemaAccess instanceof ReadSchemaAccess) |
| { |
| ReadSchemaAccess readSchemaAccess = (ReadSchemaAccess)schemaAccess; |
| readSchemaAccess.incrementReaders(); |
| } |
| else |
| { |
| schemaAccess = null; |
| } |
| } |
| |
| if (schemaAccess == null) |
| { |
| schemaAccess = new ReadSchemaAccess(); |
| schemaAccessQueue.addLast(schemaAccess); |
| } |
| } |
| } |
| |
| for (;;) |
| { |
| synchronized (schemaAccessQueue) |
| { |
| if (schemaAccessQueue.getFirst() == schemaAccess) |
| { |
| return; |
| } |
| |
| try |
| { |
| schemaAccessQueue.wait(); |
| } |
| catch (InterruptedException ex) |
| { |
| throw WrappedException.wrap(ex); |
| } |
| } |
| } |
| } |
| |
| public void endSchemaAccess() |
| { |
| synchronized (schemaAccessQueue) |
| { |
| SchemaAccess schemaAccess = schemaAccessQueue.getFirst(); |
| if (schemaAccess instanceof ReadSchemaAccess) |
| { |
| ReadSchemaAccess readSchemaAccess = (ReadSchemaAccess)schemaAccess; |
| if (readSchemaAccess.decrementReaders()) |
| { |
| return; |
| } |
| } |
| |
| schemaAccessQueue.removeFirst(); |
| schemaAccessQueue.notifyAll(); |
| } |
| } |
| |
| public String convertString(DBPreparedStatement preparedStatement, int parameterIndex, String value) |
| { |
| return adapter.convertString(preparedStatement, parameterIndex, value); |
| } |
| |
| public String convertString(DBResultSet resultSet, int columnIndex, String value) |
| { |
| return adapter.convertString(resultSet, columnIndex, value); |
| } |
| |
| public String convertString(DBResultSet resultSet, String columnLabel, String value) |
| { |
| return adapter.convertString(resultSet, columnLabel, value); |
| } |
| |
| /** |
| * @author Eike Stepper |
| */ |
| private interface SchemaAccess |
| { |
| } |
| |
| /** |
| * @author Eike Stepper |
| */ |
| private final class ReadSchemaAccess implements SchemaAccess |
| { |
| private int readers = 1; |
| |
| public void incrementReaders() |
| { |
| ++readers; |
| } |
| |
| public boolean decrementReaders() |
| { |
| return --readers > 0; |
| } |
| |
| @Override |
| public String toString() |
| { |
| return "READERS[" + readers + "]"; |
| } |
| } |
| |
| /** |
| * @author Eike Stepper |
| */ |
| private final class WriteSchemaAccess implements SchemaAccess |
| { |
| @Override |
| public String toString() |
| { |
| return "WRITER"; |
| } |
| } |
| |
| /** |
| * @author Eike Stepper |
| */ |
| private final class SchemaChangedEventImpl extends Event implements SchemaChangedEvent |
| { |
| private static final long serialVersionUID = 1L; |
| |
| private final IDBSchemaDelta schemaDelta; |
| |
| public SchemaChangedEventImpl(IDBSchemaDelta schemaDelta) |
| { |
| super(DBDatabase.this); |
| this.schemaDelta = schemaDelta; |
| } |
| |
| @Override |
| public IDBDatabase getSource() |
| { |
| return (IDBDatabase)super.getSource(); |
| } |
| |
| public IDBSchemaDelta getSchemaDelta() |
| { |
| return schemaDelta; |
| } |
| } |
| } |