Skip to content
Snippets Groups Projects
Commit b501152d authored by Timm Fitschen's avatar Timm Fitschen
Browse files

Merge branch 'f-refactor-transactions' into 'dev'

f-refactor-transactions -> dev

See merge request caosdb/caosdb-server!74
parents 46a5f613 1c2ac2e2
Branches
Tags
1 merge request!21Release v0.4.0
Showing
with 409 additions and 546 deletions
...@@ -53,6 +53,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ...@@ -53,6 +53,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Removed ### Removed
* Text user interface (CaosDBTerminal).
### Fixed ### Fixed
* Bug: When the user password is updated the user is deactivated. * Bug: When the user password is updated the user is deactivated.
......
...@@ -117,11 +117,6 @@ ...@@ -117,11 +117,6 @@
<artifactId>reflections</artifactId> <artifactId>reflections</artifactId>
<version>0.9.11</version> <version>0.9.11</version>
</dependency> </dependency>
<dependency>
<groupId>com.googlecode.lanterna</groupId>
<artifactId>lanterna</artifactId>
<version>2.1.9</version>
</dependency>
<dependency> <dependency>
<groupId>org.antlr</groupId> <groupId>org.antlr</groupId>
<artifactId>antlr4</artifactId> <artifactId>antlr4</artifactId>
......
...@@ -78,13 +78,9 @@ import org.caosdb.server.resource.Webinterface; ...@@ -78,13 +78,9 @@ import org.caosdb.server.resource.Webinterface;
import org.caosdb.server.resource.WebinterfaceBuildNumber; import org.caosdb.server.resource.WebinterfaceBuildNumber;
import org.caosdb.server.resource.transaction.EntityNamesResource; import org.caosdb.server.resource.transaction.EntityNamesResource;
import org.caosdb.server.resource.transaction.EntityResource; import org.caosdb.server.resource.transaction.EntityResource;
import org.caosdb.server.terminal.CaosDBTerminal;
import org.caosdb.server.terminal.StatsPanel;
import org.caosdb.server.terminal.SystemErrPanel;
import org.caosdb.server.transaction.ChecksumUpdater; import org.caosdb.server.transaction.ChecksumUpdater;
import org.caosdb.server.utils.FileUtils; import org.caosdb.server.utils.FileUtils;
import org.caosdb.server.utils.Initialization; import org.caosdb.server.utils.Initialization;
import org.caosdb.server.utils.NullPrintStream;
import org.quartz.JobDetail; import org.quartz.JobDetail;
import org.quartz.Scheduler; import org.quartz.Scheduler;
import org.quartz.SchedulerException; import org.quartz.SchedulerException;
...@@ -116,7 +112,6 @@ import org.slf4j.LoggerFactory; ...@@ -116,7 +112,6 @@ import org.slf4j.LoggerFactory;
public class CaosDBServer extends Application { public class CaosDBServer extends Application {
private static Logger logger = LoggerFactory.getLogger(CaosDBServer.class); private static Logger logger = LoggerFactory.getLogger(CaosDBServer.class);
private static boolean START_GUI = true;
private static Properties SERVER_PROPERTIES = null; private static Properties SERVER_PROPERTIES = null;
private static Component component = null; private static Component component = null;
private static ArrayList<Runnable> postShutdownHooks = new ArrayList<Runnable>(); private static ArrayList<Runnable> postShutdownHooks = new ArrayList<Runnable>();
...@@ -146,7 +141,7 @@ public class CaosDBServer extends Application { ...@@ -146,7 +141,7 @@ public class CaosDBServer extends Application {
public static void main(final String[] args) public static void main(final String[] args)
throws SecurityException, FileNotFoundException, IOException { throws SecurityException, FileNotFoundException, IOException {
try { try {
init(args); parseArguments(args);
initScheduler(); initScheduler();
initServerProperties(); initServerProperties();
initTimeZone(); initTimeZone();
...@@ -161,16 +156,22 @@ public class CaosDBServer extends Application { ...@@ -161,16 +156,22 @@ public class CaosDBServer extends Application {
} }
} }
private static void init(final String[] args) { /**
// Important change: * Parse the command line arguments.
// Make silent the default option *
START_GUI = false; * <ul>
* <li>"nobackend": flag to run caosdb without any backend (for testing purposes)
* <li>"insecure": flag to start only a http server (no https server)
* </ul>
*
* <p>Both flags are only available in the debug mode which is controlled by the `caosdb.debug`
* JVM Property.
*
* @param args
*/
private static void parseArguments(final String[] args) {
for (final String s : args) { for (final String s : args) {
if (s.equals("silent")) { if (s.equals("nobackend")) {
START_GUI = false;
} else if (s.equals("gui")) {
START_GUI = true;
} else if (s.equals("nobackend")) {
START_BACKEND = false; START_BACKEND = false;
} else if (s.equals("insecure")) { } else if (s.equals("insecure")) {
INSECURE = true; INSECURE = true;
...@@ -354,32 +355,6 @@ public class CaosDBServer extends Application { ...@@ -354,32 +355,6 @@ public class CaosDBServer extends Application {
} }
} }
public static void initGUI() throws InterruptedException {
if (START_GUI) {
final CaosDBTerminal caosDBTerminal = new CaosDBTerminal();
caosDBTerminal.setName("CaosDBTerminal");
caosDBTerminal.start();
addPreShutdownHook(
new Runnable() {
@Override
public void run() {
caosDBTerminal.shutDown();
SystemErrPanel.close();
}
});
// wait until the terminal is initialized.
Thread.sleep(1000);
// add Benchmark
StatsPanel.addStat("TransactionBenchmark", TransactionBenchmark.getRootInstance());
} else {
logger.info("NO GUI");
System.setOut(new NullPrintStream());
}
}
private static void initDatatypes(final Access access) throws Exception { private static void initDatatypes(final Access access) throws Exception {
final RetrieveDatatypes t = new RetrieveDatatypes(); final RetrieveDatatypes t = new RetrieveDatatypes();
t.setAccess(access); t.setAccess(access);
......
...@@ -129,6 +129,8 @@ import org.caosdb.server.database.backend.interfaces.UpdateUserImpl; ...@@ -129,6 +129,8 @@ import org.caosdb.server.database.backend.interfaces.UpdateUserImpl;
import org.caosdb.server.database.backend.interfaces.UpdateUserRolesImpl; import org.caosdb.server.database.backend.interfaces.UpdateUserRolesImpl;
import org.caosdb.server.database.exceptions.TransactionException; import org.caosdb.server.database.exceptions.TransactionException;
import org.caosdb.server.database.misc.TransactionBenchmark; import org.caosdb.server.database.misc.TransactionBenchmark;
import org.caosdb.server.entity.EntityInterface;
import org.caosdb.server.entity.RetrieveEntity;
import org.caosdb.server.utils.UndoHandler; import org.caosdb.server.utils.UndoHandler;
import org.caosdb.server.utils.Undoable; import org.caosdb.server.utils.Undoable;
...@@ -295,4 +297,9 @@ public abstract class BackendTransaction implements Undoable { ...@@ -295,4 +297,9 @@ public abstract class BackendTransaction implements Undoable {
this.benchmark.addMeasurement(o, time); this.benchmark.addMeasurement(o, time);
} }
} }
/** Return the type of transaction of an entity, e.g. "Retrieve" for a {@link RetrieveEntity}. */
public String getTransactionType(EntityInterface e) {
return e.getClass().getSimpleName().replace("Entity", "");
}
} }
/*
* ** header v3.0
* This file is a part of the CaosDB Project.
*
* Copyright (C) 2018 Research Group Biomedical Physics,
* Max-Planck-Institute for Dynamics and Self-Organization Göttingen
* Copyright (C) 2021 Indiscale GmbH <info@indiscale.com>
* Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
* Copyright (C) 2021 Daniel Hornung <d.hornung@indiscale.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* ** end header
*/
package org.caosdb.server.database;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.ReentrantLock;
import org.caosdb.server.database.access.Access;
import org.caosdb.server.database.access.AccessControlAccess;
import org.caosdb.server.database.access.InfoAccess;
import org.caosdb.server.database.access.InitAccess;
import org.caosdb.server.database.access.TransactionAccess;
import org.caosdb.server.transaction.AccessControlTransaction;
import org.caosdb.server.transaction.TransactionInterface;
import org.caosdb.server.transaction.WriteTransactionInterface;
import org.caosdb.server.utils.Info;
import org.caosdb.server.utils.Initialization;
import org.caosdb.server.utils.Releasable;
/**
* Acquire and release read access. {@link DatabaseAccessManager} uses this class for managing
* access to the back-end during processing updates, inserts, deletions and retrievals. Read access
* will be granted immediately for every thread that requests it, unless a thread requests that read
* access be blocked (usually by requesting write access). If this happens, all threads that already
* have read access will proceed and release their read access as usual but no NEW read permits will
* be granted.
*
* <p>This is a blockable {@link Semaphore}. Any number of threads may acquire a permit unless it is
* blocked. The blocking thread (requesting a {@link WriteAccessLock}) waits until all threads have
* released their permits.
*
* @author Timm Fitschen <t.fitschen@indiscale.com>
*/
class ReadAccessSemaphore extends Semaphore implements Releasable {
private static final long serialVersionUID = 4384921156838881337L;
private int acquired = 0; // how many threads have read access
Semaphore writersBlock =
new Semaphore(1, true); // This semaphore is blocked as long as there are any
// unreleased read permits.
public ReadAccessSemaphore() {
// this is a fair semaphore with no initial permit.
super(1, true);
}
/**
* Acquire a read access permit if and only if it has not been blocked via block(). If read access
* is currently blocked, the thread waits until the unblock() method is invoked by any thread.
*/
@Override
public void acquire() throws InterruptedException {
super.acquire(); // Protect the next few lines
if (this.acquired == 0) {
this.writersBlock.acquire();
}
this.acquired++;
super.release();
}
/**
* Release a read access permit.
*
* <p>If this is the last remaining acquired permit, also release the general writersBlock.
*/
@Override
public void release() {
this.acquired--;
if (this.acquired <= 0) { // Last permit: release
this.acquired = 0;
if (this.writersBlock.availablePermits() <= 0) {
this.writersBlock.release();
}
}
}
/**
* Acquire the permit to block further read access, if no thread currently has acquired read
* access and no thread currently has a block.
*
* <p>Consequences of calling this method are:
*
* <ul>
* <li>Further read access permits are blocked for any thread.
* <li>Every thread that has a read access already can proceed. The current thread waits until
* all threads have released their read access.
* </ul>
*
* <p>If another thread has invoked this method before, the current thread waits until the
* unblock() method is called.
*
* @throws InterruptedException
*/
public void block() throws InterruptedException {
super.reducePermits(1);
this.writersBlock.acquire();
}
/**
* Unblock read access.
*
* <p>This method releases the writersBlock introduced by calling the block() method.
*/
public void unblock() {
if (this.writersBlock.availablePermits() <= 0) {
this.writersBlock.release();
}
super.release();
}
public int waitingAquireAccess() {
return getQueueLength();
}
}
/**
* Acquire and release write access. DatabaseAccessManager uses this class for managing access to
* entities while processing updates, inserts, deletions and retrievals. Write access will be
* granted to one and only one thread if no other thread already holds read or write access permits.
* The write access seat has to be reserved before acquiring the lock.
*
* <p>The flow is as follows:
*
* <p>
*
* <pre>
* No new read
* access possible,
* Read access Read access wait for running read
* possible possible threads to fininish
*
* +------+ +----------+ +----------+
* +--> | no | ----> | reserved | ----> | acquired | --+
* | | Lock | +----------+ +----------+ |
* | +------+ \ / |
* | (1 seat together) |
* +-----------------------------------------------------+
* release()
* </pre>
*
* @author Timm Fitschen
*/
class WriteAccessLock extends ReentrantLock implements Releasable {
private static final long serialVersionUID = 833147084787201103L;
private ReadAccessSemaphore readSem = null;
private Thread reservedBy = null;
public WriteAccessLock(final ReadAccessSemaphore readSem) {
super();
this.readSem = readSem;
}
/**
* Reserve the seat for the next write access. While a write access is reserved but not yet
* acquired, all read access may still be granted. When a write access has been already granted or
* another reservation is active, the thread waits until the write access has been released.
*
* @throws InterruptedException
*/
public void reserve() throws InterruptedException {
super.lock();
this.reservedBy = Thread.currentThread();
}
/**
* Lock the write access seat. This method returns once all current read permits have been
* released.
*/
@Override
public void lockInterruptibly() throws InterruptedException {
if (!super.isHeldByCurrentThread()) {
super.lock();
}
this.readSem.block(); // Wait until all current read permits have been released.
}
@Override
public void unlock() {
if (super.isHeldByCurrentThread()) {
this.readSem.unblock();
this.reservedBy = null;
super.unlock();
}
}
@Override
public void release() {
unlock();
}
public Thread whoHasReservedAccess() {
return this.reservedBy;
}
}
/**
* Manages the read and write access to the database.
*
* @author tf
*/
public class DatabaseAccessManager {
private DatabaseAccessManager() {}
private static final DatabaseAccessManager instance = new DatabaseAccessManager();
private final ReadAccessSemaphore readAccess = new ReadAccessSemaphore();
private final WriteAccessLock writeAccess = new WriteAccessLock(this.readAccess);
public static DatabaseAccessManager getInstance() {
return instance;
}
/**
* Return the thread which has successfully reserved write access. This is the thread which will
* be the next to actually acquire write access.
*
* @return
*/
public static Thread whoHasReservedWriteAccess() {
return instance.writeAccess.whoHasReservedAccess();
}
/**
* Acquire read access. This method returns the Access object as soon as there are no active write
* permits.
*
* <p>The returned Access object can be used to read in the data base back-end.
*
* <p>Read access can be acquired parallel to other threads having read access or while another
* thread has <em>reserved</em> write access. As soon as any thread has requested to
* <em>acquire</em> write access, all other threads have to wait.
*
* @param t the {@link TransactionInterface} which requests the read access
* @return {@link Access} object which holds and abstract away all connection details.
* @throws InterruptedException
*/
public Access acquireReadAccess(final TransactionInterface t) throws InterruptedException {
this.readAccess.acquire();
return new TransactionAccess(t, this.readAccess);
}
/**
* Reserve write access. This method returns the Access object as soon as there is no other
* reserved or acquired write access.
*
* <p>The returned Access object can be used to read in the data base back-end.
*
* <p>The reservation has no effect on granting of read access permits, but only one thread may at
* any time reserve or acquire write access.
*
* @param wt - the {@link WriteTransactionInterface} which request the reservation of the write
* access.
* @return {@link Access} object which holds and abstract away all connection details.
* @throws InterruptedException
*/
public Access reserveWriteAccess(final WriteTransactionInterface wt) throws InterruptedException {
this.writeAccess.reserve();
return new TransactionAccess(wt, this.writeAccess);
}
/**
* Acquire write access. This method returns the Access object as soon as all already acquired
* read access permits have been released. When the write access is acquired, no other access can
* be acquired (read or write).
*
* <p>The returned Access object can be used to read and write in the data base back-end.
*
* @param wt - the {@link WriteTransactionInterface} which request the acquisition of the write
* access.
* @return {@link Access} object which holds and abstract away all connection details.
* @throws InterruptedException
*/
public Access acquireWriteAccess(final WriteTransactionInterface wt) throws InterruptedException {
this.writeAccess.lockInterruptibly();
return wt.getAccess();
}
/**
* Special access to be used by {@link Info}.
*
* @param i
* @return
*/
public static Access getInfoAccess(final Info i) {
return new InfoAccess(i);
}
/**
* Special access to be used during the {@link Initialization} of the caosdb server.
*
* @param initialization
* @return
*/
public static Access getInitAccess(final Initialization initialization) {
return new InitAccess(initialization);
}
/**
* Special access to be used for {@link AccessControlTransaction}s, mainly authentication and
* authorization.
*
* @param t
* @return
*/
public static Access getAccountAccess(final AccessControlTransaction t) {
return new AccessControlAccess(t);
}
}
/*
* ** header v3.0
* This file is a part of the CaosDB Project.
*
* Copyright (C) 2018 Research Group Biomedical Physics,
* Max-Planck-Institute for Dynamics and Self-Organization Göttingen
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* ** end header
*/
package org.caosdb.server.database;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.ReentrantLock;
import org.caosdb.server.database.access.Access;
import org.caosdb.server.database.access.AccessControlAccess;
import org.caosdb.server.database.access.InfoAccess;
import org.caosdb.server.database.access.InitAccess;
import org.caosdb.server.database.access.TransactionAccess;
import org.caosdb.server.entity.container.TransactionContainer;
import org.caosdb.server.transaction.AccessControlTransaction;
import org.caosdb.server.transaction.TransactionInterface;
import org.caosdb.server.transaction.WriteTransaction;
import org.caosdb.server.utils.Info;
import org.caosdb.server.utils.Initialization;
import org.caosdb.server.utils.Observable;
import org.caosdb.server.utils.Observer;
import org.caosdb.server.utils.Releasable;
/**
* Acquire and release weak access. DatabaseMonitor uses this class for managing access to entities
* during processing updates, inserts, deletions and retrievals. Weak access will be granted
* immediately for every thread that requests it, unless one or more threads requested for blocking
* the weak access (usually due to requesting for strong access). If this happens, all threads that
* already have weak access will proceed and release their weak access as usual but no NEW permits
* will be granted.
*
* @author Timm Fitschen
*/
class WeakAccessSemaphore extends Semaphore implements Observable, Releasable {
private static final long serialVersionUID = -262226321839837533L;
private int acquired = 0; // how many thread have weak access
Semaphore block = new Semaphore(1, true);
public WeakAccessSemaphore() {
// this is a fair semaphore with no initial permit.
super(1, true);
}
/**
* Acquires a weak access permit if and only if it has not been blocked via block(). If the
* WeakAccess is currently blocked, the thread wait until the unblock() method is invoked by any
* thread.
*/
@Override
public void acquire() throws InterruptedException {
super.acquire();
if (this.acquired == 0) {
this.block.acquire();
}
this.acquired++;
notifyObservers(null);
super.release();
}
/** Releases a weak access permit. */
@Override
public void release() {
this.acquired--;
notifyObservers(null);
if (this.acquired <= 0) {
this.acquired = 0;
if (this.block.availablePermits() <= 0) {
this.block.release();
}
}
}
/**
* Acquires the permit of a block of WeakAccess if no thread currently has a WeakAccess and no
* thread currently has a block. I.e. it blocks the further permission of weak access for any
* thread. Every thread that has a weak access yet can proceed. The current thread waits until any
* thread has released its weak access. If another thread has invoked this method yet the current
* thread waits until the unblock() method is called.
*
* @throws InterruptedException
*/
public void block() throws InterruptedException {
super.reducePermits(1);
this.block.acquire();
}
/**
* Unblock WeakAccess.
*
* @throws InterruptedException
*/
public void unblock() {
if (this.block.availablePermits() <= 0) {
this.block.release();
}
super.release();
}
public int waitingAquireAccess() {
return getQueueLength();
}
public int acquiredAccess() {
return this.acquired;
}
LinkedList<Observer> observers = new LinkedList<Observer>();
@Override
public boolean acceptObserver(final Observer o) {
return this.observers.add(o);
}
@Override
public void notifyObservers(final String e) {
final Iterator<Observer> it = this.observers.iterator();
while (it.hasNext()) {
final Observer o = it.next();
if (!o.notifyObserver(e, this)) {
it.remove();
}
}
}
}
/**
* Acquire and release strong access. DatabaseMonitor uses this class for managing access to
* entities during processing updates, inserts, deletions and retrievals. Strong access will be
* granted to one and only one thread if no other thread yet holds weak or strong access permits.
* The strong access has to be allocated before requesting it to be permitted. See below.
*
* @author Timm Fitschen
*/
class StrongAccessLock extends ReentrantLock implements Observable, Releasable {
private static final long serialVersionUID = -262226321839837533L;
private WeakAccessSemaphore wa = null;
private Thread allocator = null;
private Thread acquirer = null;
public StrongAccessLock(final WeakAccessSemaphore wa) {
super();
this.wa = wa;
}
/**
* Allocates the strong access permits. While a strong access is allocated but not yet acquired
* any weak access may still be granted. When a strong access is yet granted or another allocation
* is still active, the thread waits until the strong access has been released.
*
* @throws InterruptedException
*/
public void allocate() throws InterruptedException {
super.lock();
this.allocator = Thread.currentThread();
notifyObservers(null);
}
@Override
public void lockInterruptibly() throws InterruptedException {
if (!super.isHeldByCurrentThread()) {
super.lock();
}
this.acquirer = Thread.currentThread();
notifyObservers(null);
this.wa.block();
}
@Override
public void unlock() {
if (super.isHeldByCurrentThread()) {
this.wa.unblock();
this.allocator = null;
this.acquirer = null;
notifyObservers(null);
super.unlock();
}
}
@Override
public void release() {
unlock();
}
LinkedList<Observer> observers = new LinkedList<Observer>();
@Override
public boolean acceptObserver(final Observer o) {
return this.observers.add(o);
}
@Override
public void notifyObservers(final String e) {
final Iterator<Observer> it = this.observers.iterator();
while (it.hasNext()) {
final Observer o = it.next();
if (!o.notifyObserver(e, this)) {
it.remove();
}
}
}
public Thread whoHasAllocatedAccess() {
return this.allocator;
}
public Thread whoHasAcquiredAccess() {
return this.acquirer;
}
public int waitingAllocateAccess() {
return getQueueLength();
}
}
/**
* Manages the read and write access to the database.
*
* @author tf
*/
public class DatabaseMonitor {
private DatabaseMonitor() {}
private static final DatabaseMonitor instance = new DatabaseMonitor();
private final WeakAccessSemaphore wa = new WeakAccessSemaphore();
private final StrongAccessLock sa = new StrongAccessLock(this.wa);
public static DatabaseMonitor getInstance() {
return instance;
}
public void acquireWeakAccess() throws InterruptedException {
this.wa.acquire();
}
public void releaseWeakAccess() {
this.wa.release();
}
public void allocateStrongAccess() throws InterruptedException {
this.sa.allocate();
}
public void acquireStrongAccess() throws InterruptedException {
this.sa.lockInterruptibly();
}
public void releaseStrongAccess() {
this.sa.unlock();
}
public static final void acceptWeakAccessObserver(final Observer o) {
instance.wa.acceptObserver(o);
}
public static final void acceptStrongAccessObserver(final Observer o) {
instance.sa.acceptObserver(o);
}
public static int waitingAllocateStrongAccess() {
return instance.sa.waitingAllocateAccess();
}
public static int waitingAcquireWeakAccess() {
return instance.wa.waitingAquireAccess();
}
public static int acquiredWeakAccess() {
return instance.wa.acquiredAccess();
}
public static Thread whoHasAllocatedStrongAccess() {
return instance.sa.whoHasAllocatedAccess();
}
public static Thread whoHasAcquiredStrongAccess() {
return instance.sa.whoHasAcquiredAccess();
}
public Access acquiredWeakAccess(final TransactionInterface t) {
acquiredWeakAccess();
return new TransactionAccess(t, this.wa);
}
public Access allocateStrongAccess(final WriteTransaction<? extends TransactionContainer> wt)
throws InterruptedException {
allocateStrongAccess();
return new TransactionAccess(wt, this.sa);
}
public Access acquireStrongAccess(final WriteTransaction<? extends TransactionContainer> wt)
throws InterruptedException {
acquireStrongAccess();
return wt.getAccess();
}
public static Access getInfoAccess(final Info i) {
return new InfoAccess(i);
}
public static Access getInitAccess(final Initialization initialization) {
return new InitAccess(initialization);
}
public static Access getAccountAccess(final AccessControlTransaction t) {
return new AccessControlAccess(t);
}
}
...@@ -55,7 +55,7 @@ public class MySQLHelper implements DBHelper { ...@@ -55,7 +55,7 @@ public class MySQLHelper implements DBHelper {
* *
* <p>In the database, this adds a row to the transaction table with SRID, user and timestamp. * <p>In the database, this adds a row to the transaction table with SRID, user and timestamp.
*/ */
public void initTransaction(Connection connection, WriteTransaction<?> transaction) public void initTransaction(Connection connection, WriteTransaction transaction)
throws SQLException { throws SQLException {
try (CallableStatement call = connection.prepareCall("CALL set_transaction(?,?,?,?,?)")) { try (CallableStatement call = connection.prepareCall("CALL set_transaction(?,?,?,?,?)")) {
...@@ -89,7 +89,7 @@ public class MySQLHelper implements DBHelper { ...@@ -89,7 +89,7 @@ public class MySQLHelper implements DBHelper {
} else if (transaction instanceof WriteTransaction) { } else if (transaction instanceof WriteTransaction) {
connection.setReadOnly(false); connection.setReadOnly(false);
connection.setAutoCommit(false); connection.setAutoCommit(false);
initTransaction(connection, (WriteTransaction<?>) transaction); initTransaction(connection, (WriteTransaction) transaction);
} else { } else {
connection.setReadOnly(false); connection.setReadOnly(false);
connection.setAutoCommit(true); connection.setAutoCommit(true);
......
...@@ -27,11 +27,11 @@ import org.caosdb.server.database.exceptions.TransactionException; ...@@ -27,11 +27,11 @@ import org.caosdb.server.database.exceptions.TransactionException;
import org.caosdb.server.entity.EntityInterface; import org.caosdb.server.entity.EntityInterface;
import org.caosdb.server.entity.container.TransactionContainer; import org.caosdb.server.entity.container.TransactionContainer;
public class DeleteEntity extends BackendTransaction { public class DeleteEntityTransaction extends BackendTransaction {
private final TransactionContainer container; private final TransactionContainer container;
public DeleteEntity(final TransactionContainer container) { public DeleteEntityTransaction(final TransactionContainer container) {
this.container = container; this.container = container;
} }
......
...@@ -27,11 +27,11 @@ import org.caosdb.server.entity.EntityInterface; ...@@ -27,11 +27,11 @@ import org.caosdb.server.entity.EntityInterface;
import org.caosdb.server.entity.container.TransactionContainer; import org.caosdb.server.entity.container.TransactionContainer;
import org.caosdb.server.utils.EntityStatus; import org.caosdb.server.utils.EntityStatus;
public class InsertEntity extends BackendTransaction { public class InsertEntityTransaction extends BackendTransaction {
private final TransactionContainer container; private final TransactionContainer container;
public InsertEntity(final TransactionContainer container) { public InsertEntityTransaction(final TransactionContainer container) {
this.container = container; this.container = container;
} }
......
...@@ -30,22 +30,24 @@ import org.caosdb.server.entity.EntityInterface; ...@@ -30,22 +30,24 @@ import org.caosdb.server.entity.EntityInterface;
import org.caosdb.server.entity.container.TransactionContainer; import org.caosdb.server.entity.container.TransactionContainer;
import org.caosdb.server.utils.EntityStatus; import org.caosdb.server.utils.EntityStatus;
/**
* Record the current transaction in the entities transaction history.
*
* @author Timm Fitschen (t.fitschen@indiscale.com)
*/
public class InsertTransactionHistory extends BackendTransaction { public class InsertTransactionHistory extends BackendTransaction {
private final TransactionContainer container; private final TransactionContainer container;
private final String transaction;
private final UTCDateTime datetime; private final UTCDateTime datetime;
private final String user; private final String user;
private final String realm; private final String realm;
public InsertTransactionHistory( public InsertTransactionHistory(
final TransactionContainer container, final TransactionContainer container,
final String transaction,
final String realm, final String realm,
final String user, final String user,
final UTCDateTime timestamp) { final UTCDateTime timestamp) {
this.container = container; this.container = container;
this.transaction = transaction;
this.user = user; this.user = user;
this.datetime = timestamp; this.datetime = timestamp;
this.realm = realm; this.realm = realm;
...@@ -60,7 +62,7 @@ public class InsertTransactionHistory extends BackendTransaction { ...@@ -60,7 +62,7 @@ public class InsertTransactionHistory extends BackendTransaction {
|| e.getEntityStatus() == EntityStatus.VALID) { || e.getEntityStatus() == EntityStatus.VALID) {
t.execute( t.execute(
this.transaction, getTransactionType(e),
this.realm, this.realm,
this.user, this.user,
this.datetime.getUTCSeconds(), this.datetime.getUTCSeconds(),
......
...@@ -53,21 +53,21 @@ import org.caosdb.server.utils.EntityStatus; ...@@ -53,21 +53,21 @@ import org.caosdb.server.utils.EntityStatus;
* *
* @author Timm Fitschen <t.fitschen@indiscale.com> * @author Timm Fitschen <t.fitschen@indiscale.com>
*/ */
public class RetrieveFullEntity extends BackendTransaction { public class RetrieveFullEntityTransaction extends BackendTransaction {
private final Container<? extends EntityInterface> container; private final Container<? extends EntityInterface> container;
public RetrieveFullEntity(final EntityInterface entity) { public RetrieveFullEntityTransaction(final EntityInterface entity) {
final Container<EntityInterface> c = new Container<>(); final Container<EntityInterface> c = new Container<>();
c.add(entity); c.add(entity);
this.container = c; this.container = c;
} }
public RetrieveFullEntity(final Container<? extends EntityInterface> container) { public RetrieveFullEntityTransaction(final Container<? extends EntityInterface> container) {
this.container = container; this.container = container;
} }
public RetrieveFullEntity(Integer id) { public RetrieveFullEntityTransaction(Integer id) {
this(new RetrieveEntity(id)); this(new RetrieveEntity(id));
} }
......
...@@ -33,9 +33,16 @@ import org.caosdb.server.database.exceptions.TransactionException; ...@@ -33,9 +33,16 @@ import org.caosdb.server.database.exceptions.TransactionException;
import org.caosdb.server.database.proto.Rule; import org.caosdb.server.database.proto.Rule;
import org.caosdb.server.entity.EntityInterface; import org.caosdb.server.entity.EntityInterface;
import org.caosdb.server.entity.container.TransactionContainer; import org.caosdb.server.entity.container.TransactionContainer;
import org.caosdb.server.entity.wrapper.Property;
import org.caosdb.server.jobs.Job; import org.caosdb.server.jobs.Job;
import org.caosdb.server.transaction.Transaction; import org.caosdb.server.transaction.Transaction;
/**
* Load transaction rules (e.g. "Referenced entities must always exist") and their configuration
* from the back-end.
*
* @author Timm Fitschen (t.fitschen@indiscale.com)
*/
public class RuleLoader extends CacheableBackendTransaction<String, ArrayList<Rule>> { public class RuleLoader extends CacheableBackendTransaction<String, ArrayList<Rule>> {
private static final ICacheAccess<String, ArrayList<Rule>> cache = private static final ICacheAccess<String, ArrayList<Rule>> cache =
...@@ -45,6 +52,7 @@ public class RuleLoader extends CacheableBackendTransaction<String, ArrayList<Ru ...@@ -45,6 +52,7 @@ public class RuleLoader extends CacheableBackendTransaction<String, ArrayList<Ru
private final Integer entity; private final Integer entity;
private final Integer domain; private final Integer domain;
private ArrayList<Job> jobs; private ArrayList<Job> jobs;
private String transactionType;
public RuleLoader( public RuleLoader(
final Integer domain, final Integer domain,
...@@ -55,18 +63,17 @@ public class RuleLoader extends CacheableBackendTransaction<String, ArrayList<Ru ...@@ -55,18 +63,17 @@ public class RuleLoader extends CacheableBackendTransaction<String, ArrayList<Ru
this.domain = domain; this.domain = domain;
this.entity = entity; this.entity = entity;
this.e = e; this.e = e;
if (e instanceof Property) {
this.transactionType = getTransactionType(((Property) e).getDomainEntity());
} else {
this.transactionType = getTransactionType(e);
}
this.transaction = transaction; this.transaction = transaction;
} }
@Override @Override
protected String getKey() { protected String getKey() {
return "<" return "<" + this.domain + "," + this.entity + "," + this.transactionType + ">";
+ this.domain
+ ","
+ this.entity
+ ","
+ this.transaction.getClass().getSimpleName()
+ ">";
} }
public ArrayList<Job> getJobs() { public ArrayList<Job> getJobs() {
...@@ -76,7 +83,7 @@ public class RuleLoader extends CacheableBackendTransaction<String, ArrayList<Ru ...@@ -76,7 +83,7 @@ public class RuleLoader extends CacheableBackendTransaction<String, ArrayList<Ru
@Override @Override
public ArrayList<Rule> executeNoCache() throws TransactionException { public ArrayList<Rule> executeNoCache() throws TransactionException {
final RuleLoaderImpl t = getImplementation(RuleLoaderImpl.class); final RuleLoaderImpl t = getImplementation(RuleLoaderImpl.class);
return t.executeNoCache(this.domain, this.entity, this.transaction.getClass().getSimpleName()); return t.executeNoCache(this.domain, this.entity, this.transactionType);
} }
@Override @Override
......
...@@ -28,11 +28,11 @@ import org.caosdb.server.entity.EntityInterface; ...@@ -28,11 +28,11 @@ import org.caosdb.server.entity.EntityInterface;
import org.caosdb.server.entity.container.TransactionContainer; import org.caosdb.server.entity.container.TransactionContainer;
import org.caosdb.server.utils.EntityStatus; import org.caosdb.server.utils.EntityStatus;
public class UpdateEntity extends BackendTransaction { public class UpdateEntityTransaction extends BackendTransaction {
private final TransactionContainer container; private final TransactionContainer container;
public UpdateEntity(final TransactionContainer container) { public UpdateEntityTransaction(final TransactionContainer container) {
this.container = container; this.container = container;
} }
......
...@@ -289,4 +289,8 @@ public class FileProperties { ...@@ -289,4 +289,8 @@ public class FileProperties {
public void removeOnCleanUp(final String tempPath) { public void removeOnCleanUp(final String tempPath) {
this.tempPath = tempPath; this.tempPath = tempPath;
} }
public boolean hasTmpIdentifier() {
return this.tmpIdentifyer != null;
}
} }
...@@ -29,4 +29,8 @@ public class InsertEntity extends WritableEntity { ...@@ -29,4 +29,8 @@ public class InsertEntity extends WritableEntity {
public InsertEntity(final Element element) { public InsertEntity(final Element element) {
super(element); super(element);
} }
public InsertEntity(String name, Role role) {
super(name, role);
}
} }
...@@ -29,4 +29,8 @@ public class WritableEntity extends Entity { ...@@ -29,4 +29,8 @@ public class WritableEntity extends Entity {
public WritableEntity(final Element element) { public WritableEntity(final Element element) {
super(element); super(element);
} }
public WritableEntity(String name, Role role) {
super(name, role);
}
} }
/*
* ** header v3.0
* This file is a part of the CaosDB Project.
*
* Copyright (C) 2018 Research Group Biomedical Physics,
* Max-Planck-Institute for Dynamics and Self-Organization Göttingen
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* ** end header
*/
package org.caosdb.server.entity.container;
import java.util.HashMap;
import org.apache.shiro.subject.Subject;
import org.caosdb.server.entity.DeleteEntity;
public class DeleteContainer extends EntityByIdContainer {
private static final long serialVersionUID = -1279198934417710461L;
public DeleteContainer(
final Subject user,
final Long timestamp,
final String srid,
final HashMap<String, String> flags) {
super(user, timestamp, srid, flags);
}
@Override
public void add(final int id) {
add(new DeleteEntity(id));
}
@Override
public void add(int id, String version) {
add(new DeleteEntity(id, version));
}
}
/*
* ** header v3.0
* This file is a part of the CaosDB Project.
*
* Copyright (C) 2018 Research Group Biomedical Physics,
* Max-Planck-Institute for Dynamics and Self-Organization Göttingen
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* ** end header
*/
package org.caosdb.server.entity.container;
import java.util.HashMap;
import org.apache.shiro.subject.Subject;
import org.caosdb.server.entity.InsertEntity;
import org.jdom2.Element;
public class InsertContainer extends WritableContainer {
public InsertContainer(
final Subject user,
final Long timestamp,
final String srid,
final HashMap<String, String> flags) {
super(user, timestamp, srid, flags);
}
private static final long serialVersionUID = 8896630069350477274L;
@Override
public void add(final Element entity) {
add(new InsertEntity(entity));
}
}
/*
* ** header v3.0
* This file is a part of the CaosDB Project.
*
* Copyright (C) 2018 Research Group Biomedical Physics,
* Max-Planck-Institute for Dynamics and Self-Organization Göttingen
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* ** end header
*/
package org.caosdb.server.entity.container;
import java.util.HashMap;
import org.apache.shiro.subject.Subject;
import org.caosdb.server.entity.UpdateEntity;
import org.jdom2.Element;
public class UpdateContainer extends WritableContainer {
private static final long serialVersionUID = 8489287672264669883L;
public UpdateContainer(
final Subject user,
final Long timestamp,
final String srid,
final HashMap<String, String> flags) {
super(user, timestamp, srid, flags);
}
@Override
public void add(final Element entity) {
add(new UpdateEntity(entity));
}
}
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
* *
* Copyright (C) 2018 Research Group Biomedical Physics, * Copyright (C) 2018 Research Group Biomedical Physics,
* Max-Planck-Institute for Dynamics and Self-Organization Göttingen * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
* Copyright (C) 2021 Indiscale GmbH <info@indiscale.com>
* Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as * it under the terms of the GNU Affero General Public License as
...@@ -23,12 +25,12 @@ ...@@ -23,12 +25,12 @@
package org.caosdb.server.entity.container; package org.caosdb.server.entity.container;
import java.util.HashMap; import java.util.HashMap;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject; import org.apache.shiro.subject.Subject;
import org.caosdb.server.entity.FileProperties; import org.caosdb.server.entity.FileProperties;
import org.jdom2.Element;
public abstract class WritableContainer extends TransactionContainer { public class WritableContainer extends TransactionContainer {
private static final long serialVersionUID = 3011242254959617091L; private static final long serialVersionUID = -4097777313518959519L;
public WritableContainer( public WritableContainer(
final Subject user, final Subject user,
...@@ -38,7 +40,9 @@ public abstract class WritableContainer extends TransactionContainer { ...@@ -38,7 +40,9 @@ public abstract class WritableContainer extends TransactionContainer {
super(user, timestamp, srid, flags); super(user, timestamp, srid, flags);
} }
public abstract void add(final Element entity); public WritableContainer() {
this(SecurityUtils.getSubject(), System.currentTimeMillis(), null, null);
}
@Override @Override
public void addFile(final String uploadId, final FileProperties fileProperties) { public void addFile(final String uploadId, final FileProperties fileProperties) {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment