Skip to content

Commit 93fcab6

Browse files
committed
[feature] Move grammar pool to <validation> section.
1 parent e7fcc3c commit 93fcab6

File tree

4 files changed

+118
-74
lines changed

4 files changed

+118
-74
lines changed

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

Lines changed: 53 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -372,8 +372,6 @@ public Configuration(@Nullable String configFilename, Optional<Path> existHomeDi
372372
configureElement(doc, XQUERY_CONFIGURATION_ELEMENT_NAME, this::configureXQuery);
373373
// Validation
374374
configureElement(doc, XMLReaderObjectFactory.CONFIGURATION_ELEMENT_NAME, element -> configureValidation(existHomePath, element));
375-
// Grammar cache
376-
configureElement(doc, GrammarPool.GRAMMAR_POOL, element -> configureGrammarCache(existHomePath, element));
377375
// RPC server
378376
configureElement(doc, "rpc-server", this::configureRpcServer);
379377
} catch (final SAXException | IOException | ParserConfigurationException e) {
@@ -1194,11 +1192,15 @@ private void configureIndexer(final Document doc, final Element indexer) throws
11941192
setProperty(IndexManager.PROPERTY_INDEXER_MODULES, modConfig);
11951193
}
11961194

1197-
private void configureGrammarCache(final Optional<Path> dbHome, final Element validation) {
1198-
configureProperty(validation, GrammarPool.ATTRIBUTE_MAXIMUM_SIZE, GrammarPool.PROPERTY_MAXIMUM_SIZE, Configuration::asInteger, null);
1199-
configureProperty(validation, GrammarPool.ATTRIBUTE_EXPIRE_AFTER_ACCESS, GrammarPool.PROPERTY_EXPIRE_AFTER_ACCESS, Configuration::asInteger, null);
1200-
1201-
setProperty(GrammarPool.GRAMMAR_POOL, new GrammarPool(getInteger(GrammarPool.PROPERTY_MAXIMUM_SIZE), getInteger(GrammarPool.PROPERTY_EXPIRE_AFTER_ACCESS)));
1195+
private void configureGrammarCache(final NodeList grammarPoolNl) {
1196+
if (grammarPoolNl.getLength() == 0) {
1197+
setProperty(GrammarPool.GRAMMAR_POOL, new GrammarPool());
1198+
} else {
1199+
final Element grammarPoolElem = (Element) grammarPoolNl.item(0);
1200+
configureProperty(grammarPoolElem, GrammarPool.ATTRIBUTE_MAXIMUM_SIZE, GrammarPool.PROPERTY_MAXIMUM_SIZE, Configuration::asInteger, null);
1201+
configureProperty(grammarPoolElem, GrammarPool.ATTRIBUTE_EXPIRE_AFTER_ACCESS, GrammarPool.PROPERTY_EXPIRE_AFTER_ACCESS, Configuration::asInteger, null);
1202+
setProperty(GrammarPool.GRAMMAR_POOL, new GrammarPool(getInteger(GrammarPool.PROPERTY_MAXIMUM_SIZE), getInteger(GrammarPool.PROPERTY_EXPIRE_AFTER_ACCESS)));
1203+
}
12021204
}
12031205

12041206
private void configureValidation(final Optional<Path> dbHome, final Element validation) {
@@ -1207,17 +1209,51 @@ private void configureValidation(final Optional<Path> dbHome, final Element vali
12071209

12081210
// Configure the Entity Resolver
12091211
final NodeList entityResolver = validation.getElementsByTagName(XMLReaderObjectFactory.CONFIGURATION_ENTITY_RESOLVER_ELEMENT_NAME);
1210-
if (entityResolver.getLength() == 0) {
1211-
return;
1212+
if (entityResolver.getLength() != 0) {
1213+
final Element elemEntityResolver = (Element) entityResolver.item(0);
1214+
setupEntityResolver(dbHome, elemEntityResolver);
12121215
}
1216+
1217+
// Configure the grammar pool
1218+
final NodeList grammarPoolNl = validation.getElementsByTagName(GrammarPool.GRAMMAR_POOL);
1219+
configureGrammarCache(grammarPoolNl);
1220+
1221+
}
1222+
1223+
private void setupEntityResolver(final Optional<Path> dbHome, final Element elemEntityResolver) {
12131224
LOG.info("Creating xmlresolver.org OASIS Catalog resolver");
12141225

1215-
final Element elemEntityResolver = (Element) entityResolver.item(0);
12161226
final NodeList nlCatalogs = elemEntityResolver.getElementsByTagName(XMLReaderObjectFactory.CONFIGURATION_CATALOG_ELEMENT_NAME);
12171227

1218-
// Determine webapps directory. SingleInstanceConfiguration cannot
1219-
// be used at this phase. Trick is to check whether dbHOME is
1220-
// pointing to a WEB-INF directory, meaning inside the war file.
1228+
final Path webappHome = getWebappHome(dbHome, nlCatalogs);
1229+
1230+
// Store all configured URIs
1231+
final List<String> catalogUris = getCatalogUris(dbHome, nlCatalogs, webappHome);
1232+
setProperty(XMLReaderObjectFactory.CATALOG_URIS, catalogUris);
1233+
1234+
setupResolver(catalogUris);
1235+
}
1236+
1237+
private void setupResolver(final List<String> catalogUris) {
1238+
// Create and Store the resolver
1239+
try {
1240+
final List<Tuple2<String, Optional<InputSource>>> catalogs = catalogUris.stream()
1241+
.map(catalogUri -> Tuple(catalogUri, Optional.<InputSource>empty()))
1242+
.toList();
1243+
final Resolver resolver = ResolverFactory.newResolver(catalogs);
1244+
setProperty(XMLReaderObjectFactory.CATALOG_RESOLVER, resolver);
1245+
} catch (final URISyntaxException e) {
1246+
LOG.error("Unable to parse catalog uri: {}", e.getMessage(), e);
1247+
}
1248+
}
1249+
1250+
/*
1251+
Determine webapps directory. SingleInstanceConfiguration cannot
1252+
be used at this phase. Trick is to check whether dbHOME is
1253+
pointing to a WEB-INF directory, meaning inside the war file.
1254+
*/
1255+
private static Path getWebappHome(Optional<Path> dbHome, NodeList nlCatalogs) {
1256+
12211257
final Path webappHome = dbHome.map(h -> {
12221258
if (FileUtils.fileName(h).endsWith("WEB-INF")) {
12231259
return h.getParent().toAbsolutePath();
@@ -1230,7 +1266,10 @@ private void configureValidation(final Optional<Path> dbHome, final Element vali
12301266
LOG.debug("Using dbHome={}", dbHome);
12311267
LOG.debug("using webappHome={}", webappHome);
12321268
}
1269+
return webappHome;
1270+
}
12331271

1272+
private static List<String> getCatalogUris(Optional<Path> dbHome, NodeList nlCatalogs, Path webappHome) {
12341273
// Get the Catalog URIs
12351274
final List<String> catalogUris = new ArrayList<>();
12361275
for (int i = 0; i < nlCatalogs.getLength(); i++) {
@@ -1252,20 +1291,7 @@ private void configureValidation(final Optional<Path> dbHome, final Element vali
12521291
catalogUris.add(uri);
12531292
}
12541293
}
1255-
1256-
// Store all configured URIs
1257-
setProperty(XMLReaderObjectFactory.CATALOG_URIS, catalogUris);
1258-
1259-
// Create and Store the resolver
1260-
try {
1261-
final List<Tuple2<String, Optional<InputSource>>> catalogs = catalogUris.stream()
1262-
.map(catalogUri -> Tuple(catalogUri, Optional.<InputSource>empty()))
1263-
.toList();
1264-
final Resolver resolver = ResolverFactory.newResolver(catalogs);
1265-
setProperty(XMLReaderObjectFactory.CATALOG_RESOLVER, resolver);
1266-
} catch (final URISyntaxException e) {
1267-
LOG.error("Unable to parse catalog uri: {}", e.getMessage(), e);
1268-
}
1294+
return catalogUris;
12691295
}
12701296

12711297
private void configureRpcServer(final Element validation) throws DatabaseConfigurationException {

exist-core/src/main/java/org/exist/validation/GrammarPool.java

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323

2424
import com.github.benmanes.caffeine.cache.Cache;
2525
import com.github.benmanes.caffeine.cache.Caffeine;
26+
import org.apache.logging.log4j.Level;
27+
import org.apache.logging.log4j.LogManager;
28+
import org.apache.logging.log4j.Logger;
2629
import org.apache.xerces.xni.grammars.Grammar;
2730
import org.apache.xerces.xni.grammars.XMLGrammarDescription;
2831
import org.apache.xerces.xni.grammars.XMLGrammarPool;
@@ -38,12 +41,14 @@
3841
*/
3942
public class GrammarPool implements XMLGrammarPool {
4043

44+
private static final Logger LOGGER = LogManager.getLogger(GrammarPool.class);
45+
4146
public static final String GRAMMAR_POOL = "grammar-pool";
4247

43-
public static final String ATTRIBUTE_MAXIMUM_SIZE = "maximum-size";
48+
public static final String ATTRIBUTE_MAXIMUM_SIZE = "size";
4449
public static final String PROPERTY_MAXIMUM_SIZE = "grammar-cache.maximum-size";
4550

46-
public static final String ATTRIBUTE_EXPIRE_AFTER_ACCESS = "expire-after-access";
51+
public static final String ATTRIBUTE_EXPIRE_AFTER_ACCESS = "expire";
4752
public static final String PROPERTY_EXPIRE_AFTER_ACCESS = "grammar-cache.expire-after-access";
4853

4954
private final Cache<GrammarKey, Grammar> grammarCache;
@@ -53,7 +58,7 @@ public class GrammarPool implements XMLGrammarPool {
5358
* Constructor. Default 128 entries, lifetime after last access is 60 minutes.
5459
*/
5560
public GrammarPool() {
56-
this(500, 60);
61+
this(-1, -1L);
5762
}
5863

5964
/**
@@ -66,14 +71,20 @@ public GrammarPool(final long maxSize, final long seconds) {
6671

6772
Caffeine<Object, Object> cafeineBuilder = Caffeine.newBuilder();
6873

69-
if(maxSize>0){
74+
if (maxSize > 0) {
7075
cafeineBuilder = cafeineBuilder.maximumSize(maxSize);
7176
}
7277

73-
if(seconds>0){
78+
if (seconds > 0L) {
7479
cafeineBuilder.expireAfterAccess(seconds, TimeUnit.SECONDS);
7580
}
7681

82+
final String sizeTxt =maxSize > 0 ? String.valueOf(maxSize) : "unlimited";
83+
final String expireTxt = seconds > 0L ? seconds + " seconds" : "infinite";
84+
85+
final Level level = seconds > 0L && maxSize > 0 ? Level.INFO : Level.WARN;
86+
LOGGER.log(level, "Grammar cache: size={}, lifetime={}", sizeTxt, expireTxt);
87+
7788
grammarCache = cafeineBuilder.build();
7889
}
7990

@@ -91,6 +102,7 @@ public Grammar[] retrieveInitialGrammarSet(final String grammarType) {
91102
@Override
92103
public void cacheGrammars(final String grammarType, final Grammar[] grammars) {
93104
if (isLocked || grammars == null) return;
105+
94106
Arrays.stream(grammars).forEach(grammar -> {
95107
final XMLGrammarDescription desc = grammar.getGrammarDescription();
96108
grammarCache.put(new GrammarKey(desc), grammar);
@@ -115,6 +127,7 @@ public void unlockPool() {
115127
@Override
116128
public void clear() {
117129
if (isLocked) return;
130+
118131
grammarCache.invalidateAll();
119132
}
120133

exist-distribution/src/main/config/conf.xml

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
serializer
2626
transformer
2727
validation
28-
grammar-pool
28+
grammar-pool
2929
xquery
3030
builtin-modules
3131
module
@@ -903,18 +903,23 @@
903903
<entity-resolver>
904904
<catalog uri="${WEBAPP_HOME}/WEB-INF/catalog.xml"/>
905905
</entity-resolver>
906-
</validation>
907906

908-
<!--
909-
Specify configure the characteristics of the grammar pool.
910-
- maximum-size
911-
Specifies the maximum number of entries the cache may contain.
912-
When zero, grammars will be evicted immediately after being loaded into the cache.
913-
- expire-after-access
914-
The time (in seconds) after which an unused grammar should
915-
expire and be removed from the grammar pool.
916-
-->
917-
<grammar-pool maximum-size="500" expire-after-access="3600"/>
907+
<!--
908+
Configure the characteristics of the parser grammar pool. The grammar pool contains
909+
compiled versions of XSD and DTD files.
910+
911+
- size
912+
Specifies the maximum number of entries the cache may contain.
913+
Value 0: grammars will be evicted immediately after being loaded into the cache.
914+
Value -1: no maximum for the number of cached grammars.
915+
916+
- expire
917+
The time (in seconds) after which an unused grammar should expire and be removed
918+
from the grammar pool.
919+
Value -1: grammars will not be removed over time.
920+
-->
921+
<grammar-pool size="500" expire="3600"/>
922+
</validation>
918923

919924
<!--
920925
Define modules that contain xQuery functions.

schema/conf.xsd

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,6 +1126,35 @@
11261126
</xs:sequence>
11271127
</xs:complexType>
11281128
</xs:element>
1129+
<xs:element name="grammar-pool">
1130+
<xs:complexType>
1131+
<xs:attribute name="size">
1132+
<xs:annotation>
1133+
<xs:documentation>
1134+
Specifies the maximum number of entries the cache may contain.
1135+
</xs:documentation>
1136+
</xs:annotation>
1137+
<xs:simpleType>
1138+
<xs:restriction base="xs:integer">
1139+
<xs:minInclusive value="-1"/>
1140+
</xs:restriction>
1141+
</xs:simpleType>
1142+
</xs:attribute>
1143+
<xs:attribute name="expire">
1144+
<xs:annotation>
1145+
<xs:documentation>
1146+
The time (in seconds) after which an unused grammar should
1147+
expire and be removed from the grammar pool.
1148+
</xs:documentation>
1149+
</xs:annotation>
1150+
<xs:simpleType>
1151+
<xs:restriction base="xs:integer">
1152+
<xs:minInclusive value="-1"/>
1153+
</xs:restriction>
1154+
</xs:simpleType>
1155+
</xs:attribute>
1156+
</xs:complexType>
1157+
</xs:element>
11291158
</xs:sequence>
11301159
<xs:attribute name="mode" default="auto">
11311160
<xs:annotation>
@@ -1149,36 +1178,7 @@
11491178
</xs:attribute>
11501179
</xs:complexType>
11511180
</xs:element>
1152-
<xs:element name="grammar-pool">
1153-
<xs:complexType>
1154-
<xs:attribute name="maximum-size" default="500">
1155-
<xs:annotation>
1156-
<xs:documentation>
1157-
Specifies the maximum number of entries the cache may contain.
1158-
When zero, grammars will be evicted immediately after being loaded into the cache.
1159-
</xs:documentation>
1160-
</xs:annotation>
1161-
<xs:simpleType>
1162-
<xs:restriction base="xs:integer">
1163-
<xs:minInclusive value="0"/>
1164-
</xs:restriction>
1165-
</xs:simpleType>
1166-
</xs:attribute>
1167-
<xs:attribute name="expire-after-access" default="3600">
1168-
<xs:annotation>
1169-
<xs:documentation>
1170-
The time (in seconds) after which an unused grammar should
1171-
expire and be removed from the grammar pool.
1172-
</xs:documentation>
1173-
</xs:annotation>
1174-
<xs:simpleType>
1175-
<xs:restriction base="xs:integer">
1176-
<xs:minInclusive value="0"/>
1177-
</xs:restriction>
1178-
</xs:simpleType>
1179-
</xs:attribute>
1180-
</xs:complexType>
1181-
</xs:element>
1181+
11821182

11831183
<xs:element name="xquery">
11841184
<xs:complexType>

0 commit comments

Comments
 (0)