2020import com .atomgraph .linkeddatahub .apps .model .AdminApplication ;
2121import com .atomgraph .linkeddatahub .apps .model .EndUserApplication ;
2222import com .atomgraph .linkeddatahub .client .LinkedDataClient ;
23+ import com .atomgraph .linkeddatahub .resource .admin .Clear ;
24+ import com .atomgraph .linkeddatahub .server .security .AgentContext ;
2325import com .atomgraph .linkeddatahub .server .util .UriPath ;
2426import com .atomgraph .linkeddatahub .server .util .XsltMasterUpdater ;
2527import jakarta .inject .Inject ;
3436import jakarta .ws .rs .WebApplicationException ;
3537import jakarta .ws .rs .client .Client ;
3638import jakarta .ws .rs .client .WebTarget ;
39+ import jakarta .ws .rs .container .ResourceContext ;
3740import jakarta .ws .rs .core .Context ;
3841import jakarta .ws .rs .core .MediaType ;
42+ import jakarta .ws .rs .core .MultivaluedHashMap ;
3943import jakarta .ws .rs .core .Response ;
4044import jakarta .ws .rs .core .UriBuilder ;
45+ import org .apache .commons .codec .binary .Hex ;
4146import org .apache .jena .rdf .model .Model ;
47+ import org .apache .jena .rdf .model .ModelFactory ;
4248import org .apache .jena .rdf .model .Resource ;
49+ import org .apache .jena .vocabulary .OWL ;
4350import org .slf4j .Logger ;
4451import org .slf4j .LoggerFactory ;
4552import java .io .IOException ;
4653import java .net .URI ;
54+ import java .nio .charset .StandardCharsets ;
4755import java .nio .file .Files ;
4856import java .nio .file .Path ;
4957import java .nio .file .Paths ;
58+ import java .security .MessageDigest ;
59+ import java .security .NoSuchAlgorithmException ;
5060import java .util .ArrayList ;
5161import java .util .List ;
62+ import java .util .Optional ;
5263import java .util .Set ;
5364import org .apache .jena .util .FileManager ;
5465
5566/**
5667 * JAX-RS resource that installs a LinkedDataHub package.
5768 * Package installation involves:
5869 * 1. Fetching package metadata
59- * 2. Downloading package ontology (ns.ttl) and posting to namespace graph
60- * 3. Downloading package stylesheet (layout.xsl) and saving to /static/packages/
61- * 4. Regenerating application master stylesheet
62- * 5. Adding ldh:import triple to application
70+ * 2. Downloading package ontology and PUTting as new document under model/ontologies/{hash}/
71+ * 3. Adding owl:imports of package ontology to namespace ontology
72+ * 4. Downloading package stylesheet (layout.xsl) and saving to /static/{package-path}/
73+ * 5. Regenerating application master stylesheet
74+ * 6. Adding ldh:import triple to application (TODO)
6375 *
6476 * @author Martynas Jusevičius {@literal <[email protected] >} 6577 */
@@ -70,24 +82,29 @@ public class Install
7082 private final com .atomgraph .linkeddatahub .apps .model .Application application ;
7183 private final com .atomgraph .linkeddatahub .Application system ;
7284 private final DataManager dataManager ;
85+ private final Optional <AgentContext > agentContext ;
7386
7487 @ Context ServletContext servletContext ;
88+ @ Context ResourceContext resourceContext ;
7589
7690 /**
7791 * Constructs endpoint.
7892 *
7993 * @param application matched application (admin app)
8094 * @param system system application
8195 * @param dataManager data manager
96+ * @param agentContext authenticated agent context
8297 */
8398 @ Inject
8499 public Install (com .atomgraph .linkeddatahub .apps .model .Application application ,
85100 com .atomgraph .linkeddatahub .Application system ,
86- DataManager dataManager )
101+ DataManager dataManager ,
102+ Optional <AgentContext > agentContext )
87103 {
88104 this .application = application ;
89105 this .system = system ;
90106 this .dataManager = dataManager ;
107+ this .agentContext = agentContext ;
91108 }
92109
93110 /**
@@ -125,7 +142,7 @@ public Response post(@FormParam("package-uri") String packageURI, @HeaderParam("
125142 // 2. Download and install ontology
126143 if (log .isDebugEnabled ()) log .debug ("Downloading package ontology from: {}" , ontology .getURI ());
127144 Model ontologyModel = downloadOntology (ontology .getURI ());
128- installOntology (endUserApp , ontologyModel );
145+ installOntology (endUserApp , ontologyModel , ontology . getURI () );
129146
130147 // 3. Download and install stylesheet if present
131148 if (stylesheetURI != null )
@@ -150,7 +167,7 @@ public Response post(@FormParam("package-uri") String packageURI, @HeaderParam("
150167 catch (BadRequestException | IOException e )
151168 {
152169 log .error ("Failed to install package: {}" , packageURI , e );
153- throw new InternalServerErrorException ("Package installation failed: " + e .getMessage (), e );
170+ throw new WebApplicationException ("Package installation failed: " + e .getMessage (), e );
154171 }
155172 }
156173
@@ -242,18 +259,68 @@ private String downloadStylesheet(URI uri) throws IOException
242259 }
243260
244261 /**
245- * Installs ontology by POSTing to namespace graph.
262+ * Installs ontology by PUTting as a new document and adding owl:imports to namespace ontology.
263+ *
264+ * @param app the end-user application
265+ * @param ontologyModel the package ontology model
266+ * @param packageOntologyURI the package ontology URI
267+ * @throws IOException if installation fails
246268 */
247- private void installOntology (EndUserApplication app , Model ontologyModel ) throws IOException
269+ private void installOntology (EndUserApplication app , Model ontologyModel , String packageOntologyURI ) throws IOException
248270 {
249- if (log .isDebugEnabled ()) log .debug ("Posting package ontology to namespace graph" );
250-
251- // POST to admin namespace graph
252271 AdminApplication adminApp = app .getAdminApplication ();
253- String namespaceGraphURI = UriBuilder .fromUri (adminApp .getBaseURI ()).path ("model/ontologies/namespace" ).build ().toString ();
254272
255- // Use Graph Store Protocol to add ontology to namespace graph
256- adminApp .getService ().getGraphStoreClient ().add (namespaceGraphURI , ontologyModel );
273+ // 1. Create hash of package URI to use as document slug
274+ String hash ;
275+ try
276+ {
277+ MessageDigest md = MessageDigest .getInstance ("SHA-1" );
278+ md .update (packageOntologyURI .getBytes (StandardCharsets .UTF_8 ));
279+ hash = Hex .encodeHexString (md .digest ());
280+ if (log .isDebugEnabled ()) log .debug ("Package ontology URI '{}' hashed to '{}'" , packageOntologyURI , hash );
281+ }
282+ catch (NoSuchAlgorithmException e )
283+ {
284+ throw new IOException ("Failed to hash package ontology URI" , e );
285+ }
286+
287+ // 2. PUT package ontology as a document under model/ontologies/{hash}/ (overwrites if exists)
288+ URI ontologyDocumentURI = UriBuilder .fromUri (adminApp .getBaseURI ()).path ("ontologies/{hash}/" ).build (hash );
289+ if (log .isDebugEnabled ()) log .debug ("PUTting package ontology to document: {}" , ontologyDocumentURI );
290+
291+ LinkedDataClient ldc = LinkedDataClient .create (getSystem ().getClient (), getSystem ().getMediaTypes ());
292+
293+ // Delegate agent credentials if authenticated
294+ if (getAgentContext ().isPresent ())
295+ {
296+ if (log .isDebugEnabled ()) log .debug ("Delegating agent credentials for PUT request" );
297+ ldc = ldc .delegation (adminApp .getBaseURI (), getAgentContext ().get ());
298+ }
299+
300+ try (Response putResponse = ldc .put (ontologyDocumentURI , ontologyModel , new MultivaluedHashMap <>()))
301+ {
302+ if (!putResponse .getStatusInfo ().getFamily ().equals (Response .Status .Family .SUCCESSFUL ))
303+ {
304+ throw new IOException ("Failed to PUT package ontology to " + ontologyDocumentURI + ": " + putResponse .getStatus ());
305+ }
306+ if (log .isDebugEnabled ()) log .debug ("Package ontology PUT response status: {}" , putResponse .getStatus ());
307+ }
308+
309+ // 3. Add owl:imports triple to namespace ontology in namespace graph
310+ String namespaceOntologyURI = app .getOntology ().getURI ();
311+ String namespaceGraphURI = UriBuilder .fromUri (adminApp .getBaseURI ()).path ("ontologies/namespace/" ).build ().toString ();
312+
313+ if (log .isDebugEnabled ()) log .debug ("Adding owl:imports from namespace ontology '{}' to package ontology '{}'" , namespaceOntologyURI , packageOntologyURI );
314+
315+ Model importsModel = ModelFactory .createDefaultModel ();
316+ Resource nsOntology = importsModel .createResource (namespaceOntologyURI );
317+ nsOntology .addProperty (OWL .imports , importsModel .createResource (packageOntologyURI ));
318+
319+ adminApp .getService ().getGraphStoreClient ().add (namespaceGraphURI , importsModel );
320+
321+ // 4. Clear and reload namespace ontology from cache
322+ if (log .isDebugEnabled ()) log .debug ("Clearing and reloading namespace ontology '{}'" , namespaceOntologyURI );
323+ getResourceContext ().getResource (Clear .class ).post (namespaceOntologyURI , null );
257324 }
258325
259326 /**
@@ -348,12 +415,32 @@ public ServletContext getServletContext()
348415
349416 /**
350417 * Returns RDF data manager.
351- *
418+ *
352419 * @return RDF data manager
353420 */
354421 public DataManager getDataManager ()
355422 {
356423 return dataManager ;
357424 }
358-
425+
426+ /**
427+ * Returns JAX-RS resource context.
428+ *
429+ * @return resource context
430+ */
431+ public ResourceContext getResourceContext ()
432+ {
433+ return resourceContext ;
434+ }
435+
436+ /**
437+ * Returns the authenticated agent context.
438+ *
439+ * @return agent context
440+ */
441+ public Optional <AgentContext > getAgentContext ()
442+ {
443+ return agentContext ;
444+ }
445+
359446}
0 commit comments