Finished new implementation with annotations
This commit is contained in:
parent
7123228027
commit
41b2bb77a2
162
pom.xml
162
pom.xml
@ -1,86 +1,94 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<groupId>org.warp</groupId>
|
<groupId>org.warp</groupId>
|
||||||
<artifactId>jcwdb</artifactId>
|
<artifactId>jcwdb</artifactId>
|
||||||
<version>1.0-SNAPSHOT</version>
|
<version>1.1-SNAPSHOT</version>
|
||||||
|
|
||||||
<name>jcwdb</name>
|
<name>jcwdb</name>
|
||||||
<!-- FIXME change it to the project's website -->
|
<!-- FIXME change it to the project's website -->
|
||||||
<url>http://www.example.com</url>
|
<url>http://www.example.com</url>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<maven.compiler.source>11</maven.compiler.source>
|
<maven.compiler.source>10</maven.compiler.source>
|
||||||
<maven.compiler.target>11</maven.compiler.target>
|
<maven.compiler.target>10</maven.compiler.target>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<repositories>
|
||||||
<dependency>
|
<repository>
|
||||||
<groupId>junit</groupId>
|
<id>sonatype-snapshots</id>
|
||||||
<artifactId>junit</artifactId>
|
<name>sonatype snapshots repo</name>
|
||||||
<version>4.11</version>
|
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
|
||||||
<scope>test</scope>
|
</repository>
|
||||||
</dependency>
|
</repositories>
|
||||||
<dependency>
|
|
||||||
<groupId>it.unimi.dsi</groupId>
|
|
||||||
<artifactId>fastutil</artifactId>
|
|
||||||
<version>8.2.2</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.esotericsoftware</groupId>
|
|
||||||
<artifactId>kryo</artifactId>
|
|
||||||
<version>5.0.0-RC1</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>net.openhft</groupId>
|
|
||||||
<artifactId>zero-allocation-hashing</artifactId>
|
|
||||||
<version>0.8</version>
|
|
||||||
</dependency>
|
|
||||||
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.commons</groupId>
|
|
||||||
<artifactId>commons-lang3</artifactId>
|
|
||||||
<version>3.8.1</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
<dependencies>
|
||||||
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
|
<dependency>
|
||||||
<plugins>
|
<groupId>junit</groupId>
|
||||||
<plugin>
|
<artifactId>junit</artifactId>
|
||||||
<artifactId>maven-clean-plugin</artifactId>
|
<version>4.11</version>
|
||||||
<version>3.0.0</version>
|
<scope>test</scope>
|
||||||
</plugin>
|
</dependency>
|
||||||
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
|
<dependency>
|
||||||
<plugin>
|
<groupId>it.unimi.dsi</groupId>
|
||||||
<artifactId>maven-resources-plugin</artifactId>
|
<artifactId>fastutil</artifactId>
|
||||||
<version>3.0.2</version>
|
<version>8.2.2</version>
|
||||||
</plugin>
|
</dependency>
|
||||||
<plugin>
|
<dependency>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<groupId>com.esotericsoftware</groupId>
|
||||||
<version>3.7.0</version>
|
<artifactId>kryo</artifactId>
|
||||||
</plugin>
|
<version>5.0.0-RC2-SNAPSHOT</version>
|
||||||
<plugin>
|
</dependency>
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<dependency>
|
||||||
<version>2.22.1</version>
|
<groupId>net.openhft</groupId>
|
||||||
</plugin>
|
<artifactId>zero-allocation-hashing</artifactId>
|
||||||
<plugin>
|
<version>0.8</version>
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
</dependency>
|
||||||
<version>3.0.2</version>
|
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
|
||||||
</plugin>
|
<dependency>
|
||||||
<plugin>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>maven-install-plugin</artifactId>
|
<artifactId>commons-lang3</artifactId>
|
||||||
<version>2.5.2</version>
|
<version>3.8.1</version>
|
||||||
</plugin>
|
</dependency>
|
||||||
<plugin>
|
</dependencies>
|
||||||
<artifactId>maven-deploy-plugin</artifactId>
|
|
||||||
<version>2.8.2</version>
|
<build>
|
||||||
</plugin>
|
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
|
||||||
</plugins>
|
<plugins>
|
||||||
</pluginManagement>
|
<plugin>
|
||||||
</build>
|
<artifactId>maven-clean-plugin</artifactId>
|
||||||
|
<version>3.0.0</version>
|
||||||
|
</plugin>
|
||||||
|
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
|
<version>3.0.2</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.7.0</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>2.22.1</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<version>3.0.2</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-install-plugin</artifactId>
|
||||||
|
<version>2.5.2</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-deploy-plugin</artifactId>
|
||||||
|
<version>2.8.2</version>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</pluginManagement>
|
||||||
|
</build>
|
||||||
</project>
|
</project>
|
||||||
|
@ -29,7 +29,7 @@ public class FileAllocator implements AutoCloseable {
|
|||||||
this.fileSize = this.dataFileChannel.size();
|
this.fileSize = this.dataFileChannel.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public FileAllocator(SeekableByteChannel dataFileChannel, long fileSize, Long2IntMap freeBytes) throws IOException {
|
public FileAllocator(SeekableByteChannel dataFileChannel, long fileSize, Long2IntMap freeBytes) {
|
||||||
this.dataFileChannel = dataFileChannel;
|
this.dataFileChannel = dataFileChannel;
|
||||||
this.fileSize = fileSize;
|
this.fileSize = fileSize;
|
||||||
this.freeBytes.putAll(freeBytes);
|
this.freeBytes.putAll(freeBytes);
|
||||||
@ -44,16 +44,20 @@ public class FileAllocator implements AutoCloseable {
|
|||||||
public long allocate(int size) {
|
public long allocate(int size) {
|
||||||
checkClosed();
|
checkClosed();
|
||||||
synchronized (allocateLock) {
|
synchronized (allocateLock) {
|
||||||
long offset = allocateIntoUnusedParts(size);
|
long offset;
|
||||||
if (offset == -1) {
|
if ((offset = allocateIntoUnusedParts(size)) != -1) {
|
||||||
return allocateToEnd(size);
|
if (offset + size > fileSize) {
|
||||||
} else {
|
fileSize = offset + size;
|
||||||
|
}
|
||||||
return offset;
|
return offset;
|
||||||
|
} else {
|
||||||
|
return allocateToEnd(size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private long allocateIntoUnusedParts(int size) {
|
private long allocateIntoUnusedParts(int size) {
|
||||||
|
if (FileIndexManager.ALWAYS_ALLOCATE_NEW) return -1;
|
||||||
Stream<Map.Entry<Long,Integer>> sorted =
|
Stream<Map.Entry<Long,Integer>> sorted =
|
||||||
freeBytes.entrySet().stream()
|
freeBytes.entrySet().stream()
|
||||||
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()));
|
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()));
|
||||||
@ -104,6 +108,7 @@ public class FileAllocator implements AutoCloseable {
|
|||||||
*/
|
*/
|
||||||
public void markFree(long startPosition, int length) {
|
public void markFree(long startPosition, int length) {
|
||||||
checkClosed();
|
checkClosed();
|
||||||
|
if (FileIndexManager.ALWAYS_ALLOCATE_NEW) return;
|
||||||
|
|
||||||
if (freeBytes.containsKey(startPosition + length)) {
|
if (freeBytes.containsKey(startPosition + length)) {
|
||||||
int secondLength = freeBytes.remove(startPosition + length);
|
int secondLength = freeBytes.remove(startPosition + length);
|
||||||
|
@ -4,7 +4,7 @@ import com.esotericsoftware.kryo.io.Input;
|
|||||||
import com.esotericsoftware.kryo.io.Output;
|
import com.esotericsoftware.kryo.io.Output;
|
||||||
import it.unimi.dsi.fastutil.longs.*;
|
import it.unimi.dsi.fastutil.longs.*;
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
||||||
import org.warp.jcwdb.ann.Database;
|
import org.warp.jcwdb.ann.DatabaseManager;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
@ -13,14 +13,14 @@ import java.nio.channels.SeekableByteChannel;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
public class FileIndexManager implements IndexManager {
|
public class FileIndexManager implements IndexManager {
|
||||||
|
public static final boolean ALWAYS_ALLOCATE_NEW = false;
|
||||||
private final SeekableByteChannel dataFileChannel, metadataFileChannel;
|
private final SeekableByteChannel dataFileChannel, metadataFileChannel;
|
||||||
private volatile long metadataFileChannelSize;
|
private volatile long metadataFileChannelSize;
|
||||||
private final FileAllocator fileAllocator;
|
private final FileAllocator fileAllocator;
|
||||||
private final ByteBuffer metadataByteBuffer = ByteBuffer.allocateDirect(IndexDetails.TOTAL_BYTES);
|
private final ByteBuffer metadataByteBuffer = ByteBuffer.allocateDirect(IndexDetails.TOTAL_BYTES);
|
||||||
private final ByteBuffer maskByteBuffer = ByteBuffer.allocateDirect(Integer.BYTES);
|
private final ByteBuffer maskByteBuffer = ByteBuffer.allocateDirect(Long.BYTES);
|
||||||
private volatile boolean closed;
|
private volatile boolean closed;
|
||||||
private final Object closeLock = new Object();
|
private final Object closeLock = new Object();
|
||||||
private final Object metadataByteBufferLock = new Object();
|
private final Object metadataByteBufferLock = new Object();
|
||||||
@ -36,19 +36,17 @@ public class FileIndexManager implements IndexManager {
|
|||||||
/**
|
/**
|
||||||
* Edit this using editIndex()
|
* Edit this using editIndex()
|
||||||
*/
|
*/
|
||||||
private final LongSet dirtyLoadedIndices, flushingAllowedIndices, removedIndices;
|
private final LongSet dirtyLoadedIndices, removedIndices;
|
||||||
private long firstAllocableIndex;
|
private long firstAllocableIndex;
|
||||||
|
|
||||||
public FileIndexManager(Path dataFile, Path metadataFile) throws IOException {
|
public FileIndexManager(Path dataFile, Path metadataFile) throws IOException {
|
||||||
if (Cleaner.DISABLE_CLEANER) {
|
if (Cleaner.DISABLE_CLEANER) {
|
||||||
loadedIndices = new Long2ObjectOpenHashMap<>();
|
loadedIndices = new Long2ObjectOpenHashMap<>();
|
||||||
dirtyLoadedIndices = new LongOpenHashSet();
|
dirtyLoadedIndices = new LongOpenHashSet();
|
||||||
flushingAllowedIndices = new LongOpenHashSet();
|
|
||||||
removedIndices = new LongOpenHashSet();
|
removedIndices = new LongOpenHashSet();
|
||||||
} else {
|
} else {
|
||||||
loadedIndices = new Long2ObjectLinkedOpenHashMap<>();
|
loadedIndices = new Long2ObjectLinkedOpenHashMap<>();
|
||||||
dirtyLoadedIndices = new LongLinkedOpenHashSet();
|
dirtyLoadedIndices = new LongLinkedOpenHashSet();
|
||||||
flushingAllowedIndices = new LongLinkedOpenHashSet();
|
|
||||||
removedIndices = new LongLinkedOpenHashSet();
|
removedIndices = new LongLinkedOpenHashSet();
|
||||||
}
|
}
|
||||||
if (Files.notExists(dataFile)) {
|
if (Files.notExists(dataFile)) {
|
||||||
@ -59,53 +57,57 @@ public class FileIndexManager implements IndexManager {
|
|||||||
}
|
}
|
||||||
dataFileChannel = Files.newByteChannel(dataFile, StandardOpenOption.READ, StandardOpenOption.WRITE);
|
dataFileChannel = Files.newByteChannel(dataFile, StandardOpenOption.READ, StandardOpenOption.WRITE);
|
||||||
metadataFileChannel = Files.newByteChannel(metadataFile, StandardOpenOption.READ, StandardOpenOption.WRITE);
|
metadataFileChannel = Files.newByteChannel(metadataFile, StandardOpenOption.READ, StandardOpenOption.WRITE);
|
||||||
fileAllocator = createFileAllocator(dataFileChannel, metadataFileChannel.position(0));
|
|
||||||
metadataFileChannelSize = metadataFileChannel.size();
|
metadataFileChannelSize = metadataFileChannel.size();
|
||||||
|
fileAllocator = createFileAllocator(dataFileChannel, metadataFileChannel.position(0));
|
||||||
firstAllocableIndex = getMetadataFileChannelSize() / (long) IndexDetails.TOTAL_BYTES;
|
firstAllocableIndex = getMetadataFileChannelSize() / (long) IndexDetails.TOTAL_BYTES;
|
||||||
if (firstAllocableIndex == 0) {
|
|
||||||
firstAllocableIndex = 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private long getMetadataFileChannelSize() throws IOException {
|
private long getMetadataFileChannelSize() {
|
||||||
return metadataFileChannelSize;
|
return metadataFileChannelSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
private FileAllocator createFileAllocator(final SeekableByteChannel dataFileChannel, final SeekableByteChannel metadataFileChannel) throws IOException {
|
private FileAllocator createFileAllocator(final SeekableByteChannel dataFileChannel, final SeekableByteChannel metadataFileChannel) throws IOException {
|
||||||
Long2IntMap freeBytes = new Long2IntRBTreeMap();
|
if (ALWAYS_ALLOCATE_NEW) {
|
||||||
Long2IntMap usedBytes = new Long2IntRBTreeMap();
|
return new FileAllocator(dataFileChannel);
|
||||||
long firstOffset = 0;
|
} else {
|
||||||
while (metadataFileChannel.position() + IndexDetails.TOTAL_BYTES <= getMetadataFileChannelSize()) {
|
Long2IntMap freeBytes = new Long2IntRBTreeMap();
|
||||||
IndexDetails indexDetails = readIndexDetailsAt(metadataFileChannel);
|
Long2IntMap usedBytes = new Long2IntRBTreeMap();
|
||||||
if (indexDetails != null) {
|
long firstOffset = 0;
|
||||||
long offset = indexDetails.getOffset();
|
metadataFileChannel.position(0);
|
||||||
usedBytes.put(offset, indexDetails.getSize());
|
while (metadataFileChannel.position() + IndexDetails.TOTAL_BYTES <= getMetadataFileChannelSize()) {
|
||||||
if (offset < firstOffset) {
|
IndexDetails indexDetails = readIndexDetailsAt(metadataFileChannel);
|
||||||
firstOffset = offset;
|
if (indexDetails != null) {
|
||||||
|
long offset = indexDetails.getOffset();
|
||||||
|
if (!usedBytes.containsKey(offset) || indexDetails.getSize() > usedBytes.get(offset)) {
|
||||||
|
usedBytes.put(offset, indexDetails.getSize());
|
||||||
|
}
|
||||||
|
if (offset < firstOffset) {
|
||||||
|
firstOffset = offset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
long previousEntryOffset = 0;
|
long previousEntryOffset = 0;
|
||||||
long previousEntrySize = 0;
|
long previousEntrySize = 0;
|
||||||
ObjectIterator<Long2IntMap.Entry> it = usedBytes.long2IntEntrySet().iterator();
|
ObjectIterator<Long2IntMap.Entry> it = usedBytes.long2IntEntrySet().iterator();
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
final Long2IntMap.Entry entry = it.next();
|
final Long2IntMap.Entry entry = it.next();
|
||||||
final long entryOffset = entry.getLongKey();
|
final long entryOffset = entry.getLongKey();
|
||||||
final long entrySize = entry.getIntValue();
|
final long entrySize = entry.getIntValue();
|
||||||
it.remove();
|
it.remove();
|
||||||
|
|
||||||
if (previousEntryOffset + previousEntrySize < entryOffset) {
|
if (previousEntryOffset + previousEntrySize < entryOffset) {
|
||||||
freeBytes.put(previousEntryOffset + previousEntrySize, (int) (entryOffset - (previousEntryOffset + previousEntrySize)));
|
freeBytes.put(previousEntryOffset + previousEntrySize, (int) (entryOffset - (previousEntryOffset + previousEntrySize)));
|
||||||
|
}
|
||||||
|
|
||||||
|
previousEntryOffset = entryOffset;
|
||||||
|
previousEntrySize = entrySize;
|
||||||
}
|
}
|
||||||
|
|
||||||
previousEntryOffset = entryOffset;
|
final long fileSize = previousEntryOffset + previousEntrySize;
|
||||||
previousEntrySize = entrySize;
|
|
||||||
|
return new FileAllocator(dataFileChannel, fileSize, freeBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
final long fileSize = previousEntryOffset + previousEntrySize;
|
|
||||||
|
|
||||||
return new FileAllocator(dataFileChannel, fileSize, freeBytes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -121,7 +123,7 @@ public class FileIndexManager implements IndexManager {
|
|||||||
public IndexDetails set(long index, int size, DBWriter data) throws IOException {
|
public IndexDetails set(long index, int size, DBWriter data) throws IOException {
|
||||||
checkClosed();
|
checkClosed();
|
||||||
IndexDetails indexDetails = getIndexMetadataUnsafe(index);
|
IndexDetails indexDetails = getIndexMetadataUnsafe(index);
|
||||||
if (indexDetails == null || indexDetails.getSize() < size) {
|
if (ALWAYS_ALLOCATE_NEW || indexDetails == null || indexDetails.getSize() < size) {
|
||||||
// Allocate new space
|
// Allocate new space
|
||||||
IndexDetails newDetails = allocateAndWrite(index, size, data);
|
IndexDetails newDetails = allocateAndWrite(index, size, data);
|
||||||
if (indexDetails != null) {
|
if (indexDetails != null) {
|
||||||
@ -144,16 +146,6 @@ public class FileIndexManager implements IndexManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setFlushingAllowed(long index, boolean isUnloadingAllowed) {
|
|
||||||
checkClosed();
|
|
||||||
if (isUnloadingAllowed) {
|
|
||||||
flushingAllowedIndices.add(index);
|
|
||||||
} else {
|
|
||||||
flushingAllowedIndices.remove(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long add(int size) {
|
public long add(int size) {
|
||||||
checkClosed();
|
checkClosed();
|
||||||
@ -192,7 +184,6 @@ public class FileIndexManager implements IndexManager {
|
|||||||
throw new IOException("Unable to write " + size + " in a space of " + indexDetails.getSize());
|
throw new IOException("Unable to write " + size + " in a space of " + indexDetails.getSize());
|
||||||
}
|
}
|
||||||
final long offset = indexDetails.getOffset();
|
final long offset = indexDetails.getOffset();
|
||||||
|
|
||||||
final Output o = new Output(Channels.newOutputStream(dataFileChannel.position(offset)), size);
|
final Output o = new Output(Channels.newOutputStream(dataFileChannel.position(offset)), size);
|
||||||
data.write(o);
|
data.write(o);
|
||||||
o.flush();
|
o.flush();
|
||||||
@ -214,7 +205,6 @@ public class FileIndexManager implements IndexManager {
|
|||||||
}
|
}
|
||||||
synchronized (indicesMapsAccessLock) {
|
synchronized (indicesMapsAccessLock) {
|
||||||
dirtyLoadedIndices.remove(index);
|
dirtyLoadedIndices.remove(index);
|
||||||
flushingAllowedIndices.remove(index);
|
|
||||||
loadedIndices.remove(index);
|
loadedIndices.remove(index);
|
||||||
removedIndices.add(index);
|
removedIndices.add(index);
|
||||||
}
|
}
|
||||||
@ -225,7 +215,6 @@ public class FileIndexManager implements IndexManager {
|
|||||||
synchronized (indicesMapsAccessLock) {
|
synchronized (indicesMapsAccessLock) {
|
||||||
removedIndices.remove(index);
|
removedIndices.remove(index);
|
||||||
dirtyLoadedIndices.remove(index);
|
dirtyLoadedIndices.remove(index);
|
||||||
flushingAllowedIndices.remove(index);
|
|
||||||
loadedIndices.remove(index);
|
loadedIndices.remove(index);
|
||||||
}
|
}
|
||||||
// Update indices metadata
|
// Update indices metadata
|
||||||
@ -238,7 +227,6 @@ public class FileIndexManager implements IndexManager {
|
|||||||
if (dirtyLoadedIndices.contains(index)) {
|
if (dirtyLoadedIndices.contains(index)) {
|
||||||
indexDetails = loadedIndices.get(index);
|
indexDetails = loadedIndices.get(index);
|
||||||
dirtyLoadedIndices.remove(index);
|
dirtyLoadedIndices.remove(index);
|
||||||
flushingAllowedIndices.remove(index);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isDirty) {
|
if (isDirty) {
|
||||||
@ -302,7 +290,6 @@ public class FileIndexManager implements IndexManager {
|
|||||||
synchronized (indicesMapsAccessLock) {
|
synchronized (indicesMapsAccessLock) {
|
||||||
loadedIndices.put(index, details);
|
loadedIndices.put(index, details);
|
||||||
dirtyLoadedIndices.add(index);
|
dirtyLoadedIndices.add(index);
|
||||||
flushingAllowedIndices.remove(index);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,7 +298,6 @@ public class FileIndexManager implements IndexManager {
|
|||||||
long newIndex = firstAllocableIndex++;
|
long newIndex = firstAllocableIndex++;
|
||||||
loadedIndices.put(newIndex, indexDetails);
|
loadedIndices.put(newIndex, indexDetails);
|
||||||
dirtyLoadedIndices.add(newIndex);
|
dirtyLoadedIndices.add(newIndex);
|
||||||
flushingAllowedIndices.remove(newIndex);
|
|
||||||
removedIndices.remove(newIndex);
|
removedIndices.remove(newIndex);
|
||||||
return newIndex;
|
return newIndex;
|
||||||
}
|
}
|
||||||
@ -442,21 +428,17 @@ public class FileIndexManager implements IndexManager {
|
|||||||
long lastIndex = -2;
|
long lastIndex = -2;
|
||||||
synchronized (indicesMapsAccessLock) {
|
synchronized (indicesMapsAccessLock) {
|
||||||
for (long index : dirtyLoadedIndices) {
|
for (long index : dirtyLoadedIndices) {
|
||||||
if (!flushingAllowedIndices.contains(index)) {
|
IndexDetails indexDetails = loadedIndices.get(index);
|
||||||
IndexDetails indexDetails = loadedIndices.get(index);
|
long position = index * IndexDetails.TOTAL_BYTES;
|
||||||
long position = index * IndexDetails.TOTAL_BYTES;
|
resizeMetadataFileChannel(position);
|
||||||
resizeMetadataFileChannel(position);
|
if (index - lastIndex != 1) {
|
||||||
if (index - lastIndex != 1) {
|
metadata = metadata.position(position);
|
||||||
metadata = metadata.position(position);
|
|
||||||
}
|
|
||||||
writeIndexDetails(metadata, indexDetails);
|
|
||||||
lastIndex = index;
|
|
||||||
flushedIndices++;
|
|
||||||
}
|
}
|
||||||
|
writeIndexDetails(metadata, indexDetails);
|
||||||
|
lastIndex = index;
|
||||||
|
flushedIndices++;
|
||||||
}
|
}
|
||||||
dirtyLoadedIndices.clear();
|
dirtyLoadedIndices.clear();
|
||||||
dirtyLoadedIndices.addAll(flushingAllowedIndices);
|
|
||||||
flushingAllowedIndices.clear();
|
|
||||||
}
|
}
|
||||||
return flushedIndices;
|
return flushedIndices;
|
||||||
}
|
}
|
||||||
@ -484,12 +466,12 @@ public class FileIndexManager implements IndexManager {
|
|||||||
long removedIndices = 0;
|
long removedIndices = 0;
|
||||||
LongArrayList toUnload = new LongArrayList();
|
LongArrayList toUnload = new LongArrayList();
|
||||||
synchronized (indicesMapsAccessLock) {
|
synchronized (indicesMapsAccessLock) {
|
||||||
if (loadedIndices.size() > Database.MAX_LOADED_INDICES) {
|
if (loadedIndices.size() > DatabaseManager.MAX_LOADED_INDICES) {
|
||||||
long count = loadedIndices.size();
|
long count = loadedIndices.size();
|
||||||
LongIterator it = loadedIndices.keySet().iterator();
|
LongIterator it = loadedIndices.keySet().iterator();
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
long loadedIndex = it.nextLong();
|
long loadedIndex = it.nextLong();
|
||||||
if (count < Database.MAX_LOADED_INDICES * 3l / 2l) {
|
if (count < DatabaseManager.MAX_LOADED_INDICES * 3l / 2l) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
toUnload.add(loadedIndex);
|
toUnload.add(loadedIndex);
|
||||||
|
@ -13,7 +13,6 @@ public interface IndexManager extends Cleanable {
|
|||||||
long add(int size, DBWriter writer) throws IOException;
|
long add(int size, DBWriter writer) throws IOException;
|
||||||
FullIndexDetails addAndGetDetails(int size, DBWriter writer) throws IOException;
|
FullIndexDetails addAndGetDetails(int size, DBWriter writer) throws IOException;
|
||||||
IndexDetails set(long index, int size, DBWriter writer) throws IOException;
|
IndexDetails set(long index, int size, DBWriter writer) throws IOException;
|
||||||
void setFlushingAllowed(long index, boolean isUnloadingAllowed);
|
|
||||||
void delete(long index) throws IOException;
|
void delete(long index) throws IOException;
|
||||||
boolean has(long index);
|
boolean has(long index);
|
||||||
void close() throws IOException;
|
void close() throws IOException;
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
package org.warp.jcwdb.ann;
|
|
||||||
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
public @interface DBClass {
|
|
||||||
int classTypeId();
|
|
||||||
}
|
|
35
src/main/java/org/warp/jcwdb/ann/DBDBObjectList.java
Normal file
35
src/main/java/org/warp/jcwdb/ann/DBDBObjectList.java
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package org.warp.jcwdb.ann;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
public class DBDBObjectList<T extends DBObject> extends DBList<T> {
|
||||||
|
|
||||||
|
@DBField(id = 2, type = DBDataType.OBJECT)
|
||||||
|
public Class<T> type;
|
||||||
|
|
||||||
|
public DBDBObjectList(JCWDatabase db, Class<T> type) {
|
||||||
|
super(db, 10);
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DBDBObjectList(JCWDatabase db, Class<T> type, int maxLoadedItems) {
|
||||||
|
super(db, maxLoadedItems);
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DBDBObjectList(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException {
|
||||||
|
super(database, objectInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected T loadObjectFromDatabase(long uid) throws IOException {
|
||||||
|
return database.loadDBObject(type, uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected long saveObjectToDatabase(long uid, T value) throws IOException {
|
||||||
|
database.writeObjectProperty(uid, DBDataType.DATABASE_OBJECT, value);
|
||||||
|
return uid;
|
||||||
|
}
|
||||||
|
}
|
@ -3,5 +3,6 @@ package org.warp.jcwdb.ann;
|
|||||||
public enum DBDataType {
|
public enum DBDataType {
|
||||||
DATABASE_OBJECT,
|
DATABASE_OBJECT,
|
||||||
OBJECT,
|
OBJECT,
|
||||||
INTEGER
|
INTEGER,
|
||||||
|
UID_LIST
|
||||||
}
|
}
|
171
src/main/java/org/warp/jcwdb/ann/DBList.java
Normal file
171
src/main/java/org/warp/jcwdb/ann/DBList.java
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
package org.warp.jcwdb.ann;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||||
|
|
||||||
|
import java.io.IOError;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.StringJoiner;
|
||||||
|
|
||||||
|
public abstract class DBList<T> extends DBObject {
|
||||||
|
@DBField(id = 0, type = DBDataType.INTEGER)
|
||||||
|
private int maxLoadedItems;
|
||||||
|
|
||||||
|
@DBField(id = 1, type = DBDataType.UID_LIST)
|
||||||
|
private LongArrayList itemIndices;
|
||||||
|
|
||||||
|
public DBList(JCWDatabase db, int maxLoadedItems) {
|
||||||
|
super(db);
|
||||||
|
this.maxLoadedItems = maxLoadedItems;
|
||||||
|
this.itemIndices = new LongArrayList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DBList(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException {
|
||||||
|
super(database, objectInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxLoadedItems() {
|
||||||
|
return maxLoadedItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(T obj) {
|
||||||
|
try {
|
||||||
|
itemIndices.add(saveObjectToDatabase(database.allocateNullValue(), obj));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IOError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return this.itemIndices.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private T loadObjectAt(int i) throws IOException {
|
||||||
|
return loadObjectFromDatabase(itemIndices.getLong(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract T loadObjectFromDatabase(long uid) throws IOException;
|
||||||
|
|
||||||
|
protected abstract long saveObjectToDatabase(long uid, T value) throws IOException;
|
||||||
|
|
||||||
|
public DBListIterator<T> iterator() {
|
||||||
|
return new DBListIterator<>() {
|
||||||
|
private int position = itemIndices.size();
|
||||||
|
private LinkedList<T> cachedItems;
|
||||||
|
|
||||||
|
private int objectToSavePosition;
|
||||||
|
private T objectToSave;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
if (position > 0) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
saveObservedObject();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IOError(e);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T next() {
|
||||||
|
position--;
|
||||||
|
if (position < 0) {
|
||||||
|
throw new NullPointerException("Position < 0");
|
||||||
|
}
|
||||||
|
if (cachedItems == null || cachedItems.size() == 0) {
|
||||||
|
try {
|
||||||
|
cachedItems = fillCache(position);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return switchObservedObject(position, cachedItems.removeFirst());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
try {
|
||||||
|
saveObservedObject();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
itemIndices.removeLong(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void insert(T obj) {
|
||||||
|
try {
|
||||||
|
itemIndices.add(position, saveObjectToDatabase(database.allocateNullValue(), obj));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IOError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(T obj) {
|
||||||
|
try {
|
||||||
|
itemIndices.set(position, saveObjectToDatabase(database.allocateNullValue(), obj));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IOError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private LinkedList<T> fillCache(int position) throws IOException {
|
||||||
|
LinkedList<T> cachedItems = new LinkedList<>();
|
||||||
|
int firstObjectIndex = position;
|
||||||
|
int lastObjectIndex = firstObjectIndex - maxLoadedItems;
|
||||||
|
if (lastObjectIndex < 0) {
|
||||||
|
lastObjectIndex = 0;
|
||||||
|
}
|
||||||
|
for (int i = firstObjectIndex; i >= lastObjectIndex; i--) {
|
||||||
|
cachedItems.addLast(loadObjectAt(i));
|
||||||
|
}
|
||||||
|
return cachedItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveObservedObject() throws IOException {
|
||||||
|
if (objectToSave != null) {
|
||||||
|
itemIndices.set(objectToSavePosition, saveObjectToDatabase(database.allocateNullValue(), objectToSave));
|
||||||
|
objectToSave = null;
|
||||||
|
objectToSavePosition = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private T switchObservedObject(int position, T obj) throws IOException {
|
||||||
|
saveObservedObject();
|
||||||
|
objectToSave = obj;
|
||||||
|
objectToSavePosition = position;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface DBListIterator<T> {
|
||||||
|
boolean hasNext();
|
||||||
|
|
||||||
|
T next();
|
||||||
|
|
||||||
|
void remove();
|
||||||
|
|
||||||
|
void insert(T obj);
|
||||||
|
|
||||||
|
void set(T obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new StringJoiner(", ", DBList.class.getSimpleName() + "[", "]")
|
||||||
|
.add("size=" + size())
|
||||||
|
.add("maxLoadedItems=" + maxLoadedItems)
|
||||||
|
.add("itemIndices=" + (itemIndices.size() < 100 ? itemIndices : "["+itemIndices.size()+" items]"))
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,16 @@
|
|||||||
package org.warp.jcwdb.ann;
|
package org.warp.jcwdb.ann;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.reflect.FieldUtils;
|
||||||
|
|
||||||
|
import java.io.IOError;
|
||||||
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public abstract class DBObject {
|
public abstract class DBObject {
|
||||||
private final Database database;
|
protected final DatabaseManager database;
|
||||||
private final long uid;
|
|
||||||
private Field[] fields;
|
private Field[] fields;
|
||||||
private DBDataType[] fieldTypes;
|
private DBDataType[] fieldTypes;
|
||||||
private long[] fieldUIDs;
|
private long[] fieldUIDs;
|
||||||
@ -17,17 +21,103 @@ public abstract class DBObject {
|
|||||||
private long[] propertyUIDs;
|
private long[] propertyUIDs;
|
||||||
private boolean[] loadedProperties;
|
private boolean[] loadedProperties;
|
||||||
private Object[] loadedPropertyValues;
|
private Object[] loadedPropertyValues;
|
||||||
|
private Map<String, DBPropertySetter> setterMethods;
|
||||||
|
private Map<String, DBPropertyGetter> getterMethods;
|
||||||
|
private final Object fieldsAccessLock = new Object();
|
||||||
|
private final Object propertiesAccessLock = new Object();
|
||||||
|
|
||||||
public DBObject(Database database) {
|
public DBObject(JCWDatabase database) {
|
||||||
this.database = database;
|
this.database = database.getDatabaseManager();
|
||||||
this.uid = database.newDBObject(this);
|
try {
|
||||||
database.preloadDBObject(this);
|
initializeDBObject();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DBObject(Database database, long uid) {
|
private void initializeDBObject() throws IOException {
|
||||||
this.database = database;
|
initializeDBObjectFields();
|
||||||
this.uid = uid;
|
initializeDBObjectProperties();
|
||||||
database.preloadDBObject(this);
|
}
|
||||||
|
|
||||||
|
private void initializeDBObjectFields() throws IOException {
|
||||||
|
// Declare the variables needed to get the biggest field Id
|
||||||
|
Field[] unorderedFields = database.getFields(this);
|
||||||
|
// Find the biggest field Id
|
||||||
|
int biggestFieldId = database.getBiggestFieldId(unorderedFields);
|
||||||
|
|
||||||
|
// Allocate new UIDs
|
||||||
|
fieldUIDs = database.allocateNewUIDs(biggestFieldId + 1);
|
||||||
|
|
||||||
|
// Declare the other variables
|
||||||
|
Field[] fields = new Field[biggestFieldId + 1];
|
||||||
|
DBDataType[] orderedFieldTypes = new DBDataType[biggestFieldId + 1];
|
||||||
|
|
||||||
|
// Load all fields metadata and load them
|
||||||
|
for (Field field : unorderedFields) {
|
||||||
|
DBField fieldAnnotation = field.getAnnotation(DBField.class);
|
||||||
|
int fieldId = fieldAnnotation.id();
|
||||||
|
DBDataType fieldType = fieldAnnotation.type();
|
||||||
|
database.loadField(this, field, fieldType, fieldUIDs[fieldId]);
|
||||||
|
fields[fieldId] = field;
|
||||||
|
orderedFieldTypes[fieldId] = fieldType;
|
||||||
|
}
|
||||||
|
// Set fields metadata
|
||||||
|
setFields(fields, orderedFieldTypes, fieldUIDs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeDBObjectProperties() {
|
||||||
|
// Declare the variables needed to get the biggest property Id
|
||||||
|
Method[] unorderedPropertyGetters = database.getPropertyGetters(this);
|
||||||
|
Method[] unorderedPropertySetters = database.getPropertySetters(this);
|
||||||
|
|
||||||
|
// Find the biggest property Id
|
||||||
|
int biggestGetter = database.getBiggestPropertyGetterId(unorderedPropertyGetters);
|
||||||
|
int biggestSetter = database.getBiggestPropertySetterId(unorderedPropertySetters);
|
||||||
|
int biggestPropertyId = biggestGetter > biggestSetter ? biggestGetter : biggestSetter;
|
||||||
|
|
||||||
|
// Allocate new UIDs
|
||||||
|
propertyUIDs = database.allocateNewUIDs(biggestPropertyId + 1);
|
||||||
|
|
||||||
|
for (Method property : unorderedPropertySetters) {
|
||||||
|
DBPropertySetter fieldAnnotation = property.getAnnotation(DBPropertySetter.class);
|
||||||
|
int propertyId = fieldAnnotation.id();
|
||||||
|
if (propertyId > biggestPropertyId) {
|
||||||
|
biggestPropertyId = propertyId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declare the other variables
|
||||||
|
DBDataType[] propertyTypes = new DBDataType[biggestPropertyId + 1];
|
||||||
|
Method[] propertyGetters = new Method[biggestPropertyId + 1];
|
||||||
|
Method[] propertySetters = new Method[biggestPropertyId + 1];
|
||||||
|
Map<String, DBPropertySetter> setterMethods = new LinkedHashMap<>();
|
||||||
|
Map<String, DBPropertyGetter> getterMethods = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
// Load the properties metadata
|
||||||
|
for (Method property : unorderedPropertyGetters) {
|
||||||
|
DBPropertyGetter propertyAnnotation = property.getAnnotation(DBPropertyGetter.class);
|
||||||
|
int propertyId = propertyAnnotation.id();
|
||||||
|
DBDataType propertyType = propertyAnnotation.type();
|
||||||
|
propertyTypes[propertyId] = propertyType;
|
||||||
|
propertyGetters[propertyId] = property;
|
||||||
|
getterMethods.put(property.getName(), propertyAnnotation);
|
||||||
|
}
|
||||||
|
for (Method property : unorderedPropertySetters) {
|
||||||
|
DBPropertySetter propertyAnnotation = property.getAnnotation(DBPropertySetter.class);
|
||||||
|
int propertyId = propertyAnnotation.id();
|
||||||
|
DBDataType propertyType = propertyAnnotation.type();
|
||||||
|
propertyTypes[propertyId] = propertyType;
|
||||||
|
propertySetters[propertyId] = property;
|
||||||
|
setterMethods.put(property.getName(), propertyAnnotation);
|
||||||
|
}
|
||||||
|
// Set properties metadata
|
||||||
|
setProperties(propertyGetters, propertySetters, propertyTypes, propertyUIDs, setterMethods, getterMethods);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DBObject(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException {
|
||||||
|
this.database = database.getDatabaseManager();
|
||||||
|
this.database.preloadDBObject(this, objectInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> T getProperty() {
|
public <T> T getProperty() {
|
||||||
@ -36,71 +126,86 @@ public abstract class DBObject {
|
|||||||
try {
|
try {
|
||||||
int propertyId = stackFrame.getDeclaringClass().getDeclaredMethod(stackFrame.getMethodName()).getAnnotation(DBPropertyGetter.class).id();
|
int propertyId = stackFrame.getDeclaringClass().getDeclaredMethod(stackFrame.getMethodName()).getAnnotation(DBPropertyGetter.class).id();
|
||||||
return getProperty(propertyId);
|
return getProperty(propertyId);
|
||||||
} catch (NoSuchMethodException e) {
|
} catch (IOException | NoSuchMethodException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
<T> void setLoadedProperty(int propertyId, T value) {
|
<T> void setLoadedProperty(int propertyId, T value) {
|
||||||
loadedPropertyValues[propertyId] = value;
|
loadedPropertyValues[propertyId] = value;
|
||||||
|
loadedProperties[propertyId] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private <T> T getProperty(int propertyId) {
|
private <T> T getProperty(int propertyId) throws IOException {
|
||||||
if (!loadedProperties[propertyId]) {
|
synchronized (propertiesAccessLock) {
|
||||||
try {
|
if (!loadedProperties[propertyId]) {
|
||||||
database.loadProperty(this, propertyId, propertyGetters[propertyId], propertyTypes[propertyId], propertyUIDs[propertyId]);
|
long propertyUID = propertyUIDs[propertyId];
|
||||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
|
database.loadProperty(this, propertyId, propertyGetters[propertyId], propertyTypes[propertyId], propertyUID);
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
}
|
||||||
|
return (T) loadedPropertyValues[propertyId];
|
||||||
}
|
}
|
||||||
return (T) loadedPropertyValues[propertyId];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> void setProperty(T value) {
|
public <T> void setProperty(T value) {
|
||||||
StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
|
StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
|
||||||
StackWalker.StackFrame stackFrame = walker.walk(f -> f.skip(1).findFirst().orElse(null));
|
StackWalker.StackFrame stackFrame = walker.walk(f -> f.skip(1).findFirst().orElse(null));
|
||||||
try {
|
DBPropertySetter propertyAnnotation = setterMethods.get(stackFrame.getMethodName());
|
||||||
int propertyId = stackFrame.getDeclaringClass().getDeclaredMethod(stackFrame.getMethodName(), value.getClass()).getAnnotation(DBPropertySetter.class).id();
|
setProperty(propertyAnnotation.id(), propertyAnnotation.type(), value);
|
||||||
setProperty(propertyId, value);
|
}
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
throw new RuntimeException(e);
|
public <T> void setProperty(int propertyId, DBDataType propertyType, T value) {
|
||||||
|
synchronized (propertiesAccessLock) {
|
||||||
|
loadedPropertyValues[propertyId] = value;
|
||||||
|
loadedProperties[propertyId] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> void setProperty(int propertyId, T value) {
|
public void writeToDisk(long uid) {
|
||||||
loadedPropertyValues[propertyId] = value;
|
//System.err.println("Saving object " + uid + ":" + this);
|
||||||
loadedProperties[propertyId] = true;
|
try {
|
||||||
}
|
synchronized (propertiesAccessLock) {
|
||||||
|
synchronized (fieldsAccessLock) {
|
||||||
public void close() {
|
database.writeObjectInfo(uid, fieldUIDs, propertyUIDs);
|
||||||
database.saveObject(this);
|
}
|
||||||
|
}
|
||||||
|
synchronized (fieldsAccessLock) {
|
||||||
|
for (int i = 0; i < fieldUIDs.length; i++) {
|
||||||
|
try {
|
||||||
|
database.writeObjectProperty(fieldUIDs[i], fieldTypes[i], fields[i].get(this));
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new IOError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
synchronized (propertiesAccessLock) {
|
||||||
|
for (int i = 0; i < propertyUIDs.length; i++) {
|
||||||
|
database.writeObjectProperty(propertyUIDs[i], propertyTypes[i], loadedPropertyValues[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IOError(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void setFields(Field[] fields, DBDataType[] fieldTypes, long[] fieldUIDs) {
|
public final void setFields(Field[] fields, DBDataType[] fieldTypes, long[] fieldUIDs) {
|
||||||
this.fields = fields;
|
synchronized (fieldsAccessLock) {
|
||||||
this.fieldTypes = fieldTypes;
|
this.fields = fields;
|
||||||
this.fieldUIDs = fieldUIDs;
|
this.fieldTypes = fieldTypes;
|
||||||
|
this.fieldUIDs = fieldUIDs;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void setProperties(Method[] propertyGetters, Method[] propertySetters, DBDataType[] propertyTypes, long[] propertyUIDs) {
|
public final void setProperties(Method[] propertyGetters, Method[] propertySetters, DBDataType[] propertyTypes, long[] propertyUIDs, Map<String, DBPropertySetter> setterMethods, Map<String, DBPropertyGetter> getterMethods) {
|
||||||
this.propertyGetters = propertyGetters;
|
synchronized (propertiesAccessLock) {
|
||||||
this.propertySetters = propertySetters;
|
this.propertyGetters = propertyGetters;
|
||||||
this.propertyTypes = propertyTypes;
|
this.propertySetters = propertySetters;
|
||||||
this.propertyUIDs = propertyUIDs;
|
this.propertyTypes = propertyTypes;
|
||||||
this.loadedProperties = new boolean[this.propertyUIDs.length];
|
this.propertyUIDs = propertyUIDs;
|
||||||
this.loadedPropertyValues = new Object[this.propertyUIDs.length];
|
this.loadedProperties = new boolean[this.propertyUIDs.length];
|
||||||
}
|
this.loadedPropertyValues = new Object[this.propertyUIDs.length];
|
||||||
|
this.setterMethods = setterMethods;
|
||||||
public final long getUID() {
|
this.getterMethods = getterMethods;
|
||||||
return uid;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public long[] getAllFieldUIDs() {
|
|
||||||
return fieldUIDs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long[] getAllPropertyUIDs() {
|
|
||||||
return propertyUIDs;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,12 +8,18 @@ import java.io.IOException;
|
|||||||
public class DBObjectIndicesManager {
|
public class DBObjectIndicesManager {
|
||||||
private final FileIndexManager indices;
|
private final FileIndexManager indices;
|
||||||
|
|
||||||
public DBObjectIndicesManager(FileIndexManager indices) {
|
DBObjectIndicesManager(FileIndexManager indices) {
|
||||||
this.indices = indices;
|
this.indices = indices;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long allocate(int fieldsCount, int propertiesCount) {
|
public long allocate(int fieldsCount, int propertiesCount) {
|
||||||
return indices.add(calculateObjectSize(fieldsCount, propertiesCount));
|
long uid = indices.add(calculateObjectSize(fieldsCount, propertiesCount));
|
||||||
|
//System.err.println("ALLOCATED UID " + uid);
|
||||||
|
return uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNull(long uid) throws IOException {
|
||||||
|
indices.set(uid, 0, (w) -> w.write(new byte[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set(long uid, long[] fields, long[] properties) throws IOException {
|
public void set(long uid, long[] fields, long[] properties) throws IOException {
|
||||||
@ -23,7 +29,7 @@ public class DBObjectIndicesManager {
|
|||||||
for (int i = 0; i < fields.length; i++) {
|
for (int i = 0; i < fields.length; i++) {
|
||||||
w.writeLong(fields[i]);
|
w.writeLong(fields[i]);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < fields.length; i++) {
|
for (int i = 0; i < properties.length; i++) {
|
||||||
w.writeLong(properties[i]);
|
w.writeLong(properties[i]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -31,6 +37,9 @@ public class DBObjectIndicesManager {
|
|||||||
|
|
||||||
public DBObjectInfo get(long uid) throws IOException {
|
public DBObjectInfo get(long uid) throws IOException {
|
||||||
return indices.get(uid, (i, size) -> {
|
return indices.get(uid, (i, size) -> {
|
||||||
|
if (size < Integer.BYTES * 2) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
long[] indices = new long[i.readInt()];
|
long[] indices = new long[i.readInt()];
|
||||||
long[] properties = new long[i.readInt()];
|
long[] properties = new long[i.readInt()];
|
||||||
if (size != calculateObjectSize(indices, properties)) {
|
if (size != calculateObjectSize(indices, properties)) {
|
||||||
@ -46,6 +55,10 @@ public class DBObjectIndicesManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean has(long uid) {
|
||||||
|
return indices.has(uid);
|
||||||
|
}
|
||||||
|
|
||||||
private int calculateObjectSize(long[] fields, long[] properties) {
|
private int calculateObjectSize(long[] fields, long[] properties) {
|
||||||
return calculateObjectSize(fields.length, properties.length);
|
return calculateObjectSize(fields.length, properties.length);
|
||||||
}
|
}
|
||||||
|
25
src/main/java/org/warp/jcwdb/ann/DBObjectList.java
Normal file
25
src/main/java/org/warp/jcwdb/ann/DBObjectList.java
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package org.warp.jcwdb.ann;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class DBObjectList<T> extends DBList<T> {
|
||||||
|
|
||||||
|
public DBObjectList(JCWDatabase db, int maxLoadedItems) {
|
||||||
|
super(db, maxLoadedItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DBObjectList(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException {
|
||||||
|
super(database, objectInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected T loadObjectFromDatabase(long uid) throws IOException {
|
||||||
|
return database.loadObject(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected long saveObjectToDatabase(long uid, T value) throws IOException {
|
||||||
|
database.writeObjectProperty(uid, DBDataType.OBJECT, value);
|
||||||
|
return uid;
|
||||||
|
}
|
||||||
|
}
|
@ -1,275 +0,0 @@
|
|||||||
package org.warp.jcwdb.ann;
|
|
||||||
|
|
||||||
import com.esotericsoftware.kryo.Kryo;
|
|
||||||
import org.apache.commons.lang3.reflect.FieldUtils;
|
|
||||||
import org.apache.commons.lang3.reflect.MethodUtils;
|
|
||||||
import org.warp.jcwdb.FileIndexManager;
|
|
||||||
|
|
||||||
import java.io.IOError;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
|
|
||||||
public class Database {
|
|
||||||
|
|
||||||
public static final long MAX_LOADED_INDICES = 100000;
|
|
||||||
private final DBObjectIndicesManager objectIndicesManager;
|
|
||||||
private final FileIndexManager indices;
|
|
||||||
private final LinkedList<WeakReference<DBObject>> loadedObjects = new LinkedList<>();
|
|
||||||
private static final Kryo kryo = new Kryo();
|
|
||||||
|
|
||||||
public Database(Path dataFile, Path metadataFile) throws IOException {
|
|
||||||
this.indices = new FileIndexManager(dataFile, metadataFile);
|
|
||||||
this.objectIndicesManager = new DBObjectIndicesManager(this.indices);
|
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
|
||||||
try {
|
|
||||||
Database.this.close();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void close() throws IOException {
|
|
||||||
indices.close();
|
|
||||||
for (WeakReference<DBObject> loadedObjectReference : loadedObjects) {
|
|
||||||
DBObject loadedObject = loadedObjectReference.get();
|
|
||||||
if (loadedObject != null) {
|
|
||||||
loadedObject.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void preloadDBObject(DBObject obj) {
|
|
||||||
DBObjectIndicesManager.DBObjectInfo UIDs = readUIDs(obj.getUID());
|
|
||||||
|
|
||||||
preloadDBObjectFields(obj, UIDs.getFields());
|
|
||||||
preloadDBObjectProperties(obj, UIDs.getProperties());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void preloadDBObjectFields(DBObject obj, long[] fieldUIDs) {
|
|
||||||
// Declare the variables needed to get the biggest field Id
|
|
||||||
Field[] unorderedFields = getFields(obj);
|
|
||||||
// Find the biggest field Id
|
|
||||||
int biggestFieldId = getBiggestFieldId(unorderedFields);
|
|
||||||
|
|
||||||
// Declare the other variables
|
|
||||||
Field[] fields = new Field[biggestFieldId + 1];
|
|
||||||
DBDataType[] orderedFieldTypes = new DBDataType[biggestFieldId + 1];
|
|
||||||
|
|
||||||
// Load all fields metadata and load them
|
|
||||||
for (Field field : unorderedFields) {
|
|
||||||
DBField fieldAnnotation = field.getAnnotation(DBField.class);
|
|
||||||
int fieldId = fieldAnnotation.id();
|
|
||||||
DBDataType fieldType = fieldAnnotation.type();
|
|
||||||
try {
|
|
||||||
loadField(obj, field, fieldType, fieldUIDs[fieldId]);
|
|
||||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
fields[fieldId] = field;
|
|
||||||
orderedFieldTypes[fieldId] = fieldType;
|
|
||||||
}
|
|
||||||
// Set fields metadata
|
|
||||||
obj.setFields(fields, orderedFieldTypes, fieldUIDs);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void preloadDBObjectProperties(DBObject obj, long[] propertyUIDs) {
|
|
||||||
// Declare the variables needed to get the biggest property Id
|
|
||||||
Method[] unorderedPropertyGetters = getPropertyGetters(obj);
|
|
||||||
Method[] unorderedPropertySetters = getPropertySetters(obj);
|
|
||||||
|
|
||||||
// Find the biggest property Id
|
|
||||||
int biggestGetter = getBiggestPropertyGetterId(unorderedPropertyGetters);
|
|
||||||
int biggestSetter = getBiggestPropertySetterId(unorderedPropertySetters);
|
|
||||||
int biggestPropertyId = biggestGetter > biggestSetter ? biggestGetter : biggestSetter;
|
|
||||||
|
|
||||||
for (Method property : unorderedPropertySetters) {
|
|
||||||
DBPropertySetter fieldAnnotation = property.getAnnotation(DBPropertySetter.class);
|
|
||||||
int propertyId = fieldAnnotation.id();
|
|
||||||
if (propertyId > biggestPropertyId) {
|
|
||||||
biggestPropertyId = propertyId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Declare the other variables
|
|
||||||
DBDataType[] propertyTypes = new DBDataType[biggestPropertyId + 1];
|
|
||||||
Method[] propertyGetters = new Method[biggestPropertyId + 1];
|
|
||||||
Method[] propertySetters = new Method[biggestPropertyId + 1];
|
|
||||||
|
|
||||||
// Load the properties metadata
|
|
||||||
for (Method property : unorderedPropertyGetters) {
|
|
||||||
DBPropertyGetter propertyAnnotation = property.getAnnotation(DBPropertyGetter.class);
|
|
||||||
int propertyId = propertyAnnotation.id();
|
|
||||||
DBDataType propertyType = propertyAnnotation.type();
|
|
||||||
propertyTypes[propertyId] = propertyType;
|
|
||||||
propertyGetters[propertyId] = property;
|
|
||||||
}
|
|
||||||
for (Method property : unorderedPropertySetters) {
|
|
||||||
DBPropertySetter propertyAnnotation = property.getAnnotation(DBPropertySetter.class);
|
|
||||||
int propertyId = propertyAnnotation.id();
|
|
||||||
DBDataType propertyType = propertyAnnotation.type();
|
|
||||||
propertyTypes[propertyId] = propertyType;
|
|
||||||
propertySetters[propertyId] = property;
|
|
||||||
}
|
|
||||||
// Set properties metadata
|
|
||||||
obj.setProperties(propertyGetters, propertySetters, propertyTypes, propertyUIDs);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Method[] getPropertyGetters(DBObject obj) {
|
|
||||||
return MethodUtils.getMethodsWithAnnotation(obj.getClass(), DBPropertyGetter.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Method[] getPropertySetters(DBObject obj) {
|
|
||||||
return MethodUtils.getMethodsWithAnnotation(obj.getClass(), DBPropertySetter.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Field[] getFields(DBObject obj) {
|
|
||||||
return FieldUtils.getFieldsWithAnnotation(obj.getClass(), DBField.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getBiggestPropertyGetterId(Method[] unorderedPropertyGetters) {
|
|
||||||
int biggestPropertyId = -1;
|
|
||||||
for (Method property : unorderedPropertyGetters) {
|
|
||||||
DBPropertyGetter fieldAnnotation = property.getAnnotation(DBPropertyGetter.class);
|
|
||||||
int propertyId = fieldAnnotation.id();
|
|
||||||
if (propertyId > biggestPropertyId) {
|
|
||||||
biggestPropertyId = propertyId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return biggestPropertyId;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getBiggestPropertySetterId(Method[] unorderedPropertySetters) {
|
|
||||||
int biggestPropertyId = -1;
|
|
||||||
for (Method property : unorderedPropertySetters) {
|
|
||||||
DBPropertySetter fieldAnnotation = property.getAnnotation(DBPropertySetter.class);
|
|
||||||
int propertyId = fieldAnnotation.id();
|
|
||||||
if (propertyId > biggestPropertyId) {
|
|
||||||
biggestPropertyId = propertyId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return biggestPropertyId;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private int getBiggestFieldId(Field[] unorderedFields) {
|
|
||||||
int biggestFieldId = -1;
|
|
||||||
for (Field field : unorderedFields) {
|
|
||||||
DBField fieldAnnotation = field.getAnnotation(DBField.class);
|
|
||||||
int propertyId = fieldAnnotation.id();
|
|
||||||
if (propertyId > biggestFieldId) {
|
|
||||||
biggestFieldId = propertyId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return biggestFieldId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadProperty(DBObject obj, int propertyId, Method property, DBDataType propertyType, long propertyUID) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
|
|
||||||
switch (propertyType) {
|
|
||||||
case DATABASE_OBJECT:
|
|
||||||
DBObject fieldDBObjectValue = (DBObject) property.getDeclaringClass().getConstructor(Database.class, long.class).newInstance(this, propertyUID);
|
|
||||||
obj.setLoadedProperty(propertyId, fieldDBObjectValue);
|
|
||||||
break;
|
|
||||||
case OBJECT:
|
|
||||||
Object fieldObjectValue = loadObject(propertyUID);
|
|
||||||
obj.setLoadedProperty(propertyId, fieldObjectValue);
|
|
||||||
break;
|
|
||||||
case INTEGER:
|
|
||||||
int fieldIntValue = loadInt(propertyUID);
|
|
||||||
obj.setLoadedProperty(propertyId, fieldIntValue);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new NullPointerException("Unknown Field Type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadField(DBObject obj, Field field, DBDataType fieldType, long fieldUID) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
|
|
||||||
switch (fieldType) {
|
|
||||||
case DATABASE_OBJECT:
|
|
||||||
DBObject fieldDBObjectValue = (DBObject) field.getDeclaringClass().getConstructor(Database.class, long.class).newInstance(this, fieldUID);
|
|
||||||
field.set(obj, fieldDBObjectValue);
|
|
||||||
break;
|
|
||||||
case OBJECT:
|
|
||||||
Object fieldObjectValue = loadObject(fieldUID);
|
|
||||||
field.set(obj, fieldObjectValue);
|
|
||||||
break;
|
|
||||||
case INTEGER:
|
|
||||||
int fieldIntValue = loadInt(fieldUID);
|
|
||||||
field.setInt(obj, fieldIntValue);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new NullPointerException("Unknown Field Type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <T> T loadObject(long uid) {
|
|
||||||
try {
|
|
||||||
return (T) indices.get(uid, (i, size) -> size == 0 ? null : kryo.readClassAndObject(i));
|
|
||||||
} catch (IOException ex) {
|
|
||||||
throw new IOError(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int loadInt(long uid) {
|
|
||||||
try {
|
|
||||||
return indices.get(uid, (i, size) -> size == 0 ? 0 : i.readInt());
|
|
||||||
} catch (IOException ex) {
|
|
||||||
throw new IOError(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void watchObject(DBObject obj) {
|
|
||||||
loadedObjects.add(new WeakReference<>(obj));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param uid
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private DBObjectIndicesManager.DBObjectInfo readUIDs(long uid) {
|
|
||||||
try {
|
|
||||||
return objectIndicesManager.get(uid);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new IOError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void saveObject(DBObject obj) {
|
|
||||||
System.out.println("Saving object " + obj.getUID());
|
|
||||||
try {
|
|
||||||
objectIndicesManager.set(obj.getUID(), obj.getAllFieldUIDs(), obj.getAllPropertyUIDs());
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new IOError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public long newDBObject(DBObject obj) {
|
|
||||||
int fieldsCount = getBiggestFieldId(getFields(obj)) + 1;
|
|
||||||
int biggestGetter = getBiggestPropertyGetterId(getPropertyGetters(obj));
|
|
||||||
int biggestSetter = getBiggestPropertySetterId(getPropertySetters(obj));
|
|
||||||
int propertiesCount = (biggestGetter > biggestSetter ? biggestGetter : biggestSetter) + 1;
|
|
||||||
|
|
||||||
long uid = objectIndicesManager.allocate(fieldsCount, propertiesCount);
|
|
||||||
long[] fields = new long[fieldsCount];
|
|
||||||
for (int i = 0; i < fieldsCount; i++) {
|
|
||||||
fields[i] = indices.add(0);
|
|
||||||
}
|
|
||||||
long[] properties = new long[propertiesCount];
|
|
||||||
for (int i = 0; i < propertiesCount; i++) {
|
|
||||||
properties[i] = indices.add(0);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
objectIndicesManager.set(uid, fields, properties);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new IOError(e);
|
|
||||||
}
|
|
||||||
return uid;
|
|
||||||
}
|
|
||||||
}
|
|
447
src/main/java/org/warp/jcwdb/ann/DatabaseManager.java
Normal file
447
src/main/java/org/warp/jcwdb/ann/DatabaseManager.java
Normal file
@ -0,0 +1,447 @@
|
|||||||
|
package org.warp.jcwdb.ann;
|
||||||
|
|
||||||
|
import com.esotericsoftware.kryo.Kryo;
|
||||||
|
import com.esotericsoftware.kryo.Registration;
|
||||||
|
import com.esotericsoftware.kryo.io.Output;
|
||||||
|
import it.unimi.dsi.fastutil.booleans.BooleanArrayList;
|
||||||
|
import it.unimi.dsi.fastutil.bytes.ByteArrayList;
|
||||||
|
import it.unimi.dsi.fastutil.chars.CharArrayList;
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
|
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||||
|
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
|
||||||
|
import org.apache.commons.lang3.reflect.FieldUtils;
|
||||||
|
import org.apache.commons.lang3.reflect.MethodUtils;
|
||||||
|
import org.warp.jcwdb.Cleanable;
|
||||||
|
import org.warp.jcwdb.Cleaner;
|
||||||
|
import org.warp.jcwdb.FileIndexManager;
|
||||||
|
import org.warp.jcwdb.ann.exampleimpl.ClassWithList;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOError;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class DatabaseManager implements Cleanable {
|
||||||
|
|
||||||
|
public static final long MAX_LOADED_INDICES = 100000;
|
||||||
|
private final DBObjectIndicesManager objectIndicesManager;
|
||||||
|
private final FileIndexManager indices;
|
||||||
|
private final Cleaner cleaner;
|
||||||
|
private final JCWDatabase jcwDatabase;
|
||||||
|
private DBObject loadedRootObject = null;
|
||||||
|
private final Kryo kryo = new Kryo();
|
||||||
|
private volatile boolean closed;
|
||||||
|
|
||||||
|
DatabaseManager(JCWDatabase jcwDatabase, Path dataFile, Path metadataFile) throws IOException {
|
||||||
|
this.jcwDatabase = jcwDatabase;
|
||||||
|
kryo.setRegistrationRequired(true);
|
||||||
|
registerDefaultClasses();
|
||||||
|
this.indices = new FileIndexManager(dataFile, metadataFile);
|
||||||
|
if (!indices.has(0)) {
|
||||||
|
allocateNullValue();
|
||||||
|
}
|
||||||
|
this.objectIndicesManager = new DBObjectIndicesManager(this.indices);
|
||||||
|
this.cleaner = new Cleaner(this);
|
||||||
|
this.cleaner.start();
|
||||||
|
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||||
|
try {
|
||||||
|
DatabaseManager.this.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerDefaultClasses() {
|
||||||
|
int id = -90;
|
||||||
|
registerClass(boolean[].class, id++);
|
||||||
|
registerClass(byte[].class, id++);
|
||||||
|
registerClass(short[].class, id++);
|
||||||
|
registerClass(char[].class, id++);
|
||||||
|
registerClass(int[].class, id++);
|
||||||
|
registerClass(long[].class, id++);
|
||||||
|
registerClass(Boolean[].class, id++);
|
||||||
|
registerClass(Byte[].class, id++);
|
||||||
|
registerClass(Short[].class, id++);
|
||||||
|
registerClass(Character[].class, id++);
|
||||||
|
registerClass(Integer[].class, id++);
|
||||||
|
registerClass(Long[].class, id++);
|
||||||
|
registerClass(String.class, id++);
|
||||||
|
registerClass(String[].class, id++);
|
||||||
|
registerClass(Boolean.class, id++);
|
||||||
|
registerClass(Byte.class, id++);
|
||||||
|
registerClass(Short.class, id++);
|
||||||
|
registerClass(Character.class, id++);
|
||||||
|
registerClass(Integer.class, id++);
|
||||||
|
registerClass(Class.class, id++);
|
||||||
|
registerClass(Object.class, id++);
|
||||||
|
registerClass(Object[].class, id++);
|
||||||
|
registerClass(Long.class, id++);
|
||||||
|
registerClass(String.class, id++);
|
||||||
|
registerClass(String[].class, id++);
|
||||||
|
registerClass(boolean[][].class, id++);
|
||||||
|
registerClass(byte[][].class, id++);
|
||||||
|
registerClass(short[][].class, id++);
|
||||||
|
registerClass(char[][].class, id++);
|
||||||
|
registerClass(int[][].class, id++);
|
||||||
|
registerClass(long[][].class, id++);
|
||||||
|
registerClass(String[][].class, id++);
|
||||||
|
registerClass(List.class, id++);
|
||||||
|
registerClass(ArrayList.class, id++);
|
||||||
|
registerClass(LinkedList.class, id++);
|
||||||
|
registerClass(Set.class, id++);
|
||||||
|
registerClass(HashSet.class, id++);
|
||||||
|
registerClass(LinkedHashSet.class, id++);
|
||||||
|
registerClass(Map.class, id++);
|
||||||
|
registerClass(HashMap.class, id++);
|
||||||
|
registerClass(LinkedHashMap.class, id++);
|
||||||
|
registerClass(TreeMap.class, id++);
|
||||||
|
registerClass(BooleanArrayList.class, id++);
|
||||||
|
registerClass(ByteArrayList.class, id++);
|
||||||
|
registerClass(ShortArrayList.class, id++);
|
||||||
|
registerClass(CharArrayList.class, id++);
|
||||||
|
registerClass(IntArrayList.class, id++);
|
||||||
|
registerClass(LongArrayList.class, id++);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T extends DBObject> T loadRoot(Class<T> rootType) throws IOException {
|
||||||
|
if (loadedRootObject != null) {
|
||||||
|
throw new RuntimeException("Root already set!");
|
||||||
|
}
|
||||||
|
if (isDBObjectNull(0)) {
|
||||||
|
try {
|
||||||
|
T root = rootType.getConstructor(JCWDatabase.class).newInstance(this.jcwDatabase);
|
||||||
|
loadedRootObject = root;
|
||||||
|
return root;
|
||||||
|
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
|
||||||
|
throw new IOError(e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
T root = (T) loadDBObject(rootType, 0);
|
||||||
|
loadedRootObject = root;
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() throws IOException {
|
||||||
|
if (!closed) {
|
||||||
|
closed = true;
|
||||||
|
DatabaseManager.this.cleaner.stop();
|
||||||
|
if (loadedRootObject != null) {
|
||||||
|
loadedRootObject.writeToDisk(0);
|
||||||
|
}
|
||||||
|
indices.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void preloadDBObject(DBObject obj, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException {
|
||||||
|
preloadDBObjectFields(obj, objectInfo.getFields());
|
||||||
|
preloadDBObjectProperties(obj, objectInfo.getProperties());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void preloadDBObjectFields(DBObject obj, long[] fieldUIDs) throws IOException {
|
||||||
|
// Declare the variables needed to get the biggest field Id
|
||||||
|
Field[] unorderedFields = getFields(obj);
|
||||||
|
// Find the biggest field Id
|
||||||
|
int biggestFieldId = getBiggestFieldId(unorderedFields);
|
||||||
|
|
||||||
|
// Declare the other variables
|
||||||
|
Field[] fields = new Field[biggestFieldId + 1];
|
||||||
|
DBDataType[] orderedFieldTypes = new DBDataType[biggestFieldId + 1];
|
||||||
|
|
||||||
|
// Load all fields metadata and load them
|
||||||
|
for (Field field : unorderedFields) {
|
||||||
|
DBField fieldAnnotation = field.getAnnotation(DBField.class);
|
||||||
|
int fieldId = fieldAnnotation.id();
|
||||||
|
DBDataType fieldType = fieldAnnotation.type();
|
||||||
|
loadField(obj, field, fieldType, fieldUIDs[fieldId]);
|
||||||
|
fields[fieldId] = field;
|
||||||
|
orderedFieldTypes[fieldId] = fieldType;
|
||||||
|
}
|
||||||
|
// Set fields metadata
|
||||||
|
obj.setFields(fields, orderedFieldTypes, fieldUIDs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void preloadDBObjectProperties(DBObject obj, long[] propertyUIDs) {
|
||||||
|
// Declare the variables needed to get the biggest property Id
|
||||||
|
Method[] unorderedPropertyGetters = getPropertyGetters(obj);
|
||||||
|
Method[] unorderedPropertySetters = getPropertySetters(obj);
|
||||||
|
|
||||||
|
// Find the biggest property Id
|
||||||
|
int biggestGetter = getBiggestPropertyGetterId(unorderedPropertyGetters);
|
||||||
|
int biggestSetter = getBiggestPropertySetterId(unorderedPropertySetters);
|
||||||
|
int biggestPropertyId = biggestGetter > biggestSetter ? biggestGetter : biggestSetter;
|
||||||
|
|
||||||
|
for (Method property : unorderedPropertySetters) {
|
||||||
|
DBPropertySetter fieldAnnotation = property.getAnnotation(DBPropertySetter.class);
|
||||||
|
int propertyId = fieldAnnotation.id();
|
||||||
|
if (propertyId > biggestPropertyId) {
|
||||||
|
biggestPropertyId = propertyId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declare the other variables
|
||||||
|
DBDataType[] propertyTypes = new DBDataType[biggestPropertyId + 1];
|
||||||
|
Method[] propertyGetters = new Method[biggestPropertyId + 1];
|
||||||
|
Method[] propertySetters = new Method[biggestPropertyId + 1];
|
||||||
|
Map<String, DBPropertySetter> setterMethods = new LinkedHashMap<>();
|
||||||
|
Map<String, DBPropertyGetter> getterMethods = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
// Load the properties metadata
|
||||||
|
for (Method property : unorderedPropertyGetters) {
|
||||||
|
DBPropertyGetter propertyAnnotation = property.getAnnotation(DBPropertyGetter.class);
|
||||||
|
int propertyId = propertyAnnotation.id();
|
||||||
|
DBDataType propertyType = propertyAnnotation.type();
|
||||||
|
propertyTypes[propertyId] = propertyType;
|
||||||
|
propertyGetters[propertyId] = property;
|
||||||
|
getterMethods.put(property.getName(), propertyAnnotation);
|
||||||
|
}
|
||||||
|
for (Method property : unorderedPropertySetters) {
|
||||||
|
DBPropertySetter propertyAnnotation = property.getAnnotation(DBPropertySetter.class);
|
||||||
|
int propertyId = propertyAnnotation.id();
|
||||||
|
DBDataType propertyType = propertyAnnotation.type();
|
||||||
|
propertyTypes[propertyId] = propertyType;
|
||||||
|
propertySetters[propertyId] = property;
|
||||||
|
setterMethods.put(property.getName(), propertyAnnotation);
|
||||||
|
}
|
||||||
|
// Set properties metadata
|
||||||
|
obj.setProperties(propertyGetters, propertySetters, propertyTypes, propertyUIDs, setterMethods, getterMethods);
|
||||||
|
}
|
||||||
|
|
||||||
|
Method[] getPropertyGetters(DBObject obj) {
|
||||||
|
return MethodUtils.getMethodsWithAnnotation(obj.getClass(), DBPropertyGetter.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
Method[] getPropertySetters(DBObject obj) {
|
||||||
|
return MethodUtils.getMethodsWithAnnotation(obj.getClass(), DBPropertySetter.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Field[] getFields(DBObject obj) {
|
||||||
|
return FieldUtils.getFieldsWithAnnotation(obj.getClass(), DBField.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getBiggestPropertyGetterId(Method[] unorderedPropertyGetters) {
|
||||||
|
int biggestPropertyId = -1;
|
||||||
|
for (Method property : unorderedPropertyGetters) {
|
||||||
|
DBPropertyGetter fieldAnnotation = property.getAnnotation(DBPropertyGetter.class);
|
||||||
|
int propertyId = fieldAnnotation.id();
|
||||||
|
if (propertyId > biggestPropertyId) {
|
||||||
|
biggestPropertyId = propertyId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return biggestPropertyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getBiggestPropertySetterId(Method[] unorderedPropertySetters) {
|
||||||
|
int biggestPropertyId = -1;
|
||||||
|
for (Method property : unorderedPropertySetters) {
|
||||||
|
DBPropertySetter fieldAnnotation = property.getAnnotation(DBPropertySetter.class);
|
||||||
|
int propertyId = fieldAnnotation.id();
|
||||||
|
if (propertyId > biggestPropertyId) {
|
||||||
|
biggestPropertyId = propertyId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return biggestPropertyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected int getBiggestFieldId(Field[] unorderedFields) {
|
||||||
|
int biggestFieldId = -1;
|
||||||
|
for (Field field : unorderedFields) {
|
||||||
|
DBField fieldAnnotation = field.getAnnotation(DBField.class);
|
||||||
|
int propertyId = fieldAnnotation.id();
|
||||||
|
if (propertyId > biggestFieldId) {
|
||||||
|
biggestFieldId = propertyId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return biggestFieldId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void loadProperty(DBObject obj, int propertyId, Method property, DBDataType propertyType, long propertyUID) throws IOException {
|
||||||
|
switch (propertyType) {
|
||||||
|
case DATABASE_OBJECT:
|
||||||
|
DBObject fieldDBObjectValue = loadDBObject((Class<? extends DBObject>) property.getReturnType(), propertyUID);
|
||||||
|
//System.err.println("Loading prop. DBObj " + propertyUID + ":" + fieldDBObjectValue);
|
||||||
|
obj.setLoadedProperty(propertyId, fieldDBObjectValue);
|
||||||
|
break;
|
||||||
|
case OBJECT:
|
||||||
|
Object fieldObjectValue = loadObject(propertyUID);
|
||||||
|
//System.err.println("Loading prop. Obj " + propertyUID + ":" + fieldObjectValue);
|
||||||
|
obj.setLoadedProperty(propertyId, fieldObjectValue);
|
||||||
|
break;
|
||||||
|
case UID_LIST:
|
||||||
|
LongArrayList fieldListObjectValue = loadListObject(propertyUID);
|
||||||
|
//System.err.println("Loading prop. LOb " + propertyUID + ":" + fieldListObjectValue);
|
||||||
|
obj.setLoadedProperty(propertyId, fieldListObjectValue);
|
||||||
|
break;
|
||||||
|
case INTEGER:
|
||||||
|
int fieldIntValue = loadInt(propertyUID);
|
||||||
|
//System.err.println("Loading prop. Int " + propertyUID + ":" + fieldIntValue);
|
||||||
|
obj.setLoadedProperty(propertyId, fieldIntValue);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new NullPointerException("Unknown Field Type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void loadField(DBObject obj, Field field, DBDataType fieldType, long fieldUID) throws IOException {
|
||||||
|
try {
|
||||||
|
switch (fieldType) {
|
||||||
|
case DATABASE_OBJECT:
|
||||||
|
DBObject fieldDBObjectValue = loadDBObject((Class<? extends DBObject>) field.getType(), fieldUID);
|
||||||
|
//System.err.println("Loading field DBObj " + fieldUID + ":" + fieldDBObjectValue);
|
||||||
|
FieldUtils.writeField(field, obj, fieldDBObjectValue, true);
|
||||||
|
break;
|
||||||
|
case OBJECT:
|
||||||
|
Object fieldObjectValue = loadObject(fieldUID);
|
||||||
|
//System.err.println("Loading field Obj " + fieldUID + ":" + fieldObjectValue);
|
||||||
|
FieldUtils.writeField(field, obj, fieldObjectValue, true);
|
||||||
|
break;
|
||||||
|
case UID_LIST:
|
||||||
|
LongArrayList fieldListObjectValue = loadListObject(fieldUID);
|
||||||
|
//System.err.println("Loading field LOb " + fieldUID + ":" + fieldObjectValue);
|
||||||
|
FieldUtils.writeField(field, obj, fieldListObjectValue, true);
|
||||||
|
break;
|
||||||
|
case INTEGER:
|
||||||
|
int fieldIntValue = loadInt(fieldUID);
|
||||||
|
//System.err.println("Loading field Int " + fieldUID + ":" + fieldIntValue);
|
||||||
|
FieldUtils.writeField(field, obj, fieldIntValue, true);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new NullPointerException("Unknown Field Type");
|
||||||
|
}
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends DBObject> T loadDBObject(Class<T> type, long propertyUID) throws IOException {
|
||||||
|
try {
|
||||||
|
DBObjectIndicesManager.DBObjectInfo objectInfo = readUIDs(propertyUID);
|
||||||
|
if (objectInfo == null) return null;
|
||||||
|
return type.getDeclaredConstructor(JCWDatabase.class, DBObjectIndicesManager.DBObjectInfo.class).newInstance(jcwDatabase, objectInfo);
|
||||||
|
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isDBObjectNull(long uid) {
|
||||||
|
try {
|
||||||
|
return !objectIndicesManager.has(uid) || objectIndicesManager.get(uid) == null;
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new IOError(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T loadObject(long uid) throws IOException {
|
||||||
|
return indices.get(uid, (i, size) -> size == 0 ? null : (T) kryo.readClassAndObject(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private LongArrayList loadListObject(long uid) throws IOException {
|
||||||
|
return indices.get(uid, (i, size) -> {
|
||||||
|
if (size == 0) return null;
|
||||||
|
LongArrayList list = new LongArrayList();
|
||||||
|
int listSize = i.readVarInt(true);
|
||||||
|
for (int li = 0; li < listSize; li++) {
|
||||||
|
list.add(i.readVarLong(true));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public int loadInt(long uid) throws IOException {
|
||||||
|
return indices.get(uid, (i, size) -> size == 0 ? 0 : i.readInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param uid
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public DBObjectIndicesManager.DBObjectInfo readUIDs(long uid) {
|
||||||
|
try {
|
||||||
|
return objectIndicesManager.get(uid);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IOError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean exists(long uid) {
|
||||||
|
return objectIndicesManager.has(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeObjectInfo(long uid, long[] fieldUIDs, long[] propertyUIDs) throws IOException {
|
||||||
|
//System.err.println("Saving obj. " + uid);
|
||||||
|
this.objectIndicesManager.set(uid, fieldUIDs, propertyUIDs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeObjectInfoNull(long uid) throws IOException {
|
||||||
|
this.objectIndicesManager.setNull(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> void writeObjectProperty(long uid, DBDataType propertyType, T loadedPropertyValue) throws IOException {
|
||||||
|
switch (propertyType) {
|
||||||
|
case INTEGER:
|
||||||
|
indices.set(uid, Integer.BYTES, (o) -> o.writeInt((int) loadedPropertyValue));
|
||||||
|
//System.err.println("Saving prop. Int " + uid + ":" + loadedPropertyValue);
|
||||||
|
break;
|
||||||
|
case OBJECT:
|
||||||
|
Output baosOutput = new Output(new ByteArrayOutputStream());
|
||||||
|
kryo.writeClassAndObject(baosOutput, loadedPropertyValue);
|
||||||
|
//System.err.println("Saving prop. Obj " + uid + ":" + loadedPropertyValue);
|
||||||
|
byte[] out = baosOutput.toBytes();
|
||||||
|
indices.set(uid, out.length, o -> o.write(out, 0, out.length));
|
||||||
|
break;
|
||||||
|
case UID_LIST:
|
||||||
|
LongArrayList list = (LongArrayList) loadedPropertyValue;
|
||||||
|
final int listSize = list.size();
|
||||||
|
Output baosListOutput = new Output(Long.BYTES * 100, Long.BYTES * listSize);
|
||||||
|
baosListOutput.writeVarInt(listSize, true);
|
||||||
|
for (int i = 0; i < listSize; i++) {
|
||||||
|
baosListOutput.writeVarLong(list.getLong(i), true);
|
||||||
|
}
|
||||||
|
//System.err.println("Saving prop. LOb " + uid + ":" + loadedPropertyValue);
|
||||||
|
byte[] outList = baosListOutput.toBytes();
|
||||||
|
indices.set(uid, outList.length, o -> o.write(outList, 0, outList.length));
|
||||||
|
break;
|
||||||
|
case DATABASE_OBJECT:
|
||||||
|
//System.err.println("Saving prop. DBObj " + uid + ":" + loadedPropertyValue);
|
||||||
|
if (loadedPropertyValue == null) {
|
||||||
|
writeObjectInfoNull(uid);
|
||||||
|
} else {
|
||||||
|
((DBObject) loadedPropertyValue).writeToDisk(uid);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerClass(Class<?> clazz, int id) {
|
||||||
|
kryo.register(clazz, 100 + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long allocateNullValue() {
|
||||||
|
return indices.add(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long clean() {
|
||||||
|
return 0;//indices.clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long[] allocateNewUIDs(int quantity) {
|
||||||
|
long[] ids = new long[quantity];
|
||||||
|
for (int i = 0; i < quantity; i++) {
|
||||||
|
ids[i] = allocateNullValue();
|
||||||
|
}
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
}
|
32
src/main/java/org/warp/jcwdb/ann/JCWDatabase.java
Normal file
32
src/main/java/org/warp/jcwdb/ann/JCWDatabase.java
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package org.warp.jcwdb.ann;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
public class JCWDatabase {
|
||||||
|
private final DatabaseManager database;
|
||||||
|
|
||||||
|
public JCWDatabase(Path dataFile, Path metadataFile) throws IOException {
|
||||||
|
this.database = new DatabaseManager(this, dataFile, metadataFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends DBObject> T loadRoot(Class<T> rootClass) {
|
||||||
|
try {
|
||||||
|
return database.loadRoot(rootClass);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseManager getDatabaseManager() {
|
||||||
|
return database;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerClass(Class<?> clazz, int id) {
|
||||||
|
database.registerClass(clazz, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() throws IOException {
|
||||||
|
database.close();
|
||||||
|
}
|
||||||
|
}
|
@ -2,14 +2,17 @@ package org.warp.jcwdb.ann.exampleimpl;
|
|||||||
|
|
||||||
import org.warp.jcwdb.ann.*;
|
import org.warp.jcwdb.ann.*;
|
||||||
|
|
||||||
@DBClass(classTypeId = 0)
|
import java.io.IOException;
|
||||||
|
import java.util.StringJoiner;
|
||||||
|
|
||||||
public class Class1 extends DBObject {
|
public class Class1 extends DBObject {
|
||||||
public Class1(Database database) {
|
|
||||||
|
public Class1(JCWDatabase database) {
|
||||||
super(database);
|
super(database);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Class1(Database database, long uid) {
|
public Class1(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException {
|
||||||
super(database, uid);
|
super(database, objectInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DBField(id = 0, type = DBDataType.OBJECT)
|
@DBField(id = 0, type = DBDataType.OBJECT)
|
||||||
@ -18,6 +21,9 @@ public class Class1 extends DBObject {
|
|||||||
@DBField(id = 1, type = DBDataType.INTEGER)
|
@DBField(id = 1, type = DBDataType.INTEGER)
|
||||||
public int value2;
|
public int value2;
|
||||||
|
|
||||||
|
@DBField(id = 2, type = DBDataType.INTEGER)
|
||||||
|
public int value3;
|
||||||
|
|
||||||
@DBPropertyGetter(id = 0, type = DBDataType.OBJECT)
|
@DBPropertyGetter(id = 0, type = DBDataType.OBJECT)
|
||||||
public String getValue3() {
|
public String getValue3() {
|
||||||
return getProperty();
|
return getProperty();
|
||||||
@ -37,4 +43,26 @@ public class Class1 extends DBObject {
|
|||||||
public void setValue4(Class1 value) {
|
public void setValue4(Class1 value) {
|
||||||
setProperty(value);
|
setProperty(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DBPropertyGetter(id = 2, type = DBDataType.OBJECT)
|
||||||
|
public String getValueStr() {
|
||||||
|
return getProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertySetter(id = 2, type = DBDataType.OBJECT)
|
||||||
|
public void setValueStr(String value) {
|
||||||
|
setProperty(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new StringJoiner(", ", Class1.class.getSimpleName() + "[", "]")
|
||||||
|
.add("value1='" + value1 + "'")
|
||||||
|
.add("value2=" + value2)
|
||||||
|
.add("value3=" + value3)
|
||||||
|
.add("getValue3=" + getValue3())
|
||||||
|
.add("getValue4=" + getValue4())
|
||||||
|
.add("getValueStr=" + getValueStr())
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
40
src/main/java/org/warp/jcwdb/ann/exampleimpl/Class2.java
Normal file
40
src/main/java/org/warp/jcwdb/ann/exampleimpl/Class2.java
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package org.warp.jcwdb.ann.exampleimpl;
|
||||||
|
|
||||||
|
import org.warp.jcwdb.ann.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.StringJoiner;
|
||||||
|
|
||||||
|
public class Class2 extends Class1 {
|
||||||
|
|
||||||
|
//@DBField(id = 3, type = DBDataType.DATABASE_OBJECT)
|
||||||
|
//public DBDBObjectList<Class1> value4;
|
||||||
|
|
||||||
|
@DBField(id = 3, type = DBDataType.DATABASE_OBJECT)
|
||||||
|
public Class1 value4;
|
||||||
|
|
||||||
|
@DBField(id = 4, type = DBDataType.INTEGER)
|
||||||
|
public int value5;
|
||||||
|
|
||||||
|
public Class2(JCWDatabase database) {
|
||||||
|
super(database);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class2(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException {
|
||||||
|
super(database, objectInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new StringJoiner(", ", Class1.class.getSimpleName() + "[", "]")
|
||||||
|
.add("value1='" + value1 + "'")
|
||||||
|
.add("value2=" + value2)
|
||||||
|
.add("value3=" + value3)
|
||||||
|
.add("value4=" + value4)
|
||||||
|
.add("value5=" + value5)
|
||||||
|
.add("getValue3=" + getValue3())
|
||||||
|
.add("getValue4=" + getValue4())
|
||||||
|
.add("getValueStr=" + getValueStr())
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package org.warp.jcwdb.ann.exampleimpl;
|
||||||
|
|
||||||
|
import org.warp.jcwdb.ann.*;
|
||||||
|
|
||||||
|
import java.io.IOError;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.StringJoiner;
|
||||||
|
|
||||||
|
public class ClassWithList extends DBObject {
|
||||||
|
|
||||||
|
public ClassWithList(JCWDatabase database) {
|
||||||
|
super(database);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClassWithList(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException {
|
||||||
|
super(database, objectInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBField(id = 0, type = DBDataType.DATABASE_OBJECT)
|
||||||
|
public DBDBObjectList<IntClass> valueList;
|
||||||
|
|
||||||
|
|
||||||
|
@DBPropertySetter(id = 0, type = DBDataType.DATABASE_OBJECT)
|
||||||
|
public void setList(DBDBObjectList<IntClass> value) {
|
||||||
|
setProperty(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertyGetter(id = 0, type = DBDataType.DATABASE_OBJECT)
|
||||||
|
public DBDBObjectList<IntClass> getList() {
|
||||||
|
return getProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
DBDBObjectList<IntClass> list;
|
||||||
|
boolean listErrored = false;
|
||||||
|
try {
|
||||||
|
list = getList();
|
||||||
|
} catch (IOError | Exception ex) {
|
||||||
|
list = null;
|
||||||
|
listErrored = true;
|
||||||
|
}
|
||||||
|
return new StringJoiner(", ", ClassWithList.class.getSimpleName() + "[", "]")
|
||||||
|
.add("valueList=" + valueList)
|
||||||
|
.add("getList=" + (listErrored ? "{error}" : (list == null ? list : list.size() < 100 ? list : "[" + list.size() + " items])")))
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
27
src/main/java/org/warp/jcwdb/ann/exampleimpl/IntClass.java
Normal file
27
src/main/java/org/warp/jcwdb/ann/exampleimpl/IntClass.java
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package org.warp.jcwdb.ann.exampleimpl;
|
||||||
|
|
||||||
|
import org.warp.jcwdb.ann.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.StringJoiner;
|
||||||
|
|
||||||
|
public class IntClass extends DBObject {
|
||||||
|
|
||||||
|
public IntClass(JCWDatabase database) {
|
||||||
|
super(database);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntClass(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException {
|
||||||
|
super(database, objectInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBField(id = 0, type = DBDataType.INTEGER)
|
||||||
|
public int value;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new StringJoiner(", ", IntClass.class.getSimpleName() + "[", "]")
|
||||||
|
.add("value=" + value)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package org.warp.jcwdb.ann.exampleimpl;
|
package org.warp.jcwdb.ann.exampleimpl;
|
||||||
|
|
||||||
import org.warp.jcwdb.ann.Database;
|
import org.warp.jcwdb.ann.DBDBObjectList;
|
||||||
|
import org.warp.jcwdb.ann.JCWDatabase;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
@ -8,25 +9,57 @@ import java.nio.file.Paths;
|
|||||||
public class Main {
|
public class Main {
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
Database db = new Database(Paths.get("database_temp.db"), Paths.get("database_temp.idx"));
|
long t0 = System.currentTimeMillis();
|
||||||
Class1 class1 = new Class1(db, 0);
|
JCWDatabase db = new JCWDatabase(Paths.get("N:\\TimedTemp\\database_temp.db"), Paths.get("N:\\TimedTemp\\database_temp.idx"));
|
||||||
class1.value1 = "ciao";
|
db.registerClass(Class1.class, 0);
|
||||||
|
db.registerClass(Class2.class, 1);
|
||||||
|
Class2 class1 = db.loadRoot(Class2.class);
|
||||||
|
long t1 = System.currentTimeMillis();
|
||||||
|
System.err.println("Loading took " + (t1-t0)/1000d + " seconds");
|
||||||
|
t0 = System.currentTimeMillis();
|
||||||
|
System.err.println("[MAIN] class1="+class1);
|
||||||
|
class1.value1 = "ciaoooooooooooooooooooooo";
|
||||||
class1.value2 = 3;
|
class1.value2 = 3;
|
||||||
System.out.println("value3="+class1.getValue3());
|
class1.value5 = 5;
|
||||||
|
System.err.println("[MAIN] value3="+class1.getValue3());
|
||||||
class1.setValue3("Ciao 3");
|
class1.setValue3("Ciao 3");
|
||||||
System.out.println("value3="+class1.getValue3());
|
System.err.println("[MAIN] value3="+class1.getValue3());
|
||||||
|
|
||||||
Class1 nested = new Class1(db);
|
System.err.println("[MAIN] propString="+class1.getValueStr());
|
||||||
class1.setValue4(nested);
|
class1.setValueStr("Ciao String");
|
||||||
nested.setValue3("Ciao nested 3");
|
System.err.println("[MAIN] propString="+class1.getValueStr());
|
||||||
try {
|
|
||||||
class1.close();
|
System.err.println("[MAIN] getValue4="+class1.getValue4());
|
||||||
nested.close();
|
t1 = System.currentTimeMillis();
|
||||||
System.out.println(class1.getValue4().getValue3());
|
System.err.println("Post-loading took " + (t1-t0)/1000d + " seconds");
|
||||||
} catch (Exception ex) {
|
t0 = System.currentTimeMillis();
|
||||||
ex.printStackTrace();
|
for (int i = 0; i < 200; i++) {
|
||||||
} catch (Throwable throwable) {
|
Class1 nested;
|
||||||
throwable.printStackTrace();
|
if ((nested = class1.getValue4()) == null) {
|
||||||
|
//System.err.println("[MAIN] Created nested class");
|
||||||
|
class1.setValue4(nested = new Class1(db));
|
||||||
|
}
|
||||||
|
nested.getValue3();
|
||||||
|
//System.err.println("[MAIN] value4="+class1.getValue4());
|
||||||
|
//System.err.println("[MAIN] nested value3="+nested.getValue3());
|
||||||
|
nested.setValue3("Ciao nested 3");
|
||||||
|
//System.err.println("[MAIN] nested value3=" + class1.getValue4().getValue3());
|
||||||
}
|
}
|
||||||
|
t1 = System.currentTimeMillis();
|
||||||
|
System.err.println("Took " + (t1-t0)/1000d + " seconds");
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (class1.value4 == null) {
|
||||||
|
class1.value4 = new DBDBObjectList<>(db, 100, Class1.class);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 15; i++) {
|
||||||
|
Class1 c1 = new Class1(db);
|
||||||
|
c1.value1 = "" + i;
|
||||||
|
c1.value2 = i;
|
||||||
|
c1.setValueStr("" + i);
|
||||||
|
class1.value4.add(c1);
|
||||||
|
}*/
|
||||||
|
class1.value4 = new Class1(db);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
package org.warp.jcwdb.ann.exampleimpl;
|
||||||
|
|
||||||
|
import org.warp.jcwdb.ann.DBDBObjectList;
|
||||||
|
import org.warp.jcwdb.ann.DBList;
|
||||||
|
import org.warp.jcwdb.ann.JCWDatabase;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
public class MainSingleClass {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
long t0 = System.currentTimeMillis();
|
||||||
|
Files.deleteIfExists(Paths.get("N:\\TimedTemp\\database_t.db"));
|
||||||
|
Files.deleteIfExists(Paths.get("N:\\TimedTemp\\database_t.idx"));
|
||||||
|
for (int i = 0; i < 18; i++) {
|
||||||
|
doIt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void doIt() throws IOException {
|
||||||
|
System.err.println("doIt");
|
||||||
|
JCWDatabase db = new JCWDatabase(Paths.get("N:\\TimedTemp\\database_t.db"), Paths.get("N:\\TimedTemp\\database_t.idx"));
|
||||||
|
db.registerClass(ClassWithList.class, 0);
|
||||||
|
db.registerClass(IntClass.class, 1);
|
||||||
|
ClassWithList classWithList = db.loadRoot(ClassWithList.class);
|
||||||
|
System.err.println("[MAIN init] classWithList="+classWithList);
|
||||||
|
|
||||||
|
if (classWithList.valueList == null) {
|
||||||
|
System.out.println("Get list was null");
|
||||||
|
classWithList.valueList = new DBDBObjectList<>(db, IntClass.class, 10000);
|
||||||
|
}
|
||||||
|
DBDBObjectList<IntClass> list = classWithList.valueList;
|
||||||
|
|
||||||
|
for (int i = 0; i < 1000000; i++) {
|
||||||
|
IntClass intClass = new IntClass(db);
|
||||||
|
intClass.value = i+0xFF00;
|
||||||
|
//System.err.println("[WRITE]" + intClass.value);
|
||||||
|
list.add(intClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
DBList.DBListIterator<IntClass> it = list.iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
IntClass intClass = it.next();
|
||||||
|
//System.err.println("[READ]" + intClass.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.err.println("[MAIN end.] singleClass="+classWithList);
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
package org.warp.jcwdb.ann.exampleimpl;
|
||||||
|
|
||||||
|
import org.warp.jcwdb.ann.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.StringJoiner;
|
||||||
|
|
||||||
|
public class SingleClass extends DBObject {
|
||||||
|
|
||||||
|
@DBField(id = 0, type = DBDataType.INTEGER)
|
||||||
|
public int valueInt;
|
||||||
|
|
||||||
|
@DBField(id = 1, type = DBDataType.OBJECT)
|
||||||
|
public String valueObj;
|
||||||
|
|
||||||
|
@DBField(id = 2, type = DBDataType.DATABASE_OBJECT)
|
||||||
|
public IntClass valueDB;
|
||||||
|
|
||||||
|
@DBPropertySetter(id = 0, type = DBDataType.INTEGER)
|
||||||
|
public void setInt(int value) {
|
||||||
|
setProperty(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertySetter(id = 1, type = DBDataType.OBJECT)
|
||||||
|
public void setObj(String value) {
|
||||||
|
setProperty(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertySetter(id = 2, type = DBDataType.DATABASE_OBJECT)
|
||||||
|
public void setDB(IntClass value) {
|
||||||
|
setProperty(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertyGetter(id = 0, type = DBDataType.INTEGER)
|
||||||
|
public int getInt() {
|
||||||
|
return getProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertyGetter(id = 1, type = DBDataType.OBJECT)
|
||||||
|
public String getObj() {
|
||||||
|
return getProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DBPropertyGetter(id = 2, type = DBDataType.DATABASE_OBJECT)
|
||||||
|
public IntClass getDB() {
|
||||||
|
return getProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SingleClass(JCWDatabase database) {
|
||||||
|
super(database);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SingleClass(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException {
|
||||||
|
super(database, objectInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new StringJoiner(", ", SingleClass.class.getSimpleName() + "[", "]")
|
||||||
|
.add("valueInt=" + valueInt)
|
||||||
|
.add("valueObj='" + valueObj + "'")
|
||||||
|
.add("valueDB=" + valueDB)
|
||||||
|
.add("getInt=" + getInt())
|
||||||
|
.add("getObj='" + getObj() + "'")
|
||||||
|
.add("getDB=" + getDB())
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,13 @@
|
|||||||
package org.warp.jcwdb;
|
package org.warp.jcwdb;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.warp.jcwdb.ann.JCWDatabase;
|
||||||
|
import org.warp.jcwdb.ann.exampleimpl.IntClass;
|
||||||
|
import org.warp.jcwdb.ann.exampleimpl.SingleClass;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
@ -15,4 +22,50 @@ public class AppTest {
|
|||||||
public void shouldAnswerWithTrue() {
|
public void shouldAnswerWithTrue() {
|
||||||
assertTrue(true);
|
assertTrue(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void simpleClassTest() throws IOException {
|
||||||
|
long t0 = System.currentTimeMillis();
|
||||||
|
Files.deleteIfExists(Paths.get("N:\\TimedTemp\\database_s.db"));
|
||||||
|
Files.deleteIfExists(Paths.get("N:\\TimedTemp\\database_s.idx"));
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
doSingleClassTest();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void doSingleClassTest() throws IOException {
|
||||||
|
System.err.println("doSingleClassTest");
|
||||||
|
JCWDatabase db = new JCWDatabase(Paths.get("N:\\TimedTemp\\database_s.db"), Paths.get("N:\\TimedTemp\\database_s.idx"));
|
||||||
|
db.registerClass(SingleClass.class, 0);
|
||||||
|
db.registerClass(IntClass.class, 1);
|
||||||
|
SingleClass singleClass = db.loadRoot(SingleClass.class);
|
||||||
|
System.err.println("[MAIN init] singleClass="+singleClass);
|
||||||
|
|
||||||
|
singleClass.getInt();
|
||||||
|
singleClass.setInt((int) (Math.random() * 100));
|
||||||
|
|
||||||
|
singleClass.getObj();
|
||||||
|
singleClass.setObj(String.valueOf(Math.random() * 100));
|
||||||
|
|
||||||
|
if (singleClass.getDB() == null) {
|
||||||
|
System.out.println("Get db was null");
|
||||||
|
singleClass.setDB(new IntClass(db));
|
||||||
|
}
|
||||||
|
IntClass intClass = singleClass.getDB();
|
||||||
|
intClass.value = (int) (Math.random() * 100);
|
||||||
|
|
||||||
|
singleClass.valueInt = (int) (Math.random() * 100);
|
||||||
|
|
||||||
|
singleClass.valueObj = String.valueOf(Math.random() * 100);
|
||||||
|
|
||||||
|
if (singleClass.valueDB == null) {
|
||||||
|
System.out.println("Value db was null");
|
||||||
|
singleClass.valueDB = new IntClass(db);
|
||||||
|
}
|
||||||
|
singleClass.valueDB.value = (int) (Math.random() * 100);
|
||||||
|
|
||||||
|
|
||||||
|
System.err.println("[MAIN end.] singleClass="+singleClass);
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user