Skip to content

Commit

Permalink
Change FedoraNodes.describe() to stream
Browse files Browse the repository at this point in the history
 Partial resolution of: https://www.pivotaltracker.com/story/show/59244014

- All tests passing for FedoraNodes.describe() streaming
- Improved unit test
- Licenses...
  • Loading branch information
ajs6f authored and Andrew Woods committed Nov 21, 2013
1 parent 34aab66 commit 6444f24
Show file tree
Hide file tree
Showing 47 changed files with 717 additions and 263 deletions.
156 changes: 68 additions & 88 deletions fcrepo-http-api/src/main/java/org/fcrepo/http/api/FedoraNodes.java
Expand Up @@ -16,6 +16,8 @@

package org.fcrepo.http.api;

import static com.hp.hpl.jena.graph.NodeFactory.createURI;
import static com.hp.hpl.jena.graph.Triple.create;
import static com.hp.hpl.jena.rdf.model.ModelFactory.createDefaultModel;
import static com.sun.jersey.api.Responses.clientError;
import static com.sun.jersey.api.Responses.notAcceptable;
Expand All @@ -42,9 +44,7 @@
import static org.fcrepo.jcr.FedoraJcrTypes.FEDORA_DATASTREAM;
import static org.fcrepo.jcr.FedoraJcrTypes.FEDORA_OBJECT;
import static org.fcrepo.kernel.RdfLexicon.FIRST_PAGE;
import static org.fcrepo.kernel.RdfLexicon.HAS_CHILD_COUNT;
import static org.fcrepo.kernel.RdfLexicon.NEXT_PAGE;
import static org.fcrepo.kernel.rdf.GraphProperties.INLINED_RESOURCES_MODEL;
import static org.fcrepo.kernel.rdf.GraphProperties.PROBLEMS_MODEL_NAME;
import static org.slf4j.LoggerFactory.getLogger;

Expand Down Expand Up @@ -95,15 +95,16 @@
import org.fcrepo.kernel.Datastream;
import org.fcrepo.kernel.FedoraResource;
import org.fcrepo.kernel.rdf.GraphSubjects;
import org.fcrepo.kernel.utils.iterators.RdfStream;
import org.modeshape.jcr.api.JcrConstants;
import org.slf4j.Logger;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import com.codahale.metrics.annotation.Timed;
import com.hp.hpl.jena.graph.Node;
import com.hp.hpl.jena.query.Dataset;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.Resource;

