Skip to content

Commit e371efd

Browse files
authored
Merge pull request #3897 from adamretter/bugfix/close-sql-connection-from-library-module
Cleanup all module contexts
2 parents 3ec8b12 + 47bb890 commit e371efd

File tree

15 files changed

+1046
-185
lines changed

15 files changed

+1046
-185
lines changed

exist-core/pom.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,7 @@
680680
<exclude>src/test/java/org/exist/util/CollectionOfArrayIteratorTest.java</exclude>
681681
<exclude>src/main/java/org/exist/xquery/Cardinality.java</exclude>
682682
<exclude>src/test/java/org/exist/xquery/ImportModuleTest.java</exclude>
683+
<exclude>src/test/java/org/exist/xquery/XQueryContextAttributesTest.java</exclude>
683684
<exclude>src/main/java/org/exist/xquery/functions/map/MapType.java</exclude>
684685
<exclude>src/test/java/org/exist/xquery/functions/session/AbstractSessionTest.java</exclude>
685686
<exclude>src/test/java/org/exist/xquery/functions/xmldb/AbstractXMLDBTest.java</exclude>
@@ -819,6 +820,7 @@ The original license statement is also included below.]]></preamble>
819820
<include>src/test/java/org/exist/util/CollectionOfArrayIteratorTest.java</include>
820821
<include>src/main/java/org/exist/xquery/Cardinality.java</include>
821822
<include>src/test/java/org/exist/xquery/ImportModuleTest.java</include>
823+
<include>src/test/java/org/exist/xquery/XQueryContextAttributesTest.java</include>
822824
<include>src/main/java/org/exist/xquery/functions/map/MapType.java</include>
823825
<include>src/test/java/org/exist/xquery/functions/session/AbstractSessionTest.java</include>
824826
<include>src/test/java/org/exist/xquery/functions/xmldb/AbstractXMLDBTest.java</include>

