11/*
2+ * Elemental
3+ * Copyright (C) 2024, Evolved Binary Ltd
4+ *
5+ 6+ * https://www.evolvedbinary.com | https://www.elemental.xyz
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; version 2.1.
11+ *
12+ * This library is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+ * Lesser General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU Lesser General Public
18+ * License along with this library; if not, write to the Free Software
19+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20+ *
21+ * NOTE: Parts of this file contain code from 'The eXist-db Authors'.
22+ * The original license header is included below.
23+ *
24+ * =====================================================================
25+ *
226 * eXist-db Open Source Native XML Database
327 * Copyright (C) 2001 The eXist-db Authors
428 *
4973import org .exist .xquery .value .Sequence ;
5074import org .exist .xquery .value .StringValue ;
5175
76+ import javax .annotation .Nullable ;
77+
5278import static com .evolvedbinary .j8fu .tuple .Tuple .Tuple ;
5379
5480/**
@@ -232,33 +258,47 @@ private void prepare(final TriggerEvent event, final DBBroker broker, final Txn
232258 LOG .warn (e .getMessage ());
233259 return ;
234260 }
235-
236- final XQueryContext context = new XQueryContext (broker .getBrokerPool ());
237- CompiledXQuery compiledQuery = null ;
238- try {
239- //compile the XQuery
240- compiledQuery = service .compile (context , query );
241- declareExternalVariables (context , TriggerPhase .BEFORE , event , src , dst , isCollection );
242-
243- } catch (final XPathException | IOException | PermissionDeniedException e ) {
244- TriggerStatePerThread .clear ();
245- throw new TriggerException (PREPARE_EXCEPTION_MESSAGE , e );
246- }
247261
248- //execute the XQuery
262+ @ Nullable CompiledXQuery compiled = null ;
263+ @ Nullable XQueryContext context = null ;
249264 try {
250- //TODO : should we provide another contextSet ?
251- final NodeSet contextSet = NodeSet .EMPTY_SET ;
252- service .execute (broker , compiledQuery , contextSet );
253- //TODO : should we have a special processing ?
254- if (LOG .isDebugEnabled ()) {
255- LOG .debug ("Trigger fired for prepare" );
256- }
257- } catch (final XPathException | PermissionDeniedException e ) {
258- TriggerStatePerThread .clear ();
259- throw new TriggerException (PREPARE_EXCEPTION_MESSAGE , e );
265+
266+ compiled = broker .getBrokerPool ().getXQueryPool ().borrowCompiledXQuery (broker , query );
267+ if (compiled == null ) {
268+ context = new XQueryContext (broker .getBrokerPool ());
269+ } else {
270+ context = compiled .getContext ();
271+ context .prepareForReuse ();
272+ }
273+
274+ if (compiled == null ) {
275+ compiled = service .compile (context , query );
276+ } else {
277+ compiled .getContext ().updateContext (context );
278+ context .getWatchDog ().reset ();
279+ }
280+
281+ declareExternalVariables (context , TriggerPhase .BEFORE , event , src , dst , isCollection );
282+
283+ //execute the XQuery
284+ //TODO : should we provide another contextSet ?
285+ final NodeSet contextSet = NodeSet .EMPTY_SET ;
286+ service .execute (broker , compiled , contextSet );
287+ //TODO : should we have a special processing ?
288+ if (LOG .isDebugEnabled ()) {
289+ LOG .debug ("Trigger fired for prepare" );
290+ }
291+
292+ } catch (final XPathException | IOException | PermissionDeniedException e ) {
293+ TriggerStatePerThread .clear ();
294+ throw new TriggerException (PREPARE_EXCEPTION_MESSAGE , e );
260295 } finally {
261- context .runCleanupTasks ();
296+ if (context != null ) {
297+ context .runCleanupTasks ();
298+ }
299+ if (compiled != null ) {
300+ broker .getBrokerPool ().getXQueryPool ().returnCompiledXQuery (query , compiled );
301+ }
262302 }
263303 }
264304
@@ -276,33 +316,42 @@ private void finish(final TriggerEvent event, final DBBroker broker, final Txn t
276316 LOG .warn (e .getMessage ());
277317 return ;
278318 }
279-
280- final XQueryContext context = new XQueryContext ( broker . getBrokerPool ()) ;
281- CompiledXQuery compiledQuery = null ;
319+
320+ @ Nullable CompiledXQuery compiled = null ;
321+ @ Nullable XQueryContext context = null ;
282322 try {
283- //compile the XQuery
284- compiledQuery = service .compile (context , query );
285- declareExternalVariables (context , TriggerPhase .AFTER , event , src , dst , isCollection );
323+ compiled = broker .getBrokerPool ().getXQueryPool ().borrowCompiledXQuery (broker , query );
324+ if (compiled == null ) {
325+ context = new XQueryContext (broker .getBrokerPool ());
326+ } else {
327+ context = compiled .getContext ();
328+ context .prepareForReuse ();
329+ }
330+
331+ if (compiled == null ) {
332+ compiled = service .compile (context , query );
333+ } else {
334+ compiled .getContext ().updateContext (context );
335+ context .getWatchDog ().reset ();
336+ }
337+
338+ declareExternalVariables (context , TriggerPhase .AFTER , event , src , dst , isCollection );
339+
340+ //execute the XQuery
341+ //TODO : should we provide another contextSet ?
342+ final NodeSet contextSet = NodeSet .EMPTY_SET ;
343+ service .execute (broker , compiled , contextSet );
344+ //TODO : should we have a special processing ?
286345
287346 } catch (final XPathException | IOException | PermissionDeniedException e ) {
288- //Should never be reached
289- LOG .error (e );
290- }
291-
292- //execute the XQuery
293- try {
294- //TODO : should we provide another contextSet ?
295- final NodeSet contextSet = NodeSet .EMPTY_SET ;
296- service .execute (broker , compiledQuery , contextSet );
297- //TODO : should we have a special processing ?
298- } catch (final XPathException e ) {
299- //Should never be reached
300- LOG .error ("Error during trigger finish" , e );
301- } catch (final PermissionDeniedException e ) {
302- //Should never be reached
303- LOG .error (e );
347+ LOG .error ("Error during trigger finish" , e );
304348 } finally {
305- context .runCleanupTasks ();
349+ if (context != null ) {
350+ context .runCleanupTasks ();
351+ }
352+ if (compiled != null ) {
353+ broker .getBrokerPool ().getXQueryPool ().returnCompiledXQuery (query , compiled );
354+ }
306355 }
307356
308357 TriggerStatePerThread .clearIfFinished (TriggerPhase .AFTER );
@@ -314,28 +363,28 @@ private void finish(final TriggerEvent event, final DBBroker broker, final Txn t
314363
315364 private void declareExternalVariables (final XQueryContext context , final TriggerPhase phase , final TriggerEvent event , final XmldbURI src , final XmldbURI dst , final boolean isCollection ) throws XPathException {
316365 //declare external variables
317- context .declareVariable (bindingPrefix + "type" , new StringValue (phase .legacyPhaseName ()));
318- context .declareVariable (bindingPrefix + "event" , new StringValue (event .legacyEventName ()));
366+ context .declareVariable (bindingPrefix + "type" , true , new StringValue (phase .legacyPhaseName ()));
367+ context .declareVariable (bindingPrefix + "event" , true , new StringValue (event .legacyEventName ()));
319368 if (isCollection ) {
320- context .declareVariable (bindingPrefix + "collection" , new AnyURIValue (src ));
369+ context .declareVariable (bindingPrefix + "collection" , true , new AnyURIValue (src ));
321370 } else {
322- context .declareVariable (bindingPrefix + "collection" , new AnyURIValue (src .removeLastSegment ()));
371+ context .declareVariable (bindingPrefix + "collection" , true , new AnyURIValue (src .removeLastSegment ()));
323372 }
324- context .declareVariable (bindingPrefix + "uri" , new AnyURIValue (src ));
373+ context .declareVariable (bindingPrefix + "uri" , true , new AnyURIValue (src ));
325374 if (dst == null ) {
326- context .declareVariable (bindingPrefix + "new-uri" , Sequence .EMPTY_SEQUENCE );
375+ context .declareVariable (bindingPrefix + "new-uri" , true , Sequence .EMPTY_SEQUENCE );
327376 } else {
328- context .declareVariable (bindingPrefix + "new-uri" , new AnyURIValue (dst ));
377+ context .declareVariable (bindingPrefix + "new-uri" , true , new AnyURIValue (dst ));
329378 }
330379
331380 // For backward compatibility
332- context .declareVariable (bindingPrefix + "eventType" , new StringValue (phase .legacyPhaseName ()));
333- context .declareVariable (bindingPrefix + "triggerEvent" , new StringValue (event .legacyEventName ()));
381+ context .declareVariable (bindingPrefix + "eventType" , true , new StringValue (phase .legacyPhaseName ()));
382+ context .declareVariable (bindingPrefix + "triggerEvent" , true , new StringValue (event .legacyEventName ()));
334383 if (isCollection ) {
335- context .declareVariable (bindingPrefix + "collectionName" , new AnyURIValue (src ));
384+ context .declareVariable (bindingPrefix + "collectionName" , true , new AnyURIValue (src ));
336385 } else {
337- context .declareVariable (bindingPrefix + "collectionName" , new AnyURIValue (src .removeLastSegment ()));
338- context .declareVariable (bindingPrefix + "documentName" , new AnyURIValue (src ));
386+ context .declareVariable (bindingPrefix + "collectionName" , true , new AnyURIValue (src .removeLastSegment ()));
387+ context .declareVariable (bindingPrefix + "documentName" , true , new AnyURIValue (src ));
339388 }
340389
341390 //declare user defined parameters as external variables
@@ -344,7 +393,7 @@ private void declareExternalVariables(final XQueryContext context, final Trigger
344393 final String varName = (String ) o ;
345394 final String varValue = userDefinedVariables .getProperty (varName );
346395
347- context .declareVariable (bindingPrefix + varName , new StringValue (varValue ));
396+ context .declareVariable (bindingPrefix + varName , true , new StringValue (varValue ));
348397 }
349398 }
350399 }
@@ -356,37 +405,48 @@ private CompiledXQuery getScript(final DBBroker broker, final Txn transaction) t
356405 if (query == null ) {
357406 return null ;
358407 }
359-
360- final XQueryContext context = new XQueryContext (broker .getBrokerPool ());
361- if (query instanceof DBSource ) {
362- context .setModuleLoadPath (XmldbURI .EMBEDDED_SERVER_URI_PREFIX + ((DBSource )query ).getDocumentPath ().removeLastSegment ().toString ());
363- }
364408
365- CompiledXQuery compiledQuery ;
409+ @ Nullable CompiledXQuery compiled = null ;
410+ @ Nullable XQueryContext context = null ;
366411 try {
412+ compiled = broker .getBrokerPool ().getXQueryPool ().borrowCompiledXQuery (broker , query );
413+ if (compiled == null ) {
414+ context = new XQueryContext (broker .getBrokerPool ());
415+ } else {
416+ context = compiled .getContext ();
417+ context .prepareForReuse ();
418+ }
419+
420+ if (query instanceof DBSource ) {
421+ context .setModuleLoadPath (XmldbURI .EMBEDDED_SERVER_URI_PREFIX + ((DBSource )query ).getDocumentPath ().removeLastSegment ().toString ());
422+ }
423+
367424 //compile the XQuery
368- compiledQuery = service .compile (context , query );
425+ if (compiled == null ) {
426+ compiled = service .compile (context , query );
427+ } else {
428+ compiled .getContext ().updateContext (context );
429+ context .getWatchDog ().reset ();
430+ }
369431
370432 //declare user defined parameters as external variables
371433 if (userDefinedVariables != null ) {
372434 for (final Object o : userDefinedVariables .keySet ()) {
373435 final String varName = (String ) o ;
374436 final String varValue = userDefinedVariables .getProperty (varName );
375437
376- context .declareVariable (bindingPrefix + varName , new StringValue (varValue ));
438+ context .declareVariable (bindingPrefix + varName , true , new StringValue (varValue ));
377439 }
378440 }
379-
380- //reset & prepareForExecution for execution
381- compiledQuery .reset ();
382441
383- context .getWatchDog ().reset ();
384-
385- //do any preparation before execution
386- context .prepareForExecution ();
387-
388- return compiledQuery ;
442+ return compiled ;
389443 } catch (final XPathException | IOException | PermissionDeniedException e ) {
444+ if (context != null ) {
445+ context .runCleanupTasks ();
446+ }
447+ if (compiled != null ) {
448+ broker .getBrokerPool ().getXQueryPool ().returnCompiledXQuery (query , compiled );
449+ }
390450 LOG .warn (e .getMessage (), e );
391451 throw new TriggerException (PREPARE_EXCEPTION_MESSAGE , e );
392452 }
@@ -406,23 +466,18 @@ private void execute(final TriggerPhase phase, final TriggerEvent event, final D
406466 LOG .debug ("Execute: {} {}({}): {}" , phase , event , src , getClass ().getSimpleName ());
407467 }
408468
409- final CompiledXQuery compiledQuery ;
469+ @ Nullable CompiledXQuery compiled = null ;
470+ @ Nullable XQueryContext context = null ;
410471 try {
411- compiledQuery = getScript (broker , transaction );
412- if (compiledQuery == null ) {
472+ compiled = getScript (broker , transaction );
473+ if (compiled == null ) {
413474 // NOTE: can occur if there is no such XQueryTrigger library module available in the database
414475 TriggerStatePerThread .clearIfFinished (phase );
415476 return ;
416477 }
417- } catch (final TriggerException e ) {
418- TriggerStatePerThread .clear ();
419- throw e ;
420- }
421-
422- final XQueryContext context = compiledQuery .getContext ();
478+ context = compiled .getContext ();
423479
424- //execute the XQuery
425- try {
480+ //execute the XQuery
426481 final int nParams ;
427482 if (dst != null ) {
428483 nParams = 2 ;
@@ -443,13 +498,18 @@ private void execute(final TriggerPhase phase, final TriggerEvent event, final D
443498 args .add (new LiteralValue (context , new AnyURIValue (src )));
444499 }
445500
446- service .execute (broker , compiledQuery , Tuple (functionName , args , Optional .empty ()), null , null , true );
501+ service .execute (broker , compiled , Tuple (functionName , args , Optional .empty ()), null , null , true );
502+
503+ } catch (final TriggerException e ) {
504+ TriggerStatePerThread .clear ();
505+ throw e ;
506+
447507 } catch (final XPathException | PermissionDeniedException e ) {
448508 // if the exception just indicates that there is no function in the trigger to call, then we can just log and return
449509 if (e instanceof XPathException xpe ) {
450510 if (xpe .getErrorCode () == ErrorCodes .EXXQDY0005 || xpe .getErrorCode () == ErrorCodes .EXXQDY0006 ) {
451511 if (LOG .isDebugEnabled ()) {
452- LOG .debug ("No such function '" + functionName + "' in XQueryTrigger: " + compiledQuery .getSource ());
512+ LOG .debug ("No such function '" + functionName + "' in XQueryTrigger: " + compiled .getSource ());
453513 }
454514 TriggerStatePerThread .clearIfFinished (phase );
455515 return ;
@@ -459,8 +519,12 @@ private void execute(final TriggerPhase phase, final TriggerEvent event, final D
459519 TriggerStatePerThread .clear ();
460520 throw new TriggerException (PREPARE_EXCEPTION_MESSAGE , e );
461521 } finally {
462- compiledQuery .reset ();
463- context .runCleanupTasks ();
522+ if (context != null ) {
523+ context .runCleanupTasks ();
524+ }
525+ if (compiled != null ) {
526+ broker .getBrokerPool ().getXQueryPool ().returnCompiledXQuery (compiled .getSource (), compiled );
527+ }
464528 }
465529
466530 TriggerStatePerThread .clearIfFinished (phase );
0 commit comments