Skip to content

Commit 98562f0

Browse files
authored
Merge pull request #3890 from evolvedbinary/feature/sql-explicit-close
2 parents 57d32e2 + 1abc74e commit 98562f0

File tree

11 files changed

+457
-143
lines changed

11 files changed

+457
-143
lines changed

exist-core/src/main/java/org/exist/xquery/modules/ModuleUtils.java

Lines changed: 106 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import java.util.Map.Entry;
3131
import java.util.concurrent.locks.ReadWriteLock;
3232
import java.util.concurrent.locks.ReentrantReadWriteLock;
33+
import javax.annotation.Nullable;
3334
import javax.xml.transform.Source;
3435
import javax.xml.transform.sax.SAXSource;
3536

@@ -314,32 +315,82 @@ public synchronized ReentrantReadWriteLock getLock(final String contextMapName)
314315
return lock;
315316
}
316317
}
317-
318+
318319
/**
319-
* Retrieves a previously stored Object from the Context of an XQuery.
320+
* Stores an Object into the Context of an XQuery.
320321
*
321-
* @param context The Context of the XQuery containing the Object
322-
* @param contextMapName DOCUMENT ME!
323-
* @param objectUID The UID of the Object to retrieve from the Context of the XQuery
324-
* @param <T> class of the object stored in the context
325-
* @return the object stored in the context or null
326-
*/
327-
public static <T> T retrieveObjectFromContextMap(XQueryContext context, String contextMapName, long objectUID) {
328-
try(final ManagedLock<ReadWriteLock> readLock = ManagedLock.acquire(contextMapLocks.getLock(contextMapName), LockMode.READ_LOCK)){
329-
// get the existing object map from the context
330-
final Map<Long, T> map = (HashMap<Long, T>)context.getAttribute(contextMapName);
331-
332-
if(map == null) {
333-
return null;
322+
* @param context The Context of the XQuery to store the Object in
323+
* @param contextMapName The name of the context map
324+
* @param o The Object to store
325+
* @param <T> the type of the object being stored
326+
*
327+
* @return A unique ID representing the Object
328+
*/
329+
public static <T> long storeObjectInContextMap(final XQueryContext context, final String contextMapName, final T o) {
330+
return modifyContextMap(context, contextMapName, contextMap -> {
331+
// get an id for the map
332+
long uid = 0;
333+
while (uid == 0 || contextMap.keySet().contains(uid)) {
334+
uid = getUID();
334335
}
335336

336-
// get the connection
337-
return map.get(objectUID);
338-
}
337+
// place the object in the map
338+
contextMap.put(uid, o);
339+
340+
return uid;
341+
});
339342
}
340-
341-
public static <T> void modifyContextMap(XQueryContext context, String contextMapName, ContextMapModifier<T> modifier) {
342-
try(final ManagedLock<ReadWriteLock> writeLock = ManagedLock.acquire(contextMapLocks.getLock(contextMapName), LockMode.WRITE_LOCK)) {
343+
344+
/**
345+
* Retrieves a previously stored Object from the Context of an XQuery.
346+
*
347+
* @param context The Context of the XQuery containing the Object
348+
* @param contextMapName The name of the context map
349+
* @param objectUID The UID of the Object to retrieve from the Context of the XQuery
350+
*
351+
* @param <T> the type of the object being retrieved
352+
*
353+
* @return the object stored in the context or null
354+
*/
355+
public static @Nullable <T> T retrieveObjectFromContextMap(final XQueryContext context, final String contextMapName, final long objectUID) {
356+
return readContextMap(context, contextMapName, contextMap -> {
357+
// get the object
358+
return (T) contextMap.get(objectUID);
359+
});
360+
}
361+
362+
/**
363+
* Removes a previously stored Object from the Context of an XQuery.
364+
*
365+
* @param context The Context of the XQuery containing the Object
366+
* @param contextMapName The name of the context map
367+
* @param objectUID The UID of the Object to remove from the Context of the XQuery
368+
*
369+
* @param <T> the type of the object being removed
370+
*
371+
* @return the object that was removed from the context or null if there was no object for the UID
372+
*/
373+
public static @Nullable <T> T removeObjectFromContextMap(final XQueryContext context, final String contextMapName, final long objectUID) {
374+
return modifyContextMap(context, contextMapName, contextMap -> {
375+
// get the object
376+
return (T) contextMap.remove(objectUID);
377+
});
378+
}
379+
380+
/**
381+
* Modify a context map.
382+
*
383+
* @param context the XQuery context
384+
* @param contextMapName The name of the context map
385+
* @param modifier the modification function
386+
*
387+
* @param <T> the type of the value in the map
388+
* @param <U> the type of the return value of the modifier function
389+
*
390+
* @return the result of the modification function
391+
*/
392+
public static @Nullable <T, U> U modifyContextMap(final XQueryContext context, final String contextMapName, final ContextMapModifier<T, U> modifier) {
393+
try (final ManagedLock<ReadWriteLock> writeLock = ManagedLock.acquire(contextMapLocks.getLock(contextMapName), LockMode.WRITE_LOCK)) {
343394
// get the existing map from the context
344395
Map<Long, T> map = (Map<Long, T>)context.getAttribute(contextMapName);
345396
if(map == null) {
@@ -349,12 +400,24 @@ public static <T> void modifyContextMap(XQueryContext context, String contextMap
349400
}
350401

351402
//modify the map
352-
modifier.modify(map);
403+
return modifier.modify(map);
353404
}
354405
}
355406

407+
/**
408+
* Read a context map.
409+
*
410+
* @param context the XQuery context
411+
* @param contextMapName The name of the context map
412+
* @param reader the reader function
413+
*
414+
* @param <T> the type of the value in the map
415+
* @param <U> the type of the return value of the reader function
416+
*
417+
* @return the result of the reader function
418+
*/
356419
public static <T, U> U readContextMap(final XQueryContext context, final String contextMapName, final ContextMapReader<T, U> reader) {
357-
try(final ManagedLock<ReadWriteLock> readLock = ManagedLock.acquire(contextMapLocks.getLock(contextMapName), LockMode.READ_LOCK)) {
420+
try (final ManagedLock<ReadWriteLock> readLock = ManagedLock.acquire(contextMapLocks.getLock(contextMapName), LockMode.READ_LOCK)) {
358421
// get the existing map from the context
359422
Map<Long, T> map = (Map<Long, T>)context.getAttribute(contextMapName);
360423
if (map == null) {
@@ -367,64 +430,37 @@ public static <T, U> U readContextMap(final XQueryContext context, final String
367430
}
368431

369432
@FunctionalInterface
370-
public interface ContextMapModifier<T> {
371-
void modify(Map<Long, T> map);
433+
public interface ContextMapModifier<T, U> {
434+
U modify(final Map<Long, T> map);
372435
}
373436

374437
@FunctionalInterface
375-
public interface ContextMapReader<T, U> {
376-
U read(Map<Long, T> map);
377-
}
378-
379-
public static abstract class ContextMapEntryModifier<T> implements ContextMapModifier<T> {
380-
438+
public interface ContextMapModifierWithoutResult<T> extends ContextMapModifier<T, Void> {
381439
@Override
382-
public void modify(Map<Long, T> map) {
383-
for(final Entry<Long, T> entry : map.entrySet()) {
384-
modify(entry);
385-
}
440+
default Void modify(final Map<Long, T> map) {
441+
modifyWithoutResult(map);
442+
return null;
386443
}
387-
388-
public abstract void modify(Entry<Long, T> entry);
389-
}
390444

391-
/**
392-
* Stores an Object in the Context of an XQuery.
393-
*
394-
* @param context The Context of the XQuery to store the Object in
395-
* @param contextMapName The name of the context map
396-
* @param o The Object to store
397-
* @param <T> the class of the object being stored
398-
* @return A unique ID representing the Object
399-
*/
400-
public static <T> long storeObjectInContextMap(final XQueryContext context, final String contextMapName, final T o) {
401-
402-
try(final ManagedLock<ReadWriteLock> writeLock = ManagedLock.acquire(contextMapLocks.getLock(contextMapName), LockMode.WRITE_LOCK)) {
403-
404-
// get the existing map from the context
405-
Map<Long, T> map = (Map<Long, T>)context.getAttribute(contextMapName);
445+
void modifyWithoutResult(final Map<Long, T> map);
446+
}
406447

407-
if (map == null) {
408-
// if there is no map, create a new one
409-
map = new HashMap<>();
410-
}
448+
@FunctionalInterface
449+
public interface ContextMapReader<T, U> {
450+
U read(final Map<Long, T> map);
451+
}
411452

412-
// get an id for the map
413-
long uid = 0;
414-
while (uid == 0 || map.keySet().contains(uid)) {
415-
uid = getUID();
453+
public static abstract class ContextMapEntryModifier<T> implements ContextMapModifierWithoutResult<T> {
454+
@Override
455+
public void modifyWithoutResult(final Map<Long, T> map) {
456+
for(final Entry<Long, T> entry : map.entrySet()) {
457+
modifyEntry(entry);
416458
}
417-
418-
// place the object in the map
419-
map.put(uid, o);
420-
421-
// store the map back in the context
422-
context.setAttribute(contextMapName, map);
423-
424-
return uid;
425459
}
460+
461+
public abstract void modifyEntry(final Entry<Long, T> entry);
426462
}
427-
463+
428464
private static long getUID() {
429465
final BigInteger bi = new BigInteger(64, random);
430466
return bi.longValue();

extensions/modules/mail/src/main/java/org/exist/xquery/modules/mail/MailModule.java

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
import javax.mail.Store;
4141
import org.exist.xquery.modules.ModuleUtils;
4242
import org.exist.xquery.modules.ModuleUtils.ContextMapEntryModifier;
43-
import org.exist.xquery.modules.ModuleUtils.ContextMapModifier;
43+
import org.exist.xquery.modules.ModuleUtils.ContextMapModifierWithoutResult;
4444

4545
/**
4646
* eXist Mail Module Extension
@@ -186,7 +186,7 @@ static long storeStore(XQueryContext context, Store store) {
186186
*/
187187
static void removeStore(XQueryContext context, final long storeHandle) {
188188

189-
ModuleUtils.modifyContextMap(context, MailModule.STORES_CONTEXTVAR, (ContextMapModifier<Store>) map -> map.remove(storeHandle));
189+
ModuleUtils.modifyContextMap(context, MailModule.STORES_CONTEXTVAR, (ContextMapModifierWithoutResult<Store>) map -> map.remove(storeHandle));
190190

191191
//update the context
192192
//context.setXQueryContextVar(MailModule.STORES_CONTEXTVAR, stores);
@@ -200,15 +200,15 @@ static void removeStore(XQueryContext context, final long storeHandle) {
200200
private static void closeAllStores(XQueryContext context) {
201201
ModuleUtils.modifyContextMap(context, MailModule.STORES_CONTEXTVAR, new ContextMapEntryModifier<Store>(){
202202
@Override
203-
public void modify(Map<Long, Store> map) {
204-
super.modify(map);
203+
public void modifyWithoutResult(final Map<Long, Store> map) {
204+
super.modifyWithoutResult(map);
205205

206206
//remove all stores from map
207207
map.clear();
208208
}
209209

210210
@Override
211-
public void modify(Entry<Long, Store> entry) {
211+
public void modifyEntry(final Entry<Long, Store> entry) {
212212
final Store store = entry.getValue();
213213
try {
214214
// close the store
@@ -258,14 +258,14 @@ static long storeFolder(XQueryContext context, Folder folder) {
258258
*/
259259
static void removeFolder(final XQueryContext context, final long folderHandle) {
260260

261-
ModuleUtils.modifyContextMap(context, MailModule.FOLDERS_CONTEXTVAR, (ContextMapModifier<Folder>) map -> {
261+
ModuleUtils.modifyContextMap(context, MailModule.FOLDERS_CONTEXTVAR, (ContextMapModifierWithoutResult<Folder>) map -> {
262262

263263
//remove the message lists for the folder
264-
ModuleUtils.modifyContextMap(context, MailModule.FOLDERMSGLISTS_CONTEXTVAR, (ContextMapModifier<Map<Long, Message[]>>) map12 -> {
264+
ModuleUtils.modifyContextMap(context, MailModule.FOLDERMSGLISTS_CONTEXTVAR, (ContextMapModifierWithoutResult<Map<Long, Message[]>>) map12 -> {
265265

266266
final Map<Long, Message[]> folderMsgList = map12.get(folderHandle);
267267

268-
ModuleUtils.modifyContextMap(context, MailModule.MSGLISTS_CONTEXTVAR, (ContextMapModifier<Message[]>) map1 -> folderMsgList.keySet().forEach(map1::remove));
268+
ModuleUtils.modifyContextMap(context, MailModule.MSGLISTS_CONTEXTVAR, (ContextMapModifierWithoutResult<Message[]>) map1 -> folderMsgList.keySet().forEach(map1::remove));
269269

270270
//remove the folder message kist
271271
map12.remove(folderHandle);
@@ -286,15 +286,15 @@ private static void closeAllFolders(XQueryContext context) {
286286
ModuleUtils.modifyContextMap(context, MailModule.FOLDERS_CONTEXTVAR, new ContextMapEntryModifier<Folder>(){
287287

288288
@Override
289-
public void modify(Map<Long, Folder> map) {
290-
super.modify(map);
289+
public void modifyWithoutResult(final Map<Long, Folder> map) {
290+
super.modifyWithoutResult(map);
291291

292292
//remove all from the folders map
293293
map.clear();
294294
}
295295

296296
@Override
297-
public void modify(Entry<Long, Folder> entry) {
297+
public void modifyEntry(final Entry<Long, Folder> entry) {
298298
final Folder folder = entry.getValue();
299299

300300
//close the folder
@@ -340,7 +340,7 @@ static long storeMessageList(XQueryContext context, final Message[] msgList, fin
340340

341341
final long msgListHandle = ModuleUtils.storeObjectInContextMap(context, MailModule.MSGLISTS_CONTEXTVAR, msgList);
342342

343-
ModuleUtils.modifyContextMap(context, MailModule.FOLDERMSGLISTS_CONTEXTVAR, (ContextMapModifier<Map<Long, Message[]>>) map -> {
343+
ModuleUtils.modifyContextMap(context, MailModule.FOLDERMSGLISTS_CONTEXTVAR, (ContextMapModifierWithoutResult<Map<Long, Message[]>>) map -> {
344344
Map<Long, Message[]> folderMsgList = map.computeIfAbsent(folderHandle, k -> new HashMap<>());
345345

346346
folderMsgList.put(msgListHandle, msgList);
@@ -356,7 +356,7 @@ static long storeMessageList(XQueryContext context, final Message[] msgList, fin
356356
* @param context The context to remove the MessageList for
357357
*/
358358
static void removeMessageList(XQueryContext context, final long msgListHandle) {
359-
ModuleUtils.modifyContextMap(context, MailModule.MSGLISTS_CONTEXTVAR, (ContextMapModifier<Message[]>) map -> map.remove(msgListHandle));
359+
ModuleUtils.modifyContextMap(context, MailModule.MSGLISTS_CONTEXTVAR, (ContextMapModifierWithoutResult<Message[]>) map -> map.remove(msgListHandle));
360360

361361
// update the context
362362
//context.setXQueryContextVar( MailModule.MSGLISTS_CONTEXTVAR, msgLists );
@@ -368,7 +368,7 @@ static void removeMessageList(XQueryContext context, final long msgListHandle) {
368368
* @param context The context to close MessageLists for
369369
*/
370370
private static void closeAllMessageLists(XQueryContext context) {
371-
ModuleUtils.modifyContextMap(context, MailModule.MSGLISTS_CONTEXTVAR, (ContextMapModifier<Message[]>) Map::clear);
371+
ModuleUtils.modifyContextMap(context, MailModule.MSGLISTS_CONTEXTVAR, (ContextMapModifierWithoutResult<Message[]>) Map::clear);
372372

373373
// update the context
374374
//context.setXQueryContextVar( MailModule.MSGLISTS_CONTEXTVAR, msgLists );

extensions/modules/sql/pom.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,14 @@
138138
<header>${project.parent.relativePath}/LGPL-21-license.template.txt</header>
139139
<excludes>
140140
<exclude>src/main/java/org/exist/xquery/modules/sql/GetConnectionFunction.java</exclude>
141+
<exclude>src/main/java/org/exist/xquery/modules/sql/CloseConnectionFunction.java</exclude>
141142
<exclude>src/main/java/org/exist/xquery/modules/sql/SQLModule.java</exclude>
142143
<exclude>src/test/resources/jndi.properties</exclude>
143144
<exclude>src/test/java/org/exist/xquery/modules/sql/ConnectionIT.java</exclude>
145+
<exclude>src/test/java/org/exist/xquery/modules/sql/ConnectionPoolIT.java</exclude>
144146
<exclude>src/test/java/org/exist/xquery/modules/sql/H2DatabaseResource.java</exclude>
145147
<exclude>src/test/java/org/exist/xquery/modules/sql/JndiConnectionIT.java</exclude>
148+
<exclude>src/test/java/org/exist/xquery/modules/sql/Util.java</exclude>
146149
</excludes>
147150
</licenseSet>
148151

@@ -153,11 +156,14 @@
153156
<header>${project.parent.relativePath}/FDB-backport-LGPL-21-ONLY-license.template.txt</header>
154157
<includes>
155158
<include>src/main/java/org/exist/xquery/modules/sql/GetConnectionFunction.java</include>
159+
<include>src/main/java/org/exist/xquery/modules/sql/CloseConnectionFunction.java</include>
156160
<include>src/main/java/org/exist/xquery/modules/sql/SQLModule.java</include>
157161
<include>src/test/resources/jndi.properties</include>
158162
<include>src/test/java/org/exist/xquery/modules/sql/ConnectionIT.java</include>
163+
<include>src/test/java/org/exist/xquery/modules/sql/ConnectionPoolIT.java</include>
159164
<include>src/test/java/org/exist/xquery/modules/sql/H2DatabaseResource.java</include>
160165
<include>src/test/java/org/exist/xquery/modules/sql/JndiConnectionIT.java</include>
166+
<include>src/test/java/org/exist/xquery/modules/sql/Util.java</include>
161167
</includes>
162168

163169
</licenseSet>
@@ -169,6 +175,14 @@
169175
<plugin>
170176
<groupId>org.apache.maven.plugins</groupId>
171177
<artifactId>maven-failsafe-plugin</artifactId>
178+
<executions>
179+
<execution>
180+
<goals>
181+
<goal>integration-test</goal>
182+
<goal>verify</goal>
183+
</goals>
184+
</execution>
185+
</executions>
172186
</plugin>
173187

174188
</plugins>

0 commit comments

Comments
 (0)