Java ACI Store - Transient and transactional store for Java objects.
2 |
3 | The name of the store is derived from the acronym ACID (Atomicity, Consistency, Isolation, Durability) describing the properties of transactions.
4 | The store is designed to fulfill the first three of these properties but not the Durability.
5 |
6 | For more information see https://github.com/JanWiemer/jacis/wiki
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.dmp
2 | *.hprof
3 | *.iml
4 | *.log
5 | *.log.zip
6 | *.tmp
7 | .DS_Store
8 | .gradle
9 | .idea
10 | .metadata
11 | .settings
12 | /src/main/resources/logback.xml
13 | /storage/backup
14 | /build
15 | /deploy
16 | /target
17 | /dist
18 | /tmp
19 | /var
20 | java_pid*.*
21 | junit*.properties
22 | log
23 | test-output
24 | ivysettings-local.xml
25 | /bin/
26 | .classpath
27 | .project
28 | /.profileconfig.json
29 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/plugin/dirtycheck/object/JacisDirtyTrackingObject.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.plugin.dirtycheck.object;
6 |
7 | import org.jacis.JacisApi;
8 |
9 | /**
10 | * Interface for an object automatically tracking if it is dirty.
11 | */
12 | @JacisApi
13 | public interface JacisDirtyTrackingObject {
14 |
15 | /** @return if the object is dirty */
16 | boolean isDirty();
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/exception/ReadOnlyException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.exception;
6 |
7 | import org.jacis.JacisApi;
8 |
9 | /**
10 | * Thrown when trying to modify an object in read only mode.
11 | *
12 | * @author Jan Wiemer
13 | */
14 | @JacisApi
15 | public class ReadOnlyException extends IllegalStateException {
16 |
17 | private static final long serialVersionUID = 1L;
18 |
19 | public ReadOnlyException(String s) {
20 | super(s);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/exception/JacisInternalException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.exception;
6 |
7 | import org.jacis.JacisApi;
8 |
9 | /**
10 | * Exception thrown if an internal Jacis Error happens.
11 | *
12 | * @author Jan Wiemer
13 | */
14 | @JacisApi
15 | public class JacisInternalException extends RuntimeException {
16 |
17 | private static final long serialVersionUID = 1L;
18 |
19 | public JacisInternalException(String message) {
20 | super(message);
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/src/test/java/org/jacis/testhelper/FileUtils.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020. Jan Wiemer
3 | */
4 | package org.jacis.testhelper;
5 |
6 | import java.io.File;
7 |
8 | public abstract class FileUtils {
9 |
10 | public static boolean deleteDirectory(File directoryToBeDeleted) {
11 | File[] allContents = directoryToBeDeleted.listFiles();
12 | if (allContents != null) {
13 | for (File file : allContents) {
14 | deleteDirectory(file);
15 | }
16 | }
17 | return directoryToBeDeleted.delete();
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/JacisApi.java:
--------------------------------------------------------------------------------
1 | package org.jacis;
2 |
3 | import java.lang.annotation.Documented;
4 | import java.lang.annotation.ElementType;
5 | import java.lang.annotation.Retention;
6 | import java.lang.annotation.RetentionPolicy;
7 | import java.lang.annotation.Target;
8 |
9 | /**
10 | * The public API of the classes annotated with this annotation belong the JACIS API.
11 | *
12 | * @author Jan Wiemer
13 | */
14 | @Documented
15 | @Retention(RetentionPolicy.CLASS)
16 | @Target(ElementType.TYPE)
17 | public @interface JacisApi {
18 | // empty
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/exception/JacisTransactionException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.exception;
6 |
7 | import org.jacis.JacisApi;
8 |
9 | /**
10 | * Exception thrown in case of internal problems dealing with an external transaction system.
11 | *
12 | * @author Jan Wiemer
13 | */
14 | @JacisApi
15 | public class JacisTransactionException extends RuntimeException {
16 |
17 | private static final long serialVersionUID = 1L;
18 |
19 | public JacisTransactionException(Throwable cause) {
20 | super(cause);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/exception/JacisNoTransactionException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.exception;
6 |
7 | import org.jacis.JacisApi;
8 |
9 | /**
10 | * Exception thrown if an operation is called on a store that requires to run inside a transaction,
11 | * but there is no transaction is active.
12 | *
13 | * @author Jan Wiemer
14 | */
15 | @JacisApi
16 | public class JacisNoTransactionException extends RuntimeException {
17 |
18 | private static final long serialVersionUID = 1L;
19 |
20 | public JacisNoTransactionException(String message) {
21 | super(message);
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | jacis
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 | org.eclipse.buildship.core.gradleprojectbuilder
15 |
16 |
17 |
18 |
19 |
20 | org.eclipse.jdt.core.javanature
21 | org.eclipse.buildship.core.gradleprojectnature
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/exception/JacisTransactionAlreadyStartedException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.exception;
6 |
7 | import org.jacis.JacisApi;
8 |
9 | /**
10 | * Exception thrown on the attempt to start a new (local) transaction
11 | * while there is already a transaction active.
12 | *
13 | * @author Jan Wiemer
14 | */
15 | @JacisApi
16 | public class JacisTransactionAlreadyStartedException extends RuntimeException {
17 |
18 | private static final long serialVersionUID = 1L;
19 |
20 | public JacisTransactionAlreadyStartedException(String message) {
21 | super(message);
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/exception/JacisTransactionAlreadyPreparedForCommitException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.exception;
6 |
7 | import org.jacis.JacisApi;
8 |
9 | /**
10 | * Exception thrown on the attempt to update an object
11 | * while the current transaction is already prepared for commit.
12 | *
13 | * @author Jan Wiemer
14 | */
15 | @JacisApi
16 | public class JacisTransactionAlreadyPreparedForCommitException extends RuntimeException {
17 |
18 | private static final long serialVersionUID = 1L;
19 |
20 | public JacisTransactionAlreadyPreparedForCommitException(String message) {
21 | super(message);
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/exception/JacisUniqueIndexViolationException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.exception;
6 |
7 | import org.jacis.JacisApi;
8 |
9 | /**
10 | * Thrown when trying to commit an object that would violate a unique index
11 | * (or registering a unique index that would be violated by the existing objects).
12 | *
13 | * @author Jan Wiemer
14 | */
15 | @JacisApi
16 | public class JacisUniqueIndexViolationException extends IllegalStateException {
17 |
18 | private static final long serialVersionUID = 1L;
19 |
20 | public JacisUniqueIndexViolationException(String s) {
21 | super(s);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/exception/ReadOnlyModeNotSupportedException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.exception;
6 |
7 | import org.jacis.JacisApi;
8 |
9 | /**
10 | * Thrown when trying to access an object in read only mode when the object adapter
11 | * does not support a read only mode for this object
12 | * and is configured to throw an exception in this case.
13 | *
14 | * @author Jan Wiemer
15 | */
16 | @JacisApi
17 | public class ReadOnlyModeNotSupportedException extends IllegalStateException {
18 |
19 | private static final long serialVersionUID = 1L;
20 |
21 | public ReadOnlyModeNotSupportedException(String s) {
22 | super(s);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/exception/JacisTxCommitException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.exception;
6 |
7 | import org.jacis.JacisApi;
8 |
9 | /**
10 | * Exception attached to an exception thrown during commit to hold some additional information regarding the committed JACIS transaction.
11 | *
12 | * @author Jan Wiemer
13 | */
14 | @JacisApi
15 | public class JacisTxCommitException extends RuntimeException {
16 |
17 | private static final long serialVersionUID = 1L;
18 |
19 | public JacisTxCommitException(String message) {
20 | super(message);
21 | }
22 |
23 | public JacisTxCommitException(String message, Throwable cause) {
24 | super(message, cause);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/exception/JacisTxRollbackException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.exception;
6 |
7 | import org.jacis.JacisApi;
8 |
9 | /**
10 | * Exception attached to an exception thrown during commit to hold some additional information regarding the committed JACIS transaction.
11 | *
12 | * @author Jan Wiemer
13 | */
14 | @JacisApi
15 | public class JacisTxRollbackException extends RuntimeException {
16 |
17 | private static final long serialVersionUID = 1L;
18 |
19 | public JacisTxRollbackException(String message) {
20 | super(message);
21 | }
22 |
23 | public JacisTxRollbackException(String message, Throwable cause) {
24 | super(message, cause);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/JanWiemer/jacis/actions?query=workflow%3AJACIS-CI-Build)
2 | 
3 |
4 | # jacis
5 | Java ACI Store - Transient and transactional store for Java objects.
6 |
7 | The name of the store is derived from the acronym ACID (Atomicity, Consistency, Isolation, Durability) describing the properties of transactions. The store is designed to fulfill the first three of these properties but not the Durability.
8 |
9 | The JACIS project follows the semantic versioning policy (see https://semver.org/, API indicated by ``JacisApi`` Annotation).
10 |
11 | For more information see https://github.com/JanWiemer/jacis/wiki
12 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | ############################
2 | # JACIS VERSION:
3 | ############################
4 | version=2.1.20
5 | #=format for release: 2.1.25
6 | #=format for snapshot: 2.0.26-2023-01-01 (on the way to 2.0.26)
7 | #
8 | ############################
9 | # DEPENDENCIES
10 | ############################
11 | version_slf4j=2.0.16
12 | #= Important Note for logback:
13 | #=* logback 1.3.x compatible with JDK 8 and javax.* namespace
14 | #=* logback 1.4.x compatible with JDK >=11 and jakarta.* namespace
15 | version_logback=1.5.18
16 | version_junit=4.13.2
17 | #
18 | ############################
19 | # EXTENSIONS
20 | ############################
21 | version_eclipse_store=1.3.1
22 | version_xodus=2.0.1
23 | version_jackson=2.17.0
24 | #
25 | ###########################
26 | # MISC
27 | ############################
28 | #org.gradle.warning.mode=all
29 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/exception/JacisModificationVetoException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.exception;
6 |
7 | import org.jacis.JacisApi;
8 | import org.jacis.plugin.JacisModificationListener;
9 |
10 | /**
11 | * Thrown when a {@link JacisModificationListener} has a veto against a tracked modification during the prepare phase.
12 | * In this case it can throw this exception to roll back the whole transaction
13 | *
14 | * @author Jan Wiemer
15 | */
16 | @JacisApi
17 | public class JacisModificationVetoException extends IllegalStateException {
18 |
19 | private static final long serialVersionUID = 1L;
20 |
21 | public JacisModificationVetoException(JacisModificationListener,?> modListener, String message) {
22 | super("Modification veto by "+modListener.getClass().getSimpleName()+": "+message);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/plugin/dirtycheck/StoreEntryBasedDirtyCheck.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.plugin.dirtycheck;
6 |
7 | import org.jacis.plugin.dirtycheck.object.JacisDirtyTrackingObject;
8 |
9 | /**
10 | * Implementation of the {@link JacisDirtyCheck} that is based on objects
11 | * tracking their dirty state and provide this state via the {@link JacisDirtyTrackingObject#isDirty()} method.
12 | *
13 | * @param Key type of the store entry
14 | * @param Type of the objects in the transaction view. This is the type visible from the outside.
15 | * @author Jan Wiemer
16 | */
17 | public class StoreEntryBasedDirtyCheck implements JacisDirtyCheck {
18 |
19 | @Override
20 | public boolean isDirty(K key, TV originalValue, TV currentValue) {
21 | if(currentValue==null) {
22 | return originalValue!=null;
23 | } else {
24 | return currentValue.isDirty();
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/store/JacisReadOnlyTransactionContext.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020. Jan Wiemer
3 | */
4 |
5 | package org.jacis.store;
6 |
7 | import org.jacis.JacisApi;
8 |
9 | /**
10 | * Representing a read only version of the context of a store within a transaction.
11 | * The context contains all transactional views for objects in the store belonging to this transaction.
12 | * The context can be used to propagate a read only view of this context to another transaction in another thread.
13 | * Note that it is only possible to propagate a read only view to prevent multiple threads to work in a single transaction concurrently.
14 | *
15 | * @author Jan Wiemer
16 | */
17 | @JacisApi
18 | public interface JacisReadOnlyTransactionContext {
19 |
20 | /**
21 | * @return The id of the transaction this transactional context was retrieved from.
22 | */
23 | String getTxId();
24 |
25 | /**
26 | * @return The id of the transaction this transactional context should be used in read only mode.
27 | */
28 | String getReadOnlyTxId();
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/trackedviews/TrackedViewClustered.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017. Jan Wiemer
3 | */
4 |
5 | package org.jacis.trackedviews;
6 |
7 | import java.util.Collection;
8 |
9 | import org.jacis.JacisApi;
10 |
11 | /**
12 | * A clustered tracked view provides access to sub views by a key.
13 | * The advantage is that only the desired sub view is cloned when accessing it.
14 | *
15 | * @param The type of the original values / objects from the store
16 | * @param The key type for the sub views
17 | * @param The type of the sub views
18 | * @author Jan Wiemer
19 | */
20 | @JacisApi
21 | public interface TrackedViewClustered> extends TrackedView {
22 |
23 | /**
24 | * Return the partition / cluster of the clustered tracked view for the passed key.
25 | * Note that the method must not return null for any valid sub view key!
26 | *
27 | * @param key The key for the desired sub view (/ partition / cluster)
28 | * @return the partition / cluster of the clustered tracked view for the passed key.
29 | */
30 | SVT getSubView(SVK key);
31 |
32 | /** @return All keys that can be used to access one partition / cluster of the clustered tracked view. */
33 | Collection getSubViewKeys();
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/container/JacisContainerReadOnlyTransactionContext.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017. Jan Wiemer
3 | */
4 |
5 | package org.jacis.container;
6 |
7 | import java.util.AbstractMap;
8 | import java.util.AbstractMap.SimpleImmutableEntry;
9 | import java.util.ArrayList;
10 | import java.util.List;
11 |
12 | import org.jacis.JacisApi;
13 | import org.jacis.store.JacisReadOnlyTransactionContext;
14 | import org.jacis.store.JacisStore;
15 |
16 | /**
17 | * Representing a read only version of all the contexts of the stores
18 | * within a transaction belonging to the container.
19 | *
20 | * @author Jan Wiemer
21 | */
22 | @JacisApi
23 | public class JacisContainerReadOnlyTransactionContext {
24 |
25 | private final List, JacisReadOnlyTransactionContext>> storeContextList = new ArrayList<>();
26 |
27 | void add(JacisStore, ?> store, JacisReadOnlyTransactionContext context) {
28 | storeContextList.add(new SimpleImmutableEntry<>(store, context));
29 | }
30 |
31 | void startReadOnlyTransactionWithContext() {
32 | for (SimpleImmutableEntry, JacisReadOnlyTransactionContext> entry : storeContextList) {
33 | entry.getKey().startReadOnlyTransactionWithContext(entry.getValue());
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/plugin/dirtycheck/object/AbstractReadOnlyModeAndDirtyCheckSupportingObject.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.plugin.dirtycheck.object;
6 |
7 | import org.jacis.JacisApi;
8 | import org.jacis.exception.ReadOnlyException;
9 | import org.jacis.plugin.readonly.object.AbstractReadOnlyModeSupportingObject;
10 |
11 | /**
12 | * Abstract base class for objects supporting switching them between the usual read-write mode and a read-only mode
13 | * (see {@link AbstractReadOnlyModeSupportingObject})
14 | * and tracking if the object is dirty by tracking if the {@link #checkWritable()} method has been called.
15 | *
16 | * @author Jan Wiemer
17 | */
18 | @JacisApi
19 | public abstract class AbstractReadOnlyModeAndDirtyCheckSupportingObject extends AbstractReadOnlyModeSupportingObject implements JacisDirtyTrackingObject {
20 |
21 | private transient boolean dirty = false;
22 |
23 | /** @return if the object is dirty */
24 | @Override
25 | public boolean isDirty() {
26 | return dirty;
27 | }
28 |
29 | @Override
30 | protected void checkWritable() throws ReadOnlyException {
31 | super.checkWritable();
32 | dirty = true;
33 | }
34 |
35 | @Override
36 | public void switchToReadOnlyMode() {
37 | super.switchToReadOnlyMode();
38 | dirty = false;
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/extension/persistence/microstream/MicrostreamRoot.java:
--------------------------------------------------------------------------------
1 | package org.jacis.extension.persistence.microstream;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | import org.jacis.container.JacisContainer.StoreIdentifier;
7 | import org.jacis.store.JacisStore;
8 |
9 | /**
10 | * The root object stored by the Microstream storage manager.
11 | * Basically it stores the list of the roots for the Jacis stores.
12 | *
13 | * @author Jan Wiemer
14 | */
15 | class MicrostreamRoot {
16 |
17 | /** Map containing the root object for each JACIS store. */
18 | private final Map> storeRootMap = new HashMap<>();
19 |
20 | @Override
21 | public String toString() {
22 | return getClass().getSimpleName() + "(" + storeRootMap.values() + ")";
23 | }
24 |
25 | Map> getStoreRootMap() {
26 | return storeRootMap;
27 | }
28 |
29 | @SuppressWarnings("unchecked")
30 | public MicrostreamStoreRoot getStoreRoot(JacisStore store) {
31 | StoreIdentifier storeIdentifier = store.getStoreIdentifier();
32 | return (MicrostreamStoreRoot) storeRootMap.get(storeIdentifier);
33 | }
34 |
35 | public void setStoreRoot(JacisStore store, MicrostreamStoreRoot root) {
36 | StoreIdentifier storeIdentifier = store.getStoreIdentifier();
37 | storeRootMap.put(storeIdentifier, root);
38 | }
39 |
40 | }
--------------------------------------------------------------------------------
/src/main/java/org/jacis/plugin/objectadapter/immutable/JacisImmutableObjectAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.plugin.objectadapter.immutable;
6 |
7 | import org.jacis.plugin.objectadapter.JacisObjectAdapter;
8 |
9 | /**
10 | * Implementation of the {@link org.jacis.plugin.objectadapter.JacisObjectAdapter} that can be used if the store only contains
11 | * immutable objects. Note that this requires that the objects are never changed once they are stored in the store.
12 | *
13 | * When using this adapter it is recommended to programmatically ensure that the objects are immutable, e.g. by making all properties final.
14 | *
15 | * @param The object type (note that in this case the committed values and the values in the transactional view have the same type)
16 | * @author Jan Wiemer
17 | */
18 | public class JacisImmutableObjectAdapter implements JacisObjectAdapter {
19 |
20 | @Override
21 | public V cloneCommitted2WritableTxView(V value) {
22 | return value;
23 | }
24 |
25 | @Override
26 | public V cloneTxView2Committed(V value) {
27 | return value;
28 | }
29 |
30 | @Override
31 | public V cloneCommitted2ReadOnlyTxView(V value) {
32 | return value;
33 | }
34 |
35 | @Override
36 | public V cloneTxView2ReadOnlyTxView(V value) {
37 | return value;
38 | }
39 |
40 | @Override
41 | public String toString() {
42 | return getClass().getSimpleName();
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/plugin/readonly/DefaultJacisStoreEntryReadOnlyModeAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.plugin.readonly;
6 |
7 | import org.jacis.plugin.readonly.object.JacisReadonlyModeSupport;
8 |
9 | /**
10 | * The default implementation of the interface {@link JacisStoreEntryReadOnlyModeAdapter}.
11 | * This implementation is applicable for objects
12 | * implementing the {@link org.jacis.plugin.readonly.object.JacisReadonlyModeSupport} interface
13 | * and uses the methods declared in this interface for switching the mode.
14 | *
15 | * @param The type of the values that should be switched between read-write and read-only mode.
16 | * @author Jan Wiemer
17 | */
18 | public class DefaultJacisStoreEntryReadOnlyModeAdapter implements JacisStoreEntryReadOnlyModeAdapter {
19 |
20 | @Override
21 | public boolean isApplicableTo(V value) {
22 | return value instanceof JacisReadonlyModeSupport;
23 | }
24 |
25 | @Override
26 | public boolean isReadOnly(V value) {
27 | return ((JacisReadonlyModeSupport) value).isReadOnly();
28 | }
29 |
30 | @Override
31 | public V switchToReadOnlyMode(V value) {
32 | ((JacisReadonlyModeSupport) value).switchToReadOnlyMode();
33 | return value;
34 | }
35 |
36 | @Override
37 | public V switchToReadWriteMode(V value) {
38 | ((JacisReadonlyModeSupport) value).switchToReadWriteMode();
39 | return value;
40 | }
41 |
42 | @Override
43 | public String toString() {
44 | return getClass().getSimpleName();
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/test/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | %-5level %d{yyyy-MM-dd HH:mm:ss.SSS} [%25.25thread] - %msg \(%C{0}.java:%L\)%n
12 |
13 |
14 |
15 |
16 | log/jacis.log
17 |
18 |
19 | log/jacis-%d{yyyy-MM-dd}.%3i.log.zip
20 |
21 | 10MB
22 |
23 | 100
24 |
25 |
26 | %-5level %d{yyyy-MM-dd HH:mm:ss.SSS} [%25.25thread] - %msg \(%C{0}.java:%L\)%n
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/main/resources/logback.xml.template:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | %-5level %d{yyyy-MM-dd HH:mm:ss.SSS} [%25.25thread] - %msg \(%C{0}.java:%L\)%n
12 |
13 |
14 |
15 |
16 | log/jacis.log
17 |
18 |
19 | log/jacis-%d{yyyy-MM-dd}.%i.log.zip
20 |
21 | 100MB
22 |
23 | 10
24 |
25 |
26 | %-5level %d{yyyy-MM-dd HH:mm:ss.SSS} [%25.25thread] - %msg \(%C{0}.java:%L\)%n
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/exception/JacisStaleObjectException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.exception;
6 |
7 | import org.jacis.JacisApi;
8 |
9 | /**
10 | * Exception thrown if an object modified in this transaction has meanwhile been modified by another transaction.
11 | * In order to detect such a situation the store maintains a version counter for each (committed) version of an object.
12 | * Once an object is cloned into a transactional view the current version counter is also stored as original version
13 | * of the transactional view. Each time a transactional object is cloned back to the store of committed objects (during commit)
14 | * the version counter of the committed object is incremented. Before committing an object it is checked if the
15 | * version counter of the committed version is the same as the original version of the transactional view to commit.
16 | * If both version counters are the same the object has not been changed in the meantime, otherwise this exception is thrown
17 | *
18 | * @author Jan Wiemer
19 | */
20 | @JacisApi
21 | public class JacisStaleObjectException extends RuntimeException {
22 |
23 | private static final long serialVersionUID = 1L;
24 |
25 | /** detail message describing the reason for the stale object exception */
26 | private String details;
27 |
28 | public JacisStaleObjectException(String message) {
29 | super(message);
30 | }
31 |
32 | public String getDetails() {
33 | return details;
34 | }
35 |
36 | public JacisStaleObjectException setDetails(String details) {
37 | this.details = details;
38 | return this;
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/test/java/org/jacis/trackedview/TransactionLocalTrackedViewTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020. Jan Wiemer
3 | */
4 |
5 | package org.jacis.trackedview;
6 |
7 | import static org.junit.Assert.assertEquals;
8 |
9 | import org.jacis.container.JacisContainer;
10 | import org.jacis.store.JacisStore;
11 | import org.jacis.testhelper.JacisTestHelper;
12 | import org.jacis.testhelper.TestObject;
13 | import org.jacis.testhelper.TrackedTestView;
14 | import org.jacis.trackedviews.TrackedView;
15 | import org.junit.Test;
16 |
17 | public class TransactionLocalTrackedViewTest {
18 |
19 | @Test
20 | public void testModificationTrackingInTransaction() {
21 | JacisTestHelper testHelper = new JacisTestHelper();
22 | JacisStore store = testHelper.createTestStoreWithCloning();
23 | JacisContainer container = store.getContainer();
24 | container.withLocalTx(() -> {
25 | store.update("1", new TestObject("A1").setValue(5));
26 | });
27 | TrackedView view = new TrackedTestView();
28 | store.getTrackedViewRegistry().registerTrackedView(view);
29 | assertEquals(5, store.getTrackedViewRegistry().getView(TrackedTestView.class).getSum());
30 |
31 | container.withLocalTx(() -> {
32 | TestObject o = store.get("1");
33 | o.setValue(3);
34 | store.update("1", o);
35 | assertEquals(3, store.getTrackedViewRegistry().getView(TrackedTestView.class).getSum());
36 | o.setValue(4);
37 | store.update("1", o);
38 | assertEquals(4, store.getTrackedViewRegistry().getView(TrackedTestView.class).getSum());
39 | });
40 | assertEquals(4, store.getTrackedViewRegistry().getView(TrackedTestView.class).getSum());
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/plugin/JacisTransactionListenerAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.plugin;
6 |
7 | import org.jacis.JacisApi;
8 | import org.jacis.container.JacisContainer;
9 | import org.jacis.container.JacisTransactionHandle;
10 |
11 | /**
12 | * Adapter class to simplify creating an implementation of the {@link JacisTransactionListener} interface.
13 | * A class implementing this interface can extend this class providing empty default implementation for
14 | * all methods declared in the interface. So the implementation only has to overwrite the desired method and skip the other ones.
15 | *
16 | * @author Jan Wiemer
17 | */
18 | @Deprecated // no longer needed because interface has default implementations
19 | @JacisApi
20 | public abstract class JacisTransactionListenerAdapter implements JacisTransactionListener {
21 |
22 | @Override
23 | public void beforePrepare(JacisContainer container, JacisTransactionHandle tx) {
24 | // empty
25 | }
26 |
27 | @Override
28 | public void afterPrepare(JacisContainer container, JacisTransactionHandle tx) {
29 | // empty
30 | }
31 |
32 | @Override
33 | public void beforeCommit(JacisContainer container, JacisTransactionHandle tx) {
34 | // empty
35 | }
36 |
37 | @Override
38 | public void afterCommit(JacisContainer container, JacisTransactionHandle tx) {
39 | // empty
40 | }
41 |
42 | @Override
43 | public void beforeRollback(JacisContainer container, JacisTransactionHandle tx) {
44 | // empty
45 | }
46 |
47 | @Override
48 | public void afterRollback(JacisContainer container, JacisTransactionHandle tx) {
49 | // empty
50 | }
51 |
52 | @Override
53 | public String toString() {
54 | return getClass().getSimpleName();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/store/KeyValuePair.java:
--------------------------------------------------------------------------------
1 | package org.jacis.store;
2 |
3 | import org.jacis.JacisApi;
4 |
5 | import java.io.Serializable;
6 |
7 | @JacisApi
8 | public class KeyValuePair implements Serializable {
9 |
10 | public static KeyValuePair of(K key, TV val) {
11 | return new KeyValuePair<>(key, val);
12 | }
13 |
14 | private static final long serialVersionUID = 1L;
15 |
16 | private final K key;
17 | private final TV val;
18 |
19 | public KeyValuePair(K first, TV second) {
20 | this.key = first;
21 | this.val = second;
22 | }
23 |
24 | public K getKey() {
25 | return key;
26 | }
27 |
28 | public TV getVal() {
29 | return val;
30 | }
31 |
32 | @Override
33 | public String toString() {
34 | return "(" + key + ", " + val + ")";
35 | }
36 |
37 | @Override
38 | public int hashCode() {
39 | final int prime = 31;
40 | int result = 1;
41 | result = prime * result + ((key == null) ? 0 : key.hashCode());
42 | result = prime * result + ((val == null) ? 0 : val.hashCode());
43 | return result;
44 | }
45 |
46 | @SuppressWarnings("rawtypes")
47 | @Override
48 | public boolean equals(Object obj) {
49 | if (this == obj) {
50 | return true;
51 | }
52 | if (obj == null) {
53 | return false;
54 | }
55 | if (getClass() != obj.getClass()) {
56 | return false;
57 | }
58 | KeyValuePair other = (KeyValuePair) obj;
59 | if (key == null) {
60 | if (other.key != null) {
61 | return false;
62 | }
63 | } else if (!key.equals(other.key)) {
64 | return false;
65 | }
66 | if (val == null) {
67 | return other.val == null;
68 | } else {
69 | return val.equals(other.val);
70 | }
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/extension/objectadapter/cloning/microstream/JacisMicrostreamCloningObjectAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.extension.objectadapter.cloning.microstream;
6 |
7 | import java.io.Serializable;
8 |
9 | import org.jacis.plugin.objectadapter.cloning.AbstractJacisCloningObjectAdapter;
10 | import org.jacis.plugin.readonly.DefaultJacisStoreEntryReadOnlyModeAdapter;
11 | import org.jacis.plugin.readonly.JacisStoreEntryReadOnlyModeAdapter;
12 |
13 | /**
14 | * Generic implementation of the {@link org.jacis.plugin.objectadapter.JacisObjectAdapter} copying the objects
15 | * to and from the transactional view by means of Microstream serialization.
16 | *
17 | * @param The object type (note that in this case the committed values and the values in the transactional view have the same type)
18 | * @author Jan Wiemer
19 | */
20 | public class JacisMicrostreamCloningObjectAdapter extends AbstractJacisCloningObjectAdapter {
21 |
22 | private final MicrostreamObjectCopier copier = MicrostreamObjectCopier.New();
23 |
24 | /**
25 | * Create a cloning object adapter with the passed read only mode adapter.
26 | *
27 | * @param readOnlyModeAdapters Adapter to switch an object between read-only and read-write mode (if supported).
28 | */
29 | public JacisMicrostreamCloningObjectAdapter(JacisStoreEntryReadOnlyModeAdapter readOnlyModeAdapters) {
30 | super(readOnlyModeAdapters);
31 | }
32 |
33 | /**
34 | * Create a cloning object adapter with a default read only mode adapter (see {@link DefaultJacisStoreEntryReadOnlyModeAdapter}).
35 | */
36 | public JacisMicrostreamCloningObjectAdapter() {
37 | super();
38 | }
39 |
40 | @Override
41 | protected V cloneValue(V value) {
42 | return copier.copy(value);
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/src/test/java/org/jacis/objectadapter/serialization/JacisStoreWithSerializationTrackedViewTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.objectadapter.serialization;
6 |
7 | import static org.junit.Assert.assertEquals;
8 |
9 | import org.jacis.container.JacisContainer;
10 | import org.jacis.store.JacisStore;
11 | import org.jacis.testhelper.JacisTestHelper;
12 | import org.jacis.testhelper.TestObject;
13 | import org.jacis.testhelper.TrackedTestView;
14 | import org.jacis.trackedviews.TrackedView;
15 | import org.junit.Test;
16 |
17 | public class JacisStoreWithSerializationTrackedViewTest {
18 |
19 | @Test
20 | public void testTrackedView() {
21 | JacisTestHelper testHelper = new JacisTestHelper();
22 | JacisStore store = testHelper.createTestStoreWithSerialization();
23 | JacisContainer container = store.getContainer();
24 | container.withLocalTx(() -> {
25 | store.update("1", new TestObject("A1"));
26 | store.update("toDelete1", new TestObject("A1"));
27 | });
28 | TrackedView view = new TrackedTestView();
29 | store.getTrackedViewRegistry().registerTrackedView(view);
30 | container.withLocalTx(() -> {
31 | store.update("toDelete2", new TestObject("A1"));
32 | store.update("2", new TestObject("A2"));
33 | store.remove("toDelete2");
34 | store.update("3", new TestObject("B1"));
35 | store.update("4", new TestObject("B2"));
36 | store.remove("toDelete1");
37 | });
38 | container.withLocalTx(() -> {
39 | store.remove("toDelete2");
40 | store.update("toDelete3", new TestObject("A1"));
41 | store.remove("toDelete3");
42 | store.update("4", new TestObject("A4"));
43 | store.update("5", new TestObject("A3"));
44 | int viewVal = store.getTrackedViewRegistry().getView(TrackedTestView.class).getCount();
45 | assertEquals(5, viewVal);
46 | });
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/plugin/objectadapter/cloning/JacisCloningObjectAdapterUnsafe.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.plugin.objectadapter.cloning;
6 |
7 | import org.jacis.JacisApi;
8 | import org.jacis.plugin.readonly.DefaultJacisStoreEntryReadOnlyModeAdapter;
9 | import org.jacis.plugin.readonly.JacisStoreEntryReadOnlyModeAdapter;
10 |
11 | /**
12 | * Implementation of the {@link AbstractJacisCloningObjectAdapter} cloning the objects based on the Java clone method.
13 | * The behavior is similar to the {@link JacisCloningObjectAdapter} but returning a read only version of the object does not first check if the object supports a read only mode.
14 | *
15 | * @param The object type (note that in this case the committed values and the values in the transactional view have the same type)
16 | * @author Jan Wiemer
17 | */
18 | @JacisApi
19 | @SuppressWarnings({"unused", "UnusedReturnValue"}) // since this is an API of the library
20 | public class JacisCloningObjectAdapterUnsafe extends JacisCloningObjectAdapter {
21 |
22 | /**
23 | * Create a cloning object adapter with the passed read only mode adapter.
24 | *
25 | * @param readOnlyModeAdapters Adapter to switch an object between read-only and read-write mode (if supported).
26 | */
27 | public JacisCloningObjectAdapterUnsafe(JacisStoreEntryReadOnlyModeAdapter readOnlyModeAdapters) {
28 | super(readOnlyModeAdapters);
29 | if (readOnlyModeAdapters == null) {
30 | throw new IllegalArgumentException(this.getClass().getName() + " can only be initialized with a read only mode adapter!");
31 | }
32 | }
33 |
34 | /**
35 | * Create a cloning object adapter with a default read only mode adapter (see {@link DefaultJacisStoreEntryReadOnlyModeAdapter}).
36 | */
37 | public JacisCloningObjectAdapterUnsafe() {
38 | super();
39 | }
40 |
41 | @Override
42 | public V cloneCommitted2ReadOnlyTxView(V value) {
43 | return value;
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/extension/persistence/microstream/MicrostreamStoreEntity.java:
--------------------------------------------------------------------------------
1 | package org.jacis.extension.persistence.microstream;
2 |
3 | /**
4 | * The Microstream entity object representing the store entries.
5 | * The entity objects build a linked list (see {@link #getPrev()} and {@link #getNext()}).
6 | *
7 | * @author Jan Wiemer
8 | *
9 | * @param Key type of the store entry
10 | * @param Value type of the store entry
11 | */
12 | class MicrostreamStoreEntity {
13 |
14 | /** The key of the store entry represented by this Microstream entity object. */
15 | private K key;
16 | /** The value of the store entry represented by this Microstream entity object. */
17 | private V value;
18 | /** The next element in the linked list. */
19 | private MicrostreamStoreEntity next;
20 | /** The previous element in the linked list. */
21 | private MicrostreamStoreEntity prev;
22 |
23 | public MicrostreamStoreEntity(K key, V value) {
24 | this.key = key;
25 | this.value = value;
26 | }
27 |
28 | public K getKey() {
29 | return key;
30 | }
31 |
32 | public V getValue() {
33 | return value;
34 | }
35 |
36 | public MicrostreamStoreEntity getNext() {
37 | return next;
38 | }
39 |
40 | public MicrostreamStoreEntity getPrev() {
41 | return prev;
42 | }
43 |
44 | public MicrostreamStoreEntity setKey(K key) {
45 | this.key = key;
46 | return this;
47 | }
48 |
49 | public MicrostreamStoreEntity setValue(V value) {
50 | this.value = value;
51 | return this;
52 | }
53 |
54 | public MicrostreamStoreEntity setNext(MicrostreamStoreEntity next) {
55 | this.next = next;
56 | return this;
57 | }
58 |
59 | public MicrostreamStoreEntity setPrev(MicrostreamStoreEntity prev) {
60 | this.prev = prev;
61 | return this;
62 | }
63 |
64 | @Override
65 | public String toString() {
66 | return "Entity(" + key + ")";
67 | }
68 | }
--------------------------------------------------------------------------------
/src/main/java/org/jacis/plugin/objectadapter/cloning/JacisCloneable.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.plugin.objectadapter.cloning;
6 |
7 | import org.jacis.JacisApi;
8 |
9 | /**
10 | * This interface should be implemented by an object that should be cloned by the
11 | * {@link JacisCloningObjectAdapter} by simply calling the {@link #clone()} method.
12 | *
13 | * Note that an implementation of the clone method usually overwrites the {@link Object#clone()} method.
14 | * This means a clone can be initialized by calling 'super.clone()' with the semantic that all primitive members
15 | * and all references are already cloned by the super method. How deep referred objects (specially referred
16 | * container objects like collections) should be cloned has to be carefully designed.
17 | * The design of the stored object together with the implementation of the clone method has to guarantee
18 | * that no modification of the cloned object itself or referred (reachable) objects can modify the original
19 | * object or its referred (reachable) objects. That means an object that is suitable to be a JACIS cloneable object
20 | * must only contain properties that are:
21 | *
22 | * * primitive types, and therefore immutable;
23 | * * immutable types;
24 | * * other JACIS cloneable objects that are deeply cloned with the original object
25 | * * collections of other JACIS cloneable objects that are deeply cloned with the original object (the collection itself has to be cloned as well)
26 | *
27 | * It is also possible to simply overwrite the {@link Object#clone()} method without implementing this interface.
28 | * However, in this case the {@link JacisCloningObjectAdapter} has to call the clone method by reflection.
29 | *
30 | * @param The type of the object implementing this interface.
31 | * @author Jan Wiemer
32 | */
33 | @JacisApi
34 | public interface JacisCloneable extends Cloneable {
35 |
36 | /** @return A clone of this object */
37 | OT clone();
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/plugin/readonly/object/JacisReadonlyModeSupport.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.plugin.readonly.object;
6 |
7 | import org.jacis.JacisApi;
8 | import org.jacis.plugin.readonly.DefaultJacisStoreEntryReadOnlyModeAdapter;
9 | import org.jacis.plugin.readonly.JacisStoreEntryReadOnlyModeAdapter;
10 |
11 | /**
12 | * This interface can be implemented by objects that support switching between read-write and read-only mode.
13 | * The default implementation (class {@link DefaultJacisStoreEntryReadOnlyModeAdapter})
14 | * for the {@link JacisStoreEntryReadOnlyModeAdapter} interface uses this interface to switch the read / write mode.
15 | *
16 | * Note that the implementation of the object is responsible to ensure that an object in read-only mode really prevents
17 | * all modifications, usually by throwing an exception (e.g. an {@link org.jacis.exception.ReadOnlyException}).
18 | *
19 | * An abstract base class simplifying to implement this interface is the {@link AbstractReadOnlyModeSupportingObject} class.
20 | *
21 | * @author Jan Wiemer
22 | */
23 | @JacisApi
24 | public interface JacisReadonlyModeSupport {
25 |
26 | /**
27 | * Switches the object into the read-only mode
28 | */
29 | void switchToReadOnlyMode();
30 |
31 | /**
32 | * Switches the object into the read-write mode
33 | */
34 | void switchToReadWriteMode();
35 |
36 | /**
37 | * @return if the object is currently in the read-only mode
38 | */
39 | boolean isReadOnly();
40 |
41 | /**
42 | * @return if the object is writable for the current thread
43 | */
44 | boolean isWritable();
45 |
46 | /**
47 | * Switches the read / write mode depending on the passed parameter
48 | *
49 | * @param readOnlyMode if 'true' switch to read-only mode, if 'false' switch to read-write mode
50 | */
51 | default void switchToReadOnlyMode(boolean readOnlyMode) {
52 | if (readOnlyMode) {
53 | switchToReadOnlyMode();
54 | } else {
55 | switchToReadWriteMode();
56 | }
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/plugin/objectadapter/serialization/JacisSerializationObjectAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.plugin.objectadapter.serialization;
6 |
7 | import java.io.Serializable;
8 |
9 | import org.jacis.plugin.objectadapter.JacisObjectAdapter;
10 |
11 | /**
12 | * Abstract generic implementation of the {@link org.jacis.plugin.objectadapter.JacisObjectAdapter} copying the objects
13 | * to and from the transactional view by means of a serialization mechanism.
14 | * Serialization and de-serialization of the objects is delegated to the abstract methods
15 | * {@link #serialize(Serializable)} and {@link #deserialize(byte[])}.
16 | *
17 | * @param The object type (note that in this case the committed values and the values in the transactional view have the same type)
18 | * @author Jan Wiemer
19 | */
20 | public abstract class JacisSerializationObjectAdapter implements JacisObjectAdapter {
21 |
22 | @Override
23 | public TV cloneCommitted2WritableTxView(byte[] bytes) {
24 | return deserialize(bytes);
25 | }
26 |
27 | @Override
28 | public TV cloneCommitted2ReadOnlyTxView(byte[] bytes) {
29 | return deserialize(bytes);
30 | }
31 |
32 | @Override
33 | public TV cloneTxView2ReadOnlyTxView(TV value) {
34 | return deserialize(serialize(value));
35 | }
36 |
37 | @Override
38 | public byte[] cloneTxView2Committed(TV value) {
39 | return serialize(value);
40 | }
41 |
42 | /**
43 | * Serialize the passed object to a byte array.
44 | *
45 | * @param obj The object to serialize.
46 | * @return The bytes of the serialized object.
47 | */
48 | protected abstract byte[] serialize(TV obj);
49 |
50 | /**
51 | * De-serialize an object from the passed byte array.
52 | *
53 | * @param bytes The bytes from which to de-serialize the object.
54 | * @return The de-serialized object.
55 | */
56 | protected abstract TV deserialize(byte[] bytes);
57 |
58 | @Override
59 | public String toString() {
60 | return getClass().getSimpleName();
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/store/JacisStoreAdminInterface.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.store;
6 |
7 | import org.jacis.JacisApi;
8 | import org.jacis.container.JacisContainer;
9 | import org.jacis.container.JacisObjectTypeSpec;
10 | import org.jacis.plugin.objectadapter.JacisObjectAdapter;
11 |
12 | import java.util.List;
13 |
14 | /**
15 | * Administration interface for a JACIS store.
16 | *
17 | * This interface provides methods to add or remove transaction listeners or maintain the registered tracked views.
18 | *
19 | * @param Key type of the store entry
20 | * @param Type of the objects in the transaction view. This is the type visible from the outside.
21 | * @param Type of the objects as they are stored in the internal map of committed values. This type is not visible from the outside.
22 | * @author Jan Wiemer
23 | */
24 | @JacisApi
25 | public interface JacisStoreAdminInterface {
26 |
27 | /** @return the store */
28 | JacisStore getStore();
29 |
30 | /** @return the store identifier uniquely identifying this store inside the container */
31 | JacisContainer.StoreIdentifier getStoreIdentifier();
32 |
33 | /** @return the object type specification for the objects stored in this store */
34 | JacisObjectTypeSpec getObjectTypeSpec();
35 |
36 | /** @return the object adapter defining how to copy objects from the committed view to a transactional view and back */
37 | JacisObjectAdapter getObjectAdapter();
38 |
39 | /**
40 | * Returns a info object /type {@link StoreEntryInfo}) containing information regarding the current state of the object
41 | * (regarding the committed values and the current transactional view).
42 | *
43 | * @param key The key of the desired object.
44 | * @return a info object /type {@link StoreEntryInfo}) containing information regarding the current state of the object.
45 | */
46 | StoreEntryInfo getObjectInfo(K key);
47 |
48 | public List getAllIndexDefinitions();
49 |
50 | public List getTransactionInfos();
51 | }
52 |
--------------------------------------------------------------------------------
/src/test/java/org/jacis/testhelper/TrackedTestView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.testhelper;
6 |
7 | import java.util.List;
8 |
9 | import org.jacis.trackedviews.TrackedView;
10 | import org.slf4j.Logger;
11 | import org.slf4j.LoggerFactory;
12 |
13 | /**
14 | * Test tracked view...
15 | *
16 | * @author Jan Wiemer
17 | */
18 | public class TrackedTestView implements TrackedView {
19 |
20 | private static final Logger log = LoggerFactory.getLogger(TestObject.class);
21 |
22 | private int count = 0;
23 | private long sum = 0;
24 |
25 | @SuppressWarnings("unchecked")
26 | @Override
27 | public TrackedView clone() {
28 | try {
29 | return (TrackedView) super.clone();
30 | } catch (CloneNotSupportedException e) {
31 | throw new RuntimeException("clone murks");
32 | }
33 | }
34 |
35 | public int getCount() {
36 | return count;
37 | }
38 |
39 | public long getSum() {
40 | return sum;
41 | }
42 |
43 | @Override
44 | public void clear() {
45 | count = 0;
46 | sum = 0;
47 | }
48 |
49 | @Override
50 | public void trackModification(TestObject oldValue, TestObject newValue) {
51 | log.trace("VIEW: tack modification {} -> {} --> {}", oldValue, newValue, Thread.currentThread().getName());
52 | sum += newValue == null ? 0 : newValue.getValue();
53 | sum -= oldValue == null ? 0 : oldValue.getValue();
54 | if (oldValue == null && newValue != null) {
55 | count++;
56 | } else if (oldValue != null && newValue == null) {
57 | count--;
58 | }
59 | }
60 |
61 | @Override
62 | public void checkView(List values) {
63 | if (count != values.stream().filter(v -> v != null).count()) {
64 | throw new RuntimeException("View expects " + count + " values but we have: " + values.size() + "! Values: " + values);
65 | }
66 | long checkSum = values.stream().mapToLong(v -> v == null ? 0 : v.getValue()).sum();
67 | if (sum != checkSum) {
68 | throw new RuntimeException("View expects sum " + sum + " values but we have: " + checkSum + "! Values: " + values);
69 | }
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/store/TrackedViewTransactionLocal.java:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2020. Jan Wiemer */
2 |
3 | package org.jacis.store;
4 |
5 | import java.util.HashMap;
6 | import java.util.Map;
7 |
8 | import org.jacis.trackedviews.TrackedView;
9 |
10 | /**
11 | * Wrapper class for a tracked view in order to keep track of the modifications done in the transaction in which the view snapshot has been taken.
12 | * This class is used by the JACIS store to track all modifications done
13 | *
14 | *
inside this transaction up to the time when the snapshot is taken (in the method {@link JacisStoreTxView#getTrackedView(String, java.util.function.Supplier)})
15 | *
inside this transaction after the snapshot is taken (in the method {@link JacisStoreTxView#updateValue(StoreEntryTxView, Object)})
16 | *
17 | * Therefore if you need the view multiple times inside a transaction (with some modifications between the accesses), you do not need to clone the view again.
18 | * Note that each update will track the modification from the previous update to this update.
19 | *
20 | * @param Key type of the store entry
21 | * @param Type of the objects in the transaction view. This is the type visible from the outside.
22 | */
23 | class TrackedViewTransactionLocal {
24 |
25 | /** reference to the tracked view itself */
26 | final private TrackedView trackedView;
27 | /** map containing all objects modified during this transaction (containing always the last updated value) */
28 | final private Map lastUpdatedEntries = new HashMap<>();
29 |
30 | TrackedViewTransactionLocal(TrackedView trackedView) {
31 | this.trackedView = trackedView;
32 | }
33 |
34 | void trackModification(TV origValue, TV value, StoreEntryTxView entry) {
35 | TV lastUpdatedValue = lastUpdatedEntries.get(entry.getKey());
36 | trackedView.trackModification(lastUpdatedValue == null ? origValue : lastUpdatedValue, value);
37 | @SuppressWarnings("unchecked")
38 | TV clone = (TV) entry.getCommittedEntry().getStore().getObjectAdapter().cloneTxView2Committed(value);
39 | lastUpdatedEntries.put(entry.getKey(), clone);
40 | }
41 |
42 | TrackedView getTrackedView() {
43 | return trackedView;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/plugin/objectadapter/serialization/JacisJavaSerializationObjectAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.plugin.objectadapter.serialization;
6 |
7 | import java.io.ByteArrayInputStream;
8 | import java.io.ByteArrayOutputStream;
9 | import java.io.IOException;
10 | import java.io.ObjectInputStream;
11 | import java.io.ObjectOutputStream;
12 | import java.io.Serializable;
13 |
14 | /**
15 | * Generic implementation of the {@link org.jacis.plugin.objectadapter.JacisObjectAdapter} copying the objects
16 | * to and from the transactional view by means of Java serialization.
17 | *
18 | * @param The object type (note that in this case the committed values and the values in the transactional view have the same type)
19 | * @author Jan Wiemer
20 | */
21 | public class JacisJavaSerializationObjectAdapter extends JacisSerializationObjectAdapter {
22 |
23 | @Override
24 | protected byte[] serialize(TV obj) {
25 | if (obj == null) {
26 | return null;
27 | }
28 | try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
29 | try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
30 | oos.writeObject(obj);
31 | return bos.toByteArray();
32 | }
33 | } catch (IOException e) {
34 | throw new RuntimeException("Serialization object to byte[] failed! Object: " + obj, e);
35 | }
36 | }
37 |
38 | @SuppressWarnings("unchecked")
39 | @Override
40 | protected TV deserialize(byte[] bytes) {
41 | if (bytes == null) {
42 | return null;
43 | }
44 | try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes)) {
45 | try (ObjectInputStream ois = new ObjectInputStream(bis)) {
46 | return (TV) ois.readObject();
47 | }
48 | } catch (IOException | ClassNotFoundException e) {
49 | throw new RuntimeException("Deserialization object from byte[] failed! Bytes: " + toHexStr(bytes), e);
50 | }
51 | }
52 |
53 | private String toHexStr(byte[] bytes) {
54 | StringBuilder sb = new StringBuilder();
55 | for (byte b : bytes) {
56 | sb.append(String.format("%02X", b));
57 | }
58 | return sb.toString();
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/plugin/readonly/JacisStoreEntryReadOnlyModeAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.plugin.readonly;
6 |
7 | import org.jacis.JacisApi;
8 |
9 | /**
10 | * The read only mode adapter is used to define how objects are switched between the read-write and read-only mode (if supported).
11 | *
12 | * The default implementation (class {@link DefaultJacisStoreEntryReadOnlyModeAdapter}) is applicable for objects
13 | * implementing the {@link org.jacis.plugin.readonly.object.JacisReadonlyModeSupport} interface
14 | * and uses the methods declared in this interface for switching the mode.
15 | *
16 | * @param The type of the values that should be switched between read-write and read-only mode.
17 | * @author Jan Wiemer
18 | */
19 | @JacisApi
20 | public interface JacisStoreEntryReadOnlyModeAdapter {
21 |
22 | /**
23 | * Returns if this adapter can switch the read / write mode for the passed object
24 | *
25 | * @param value The object to check.
26 | * @return if this adapter can switch the read / write mode for the passed object
27 | */
28 | boolean isApplicableTo(V value);
29 |
30 | /**
31 | * Returns if the passed object is in read-only mode
32 | *
33 | * @param value The object to check.
34 | * @return if the passed object is in read-only mode
35 | */
36 | boolean isReadOnly(V value);
37 |
38 | /**
39 | * Switch the read / write mode for the passed object to read-only mode
40 | * Note that the calling methods will work with the returned value.
41 | * Therefore the implementation of the method may return another instance than the passed (e.g. a proxy).
42 | *
43 | * @param value The object to switch to read-only mode
44 | * @return the object with the switched mode.
45 | */
46 | V switchToReadOnlyMode(V value);
47 |
48 | /**
49 | * Switch the read / write mode for the passed object to read-write mode
50 | * Note that the calling methods will work with the returned value.
51 | * Therefore the implementation of the method may return another instance than the passed (e.g. a proxy).
52 | *
53 | * @param value The object to switch to read-write mode
54 | * @return the object with the switched mode.
55 | */
56 | V switchToReadWriteMode(V value);
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/src/test/java/org/jacis/objectadapter/cloning/JacisStoreWithCloningTrackedViewTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017. Jan Wiemer
3 | */
4 |
5 | package org.jacis.objectadapter.cloning;
6 |
7 | import static org.junit.Assert.assertEquals;
8 |
9 | import org.jacis.container.JacisContainer;
10 | import org.jacis.store.JacisStore;
11 | import org.jacis.testhelper.JacisTestHelper;
12 | import org.jacis.testhelper.TestObject;
13 | import org.jacis.testhelper.TrackedTestView;
14 | import org.jacis.trackedviews.TrackedView;
15 | import org.junit.Test;
16 |
17 | public class JacisStoreWithCloningTrackedViewTest {
18 |
19 | @Test
20 | public void testTrackedView() {
21 | JacisTestHelper testHelper = new JacisTestHelper();
22 | JacisStore store = testHelper.createTestStoreWithCloning();
23 | JacisContainer container = store.getContainer();
24 | container.withLocalTx(() -> {
25 | store.update("1", new TestObject("A1"));
26 | store.update("toDelete1", new TestObject("A1"));
27 | });
28 | TrackedView view = new TrackedTestView();
29 | store.getTrackedViewRegistry().registerTrackedView(view);
30 | container.withLocalTx(() -> {
31 | store.update("toDelete2", new TestObject("A1"));
32 | store.update("2", new TestObject("A2"));
33 | store.remove("toDelete2");
34 | store.update("3", new TestObject("B1"));
35 | store.update("4", new TestObject("B2"));
36 | store.remove("toDelete1");
37 | });
38 | container.withLocalTx(() -> {
39 | store.remove("toDelete2");
40 | store.update("toDelete3", new TestObject("A1"));
41 | store.remove("toDelete3");
42 | store.update("4", new TestObject("A4"));
43 | store.update("5", new TestObject("A3"));
44 | int viewVal = store.getTrackedViewRegistry().getView(TrackedTestView.class).getCount();
45 | assertEquals(5, viewVal);
46 | });
47 | int viewVal = store.getTrackedViewRegistry().getView(TrackedTestView.class).getCount();
48 | assertEquals(5, viewVal);
49 | store.getTrackedViewRegistry().reinitializeView(TrackedTestView.class);
50 | viewVal = store.getTrackedViewRegistry().getView(TrackedTestView.class).getCount();
51 | assertEquals(5, viewVal);
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/index/AbstractJacisMultiIndex.java:
--------------------------------------------------------------------------------
1 | package org.jacis.index;
2 |
3 | import org.jacis.JacisApi;
4 |
5 | import java.util.Set;
6 | import java.util.function.Function;
7 |
8 | /**
9 | * Abstract base class for Jacis Indices where one value object can have multiple index keys.
10 | *
11 | * @param Index key type for this unique index
12 | * @param Key type of the store entry
13 | * @param Type of the objects in the transaction view. This is the type visible from the outside.
14 | * @author Jan Wiemer
15 | */
16 | @JacisApi
17 | public abstract class AbstractJacisMultiIndex {
18 |
19 | /** Name of the index used to register it at the store. The index names have to be unique for one store. */
20 | protected final String indexName;
21 | /** Reference to the index registry storing all indices registered for a store. */
22 | protected final JacisIndexRegistry indexRegistry;
23 | /** Function defining how to compute the set of index keys from the value stored in the store. */
24 | protected final Function> indexKeyFunction;
25 |
26 | AbstractJacisMultiIndex(String indexName, Function> indexKeyFunction, JacisIndexRegistry indexRegistry) {
27 | this.indexName = indexName;
28 | this.indexKeyFunction = indexKeyFunction;
29 | this.indexRegistry = indexRegistry;
30 | }
31 |
32 | /** @return the name of the index (the name used to register the index during creation). */
33 | public String getIndexName() {
34 | return indexName;
35 | }
36 |
37 | /** @return The function to compute the set of the index keys from the stored value. */
38 | Function> getIndexKeyFunction() {
39 | return indexKeyFunction;
40 | }
41 |
42 | @Override
43 | public String toString() {
44 | return getClass().getSimpleName() + "(" + indexName + ")";
45 | }
46 |
47 | @Override
48 | public int hashCode() {
49 | return indexName.hashCode();
50 | }
51 |
52 | @SuppressWarnings("unchecked")
53 | @Override
54 | public boolean equals(Object that) {
55 | if (that == null) {
56 | return false;
57 | }
58 | if (!this.getClass().equals(that.getClass())) {
59 | return false;
60 | }
61 | return this.indexName.equals(((AbstractJacisMultiIndex) that).indexName);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/util/ConcurrentHashSet.java:
--------------------------------------------------------------------------------
1 | package org.jacis.util;
2 |
3 |
4 | import java.util.Collection;
5 | import java.util.Iterator;
6 | import java.util.Map;
7 | import java.util.Set;
8 | import java.util.concurrent.ConcurrentHashMap;
9 |
10 | public class ConcurrentHashSet implements Set {
11 | private final Map map;
12 | private static final Object OBJ = new Object();
13 |
14 | public ConcurrentHashSet(int size) {
15 | this.map = new ConcurrentHashMap<>(size);
16 | }
17 |
18 | public ConcurrentHashSet() {
19 | this.map = new ConcurrentHashMap();
20 | }
21 |
22 | public int size() {
23 | return this.map.size();
24 | }
25 |
26 | public boolean isEmpty() {
27 | return this.map.isEmpty();
28 | }
29 |
30 | public boolean contains(Object o) {
31 | return this.map.containsKey(o);
32 | }
33 |
34 | public Iterator iterator() {
35 | return this.map.keySet().iterator();
36 | }
37 |
38 | public Object[] toArray() {
39 | return this.map.keySet().toArray();
40 | }
41 |
42 | public T[] toArray(T[] a) {
43 | return this.map.keySet().toArray(a);
44 | }
45 |
46 | public boolean add(E e) {
47 | return this.map.put(e, OBJ) == null;
48 | }
49 |
50 | public boolean remove(Object o) {
51 | return this.map.remove(o) != null;
52 | }
53 |
54 | public boolean containsAll(Collection> c) {
55 | return this.map.keySet().containsAll(c);
56 | }
57 |
58 | public boolean addAll(Collection extends E> collection) {
59 | boolean changed = false;
60 | Iterator extends E> iter = collection.iterator();
61 | while (iter.hasNext()) {
62 | E e = iter.next();
63 | if (this.map.put(e, OBJ) == null) {
64 | changed = true;
65 | }
66 | }
67 | return changed;
68 | }
69 |
70 | public boolean retainAll(Collection> collection) {
71 | throw new UnsupportedOperationException();
72 | }
73 |
74 | public boolean removeAll(Collection> collection) {
75 | boolean changed = false;
76 | Iterator> iter = collection.iterator();
77 | while (iter.hasNext()) {
78 | Object e = iter.next();
79 | if (this.map.remove(e) != null) {
80 | changed = true;
81 | }
82 | }
83 | return changed;
84 | }
85 |
86 | public void clear() {
87 | this.map.clear();
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/test/java/org/jacis/testhelper/TestObjectWithoutReadOnlyMode.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.testhelper;
6 |
7 | import java.io.Serializable;
8 |
9 | import org.jacis.plugin.objectadapter.cloning.JacisCloneable;
10 |
11 | /**
12 | * A JACIS cloneable test object that does *not* provide a read only mode.
13 | *
14 | * @author Jan Wiemer
15 | */
16 | public class TestObjectWithoutReadOnlyMode implements JacisCloneable, Serializable {
17 |
18 | private static final long serialVersionUID = 1L;
19 |
20 | private String name;
21 | private long value;
22 |
23 | public TestObjectWithoutReadOnlyMode(String name, long value) {
24 | this.name = name;
25 | this.value = value;
26 | }
27 |
28 | @Override
29 | public TestObjectWithoutReadOnlyMode clone() {
30 | try {
31 | return (TestObjectWithoutReadOnlyMode) super.clone();
32 | } catch (CloneNotSupportedException e) {
33 | throw new InternalError("Could not clone " + this.getClass().getName());
34 | }
35 | }
36 |
37 | public String getName() {
38 | return name;
39 | }
40 |
41 | public TestObjectWithoutReadOnlyMode setName(String name) {
42 | this.name = name;
43 | return this;
44 | }
45 |
46 | public long getValue() {
47 | return value;
48 | }
49 |
50 | public TestObjectWithoutReadOnlyMode setValue(long value) {
51 | this.value = value;
52 | return this;
53 | }
54 |
55 | @Override
56 | public String toString() {
57 | return getClass().getSimpleName() + "(" + name + ":" + value + ")";
58 | }
59 |
60 | @Override
61 | public int hashCode() {
62 | final int prime = 31;
63 | int result = 1;
64 | result = prime * result + ((name == null) ? 0 : name.hashCode());
65 | result = prime * result + (int) (value ^ (value >>> 32));
66 | return result;
67 | }
68 |
69 | @Override
70 | public boolean equals(Object obj) {
71 | if (this == obj)
72 | return true;
73 | if (obj == null)
74 | return false;
75 | if (getClass() != obj.getClass())
76 | return false;
77 | TestObjectWithoutReadOnlyMode other = (TestObjectWithoutReadOnlyMode) obj;
78 | if (name == null) {
79 | if (other.name != null)
80 | return false;
81 | } else if (!name.equals(other.name))
82 | return false;
83 | return value == other.value;
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/src/test/java/org/jacis/JacisContainerTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis;
6 |
7 | import static org.junit.Assert.assertEquals;
8 | import static org.junit.Assert.assertNotNull;
9 |
10 | import org.jacis.container.JacisContainer;
11 | import org.jacis.container.JacisObjectTypeSpec;
12 | import org.jacis.plugin.objectadapter.cloning.JacisCloningObjectAdapter;
13 | import org.jacis.store.JacisStore;
14 | import org.jacis.store.JacisStoreAdminInterface;
15 | import org.jacis.testhelper.TestObject;
16 | import org.junit.Test;
17 | import org.slf4j.Logger;
18 | import org.slf4j.LoggerFactory;
19 |
20 | public class JacisContainerTest {
21 |
22 | private static final Logger log = LoggerFactory.getLogger(JacisContainerTest.class);
23 |
24 | @Test
25 | public void testCreateContainer() {
26 | JacisContainer container = new JacisContainer();
27 | assertNotNull(container);
28 | log.info("The JacisContainer: {}", container);
29 | }
30 |
31 | @Test
32 | public void testRegisterStore() {
33 | JacisContainer container = new JacisContainer();
34 | JacisObjectTypeSpec objectTypeSpec = new JacisObjectTypeSpec<>(String.class, TestObject.class, new JacisCloningObjectAdapter<>());
35 | objectTypeSpec.setCheckViewsOnCommit(true);
36 | objectTypeSpec.setTrackOriginalValue(true);
37 | container.createStore(objectTypeSpec);
38 | assertEquals(1, container.getAllStores().size());
39 | JacisStoreAdminInterface store = container.getStoreAdminInterface(String.class, TestObject.class);
40 | assertEquals(String.class, store.getObjectTypeSpec().getKeyClass());
41 | assertEquals(TestObject.class, store.getObjectTypeSpec().getValueClass());
42 | assertEquals(String.class, store.getObjectTypeSpec().getKeyClass());
43 | assertEquals(TestObject.class, store.getObjectTypeSpec().getValueClass());
44 | assertNotNull(store);
45 | }
46 |
47 | @Test
48 | public void testClearStores() {
49 | JacisContainer container = new JacisContainer();
50 | JacisObjectTypeSpec objectTypeSpec = new JacisObjectTypeSpec<>(String.class, TestObject.class, new JacisCloningObjectAdapter<>());
51 | container.createStore(objectTypeSpec);
52 | JacisStore store = container.getStore(String.class, TestObject.class);
53 | container.clearAllStores();
54 | assertNotNull(store);
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/plugin/txadapter/JacisTransactionAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.plugin.txadapter;
6 |
7 | import org.jacis.JacisApi;
8 | import org.jacis.container.JacisContainer;
9 | import org.jacis.container.JacisTransactionHandle;
10 |
11 | import java.util.Collection;
12 |
13 | /**
14 | * Transaction adapter that can be registered to bind the Jacis Store to externally managed transactions.
15 | *
16 | * This interface declares the methods required by the Jacis Store to interact with externally managed transactions.
17 | * Basically the Jacis store
18 | *
19 | *
20 | *
has to check if there is currently a transaction active,
21 | *
let the container join the currently active transaction,
22 | *
remove the association between the container and the external transaction (when the transaction is finished).
23 | *
24 | *
25 | * @author Jan Wiemer
26 | */
27 | @JacisApi
28 | public interface JacisTransactionAdapter {
29 |
30 | /**
31 | * @return If there is currently an active external transaction.
32 | */
33 | boolean isTransactionActive();
34 |
35 | /**
36 | * Let the container (and therefore all its stores) join the currently active external transaction.
37 | * Inside the store the transaction is represented by the returned transaction handle.
38 | * If there is no external transaction the method returns null.
39 | *
40 | * @param container The container (type {@link JacisContainer}) that shall join the external transaction
41 | * @return The transaction handle (type {@link JacisTransactionHandle}) representing the external transaction inside the jacis store.
42 | */
43 | JacisTransactionHandle joinCurrentTransaction(JacisContainer container);
44 |
45 | /**
46 | * Remove the association between the container and the external transaction (when the transaction is finished).
47 | */
48 | @SuppressWarnings("SpellCheckingInspection")
49 | void disjoinCurrentTransaction(JacisTransactionHandle transaction);
50 |
51 | /**
52 | * @param externalTransaction The external (global or local transaction)
53 | * @return the JACIS transaction handle associated with the external transaction
54 | */
55 | JacisTransactionHandle getTransactionHandle(Object externalTransaction);
56 |
57 | /**
58 | * @return all JACIS transaction handles for all active transactions
59 | */
60 | Collection getAllTransactionHandles();
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/exception/JacisTrackedViewModificationException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.exception;
6 |
7 | import org.jacis.JacisApi;
8 | import org.jacis.container.JacisContainer.StoreIdentifier;
9 | import org.jacis.container.JacisTransactionHandle;
10 | import org.jacis.store.JacisStore;
11 | import org.jacis.trackedviews.TrackedView;
12 |
13 | /**
14 | * Exception thrown in case the modification of a tracked view during commit causes an exception.
15 | *
16 | * @author Jan Wiemer
17 | */
18 | @JacisApi
19 | public class JacisTrackedViewModificationException extends RuntimeException {
20 |
21 | private static final long serialVersionUID = 1L;
22 |
23 | private final StoreIdentifier storeIdentifier;
24 | private final TrackedView> view;
25 | private final String txId;
26 | private final String txDescription;
27 | private final Object key;
28 | private final Object oldValue;
29 | private final Object newValue;
30 |
31 | public JacisTrackedViewModificationException(JacisStore, ?> store, TrackedView> view, JacisTransactionHandle transaction, Object key, Object oldValue, Object newValue, Exception e) {
32 | super(computeMessage(store, view, transaction, key, oldValue, newValue, e), e);
33 | this.view = view;
34 | this.key = key;
35 | this.oldValue = oldValue;
36 | this.newValue = newValue;
37 | storeIdentifier = store.getStoreIdentifier();
38 | txId = transaction.getTxId();
39 | txDescription = transaction.getTxDescription();
40 | }
41 |
42 | private static String computeMessage(JacisStore, ?> store, TrackedView> view, JacisTransactionHandle transaction, Object key, Object oldValue, Object newValue, Exception e) {
43 | return "Tracking modification for TX " + transaction.getTxId() + " on view " + view + " of store " + store.getStoreIdentifier() + " causes exception: >" + e.toString() + "" + key + "< from >" + oldValue + "< to >" + newValue + "<)";
45 | }
46 |
47 | public StoreIdentifier getStoreIdentifier() {
48 | return storeIdentifier;
49 | }
50 |
51 | public TrackedView> getView() {
52 | return view;
53 | }
54 |
55 | public String getTxId() {
56 | return txId;
57 | }
58 |
59 | public String getTxDescription() {
60 | return txDescription;
61 | }
62 |
63 | public Object getKey() {
64 | return key;
65 | }
66 |
67 | public Object getOldValue() {
68 | return oldValue;
69 | }
70 |
71 | public Object getNewValue() {
72 | return newValue;
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ master ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ master ]
20 | schedule:
21 | - cron: '38 5 * * 0'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 |
28 | strategy:
29 | fail-fast: false
30 | matrix:
31 | language: [ 'java' ]
32 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
33 | # Learn more:
34 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
35 |
36 | steps:
37 | - name: Checkout repository
38 | uses: actions/checkout@v2
39 |
40 | # Initializes the CodeQL tools for scanning.
41 | - name: Initialize CodeQL
42 | uses: github/codeql-action/init@v1
43 | with:
44 | languages: ${{ matrix.language }}
45 | # If you wish to specify custom queries, you can do so here or in a config file.
46 | # By default, queries listed here will override any specified in a config file.
47 | # Prefix the list here with "+" to use these queries and those in the config file.
48 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
49 |
50 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
51 | # If this step fails, then you should remove it and run the build manually (see below)
52 | - name: Autobuild
53 | uses: github/codeql-action/autobuild@v1
54 |
55 | # ℹ️ Command-line programs to run using the OS shell.
56 | # 📚 https://git.io/JvXDl
57 |
58 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
59 | # and modify them (or add more) to build your code if your project
60 | # uses a compiled language
61 |
62 | #- run: |
63 | # make bootstrap
64 | # make release
65 |
66 | - name: Perform CodeQL Analysis
67 | uses: github/codeql-action/analyze@v1
68 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/extension/persistence/xodus/XodusPersistenceAdapter.java:
--------------------------------------------------------------------------------
1 | package org.jacis.extension.persistence.xodus;
2 |
3 | import org.jacis.store.KeyValuePair;
4 |
5 | import com.fasterxml.jackson.core.JsonProcessingException;
6 | import com.fasterxml.jackson.databind.ObjectMapper;
7 |
8 | import jetbrains.exodus.ArrayByteIterable;
9 | import jetbrains.exodus.ByteIterable;
10 | import jetbrains.exodus.bindings.StringBinding;
11 | import jetbrains.exodus.env.Environment;
12 |
13 | /**
14 | * Implementation of the JACIS persistence adapter based on JetBrains Xodus
15 | * (see https://github.com/JetBrains/xodus)
16 | * using JSON to serialize the JACIS objects.
17 | *
18 | * @param Key type of the store entry
19 | * @param Value type of the store entry
20 | *
21 | * @author Jan Wiemer
22 | */
23 | public class XodusPersistenceAdapter extends AbstractXodusPersistenceAdapter {
24 |
25 | /** The JSON object mapper used to convert the objects and keys in the JACIS store to Strings. */
26 | private final ObjectMapper objectMapper;
27 |
28 | public XodusPersistenceAdapter(Environment env, ObjectMapper objectMapper, boolean traceLogging) {
29 | super(env, traceLogging);
30 | this.objectMapper = objectMapper;
31 | }
32 |
33 | public XodusPersistenceAdapter(Environment env, ObjectMapper objectMapper) {
34 | this(env, objectMapper, false);
35 | }
36 |
37 | @Override
38 | protected KeyValuePair encode(K key, V value) {
39 | try {
40 | String keyString = objectMapper.writeValueAsString(key);
41 | String valString = value == null ? null : objectMapper.writeValueAsString(value);
42 | ArrayByteIterable xodusKey = StringBinding.stringToEntry(keyString);
43 | ArrayByteIterable xodusVal = valString == null ? null : StringBinding.stringToEntry(valString);
44 | return new KeyValuePair<>(xodusKey, xodusVal);
45 | } catch (JsonProcessingException e) {
46 | throw new RuntimeException(e);
47 | }
48 | }
49 |
50 | @Override
51 | @SuppressWarnings("unchecked")
52 | protected KeyValuePair decode(ByteIterable xodusKey, ByteIterable xodusValue) {
53 | try {
54 | String keyString = StringBinding.entryToString(xodusKey);
55 | String valString = StringBinding.entryToString(xodusValue);
56 | K key = (K) objectMapper.readValue(keyString, storeIdentifier.getKeyClass());
57 | V val = (V) objectMapper.readValue(valString, storeIdentifier.getValueClass());
58 | return new KeyValuePair<>(key, val);
59 | } catch (JsonProcessingException e) {
60 | throw new RuntimeException(e);
61 | }
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/exception/JacisModificationListenerException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.exception;
6 |
7 | import org.jacis.JacisApi;
8 | import org.jacis.container.JacisContainer.StoreIdentifier;
9 | import org.jacis.container.JacisTransactionHandle;
10 | import org.jacis.plugin.JacisModificationListener;
11 | import org.jacis.store.JacisStore;
12 |
13 | /**
14 | * Exception thrown in case the modification of a tracked view during commit causes an exception.
15 | *
16 | * @author Jan Wiemer
17 | */
18 | @SuppressWarnings({"unused", "UnusedReturnValue"}) // since this is an API of the library
19 | @JacisApi
20 | public class JacisModificationListenerException extends RuntimeException {
21 |
22 | private static final long serialVersionUID = 1L;
23 |
24 | private final StoreIdentifier storeIdentifier;
25 | private final JacisModificationListener, ?> listener;
26 | private final String txId;
27 | private final String txDescription;
28 | private final Object key;
29 | private final Object oldValue;
30 | private final Object newValue;
31 |
32 | public JacisModificationListenerException(JacisStore, ?> store, JacisModificationListener, ?> listener, JacisTransactionHandle transaction, Object key, Object oldValue, Object newValue, Exception e) {
33 | super(computeMessage(store, listener, transaction, key, oldValue, newValue, e), e);
34 | this.listener = listener;
35 | this.key = key;
36 | this.oldValue = oldValue;
37 | this.newValue = newValue;
38 | storeIdentifier = store.getStoreIdentifier();
39 | txId = transaction.getTxId();
40 | txDescription = transaction.getTxDescription();
41 | }
42 |
43 | private static String computeMessage(JacisStore, ?> store, JacisModificationListener, ?> listener, JacisTransactionHandle transaction, Object key, Object oldValue, Object newValue, Exception e) {
44 | return "Tracking modification for TX " + transaction.getTxId() + " on listener " + listener + " of store " + store.getStoreIdentifier() + " causes exception: >" + e.toString() + "" + key + "< from >" + oldValue + "< to >" + newValue + "<)";
46 | }
47 |
48 | public StoreIdentifier getStoreIdentifier() {
49 | return storeIdentifier;
50 | }
51 |
52 | public JacisModificationListener, ?> getListener() {
53 | return listener;
54 | }
55 |
56 | public String getTxId() {
57 | return txId;
58 | }
59 |
60 | public String getTxDescription() {
61 | return txDescription;
62 | }
63 |
64 | public Object getKey() {
65 | return key;
66 | }
67 |
68 | public Object getOldValue() {
69 | return oldValue;
70 | }
71 |
72 | public Object getNewValue() {
73 | return newValue;
74 | }
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/src/test/java/org/jacis/performance/JacisPerformanceTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016. Jan Wiemer
3 | */
4 |
5 | package org.jacis.performance;
6 |
7 | import org.jacis.store.JacisStore;
8 | import org.jacis.testhelper.JacisTestHelper;
9 | import org.jacis.testhelper.TestObject;
10 | import org.junit.Test;
11 | import org.slf4j.Logger;
12 | import org.slf4j.LoggerFactory;
13 |
14 | public class JacisPerformanceTest {
15 |
16 | private static final Logger log = LoggerFactory.getLogger(JacisPerformanceTest.class);
17 |
18 | @Test
19 | public void testInsertPerformanceSerialization() {
20 | JacisStore store = new JacisTestHelper().createTestStoreWithSerialization();
21 | long mem0 = getUsedMem();
22 | long t0 = System.nanoTime();
23 | int nTx = 10000;
24 | int nIns = 10;
25 | doInserts(store, nTx, nIns);
26 | long duration = System.nanoTime() - t0;
27 | long mem = getUsedMem() - mem0;
28 | log.info("Jacis Store based on Serialization {} TX a {} inserts: {} ms {} bytes", nTx, nIns, milliStr(duration), memStr(mem));
29 | }
30 |
31 | @Test
32 | public void testInsertPerformanceCloning() {
33 | JacisStore store = new JacisTestHelper().createTestStoreWithCloning();
34 | long mem0 = getUsedMem();
35 | long t0 = System.nanoTime();
36 | int nTx = 10000;
37 | int nIns = 10;
38 | doInserts(store, nTx, nIns);
39 | long duration = System.nanoTime() - t0;
40 | long mem = getUsedMem() - mem0;
41 | log.info("Jacis Store based on cloning {} TX a {} inserts: {} ms {} bytes", nTx, nIns, milliStr(duration), memStr(mem));
42 | }
43 |
44 | private void doInserts(JacisStore store, int nTx, int sizeTx) {
45 | for (int i = 0; i < nTx; i++) {
46 | int txIdx = i;
47 | store.getContainer().withLocalTx(() -> {
48 | for (int j = 0; j < sizeTx; j++) {
49 | TestObject testObject = new TestObject("obj-" + txIdx + "-" + j, txIdx + j);
50 | store.update(testObject.getName(), testObject);
51 | }
52 | });
53 | }
54 | }
55 |
56 | private long getUsedMem() {
57 | for (int i = 0; i < 10; i++) {
58 | Runtime.getRuntime().gc();
59 | }
60 | return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
61 | }
62 |
63 | private String milliStr(long nanos) {
64 | return String.format("%.2f", ((double) nanos) / (double) (1000 * 1000 * 1000));
65 | }
66 |
67 | private String memStr(long bytes) {
68 | boolean si = true;
69 | int unit = si ? 1000 : 1024;
70 | if (bytes < unit) {
71 | return bytes + " B";
72 | }
73 | int exp = (int) (Math.log(bytes) / Math.log(unit));
74 | String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + (si ? "" : "i");
75 | return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/src/main/java/org/jacis/extension/persistence/microstream/MicrostreamStoreRoot.java:
--------------------------------------------------------------------------------
1 | package org.jacis.extension.persistence.microstream;
2 |
3 | import org.jacis.container.JacisContainer;
4 | import org.jacis.container.JacisContainer.StoreIdentifier;
5 |
6 | import java.util.ArrayList;
7 | import java.util.List;
8 | import java.util.Set;
9 |
10 | /**
11 | * The root object stored by the Microstream storage manager.
12 | * Basically it stores (the head of) the linked list of Microstream entity objects representing the store entries.
13 | *
14 | * @param Key type of the store entry
15 | * @param Value type of the store entry
16 | * @author Jan Wiemer
17 | */
18 | class MicrostreamStoreRoot {
19 |
20 | /** The identifier of the JACIS store represented by this root object. */
21 | private final JacisContainer.StoreIdentifier storeIdentifier;
22 | /** The first object of the linked list of Microstream entity objects representing the store entries. */
23 | private MicrostreamStoreEntity firstElement;
24 |
25 | public MicrostreamStoreRoot(JacisContainer.StoreIdentifier storeIdentifier) {
26 | this.storeIdentifier = storeIdentifier;
27 | }
28 |
29 | @Override
30 | public String toString() {
31 | String b = "StorageRoot(" + storeIdentifier.toShortString() + ")";
32 | return b;
33 | }
34 |
35 | public void add(MicrostreamStoreEntity entity, Set