exist-core/src/main/java/org/exist/xquery/XQueryContext.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ public class XQueryContext implements BinaryValueManager, Context {
209209
protected XQueryWatchDog watchdog;
210210

211211
/**
212-
* Loaded modules.
212+
* Loaded modules within this module.
213213
*
214214
* The format of the map is: <code>Map&lt;NamespaceURI, Modules&gt;</code>.
215215
*/
@@ -1402,7 +1402,13 @@ public void reset(final boolean keepGlobals) {
14021402
watchdog.reset();
14031403
}
14041404

1405-
for (final Module[] modules : allModules.values()) {
1405+
/*
1406+
NOTE: we use `modules` (and not `allModules`) here so as to only reset
1407+
the modules of this module.
1408+
The inner call to `module.reset` will be called on sub-modules
1409+
which in-turn will reset their modules, and so on.
1410+
*/
1411+
for (final Module[] modules : modules.values()) {
14061412
for (final Module module : modules) {
14071413
module.reset(this, keepGlobals);
14081414
}
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
/*
2+
* Copyright (C) 2014, Evolved Binary Ltd
3+
*
4+
* This file was originally ported from FusionDB to eXist-db by
5+
* Evolved Binary, for the benefit of the eXist-db Open Source community.
6+
* Only the ported code as it appears in this file, at the time that
7+
* it was contributed to eXist-db, was re-licensed under The GNU
8+
* Lesser General Public License v2.1 only for use in eXist-db.
9+
*
10+
* This license grant applies only to a snapshot of the code as it
11+
* appeared when ported, it does not offer or infer any rights to either
12+
* updates of this source code or access to the original source code.
13+
*
14+
* The GNU Lesser General Public License v2.1 only license follows.
15+
*
16+
* ---------------------------------------------------------------------
17+
*
18+
* Copyright (C) 2014, Evolved Binary Ltd
19+
*
20+
* This library is free software; you can redistribute it and/or
21+
* modify it under the terms of the GNU Lesser General Public
22+
* License as published by the Free Software Foundation; version 2.1.
23+
*
24+
* This library is distributed in the hope that it will be useful,
25+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
26+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27+
* Lesser General Public License for more details.
28+
*
29+
* You should have received a copy of the GNU Lesser General Public
30+
* License along with this library; if not, write to the Free Software
31+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
32+
*/
33+
package org.exist.xquery;
34+
35+
import com.evolvedbinary.j8fu.function.Function2E;
36+
import com.evolvedbinary.j8fu.tuple.Tuple2;
37+
import org.exist.EXistException;
38+
import org.exist.collections.Collection;
39+
import org.exist.collections.triggers.TriggerException;
40+
import org.exist.dom.persistent.BinaryDocument;
41+
import org.exist.security.PermissionDeniedException;
42+
import org.exist.source.DBSource;
43+
import org.exist.source.Source;
44+
import org.exist.source.StringSource;
45+
import org.exist.storage.BrokerPool;
46+
import org.exist.storage.DBBroker;
47+
import org.exist.storage.XQueryPool;
48+
import org.exist.storage.lock.Lock;
49+
import org.exist.storage.txn.Txn;
50+
import org.exist.test.ExistEmbeddedServer;
51+
import org.exist.util.LockException;
52+
import org.exist.xmldb.XmldbURI;
53+
import org.exist.xquery.value.Sequence;
54+
import org.junit.ClassRule;
55+
import org.junit.Test;
56+
57+
import java.io.IOException;
58+
import java.io.InputStream;
59+
import java.util.Optional;
60+
import java.util.Properties;
61+
62+
import static com.evolvedbinary.j8fu.tuple.Tuple.Tuple;
63+
import static org.junit.Assert.*;
64+
65+
/**
66+
* @author <a href="mailto:[email protected]">Adam Retter</a>
67+
*/
68+
public class XQueryContextAttributesTest {
69+
70+
@ClassRule
71+
public static final ExistEmbeddedServer existEmbeddedServer = new ExistEmbeddedServer(true, true);
72+
73+
@Test
74+
public void attributesOfMainModuleContextCleared() throws EXistException, LockException, TriggerException, PermissionDeniedException, IOException, XPathException {
75+
final BrokerPool brokerPool = existEmbeddedServer.getBrokerPool();
76+
try (final DBBroker broker = brokerPool.get(Optional.of(brokerPool.getSecurityManager().getSystemSubject()));
77+
final Txn transaction = brokerPool.getTransactionManager().beginTransaction()) {
78+
79+
final XmldbURI mainQueryUri = XmldbURI.create("/db/query1.xq");
80+
final Source mainQuery = new StringSource("<not-important/>");
81+
final DBSource mainQuerySource = storeQuery(broker, transaction, mainQueryUri, mainQuery);
82+
83+
final XQueryContext escapedMainQueryContext = withCompiledQuery(broker, mainQuerySource, mainCompiledQuery -> {
84+
final XQueryContext mainQueryContext = mainCompiledQuery.getContext();
85+
86+
mainQueryContext.setAttribute("attr1", "value1");
87+
mainQueryContext.setAttribute("attr2", "value2");
88+
89+
// execute the query
90+
final Sequence result = executeQuery(broker, mainCompiledQuery);
91+
assertEquals(1, result.getItemCount());
92+
93+
// intentionally escape the context from the lambda
94+
return mainQueryContext;
95+
});
96+
97+
assertNull(escapedMainQueryContext.getAttribute("attr1"));
98+
assertNull(escapedMainQueryContext.getAttribute("attr2"));
99+
assertTrue(escapedMainQueryContext.attributes.isEmpty());
100+
101+
transaction.commit();
102+
}
103+
}
104+
105+
@Test
106+
public void attributesOfLibraryModuleContextCleared() throws EXistException, LockException, TriggerException, PermissionDeniedException, IOException, XPathException {
107+
final BrokerPool brokerPool = existEmbeddedServer.getBrokerPool();
108+
try (final DBBroker broker = brokerPool.get(Optional.of(brokerPool.getSecurityManager().getSystemSubject()));
109+
final Txn transaction = brokerPool.getTransactionManager().beginTransaction()) {
110+
111+
final XmldbURI libraryQueryUri = XmldbURI.create("/db/mod1.xqm");
112+
final Source libraryQuery = new StringSource(
113+
"module namespace mod1 = 'http://mod1';\n" +
114+
"declare function mod1:f1() { <not-important/> };"
115+
);
116+
storeQuery(broker, transaction, libraryQueryUri, libraryQuery);
117+
118+
final XmldbURI mainQueryUri = XmldbURI.create("/db/query1.xq");
119+
final Source mainQuery = new StringSource(
120+
"import module namespace mod1 = 'http://mod1' at 'xmldb:exist://" + libraryQueryUri + "';\n" +
121+
"mod1:f1()"
122+
);
123+
final DBSource mainQuerySource = storeQuery(broker, transaction, mainQueryUri, mainQuery);
124+
125+
final Tuple2<XQueryContext, ModuleContext> escapedContexts = withCompiledQuery(broker, mainQuerySource, mainCompiledQuery -> {
126+
final XQueryContext mainQueryContext = mainCompiledQuery.getContext();
127+
128+
// get the context of the library module
129+
final Module[] libraryModules = mainQueryContext.getModules("http://mod1");
130+
assertEquals(1, libraryModules.length);
131+
assertTrue(libraryModules[0] instanceof ExternalModule);
132+
final ExternalModule libraryModule = (ExternalModule) libraryModules[0];
133+
final XQueryContext libraryQueryContext = libraryModule.getContext();
134+
assertTrue(libraryQueryContext instanceof ModuleContext);
135+
136+
libraryQueryContext.setAttribute("attr1", "value1");
137+
libraryQueryContext.setAttribute("attr2", "value2");
138+
139+
// execute the query
140+
final Sequence result = executeQuery(broker, mainCompiledQuery);
141+
assertEquals(1, result.getItemCount());
142+
143+
// intentionally escape the contexts from the lambda
144+
return Tuple(mainQueryContext, (ModuleContext) libraryQueryContext);
145+
});
146+
147+
final XQueryContext escapedMainQueryContext = escapedContexts._1;
148+
final ModuleContext escapedLibraryQueryContext = escapedContexts._2;
149+
assertTrue(escapedMainQueryContext != escapedLibraryQueryContext);
150+
151+
assertNull(escapedMainQueryContext.getAttribute("attr1"));
152+
assertNull(escapedMainQueryContext.getAttribute("attr2"));
153+
assertTrue(escapedMainQueryContext.attributes.isEmpty());
154+
155+
assertNull(escapedLibraryQueryContext.getAttribute("attr1"));
156+
assertNull(escapedLibraryQueryContext.getAttribute("attr2"));
157+
assertTrue(escapedLibraryQueryContext.attributes.isEmpty());
158+
159+
transaction.commit();
160+
}
161+
}
162+
163+
private static DBSource storeQuery(final DBBroker broker, final Txn transaction, final XmldbURI uri, final Source source) throws IOException, PermissionDeniedException, TriggerException, LockException, EXistException {
164+
try (final InputStream is = source.getInputStream()) {
165+
try (final Collection collection = broker.openCollection(uri.removeLastSegment(), Lock.LockMode.WRITE_LOCK)) {
166+
final BinaryDocument doc = collection.addBinaryResource(transaction, broker, uri.lastSegment(), is, "application/xquery", -1);
167+
168+
return new DBSource(broker, doc, false);
169+
}
170+
}
171+
}
172+
173+
private static <T> T withCompiledQuery(final DBBroker broker, final Source source, final Function2E<CompiledXQuery, T, XPathException, PermissionDeniedException> op) throws XPathException, PermissionDeniedException, IOException {
174+
final BrokerPool pool = broker.getBrokerPool();
175+
final XQuery xqueryService = pool.getXQueryService();
176+
final XQueryPool xqueryPool = pool.getXQueryPool();
177+
final CompiledXQuery compiledQuery = compileQuery(broker, xqueryService, xqueryPool, source);
178+
try {
179+
return op.apply(compiledQuery);
180+
} finally {
181+
if (compiledQuery != null) {
182+
xqueryPool.returnCompiledXQuery(source, compiledQuery);
183+
}
184+
}
185+
}
186+
187+
private static CompiledXQuery compileQuery(final DBBroker broker, final XQuery xqueryService, final XQueryPool xqueryPool, final Source query) throws PermissionDeniedException, XPathException, IOException {
188+
CompiledXQuery compiled = xqueryPool.borrowCompiledXQuery(broker, query);
189+
XQueryContext context;
190+
if (compiled == null) {
191+
context = new XQueryContext(broker.getBrokerPool());
192+
} else {
193+
context = compiled.getContext();
194+
context.prepareForReuse();
195+
}
196+
197+
if (compiled == null) {
198+
compiled = xqueryService.compile(broker, context, query);
199+
} else {
200+
compiled.getContext().updateContext(context);
201+
context.getWatchDog().reset();
202+
}
203+
204+
return compiled;
205+
}
206+
207+
static Sequence executeQuery(final DBBroker broker, final CompiledXQuery compiledXQuery) throws PermissionDeniedException, XPathException {
208+
final BrokerPool pool = broker.getBrokerPool();
209+
final XQuery xqueryService = pool.getXQueryService();
210+
return xqueryService.execute(broker, compiledXQuery, null, new Properties());
211+
}
212+
}

extensions/modules/sql/pom.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@
144144
<exclude>src/test/java/org/exist/xquery/modules/sql/ConnectionIT.java</exclude>
145145
<exclude>src/test/java/org/exist/xquery/modules/sql/ConnectionPoolIT.java</exclude>
146146
<exclude>src/test/java/org/exist/xquery/modules/sql/H2DatabaseResource.java</exclude>
147+
<exclude>src/test/java/org/exist/xquery/modules/sql/ImplicitConnectionCloseIT.java</exclude>
147148
<exclude>src/test/java/org/exist/xquery/modules/sql/JndiConnectionIT.java</exclude>
148149
<exclude>src/test/java/org/exist/xquery/modules/sql/Util.java</exclude>
149150
</excludes>
@@ -162,6 +163,7 @@
162163
<include>src/test/java/org/exist/xquery/modules/sql/ConnectionIT.java</include>
163164
<include>src/test/java/org/exist/xquery/modules/sql/ConnectionPoolIT.java</include>
164165
<include>src/test/java/org/exist/xquery/modules/sql/H2DatabaseResource.java</include>
166+
<include>src/test/java/org/exist/xquery/modules/sql/ImplicitConnectionCloseIT.java</include>
165167
<include>src/test/java/org/exist/xquery/modules/sql/JndiConnectionIT.java</include>
166168
<include>src/test/java/org/exist/xquery/modules/sql/Util.java</include>
167169
</includes>

extensions/modules/sql/src/main/java/org/exist/xquery/modules/sql/CloseConnectionFunction.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,15 @@
3535
import org.apache.logging.log4j.LogManager;
3636
import org.apache.logging.log4j.Logger;
3737

38-
import org.exist.xquery.BasicFunction;
39-
import org.exist.xquery.FunctionSignature;
40-
import org.exist.xquery.XPathException;
41-
import org.exist.xquery.XQueryContext;
38+
import org.exist.dom.QName;
39+
import org.exist.xquery.*;
4240
import org.exist.xquery.value.*;
4341

4442
import java.sql.Connection;
4543
import java.sql.SQLException;
4644

4745
import static org.exist.xquery.FunctionDSL.*;
48-
import static org.exist.xquery.modules.sql.SQLModule.functionSignature;
46+
import static org.exist.xquery.modules.sql.SQLModule.*;
4947

5048

5149
/**
@@ -102,4 +100,8 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro
102100
throw new XPathException(this, "Unable to close connection with handle: " + connectionUid + ". " + e.getMessage());
103101
}
104102
}
103+
104+
private static FunctionSignature functionSignature(final String name, final String description, final FunctionReturnSequenceType returnType, final FunctionParameterSequenceType... paramTypes) {
105+
return FunctionDSL.functionSignature(new QName(name, NAMESPACE_URI, PREFIX), description, returnType, paramTypes);
106+
}
105107
}

0 commit comments

Comments
 (0)