blob: da9434e8605e23bb3066c3b652d8ff917675a9a8 [file] [log] [blame]
/*
* 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;
}
}
}