Skip to content

Commit 653467f

Browse files
committed
Package installation fixes
1 parent 324adaa commit 653467f

File tree

11 files changed

+119
-19
lines changed

11 files changed

+119
-19
lines changed
File renamed without changes.

config/system.trig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
lapp:origin <https://admin.localhost:4443> ;
2222
ldt:ontology <https://w3id.org/atomgraph/linkeddatahub/admin#> ;
2323
ldt:service <urn:linkeddatahub:services/admin> ;
24-
ac:stylesheet <static/xsl/layout.xsl> ;
24+
ac:stylesheet <static/xsl/admin/layout.xsl> ;
2525
lapp:endUserApplication <urn:linkeddatahub:apps/end-user> ;
2626
lapp:frontendProxy <http://varnish-frontend:6060/> .
2727

@@ -41,6 +41,7 @@
4141
lapp:origin <https://localhost:4443> ;
4242
ldt:ontology <https://localhost:4443/ns#> ;
4343
ldt:service <urn:linkeddatahub:services/end-user> ;
44+
ac:stylesheet <static/xsl/layout.xsl> ;
4445
lapp:adminApplication <urn:linkeddatahub:apps/admin> ;
4546
lapp:frontendProxy <http://varnish-frontend:6060/> ;
4647
lapp:public true .

platform/datasets/admin.trig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,7 @@ WHERE
699699
rdfs:label "Full control" ;
700700
rdfs:comment "Allows full read/write access to all application resources" ;
701701
acl:accessToClass dh:Item, dh:Container, def:Root ;
702-
acl:accessTo <clear>, <transform> ;
702+
acl:accessTo <clear>, <transform>, <packages/install>, <packages/uninstall> ;
703703
acl:mode acl:Read, acl:Append, acl:Write, acl:Control ;
704704
acl:agentGroup <acl/groups/owners/#this> .
705705

src/main/java/com/atomgraph/linkeddatahub/resource/admin/pkg/Install.java

Lines changed: 104 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import com.atomgraph.linkeddatahub.apps.model.AdminApplication;
2121
import com.atomgraph.linkeddatahub.apps.model.EndUserApplication;
2222
import com.atomgraph.linkeddatahub.client.LinkedDataClient;
23+
import com.atomgraph.linkeddatahub.resource.admin.Clear;
24+
import com.atomgraph.linkeddatahub.server.security.AgentContext;
2325
import com.atomgraph.linkeddatahub.server.util.UriPath;
2426
import com.atomgraph.linkeddatahub.server.util.XsltMasterUpdater;
2527
import jakarta.inject.Inject;
@@ -34,32 +36,42 @@
3436
import jakarta.ws.rs.WebApplicationException;
3537
import jakarta.ws.rs.client.Client;
3638
import jakarta.ws.rs.client.WebTarget;
39+
import jakarta.ws.rs.container.ResourceContext;
3740
import jakarta.ws.rs.core.Context;
3841
import jakarta.ws.rs.core.MediaType;
42+
import jakarta.ws.rs.core.MultivaluedHashMap;
3943
import jakarta.ws.rs.core.Response;
4044
import jakarta.ws.rs.core.UriBuilder;
45+
import org.apache.commons.codec.binary.Hex;
4146
import org.apache.jena.rdf.model.Model;
47+
import org.apache.jena.rdf.model.ModelFactory;
4248
import org.apache.jena.rdf.model.Resource;
49+
import org.apache.jena.vocabulary.OWL;
4350
import org.slf4j.Logger;
4451
import org.slf4j.LoggerFactory;
4552
import java.io.IOException;
4653
import java.net.URI;
54+
import java.nio.charset.StandardCharsets;
4755
import java.nio.file.Files;
4856
import java.nio.file.Path;
4957
import java.nio.file.Paths;
58+
import java.security.MessageDigest;
59+
import java.security.NoSuchAlgorithmException;
5060
import java.util.ArrayList;
5161
import java.util.List;
62+
import java.util.Optional;
5263
import java.util.Set;
5364
import 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

Comments
 (0)