Skip to content

Commit 812a094

Browse files
committed
[feature] Adds ContentFilePool and companion tests
Backport from develop branch. Signed-off-by: Patrick Reinhart <[email protected]>
1 parent c5c1a0d commit 812a094

File tree

12 files changed

+506
-33
lines changed

12 files changed

+506
-33
lines changed

exist-core/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,11 @@
509509
<artifactId>junit-jupiter-params</artifactId>
510510
<scope>test</scope>
511511
</dependency>
512+
<dependency>
513+
<groupId>org.assertj</groupId>
514+
<artifactId>assertj-core</artifactId>
515+
<scope>test</scope>
516+
</dependency>
512517
<dependency>
513518
<groupId>org.easymock</groupId>
514519
<artifactId>easymock</artifactId>

exist-core/src/main/java/org/exist/util/Configuration.java

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.exist.start.Main;
3434
import org.exist.storage.lock.LockManager;
3535
import org.exist.storage.lock.LockTable;
36+
import org.exist.util.io.ContentFilePool;
3637
import org.exist.xquery.*;
3738
import org.w3c.dom.Document;
3839
import org.w3c.dom.Element;
@@ -96,6 +97,8 @@
9697

9798
import static com.evolvedbinary.j8fu.tuple.Tuple.Tuple;
9899
import static javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING;
100+
import static org.exist.util.io.ContentFilePool.PROPERTY_IN_MEMORY_SIZE;
101+
import static org.exist.util.io.VirtualTempPath.DEFAULT_IN_MEMORY_SIZE;
99102

100103

101104
public class Configuration implements ErrorHandler
@@ -107,6 +110,7 @@ public class Configuration implements ErrorHandler
107110
protected DocumentBuilder builder = null;
108111
protected HashMap<String, Object> config = new HashMap<>(); //Configuration
109112

113+
private static final String PRP_DETAILS = "{}: {}";
110114
private static final String XQUERY_CONFIGURATION_ELEMENT_NAME = "xquery";
111115
private static final String XQUERY_BUILTIN_MODULES_CONFIGURATION_MODULES_ELEMENT_NAME = "builtin-modules";
112116
private static final String XQUERY_BUILTIN_MODULES_CONFIGURATION_MODULE_ELEMENT_NAME = "module";
@@ -291,6 +295,11 @@ public Configuration(String configFilename, Optional<Path> existHomeDirname) thr
291295
configureValidation(existHomeDirname, (Element)validations.item(0));
292296
}
293297

