From 453099a3e619cd1b027eefa37aad64b209e55094 Mon Sep 17 00:00:00 2001 From: Ostrzyciel Date: Tue, 15 Oct 2024 22:18:31 +0200 Subject: [PATCH] Move Fuseki integration to a Jena module Follow-up from #136 Fuseki modules are not supported in all Fuseki distributions, so it makes more sense to move the functionality to a Jena module. The only disadvantage is that the module loads regardless of whether we are in a Fuseki instance or not, but that can be solved with some exception catching... --- ...ache.jena.fuseki.main.sys.FusekiAutoModule | 1 - ...org.apache.jena.sys.JenaSubsystemLifecycle | 1 + ...odule.scala => JellyFusekiLifecycle.scala} | 31 ++++++++++--------- .../jena/fuseki/JellyFusekiModuleSpec.scala | 22 ++++++------- 4 files changed, 28 insertions(+), 27 deletions(-) delete mode 100644 jena/src/main/resources/META-INF/services/org.apache.jena.fuseki.main.sys.FusekiAutoModule rename jena/src/main/scala/eu/ostrzyciel/jelly/convert/jena/fuseki/{JellyFusekiModule.scala => JellyFusekiLifecycle.scala} (60%) diff --git a/jena/src/main/resources/META-INF/services/org.apache.jena.fuseki.main.sys.FusekiAutoModule b/jena/src/main/resources/META-INF/services/org.apache.jena.fuseki.main.sys.FusekiAutoModule deleted file mode 100644 index af06007d..00000000 --- a/jena/src/main/resources/META-INF/services/org.apache.jena.fuseki.main.sys.FusekiAutoModule +++ /dev/null @@ -1 +0,0 @@ -eu.ostrzyciel.jelly.convert.jena.fuseki.JellyFusekiModule \ No newline at end of file diff --git a/jena/src/main/resources/META-INF/services/org.apache.jena.sys.JenaSubsystemLifecycle b/jena/src/main/resources/META-INF/services/org.apache.jena.sys.JenaSubsystemLifecycle index de01b4e3..e79aa3d8 100644 --- a/jena/src/main/resources/META-INF/services/org.apache.jena.sys.JenaSubsystemLifecycle +++ b/jena/src/main/resources/META-INF/services/org.apache.jena.sys.JenaSubsystemLifecycle @@ -1 +1,2 @@ eu.ostrzyciel.jelly.convert.jena.riot.JellySubsystemLifecycle +eu.ostrzyciel.jelly.convert.jena.fuseki.JellyFusekiLifecycle \ No newline at end of file diff --git a/jena/src/main/scala/eu/ostrzyciel/jelly/convert/jena/fuseki/JellyFusekiModule.scala b/jena/src/main/scala/eu/ostrzyciel/jelly/convert/jena/fuseki/JellyFusekiLifecycle.scala similarity index 60% rename from jena/src/main/scala/eu/ostrzyciel/jelly/convert/jena/fuseki/JellyFusekiModule.scala rename to jena/src/main/scala/eu/ostrzyciel/jelly/convert/jena/fuseki/JellyFusekiLifecycle.scala index 94c320b9..65be6759 100644 --- a/jena/src/main/scala/eu/ostrzyciel/jelly/convert/jena/fuseki/JellyFusekiModule.scala +++ b/jena/src/main/scala/eu/ostrzyciel/jelly/convert/jena/fuseki/JellyFusekiLifecycle.scala @@ -3,28 +3,23 @@ package eu.ostrzyciel.jelly.convert.jena.fuseki import eu.ostrzyciel.jelly.core.Constants import org.apache.jena.atlas.web.{AcceptList, MediaRange} import org.apache.jena.fuseki.{DEF, Fuseki} -import org.apache.jena.fuseki.main.FusekiServer -import org.apache.jena.fuseki.main.sys.FusekiAutoModule -import org.apache.jena.rdf.model.Model -import org.apache.jena.riot.WebContent +import org.apache.jena.sys.JenaSubsystemLifecycle import java.util -object JellyFusekiModule: +object JellyFusekiLifecycle: val mediaRangeJelly: MediaRange = new MediaRange(Constants.jellyContentType) /** - * A Fuseki module that adds Jelly content type to the list of accepted content types. + * A Jena module that adds Jelly content type to the list of accepted content types in Fuseki. + * This isn't a Fuseki module, because Fuseki modules are not supported in all distributions of Fuseki, see: + * https://github.com/apache/jena/issues/2774 * * This allows users to use the Accept header set to application/x-jelly-rdf to request Jelly RDF responses. * It works for SPARQL CONSTRUCT queries and for the Graph Store Protocol. - * - * More info on Fuseki modules: https://jena.apache.org/documentation/fuseki2/fuseki-modules.html */ -final class JellyFusekiModule extends FusekiAutoModule: - import JellyFusekiModule.* - - override def name(): String = "Jelly" +final class JellyFusekiLifecycle extends JenaSubsystemLifecycle: + import JellyFusekiLifecycle.* override def start(): Unit = try { @@ -32,16 +27,22 @@ final class JellyFusekiModule extends FusekiAutoModule: maybeAddJellyToList(DEF.rdfOffer).foreach(offer => DEF.rdfOffer = offer) maybeAddJellyToList(DEF.quadsOffer).foreach(offer => { DEF.quadsOffer = offer - Fuseki.serverLog.info(s"Added ${Constants.jellyContentType} to the list of accepted content types") + Fuseki.serverLog.info(s"Jelly: Added ${Constants.jellyContentType} to the list of accepted content types") }) } catch { + case e: NoClassDefFoundError => // ignore, we are not running Fuseki case e: IllegalAccessError => Fuseki.serverLog.warn( - s"Cannot register the ${Constants.jellyContentType} content type, because you are running an Apache Jena " + - s"Fuseki version that doesn't support content type registration. " + + s"Jelly: Cannot register the ${Constants.jellyContentType} content type, because you are running an " + + s"Apache Jena Fuseki version that doesn't support content type registration. " + s"Update to Fuseki 5.2.0 or newer for this to work." ) } + override def stop(): Unit = () + + // Initialize after JellySubsystemLifecycle + override def level(): Int = 502 + /** * Adds the Jelly content type to the list of accepted content types if it is not already present. * @param list current list of accepted content types diff --git a/jena/src/test/scala/eu/ostrzyciel/jelly/convert/jena/fuseki/JellyFusekiModuleSpec.scala b/jena/src/test/scala/eu/ostrzyciel/jelly/convert/jena/fuseki/JellyFusekiModuleSpec.scala index 3f734109..fedca240 100644 --- a/jena/src/test/scala/eu/ostrzyciel/jelly/convert/jena/fuseki/JellyFusekiModuleSpec.scala +++ b/jena/src/test/scala/eu/ostrzyciel/jelly/convert/jena/fuseki/JellyFusekiModuleSpec.scala @@ -9,38 +9,38 @@ import scala.jdk.CollectionConverters.* class JellyFusekiModuleSpec extends AnyWordSpec, Matchers: "JellyFusekiModule" should { "have a name" in { - JellyFusekiModule().name() should be ("Jelly") + JellyFusekiLifecycle().name() should be ("Jelly") } "use the correct content type for Jelly" in { - JellyFusekiModule.mediaRangeJelly.getContentTypeStr should be ("application/x-jelly-rdf") + JellyFusekiLifecycle.mediaRangeJelly.getContentTypeStr should be ("application/x-jelly-rdf") } "register the Jelly content type in the lists of accepted content types" in { val oldLists = List(DEF.constructOffer, DEF.rdfOffer, DEF.quadsOffer) for list <- oldLists do - list.entries().asScala should not contain JellyFusekiModule.mediaRangeJelly - DEF.constructOffer.entries().asScala should not contain JellyFusekiModule.mediaRangeJelly - DEF.rdfOffer.entries().asScala should not contain JellyFusekiModule.mediaRangeJelly - DEF.quadsOffer.entries().asScala should not contain JellyFusekiModule.mediaRangeJelly + list.entries().asScala should not contain JellyFusekiLifecycle.mediaRangeJelly + DEF.constructOffer.entries().asScala should not contain JellyFusekiLifecycle.mediaRangeJelly + DEF.rdfOffer.entries().asScala should not contain JellyFusekiLifecycle.mediaRangeJelly + DEF.quadsOffer.entries().asScala should not contain JellyFusekiLifecycle.mediaRangeJelly - val module = JellyFusekiModule() + val module = JellyFusekiLifecycle() module.start() val lists = List(DEF.constructOffer, DEF.rdfOffer, DEF.quadsOffer) for (list, oldList) <- lists.zip(oldLists) do - list.entries().asScala should contain (JellyFusekiModule.mediaRangeJelly) + list.entries().asScala should contain (JellyFusekiLifecycle.mediaRangeJelly) list.entries().size() should be (oldList.entries().size() + 1) } "not register the Jelly content type if it's already registered" in { - val module = JellyFusekiModule() + val module = JellyFusekiLifecycle() module.start() - DEF.rdfOffer.entries().asScala should contain (JellyFusekiModule.mediaRangeJelly) + DEF.rdfOffer.entries().asScala should contain (JellyFusekiLifecycle.mediaRangeJelly) val size1 = DEF.rdfOffer.entries().size() module.start() - DEF.rdfOffer.entries().asScala should contain (JellyFusekiModule.mediaRangeJelly) + DEF.rdfOffer.entries().asScala should contain (JellyFusekiLifecycle.mediaRangeJelly) val size2 = DEF.rdfOffer.entries().size() size2 should be (size1) }