176176import java .io .FileOutputStream ;
177177import java .io .UnsupportedEncodingException ;
178178import java .nio .charset .StandardCharsets ;
179+ import java .nio .file .Path ;
180+ import java .nio .file .Paths ;
179181import java .security .MessageDigest ;
180182import java .util .Arrays ;
181183import java .util .List ;
@@ -294,9 +296,9 @@ public class Application extends ResourceConfig
294296 private final boolean enableWebIDSignUp ;
295297 private final String oidcRefreshTokensPropertiesPath ;
296298 private final Properties oidcRefreshTokens ;
299+ private final URI contextDatasetURI ;
300+ private final Dataset contextDataset ;
297301
298- private Dataset contextDataset ;
299-
300302 /**
301303 * Constructs system application and configures it using sevlet config.
302304 *
@@ -313,6 +315,7 @@ public Application(@Context ServletConfig servletConfig) throws URISyntaxExcepti
313315 servletConfig .getServletContext ().getInitParameter (A .cacheModelLoads .getURI ()) != null ? Boolean .parseBoolean (servletConfig .getServletContext ().getInitParameter (A .cacheModelLoads .getURI ())) : true ,
314316 servletConfig .getServletContext ().getInitParameter (A .preemptiveAuth .getURI ()) != null ? Boolean .parseBoolean (servletConfig .getServletContext ().getInitParameter (A .preemptiveAuth .getURI ())) : false ,
315317 new PrefixMapper (servletConfig .getServletContext ().getInitParameter (AC .prefixMapping .getURI ()) != null ? servletConfig .getServletContext ().getInitParameter (AC .prefixMapping .getURI ()) : null ),
318+ servletConfig .getServletContext ().getInitParameter (LDHC .contextDataset .getURI ()) != null ? servletConfig .getServletContext ().getInitParameter (LDHC .contextDataset .getURI ()) : null ,
316319 com .atomgraph .client .Application .getSource (servletConfig .getServletContext (), servletConfig .getServletContext ().getInitParameter (AC .stylesheet .getURI ()) != null ? servletConfig .getServletContext ().getInitParameter (AC .stylesheet .getURI ()) : null ),
317320 servletConfig .getServletContext ().getInitParameter (AC .cacheStylesheet .getURI ()) != null ? Boolean .parseBoolean (servletConfig .getServletContext ().getInitParameter (AC .cacheStylesheet .getURI ())) : false ,
318321 servletConfig .getServletContext ().getInitParameter (AC .resolvingUncached .getURI ()) != null ? Boolean .parseBoolean (servletConfig .getServletContext ().getInitParameter (AC .resolvingUncached .getURI ())) : true ,
@@ -355,14 +358,6 @@ public Application(@Context ServletConfig servletConfig) throws URISyntaxExcepti
355358 servletConfig .getServletContext ().getInitParameter (ORCID .clientID .getURI ()) != null ? servletConfig .getServletContext ().getInitParameter (ORCID .clientID .getURI ()) : null ,
356359 servletConfig .getServletContext ().getInitParameter (ORCID .clientSecret .getURI ()) != null ? servletConfig .getServletContext ().getInitParameter (ORCID .clientSecret .getURI ()) : null
357360 );
358-
359- URI contextDatasetURI = servletConfig .getServletContext ().getInitParameter (LDHC .contextDataset .getURI ()) != null ? new URI (servletConfig .getServletContext ().getInitParameter (LDHC .contextDataset .getURI ())) : null ;
360- if (contextDatasetURI == null )
361- {
362- if (log .isErrorEnabled ()) log .error ("Context dataset URI '{}' not configured" , LDHC .contextDataset .getURI ());
363- throw new ConfigurationException (LDHC .contextDataset );
364- }
365- this .contextDataset = getDataset (servletConfig .getServletContext (), contextDatasetURI );
366361 }
367362
368363 /**
@@ -374,6 +369,7 @@ public Application(@Context ServletConfig servletConfig) throws URISyntaxExcepti
374369 * @param cacheModelLoads true if model loads should be cached
375370 * @param preemptiveAuth true if HTTP Basic auth credentials should be sent preemptively
376371 * @param locationMapper Jena's <code>LocationMapper</code> instance
372+ * @param contextDatasetURIString location of the context dataset
377373 * @param stylesheet stylesheet URI
378374 * @param cacheStylesheet true if stylesheet should be cached
379375 * @param resolvingUncached true if XLST processor should dereference URLs that are not cached
@@ -418,7 +414,8 @@ public Application(@Context ServletConfig servletConfig) throws URISyntaxExcepti
418414 */
419415 public Application (final ServletConfig servletConfig , final MediaTypes mediaTypes ,
420416 final Integer maxGetRequestSize , final boolean cacheModelLoads , final boolean preemptiveAuth ,
421- final LocationMapper locationMapper , final Source stylesheet , final boolean cacheStylesheet , final boolean resolvingUncached ,
417+ final LocationMapper locationMapper , final String contextDatasetURIString ,
418+ final Source stylesheet , final boolean cacheStylesheet , final boolean resolvingUncached ,
422419 final String clientKeyStoreURIString , final String clientKeyStorePassword ,
423420 final String secretaryCertAlias ,
424421 final String clientTrustStoreURIString , final String clientTrustStorePassword ,
@@ -433,6 +430,13 @@ public Application(final ServletConfig servletConfig, final MediaTypes mediaType
433430 final String googleClientID , final String googleClientSecret ,
434431 final String orcidClientID , final String orcidClientSecret )
435432 {
433+ if (contextDatasetURIString == null )
434+ {
435+ if (log .isErrorEnabled ()) log .error ("Context dataset URI '{}' not configured" , LDHC .contextDataset .getURI ());
436+ throw new ConfigurationException (LDHC .contextDataset );
437+ }
438+ this .contextDatasetURI = URI .create (contextDatasetURIString );
439+
436440 if (clientKeyStoreURIString == null )
437441 {
438442 if (log .isErrorEnabled ()) log .error ("Client key store ({}) not configured" , LDHC .clientKeyStore .getURI ());
@@ -664,6 +668,8 @@ public Application(final ServletConfig servletConfig, final MediaTypes mediaType
664668
665669 try
666670 {
671+ this .contextDataset = getDataset (servletConfig .getServletContext (), contextDatasetURI );
672+
667673 keyStore = KeyStore .getInstance ("PKCS12" );
668674 try (FileInputStream keyStoreInputStream = new FileInputStream (new java .io .File (new URI (clientKeyStoreURIString ))))
669675 {
@@ -1974,27 +1980,77 @@ public URI getUploadRoot()
19741980
19751981 /**
19761982 * Returns RDF dataset with LinkedDataHub application descriptions.
1977- *
1983+ *
19781984 * @return RDF dataset
19791985 */
19801986 protected Dataset getContextDataset ()
19811987 {
19821988 return contextDataset ;
19831989 }
19841990
1991+ /**
1992+ * Returns the URI of the context dataset file.
1993+ *
1994+ * @return context dataset URI
1995+ */
1996+ protected URI getContextDatasetURI ()
1997+ {
1998+ return contextDatasetURI ;
1999+ }
2000+
19852001 /**
19862002 * Returns RDF model with LinkedDataHub application descriptions.
1987- *
1988- * @return RDF model
2003+ * This method returns a union of all named graphs from the context dataset.
2004+ *
2005+ * @return RDF model (read-only union of all named graphs)
19892006 */
19902007 public Model getContextModel ()
19912008 {
1992- return ModelFactory .createModelForGraph (new GraphReadOnly (getContextDataset ().getDefaultModel ().getGraph ()));
2009+ return ModelFactory .createModelForGraph (new GraphReadOnly (getContextDataset ().getUnionModel ().getGraph ()));
2010+ }
2011+
2012+ /**
2013+ * Updates a dataspace by replacing its named graph with a new Model.
2014+ * This is a template method that can be overridden by subclasses to provide alternative implementations
2015+ * (e.g., HTTP-based updates using GraphStoreClient to a remote triplestore).
2016+ *
2017+ * Default implementation uses file-based operations via SystemConfigFileManager.
2018+ *
2019+ * @param application the dataspace application to update
2020+ * @param newModel the new RDF model to replace the existing named graph
2021+ * @throws IOException if an I/O error occurs
2022+ */
2023+ public void updateDataspace (com .atomgraph .linkeddatahub .apps .model .Application application , Model newModel ) throws IOException
2024+ {
2025+ if (application == null ) throw new IllegalArgumentException ("Application cannot be null" );
2026+ if (newModel == null ) throw new IllegalArgumentException ("Model cannot be null" );
2027+
2028+ synchronized (getContextDataset ())
2029+ {
2030+ String dataspaceURI = application .getURI ();
2031+
2032+ // Only support file-based URIs for the default implementation
2033+ if (!getContextDatasetURI ().isAbsolute () || !"file" .equals (getContextDatasetURI ().getScheme ()))
2034+ {
2035+ throw new UnsupportedOperationException ("Only file-based context dataset URIs are supported for updates in default implementation" );
2036+ }
2037+
2038+ Path configFilePath = Paths .get (getContextDatasetURI ());
2039+
2040+ // Update the named graph in the dataset
2041+ getContextDataset ().removeNamedModel (dataspaceURI ).
2042+ addNamedModel (dataspaceURI , newModel );
2043+
2044+ // Write the updated dataset back to file
2045+ com .atomgraph .linkeddatahub .server .util .SystemConfigFileManager .writeDataset (getContextDataset (), configFilePath );
2046+
2047+ if (log .isInfoEnabled ()) log .info ("Updated dataspace <{}> in file: {}" , dataspaceURI , configFilePath );
2048+ }
19932049 }
19942050
19952051 /**
19962052 * Returns true if configured to invalidate HTTP proxy cache of triplestore results.
1997- *
2053+ *
19982054 * @return true if invalidated
19992055 */
20002056 public boolean isInvalidateCache ()
0 commit comments