Skip to content

Commit

Permalink
[feature] Adds ContentFilePool
Browse files Browse the repository at this point in the history
Signed-off-by: Patrick Reinhart <patrick@reini.net>
  • Loading branch information
reinhapa committed Jan 24, 2024
1 parent 4d9cc84 commit f2691ee
Show file tree
Hide file tree
Showing 9 changed files with 270 additions and 18 deletions.
10 changes: 10 additions & 0 deletions exist-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,16 @@
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
Expand Down
6 changes: 5 additions & 1 deletion exist-core/src/main/java/org/exist/util/Configuration.java
Original file line number Diff line number Diff line change
Expand Up @@ -1582,10 +1582,14 @@ public void removeProperty(final String name) {
}

public int getInteger(final String name) {
return getInteger(name, -1);
}

public int getInteger(final String name, int defaultValue) {
return Optional.ofNullable(getProperty(name))
.filter(v -> v instanceof Integer)
.map(v -> (int) v)
.orElse(-1);
.orElse(defaultValue);
}

/**
Expand Down
4 changes: 4 additions & 0 deletions exist-core/src/main/java/org/exist/util/io/ContentFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
* @author <a href="mailto:patrick@reini.net">Patrick Reinhart</a>
Expand All @@ -42,4 +43,7 @@ default InputStream newInputStream() throws IOException {
return new ByteArrayInputStream(getBytes());
}

default OutputStream newOutputStream() throws IOException {
throw new IOException("not supported");
}
}
75 changes: 75 additions & 0 deletions exist-core/src/main/java/org/exist/util/io/ContentFilePool.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* eXist-db Open Source Native XML Database
* Copyright (C) 2001 The eXist-db Authors
*
* info@exist-db.org
* http://www.exist-db.org
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

package org.exist.util.io;

import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.exist.util.Configuration;

/**
* @author <a href="mailto:patrick@reini.net">Patrick Reinhart</a>
*/
public final class ContentFilePool extends GenericObjectPool<ContentFile> {
public static final String PROPERTY_POOL_SIZE = "content-file.pool.size";
public static final String PROPERTY_IN_MEMORY_SIZE = "content-file.in-memory-size";

public ContentFilePool(final TemporaryFileManager tempFileManager, final Configuration config, final int maxIdle) {
super(new ContentFilePoolObjectFactory(tempFileManager, toInMemorySize(config)), toPoolConfig(config, maxIdle));
}

private static int toInMemorySize(Configuration config) {
return config.getInteger(PROPERTY_IN_MEMORY_SIZE, VirtualTempPath.DEFAULT_IN_MEMORY_SIZE);
}

private static GenericObjectPoolConfig<ContentFile> toPoolConfig(final Configuration config, final int maxIdle) {
final GenericObjectPoolConfig<ContentFile> poolConfig = new GenericObjectPoolConfig<>();
poolConfig.setBlockWhenExhausted(false);
poolConfig.setLifo(true);
poolConfig.setMaxIdle(maxIdle);
poolConfig.setMaxTotal(config.getInteger(PROPERTY_POOL_SIZE));
poolConfig.setJmxNameBase("org.exist.management.exist:type=VirtualTempPathPool");
return poolConfig;
}

@Override
public ContentFile borrowObject() {
try {
return super.borrowObject();
} catch (Exception e) {
throw new IllegalStateException("Error while borrowing ContentFile", e);
}
}

@Override
public void returnObject(ContentFile obj) {
if (obj == null) {
return;
}
try {
obj.close();
super.returnObject(obj);
} catch (Exception e) {
throw new IllegalStateException("Error while returning ContentFile", e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* eXist-db Open Source Native XML Database
* Copyright (C) 2001 The eXist-db Authors
*
* info@exist-db.org
* http://www.exist-db.org
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

package org.exist.util.io;

import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;

/**
* @author <a href="mailto:patrick@reini.net">Patrick Reinhart</a>
*/
public final class ContentFilePoolObjectFactory extends BasePooledObjectFactory<ContentFile> {
private final TemporaryFileManager tempFileManager;
private final int inMemorySize;

public ContentFilePoolObjectFactory(final TemporaryFileManager tempFileManager, int inMemorySize) {
this.tempFileManager = tempFileManager;
this.inMemorySize = inMemorySize;
}

@Override
public ContentFile create() throws Exception {
return new VirtualTempPath(inMemorySize, tempFileManager);
}

@Override
public PooledObject<ContentFile> wrap(ContentFile obj) {
return new DefaultPooledObject<>(obj);
}
}
44 changes: 27 additions & 17 deletions exist-core/src/main/java/org/exist/util/io/MemoryContentsImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

Expand Down Expand Up @@ -56,7 +57,7 @@ public final class MemoryContentsImpl implements MemoryContents {
/**
* To store the contents efficiently we store the first {@value #BLOCK_SIZE}
* bytes in a {@value #BLOCK_SIZE} direct {@code byte[]}. The next
* {@value #NUMBER_OF_BLOCKS} * {@value #BLOCK_SIZE} bytes go into a indirect
* {@value #NUMBER_OF_BLOCKS} * {@value #BLOCK_SIZE} bytes go into an indirect
* {@code byte[][]} that is lazily allocated.
*/
private byte[] directBlock;
Expand Down Expand Up @@ -86,14 +87,24 @@ private void initialize() {
directBlock = new byte[BLOCK_SIZE];
if (initialBlocks > 1) {
indirectBlocks = new byte[BLOCK_SIZE][];
for (int i = 0; i < initialBlocks - 1; ++i) {
indirectBlocks[i] = new byte[BLOCK_SIZE];
for (int index = 0; index < initialBlocks - 1; ++index) {
indirectBlocks[index] = new byte[BLOCK_SIZE];
}
indirectBlocksAllocated = initialBlocks - 1;
}
size = 0L;
}

private void resetValues() {
Arrays.fill(directBlock, (byte)0);
if (indirectBlocksAllocated > 0) {
for (int index = 0; index < indirectBlocksAllocated; index++) {
Arrays.fill(indirectBlocks[index], (byte)0);
}
}
size = 0L;
}

private byte[] getBlock(int currentBlock) {
if (currentBlock == 0) {
return directBlock;
Expand All @@ -117,18 +128,18 @@ private void ensureCapacity(long capacity) {
throw new AssertionError("memory values bigger than 16MB not supported");
}
if (blocksRequired > indirectBlocksAllocated) {
for (int i = indirectBlocksAllocated; i < blocksRequired; ++i) {
indirectBlocks[i] = new byte[BLOCK_SIZE];
for (int index = indirectBlocksAllocated; index < blocksRequired; ++index) {
indirectBlocks[index] = new byte[BLOCK_SIZE];
indirectBlocksAllocated += 1;
}
}
}

private ManagedLock readLock() {
private ManagedLock<ReadWriteLock> readLock() {
return ManagedLock.acquire(lock, Lock.LockMode.READ_LOCK);
}

private ManagedLock writeLock() {
private ManagedLock<ReadWriteLock> writeLock() {
return ManagedLock.acquire(lock, Lock.LockMode.WRITE_LOCK);
}

Expand All @@ -137,21 +148,21 @@ public void reset() {
if (LOG.isDebugEnabled()) {
LOG.debug("Reset content");
}
try (ManagedLock lock = writeLock()) {
initialize();
try (ManagedLock<ReadWriteLock> lock = writeLock()) {
resetValues();
}
}

@Override
public long size() {
try (ManagedLock lock = readLock()) {
try (ManagedLock<ReadWriteLock> lock = readLock()) {
return size;
}
}

@Override
public int read(byte[] dst, long position, int off, int len) {
try (ManagedLock lock = readLock()) {
try (ManagedLock<ReadWriteLock> lock = readLock()) {
if (position >= size) {
return -1;
}
Expand All @@ -173,7 +184,7 @@ public int read(byte[] dst, long position, int off, int len) {

@Override
public long transferTo(OutputStream target, long position) throws IOException {
try (ManagedLock lock = readLock()) {
try (ManagedLock<ReadWriteLock> lock = readLock()) {
long transferred = 0L;
long toTransfer = size - position;
int currentBlock = (int) (position / BLOCK_SIZE);
Expand All @@ -193,14 +204,13 @@ public long transferTo(OutputStream target, long position) throws IOException {

@Override
public int write(byte[] src, long position, int off, int len) {
try (ManagedLock lock = writeLock()) {
try (ManagedLock<ReadWriteLock> lock = writeLock()) {
ensureCapacity(position + len);
int toWrite = min(len, Integer.MAX_VALUE);
int currentBlock = (int) (position / BLOCK_SIZE);
int startIndexInBlock = (int) (position - (currentBlock * (long) BLOCK_SIZE));
int written = 0;
while (written < toWrite) {
int lengthInBlock = min(BLOCK_SIZE - startIndexInBlock, toWrite - written);
while (written < len) {
int lengthInBlock = min(BLOCK_SIZE - startIndexInBlock, len - written);
byte[] block = getBlock(currentBlock);
System.arraycopy(src, off + written, block, startIndexInBlock, lengthInBlock);
written += lengthInBlock;
Expand All @@ -215,7 +225,7 @@ public int write(byte[] src, long position, int off, int len) {

@Override
public int writeAtEnd(byte[] src, int off, int len) {
try (ManagedLock lock = writeLock()) {
try (ManagedLock<ReadWriteLock> lock = writeLock()) {
return write(src, size, off, len);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ private OutputStream initOverflowOutputStream() throws IOException {
}
}

@Override
public OutputStream newOutputStream() throws IOException {
long stamp = lock.writeLock();
try {
Expand Down
Loading

0 comments on commit f2691ee

Please sign in to comment.