/**
* CRUD operations on Fedora Nodes
Expand Down Expand Up @@ -135,108 +136,87 @@ public class FedoraNodes extends AbstractResource {
@GET
@Produces({TURTLE, N3, N3_ALT1, N3_ALT2, RDF_XML, RDF_JSON, NTRIPLES,
TEXT_HTML})
public Dataset describe(@PathParam("path") final List<PathSegment> pathList,
public RdfStream describe(@PathParam("path") final List<PathSegment> pathList,
@QueryParam("offset") @DefaultValue("0") final int offset,
@QueryParam("limit") @DefaultValue("-1") final int limit,
@QueryParam("non-member-properties") final String nonMemberProperties,
@Context final Request request,
@Context final HttpServletResponse servletResponse,
@Context final UriInfo uriInfo) throws RepositoryException, IOException {
final String path = toPath(pathList);
logger.trace("Getting profile for {}", path);
logger.trace("Getting profile for: {}", path);

try {
final FedoraResource resource =
nodeService.getObject(session, path);


final EntityTag etag = new EntityTag(resource.getEtagValue());
final Date date = resource.getLastModifiedDate();
final Date roundedDate = new Date();
if (date != null) {
roundedDate.setTime(date.getTime() - date.getTime() % 1000);
}
final ResponseBuilder builder =
request.evaluatePreconditions(roundedDate, etag);
if (builder != null) {
final CacheControl cc = new CacheControl();
cc.setMaxAge(0);
cc.setMustRevalidate(true);
// here we are implicitly emitting a 304
// the exception is not an error, it's genuinely
// an exceptional condition
throw new WebApplicationException(builder.cacheControl(cc)
.lastModified(date).tag(etag).build());
}
final HttpGraphSubjects subjects =
new HttpGraphSubjects(session, FedoraNodes.class, uriInfo);

final int realLimit;
if (nonMemberProperties != null && limit == -1) {
realLimit = -2;
} else {
realLimit = limit;
}
final FedoraResource resource = nodeService.getObject(session, path);

final Dataset propertiesDataset =
resource.getPropertiesDataset(subjects, offset, realLimit);

final Model treeModel = propertiesDataset.getNamedModel(
propertiesDataset.getContext()
.getAsString(INLINED_RESOURCES_MODEL,
"NO SUCH MODEL"));
if (limit > 0 && treeModel != null && treeModel
.contains(subjects.getGraphSubject(resource.getNode()),
HAS_CHILD_COUNT)) {

final Model requestModel = createDefaultModel();

final long childCount = treeModel
.listObjectsOfProperty(subjects.getGraphSubject(resource.getNode()),
HAS_CHILD_COUNT)
.nextNode().asLiteral().getLong();

if (childCount > (offset + limit)) {

final Resource nextPageResource =
requestModel.createResource(uriInfo
.getRequestUriBuilder()
.replaceQueryParam("offset", offset + limit)
.replaceQueryParam("limit", limit)
.build()
.toString());
requestModel.add(subjects.getContext(), NEXT_PAGE, nextPageResource);
}
final EntityTag etag = new EntityTag(resource.getEtagValue());
final Date date = resource.getLastModifiedDate();
final Date roundedDate = new Date();
if (date != null) {
roundedDate.setTime(date.getTime() - date.getTime() % 1000);
}
final ResponseBuilder builder =
request.evaluatePreconditions(roundedDate, etag);
if (builder != null) {
final CacheControl cc = new CacheControl();
cc.setMaxAge(0);
cc.setMustRevalidate(true);
// here we are implicitly emitting a 304
// the exception is not an error, it's genuinely
// an exceptional condition
throw new WebApplicationException(builder.cacheControl(cc)
.lastModified(date).tag(etag).build());
}
final HttpGraphSubjects subjects =
new HttpGraphSubjects(session, this.getClass(), uriInfo);

final String firstPage = uriInfo
.getRequestUriBuilder()
.replaceQueryParam("offset", 0)
.replaceQueryParam("limit", limit)
.build().toString();
final Resource firstPageResource =
requestModel.createResource(firstPage);
servletResponse.addHeader("Link", firstPage + ";rel=\"first\"");
requestModel.add(subjects.getContext(), FIRST_PAGE, firstPageResource);
final int realLimit;
if (nonMemberProperties != null && limit == -1) {
realLimit = -2;
} else {
realLimit = limit;
}

propertiesDataset.addNamedModel("requestModel", requestModel);
final RdfStream rdfStream =
resource.getTriples(subjects).concat(
resource.getHierarchyTriples(subjects)).session(session)
.topic(subjects.getGraphSubject(resource.getNode())
.asNode());
if (realLimit != -2) {
final Node firstPage =
createURI(uriInfo.getRequestUriBuilder().replaceQueryParam(
"offset", 0).replaceQueryParam("limit", limit).build()
.toString().replace("&", "&amp;"));
final Node nextPage =
createURI(uriInfo.getRequestUriBuilder().replaceQueryParam(
"offset", offset + limit).replaceQueryParam("limit",
limit).build().toString().replace("&", "&amp;"));
rdfStream.concat(
create(subjects.getContext().asNode(), NEXT_PAGE.asNode(),
nextPage),
create(subjects.getContext().asNode(), FIRST_PAGE.asNode(),
firstPage)).limit(realLimit).skip(offset);

servletResponse.addHeader("Link", firstPage + ";rel=\"first\"");
}

}

if (!etag.getValue().isEmpty()) {
servletResponse.addHeader("ETag", etag.toString());
}
if (!etag.getValue().isEmpty()) {
servletResponse.addHeader("ETag", etag.toString());
}

servletResponse.addHeader("Accept-Patch", contentTypeSPARQLUpdate);
servletResponse.addHeader("Link", "http://www.w3.org/ns/ldp/Resource;rel=\"type\"");
if (resource.getLastModifiedDate() != null) {
servletResponse.addDateHeader("Last-Modified", resource
.getLastModifiedDate().getTime());
}
servletResponse.addHeader("Accept-Patch", contentTypeSPARQLUpdate);
servletResponse.addHeader("Link",
"http://www.w3.org/ns/ldp/Resource;rel=\"type\"");

addResponseInformationToDataset(resource, propertiesDataset,
uriInfo, subjects);
addResponseInformationToStream(resource, rdfStream, uriInfo,
subjects);

return propertiesDataset;
return rdfStream;

} finally {
session.logout();
}

}

Expand Down
Expand Up @@ -92,7 +92,8 @@ public RdfStream getVersionList(@PathParam("path")

final FedoraResource resource = nodeService.getObject(session, path);

return resource.getVersionTriples(translator());
return resource.getVersionTriples(translator()).session(session).topic(
translator().getGraphSubject(resource.getNode()).asNode());
}

/**
Expand Down Expand Up @@ -152,7 +153,8 @@ public RdfStream getVersion(@PathParam("path")
if (resource == null) {
throw new WebApplicationException(status(NOT_FOUND).build());
} else {
return resource.getTriples(translator());
return resource.getTriples(translator()).session(session).topic(
translator().getGraphSubject(resource.getNode()).asNode());
}
}

Expand Down
Expand Up @@ -57,7 +57,7 @@
import org.fcrepo.http.commons.responses.HtmlTemplate;
import org.fcrepo.http.commons.session.InjectedSession;
import org.fcrepo.kernel.rdf.GraphSubjects;
import org.fcrepo.kernel.utils.JcrRdfTools;
import org.fcrepo.kernel.rdf.JcrRdfTools;
import org.slf4j.Logger;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
Expand Down
@@ -0,0 +1,28 @@
/**
* Copyright 2013 DuraSpace, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fcrepo.http.api.responses;

import org.fcrepo.http.commons.responses.StreamingBaseHtmlProvider;
import org.springframework.stereotype.Component;

/**
* JAX-RS provider for taking an {@link RdfStream} and returning some nice looking
* HTML
*/
@Component
public class StreamingHtmlProvider extends StreamingBaseHtmlProvider{

}
Expand Up @@ -14,7 +14,7 @@
<hr />
#end

<form id="action_create" method="POST" enctype="multipart/form-data">
<form id="action_create" name="action_create" method="POST" enctype="multipart/form-data">
<h3>Create New Node</h3>
<div class="form-group">
<label for="new_mixin" class="control-label">
Expand Down Expand Up @@ -61,10 +61,10 @@ WHERE { }
</form>


<form id="action_delete" action="javascript:deleteItem()" method="POST">
<form id="action_delete" name="action_delete" action="javascript:deleteItem()" method="POST">
<input type="hidden" name="_method" value="DELETE" />
<h3>Delete Object</h3>
<button type="submit" class="btn btn-danger">Delete</button>
<button name="delete-button" type="submit" class="btn btn-danger">Delete</button>
<hr />
</form>

Expand Down

0 comments on commit 6444f24

Please sign in to comment.