298+
//RPC server
299+
final NodeList rpcServer = doc.getElementsByTagName("rpc-server");
300+
if (rpcServer.getLength() > 0) {
301+
configureRpcServer((Element) rpcServer.item(0));
302+
}
294303
}
295304
catch(final SAXException | IOException | ParserConfigurationException e) {
296305
LOG.error("error while reading config file: {}", configFilename, e);
@@ -1499,8 +1508,28 @@ private void configureValidation(final Optional<Path> dbHome, final Element vali
14991508
final GrammarPool gp = new GrammarPool();
15001509
config.put( XMLReaderObjectFactory.GRAMMAR_POOL, gp);
15011510
}
1502-
1503-
/**
1511+
1512+
private void configureRpcServer(final Element validation) {
1513+
final NodeList contentFile = validation.getElementsByTagName("content-file");
1514+
if (contentFile.getLength() > 0) {
1515+
final String inMemorySize = ((Element) contentFile.item(0)).getAttribute("in-memory-size");
1516+
if (inMemorySize != null) {
1517+
config.put(PROPERTY_IN_MEMORY_SIZE, parseInt(inMemorySize, DEFAULT_IN_MEMORY_SIZE));
1518+
LOG.debug(PRP_DETAILS, PROPERTY_IN_MEMORY_SIZE, config.get(PROPERTY_IN_MEMORY_SIZE));
1519+
}
1520+
}
1521+
final NodeList contentFilePool = validation.getElementsByTagName("content-file-pool");
1522+
if (contentFilePool.getLength() > 0) {
1523+
final String size = ((Element) contentFilePool.item(0)).getAttribute("size");
1524+
config.put(ContentFilePool.PROPERTY_POOL_SIZE, parseInt(size, -1));
1525+
LOG.debug(PRP_DETAILS, ContentFilePool.PROPERTY_POOL_SIZE, config.get(ContentFilePool.PROPERTY_POOL_SIZE));
1526+
final String maxIdle = ((Element) contentFilePool.item(0)).getAttribute("max-idle");
1527+
config.put(ContentFilePool.PROPERTY_POOL_MAX_IDLE, parseInt(maxIdle, 5));
1528+
LOG.debug(PRP_DETAILS, ContentFilePool.PROPERTY_POOL_MAX_IDLE, config.get(ContentFilePool.PROPERTY_POOL_MAX_IDLE));
1529+
}
1530+
}
1531+
1532+
/**
15041533
* Gets the value of a configuration attribute
15051534
*
15061535
* The value typically is specified in the conf.xml file, but can be overridden with using a System Property
@@ -1596,6 +1625,17 @@ public void removeProperty(final String name) {
15961625
config.remove(name);
15971626
}
15981627

1628+
public int getInteger(final String name) {
1629+
return getInteger(name, -1);
1630+
}
1631+
1632+
public int getInteger(final String name, final int defaultValue) {
1633+
return Optional.ofNullable(getProperty(name))
1634+
.filter(v -> v instanceof Integer)
1635+
.map(v -> (int) v)
1636+
.orElse(defaultValue);
1637+
}
1638+
15991639
/**
16001640
* Takes the passed string and converts it to a non-null <code>Boolean</code> object. If value is null, the specified default value is used.
16011641
* Otherwise, Boolean.TRUE is returned if and only if the passed string equals &quot;yes&quot; or &quot;true&quot;, ignoring case.
@@ -1633,14 +1673,6 @@ public static int parseInt(@Nullable final String value, final int defaultValue)
16331673
}
16341674
}
16351675

1636-
public int getInteger(final String name) {
1637-
return Optional.ofNullable(getProperty(name))
1638-
.filter(v -> v instanceof Integer)
1639-
.map(v -> (int)v)
1640-
.orElse(-1);
1641-
}
1642-
1643-
16441676
/**
16451677
* (non-Javadoc).
16461678
*

exist-core/src/main/java/org/exist/util/io/ContentFile.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,50 @@
2525
import java.io.ByteArrayInputStream;
2626
import java.io.IOException;
2727
import java.io.InputStream;
28+
import java.io.OutputStream;
2829

2930
/**
3031
* @author <a href="mailto:[email protected]">Patrick Reinhart</a>
3132
*/
3233
public interface ContentFile extends AutoCloseable {
3334

3435
@Override
36+
/**
37+
* Closes all resource held by the implementation as open files or off HEAP memory as example.
38+
*/
3539
void close();
3640

41+
/**
42+
* Returns the complete content as an byte array.
43+
*
44+
* @return the content as byte array
45+
*/
3746
byte[] getBytes();
3847

48+
/**
49+
* Returns the size of the conent in bytes
50+
*
51+
* @return the content size
52+
*/
3953
long size();
4054

55+
/**
56+
* Returns a new {@link InputStream} instance based on the content data.
57+
*
58+
* @return a new input stream based on the content.
59+
* @throws IOException if an error occurs accessing the content
60+
*/
4161
default InputStream newInputStream() throws IOException {
42-
return new ByteArrayInputStream(getBytes());
62+
return new ByteArrayInputStream(getBytes());
4363
}
4464

65+
/**
66+
* Returns a new {@link OutputStream} instance to write content data.
67+
* @return a new output stream for writing content
68+
* @throws IOException if an error occurs accessing the content
69+
* @throws UnsupportedOperationException if the operation is not available
70+
*/
71+
default OutputStream newOutputStream() throws IOException {
72+
throw new UnsupportedOperationException("not supported");
73+
}
4574
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* eXist-db Open Source Native XML Database
3+
* Copyright (C) 2001 The eXist-db Authors
4+
*
5+
6+
* http://www.exist-db.org
7+
*
8+
* This library is free software; you can redistribute it and/or
9+
* modify it under the terms of the GNU Lesser General Public
10+
* License as published by the Free Software Foundation; either
11+
* version 2.1 of the License, or (at your option) any later version.
12+
*
13+
* This library is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16+
* Lesser General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU Lesser General Public
19+
* License along with this library; if not, write to the Free Software
20+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21+
*/
22+
23+
package org.exist.util.io;
24+
25+
import org.apache.commons.pool2.impl.GenericObjectPool;
26+
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
27+
import org.exist.util.Configuration;
28+
29+
/**
30+
* Generic pool for {@link ContentFile} instances used to represent RPC server data up to a defined size in memory first
31+
* before storing the data in the file system using the {@link TemporaryFileManager}.
32+
*
33+
* @author <a href="mailto:[email protected]">Patrick Reinhart</a>
34+
*/
35+
public final class ContentFilePool extends GenericObjectPool<ContentFile> {
36+
/**
37+
* Variable defining the {@link ContentFile} maximum in memory stored size in bytes.
38+
*/
39+
public static final String PROPERTY_IN_MEMORY_SIZE = "rpc-server.content-file.in-memory-size";
40+
/**
41+
* Variable defining the maximum mool size holding {@link ContentFile values.}
42+
*/
43+
public static final String PROPERTY_POOL_SIZE = "rpc-server.content-file-pool.size";
44+
/**
45+
* Variable defining the maximum mool size holding {@link ContentFile values.}
46+
*/
47+
public static final String PROPERTY_POOL_MAX_IDLE = "rpc-server.content-file-pool.max-idle";
48+
49+
/**
50+
* Creates a new pool using the givem temporary file manager, configuration and maximum idle time.
51+
*
52+
* @param tempFileManager the temporary file manager used when need to swap out data bigger than max in memory size
53+
* @param config the configuration used to configure the main pool properties
54+
*/
55+
public ContentFilePool(final TemporaryFileManager tempFileManager, final Configuration config) {
56+
super(new ContentFilePoolObjectFactory(tempFileManager, toInMemorySize(config)), toPoolConfig(config));
57+
}
58+
59+
private static int toInMemorySize(final Configuration config) {
60+
return config.getInteger(PROPERTY_IN_MEMORY_SIZE, VirtualTempPath.DEFAULT_IN_MEMORY_SIZE);
61+
}
62+
63+
private static GenericObjectPoolConfig<ContentFile> toPoolConfig(final Configuration config) {
64+
final GenericObjectPoolConfig<ContentFile> poolConfig = new GenericObjectPoolConfig<>();
65+
poolConfig.setBlockWhenExhausted(false);
66+
poolConfig.setLifo(true);
67+
poolConfig.setMaxIdle(config.getInteger(PROPERTY_POOL_MAX_IDLE));
68+
poolConfig.setMaxTotal(config.getInteger(PROPERTY_POOL_SIZE));
69+
poolConfig.setJmxNameBase("org.exist.management.exist:type=ContentFilePool");
70+
return poolConfig;
71+
}
72+
73+
@Override
74+
public ContentFile borrowObject() {
75+
try {
76+
return super.borrowObject();
77+
} catch (final Exception e) {
78+
throw new IllegalStateException("Error while borrowing ContentFile", e);
79+
}
80+
}
81+
82+
@Override
83+
public void returnObject(final ContentFile obj) {
84+
if (obj == null) {
85+
return;
86+
}
87+
try {
88+
obj.close();
89+
super.returnObject(obj);
90+
} catch (final Exception e) {
91+
throw new IllegalStateException("Error while returning ContentFile", e);
92+
}
93+
}
94+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* eXist-db Open Source Native XML Database
3+
* Copyright (C) 2001 The eXist-db Authors
4+
*
5+
6+
* http://www.exist-db.org
7+
*
8+
* This library is free software; you can redistribute it and/or
9+
* modify it under the terms of the GNU Lesser General Public
10+
* License as published by the Free Software Foundation; either
11+
* version 2.1 of the License, or (at your option) any later version.
12+
*
13+
* This library is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16+
* Lesser General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU Lesser General Public
19+
* License along with this library; if not, write to the Free Software
20+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21+
*/
22+
23+
package org.exist.util.io;
24+
25+
import org.apache.commons.pool2.BasePooledObjectFactory;
26+
import org.apache.commons.pool2.PooledObject;
27+
import org.apache.commons.pool2.impl.DefaultPooledObject;
28+
29+
/**
30+
* Object factory responsible for creating {@link ContentFile} instances and wrap thos into pool objects.
31+
*
32+
* @author <a href="mailto:[email protected]">Patrick Reinhart</a>
33+
*/
34+
public final class ContentFilePoolObjectFactory extends BasePooledObjectFactory<ContentFile> {
35+
private final TemporaryFileManager tempFileManager;
36+
private final int inMemorySize;
37+
38+
/**
39+
* Creates a object factory instance using the given temporary file manager and maximum in memotx size.
40+
*
41+
* @param tempFileManager the temporary file manager used when need to swap out data bigger than max in memory size
42+
* @param inMemorySize the maximum in memory size before swappiing the data to a temporay file
43+
*/
44+
public ContentFilePoolObjectFactory(final TemporaryFileManager tempFileManager, int inMemorySize) {
45+
this.tempFileManager = tempFileManager;
46+
this.inMemorySize = inMemorySize;
47+
}
48+
49+
@Override
50+
public ContentFile create() throws Exception {
51+
return new VirtualTempPath(inMemorySize, tempFileManager);
52+
}
53+
54+
@Override
55+
public PooledObject<ContentFile> wrap(ContentFile obj) {
56+
return new DefaultPooledObject<>(obj);
57+
}
58+
}

0 commit comments

Comments
 (0)