From 594c5fbe1953fa9e50c86ae6daadb952975ef6a5 Mon Sep 17 00:00:00 2001 From: Chapman Flack Date: Fri, 14 Mar 2025 10:58:25 -0400 Subject: [PATCH] Declare S9 example functions only if s9api found The Maven build has long offered an optional -Psaxon-examples profile; without it, the examples jar gets built without the S9 example, and can be deployed in a database without the Saxon jar loaded. With the profile active, the jar is built with the S9 example included, and then that jar can't be loaded (with deploy => true) unless the database already has a Saxon jar installed and on the class path, even if there is no need for the S9 examples. That's an inconvenient restriction on later use of the examples jar based on a choice made at build time. Use conditional execution in the deployment descriptor so that even when the examples jar is built with the S9 examples included, they are only deployed if a Saxon API class is found on the class path at deploy time. That way, the examples can be built with the saxon-examples profile active and still freely used without loading a Saxon jar if those examples are not of interest. Some inconvenient situations, though less likely ones, can still arise. If the examples jar is installed when no Saxon jar is on the class path (and so does not declare the S9 example functions), but later remove_jar is called (with undeploy => true) when for some reason there is a Saxon jar on the class path, then the undeploy actions will detect the Saxon classes and try to drop the S9 functions that had not been declared. But this is a rare "so don't do that" case that is easily avoided, nothing like the serious inconvenience of never being able to use the examples jar without installing Saxon first, depending on how the jar was built. --- .../example/annotation/ConditionalDDR.java | 27 +++++++++++++- .../postgresql/pljava/example/saxon/S9.java | 36 +++++++++++++++---- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/pljava-examples/src/main/java/org/postgresql/pljava/example/annotation/ConditionalDDR.java b/pljava-examples/src/main/java/org/postgresql/pljava/example/annotation/ConditionalDDR.java index 26fabe830..d982be0be 100644 --- a/pljava-examples/src/main/java/org/postgresql/pljava/example/annotation/ConditionalDDR.java +++ b/pljava-examples/src/main/java/org/postgresql/pljava/example/annotation/ConditionalDDR.java @@ -11,6 +11,7 @@ */ package org.postgresql.pljava.example.annotation; +import org.postgresql.pljava.annotation.Function; import org.postgresql.pljava.annotation.SQLAction; /** @@ -106,4 +107,28 @@ " current_setting('pljava.implementors'), true) " + "END" ) -public class ConditionalDDR { } +public class ConditionalDDR +{ + private ConditionalDDR() { } // do not instantiate + + /** + * Tests class names in the supplied order, returning false as soon as any + * cannot be found by the class loader(s) available to the examples jar, or + * true if all can be found. + */ + @Function(variadic = true, provides = "presentOnClassPath") + public static boolean presentOnClassPath(String[] className) + { + try + { + ClassLoader myLoader = ConditionalDDR.class.getClassLoader(); + for ( String cn : className ) + Class.forName(cn, false, myLoader); + return true; + } + catch ( ClassNotFoundException e ) + { + return false; + } + } +} diff --git a/pljava-examples/src/main/java/org/postgresql/pljava/example/saxon/S9.java b/pljava-examples/src/main/java/org/postgresql/pljava/example/saxon/S9.java index fc3fb5210..9eb5fdb07 100644 --- a/pljava-examples/src/main/java/org/postgresql/pljava/example/saxon/S9.java +++ b/pljava-examples/src/main/java/org/postgresql/pljava/example/saxon/S9.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020 Tada AB and other contributors, as listed below. + * Copyright (c) 2018-2025 Tada AB and other contributors, as listed below. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the The BSD 3-Clause License @@ -111,6 +111,7 @@ import org.postgresql.pljava.ResultSetProvider; import org.postgresql.pljava.annotation.Function; +import org.postgresql.pljava.annotation.SQLAction; import org.postgresql.pljava.annotation.SQLType; import static org.postgresql.pljava.annotation.Function.OnNullInput.CALLED; @@ -288,6 +289,23 @@ * XQuery regular-expression methods provided here. * @author Chapman Flack */ +@SQLAction( + implementor = "postgresql_xml", // skip it all if no xml support + requires = "presentOnClassPath", + provides = "saxon9api", + install = + "SELECT CASE WHEN" + + " presentOnClassPath('net.sf.saxon.s9api.QName')" + + "THEN" + + " CAST(" + + " set_config('pljava.implementors', 'saxon9api,' || " + + " current_setting('pljava.implementors'), true)" + + " AS void" + + " )" + + "ELSE" + + " logMessage('INFO', 'Saxon examples skipped: s9api classes missing')" + + "END" +) public class S9 implements ResultSetProvider.Large { private S9( @@ -399,7 +417,7 @@ static class DocumentWrapUnwrap * @param sve SQL string value to use in a text node * @return XML content, the text node wrapped in a document node */ - @Function(schema="javatest") + @Function(implementor="saxon9api", schema="javatest") public static SQLXML xmltext(String sve) throws SQLException { SQLXML rx = s_dbc.createSQLXML(); @@ -457,6 +475,7 @@ public static SQLXML xmltext(String sve) throws SQLException * type to be cast to. */ @Function( + implementor="saxon9api", schema="javatest", type="pg_catalog.record", onNullInput=CALLED, @@ -615,6 +634,7 @@ public static boolean xmlcast( * namespace. */ @Function( + implementor="saxon9api", schema="javatest", onNullInput=CALLED, settings="IntervalStyle TO iso_8601" @@ -683,6 +703,7 @@ public static SQLXML xq_ret_content( * SQL value is null. */ @Function( + implementor="saxon9api", schema="javatest", onNullInput=CALLED, settings="IntervalStyle TO iso_8601" @@ -822,6 +843,7 @@ private static SQLXML returnContent( * for base64 or (the default, false) hexadecimal. */ @Function( + implementor="saxon9api", schema="javatest", onNullInput=CALLED, settings="IntervalStyle TO iso_8601" @@ -2983,7 +3005,7 @@ public void onGroupEnd(int groupNumber) * SQLFeatureNotSupportedException (0A000) if (in the current * implementation) w3cNewlines is false or omitted. */ - @Function(schema="javatest") + @Function(implementor="saxon9api", schema="javatest") public static boolean like_regex( String value, //strict String pattern, //strict @@ -3036,7 +3058,7 @@ public static boolean like_regex( * SQLFeatureNotSupportedException (0A000) if (in the current * implementation) usingOctets is true, or w3cNewlines is false or omitted. */ - @Function(schema="javatest") + @Function(implementor="saxon9api", schema="javatest") public static int occurrences_regex( String pattern, //strict @SQLType(name="\"in\"") String in, //strict @@ -3115,7 +3137,7 @@ public static int occurrences_regex( * SQLFeatureNotSupportedException (0A000) if (in the current * implementation) usingOctets is true, or w3cNewlines is false or omitted. */ - @Function(schema="javatest") + @Function(implementor="saxon9api", schema="javatest") public static int position_regex( String pattern, //strict @SQLType(name="\"in\"") String in, //strict @@ -3197,7 +3219,7 @@ public static int position_regex( * SQLFeatureNotSupportedException (0A000) if (in the current * implementation) usingOctets is true, or w3cNewlines is false or omitted. */ - @Function(schema="javatest") + @Function(implementor="saxon9api", schema="javatest") public static String substring_regex( String pattern, //strict @SQLType(name="\"in\"") String in, //strict @@ -3298,7 +3320,7 @@ public static String substring_regex( * SQLFeatureNotSupportedException (0A000) if (in the current * implementation) usingOctets is true, or w3cNewlines is false or omitted. */ - @Function(schema="javatest") + @Function(implementor="saxon9api", schema="javatest") public static String translate_regex( String pattern, //strict @SQLType(name="\"in\"") String in, //strict