Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Replace regex with jena request parser
  • Loading branch information
acoburn authored and Andrew Woods committed Oct 2, 2015
1 parent a532284 commit 6ea28b2
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 54 deletions.
Expand Up @@ -24,7 +24,7 @@
import static com.hp.hpl.jena.update.UpdateAction.execute;
import static com.hp.hpl.jena.update.UpdateFactory.create;
import static java.util.Arrays.asList;
import static java.util.regex.Pattern.compile;
import static java.util.stream.Collectors.joining;
import static org.apache.commons.codec.digest.DigestUtils.shaHex;
import static org.fcrepo.kernel.api.services.functions.JcrPropertyFunctions.isFrozen;
import static org.fcrepo.kernel.api.services.functions.JcrPropertyFunctions.property2values;
Expand All @@ -41,14 +41,15 @@
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.jcr.AccessDeniedException;
import javax.jcr.ItemNotFoundException;
Expand Down Expand Up @@ -87,6 +88,9 @@
import org.slf4j.Logger;

import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.sparql.modify.request.UpdateData;
import com.hp.hpl.jena.sparql.modify.request.UpdateDeleteWhere;
import com.hp.hpl.jena.sparql.modify.request.UpdateModify;
import com.hp.hpl.jena.update.UpdateRequest;

/**
Expand All @@ -102,16 +106,6 @@ public class FedoraResourceImpl extends JcrTools implements FedoraJcrTypes, Fedo

protected Node node;

/*
* Helps split SPARQL Update statements, e.g., on <>, to enable individual processing
*/
private static final Pattern subject = compile(".+<[a-zA-Z]*>");

/*
* Helps ensure there's no terminating slash in the predicate
*/
private static final Pattern terminated = compile("/>");

/**
* Construct a {@link org.fcrepo.kernel.api.models.FedoraResource} from an existing JCR Node
* @param node an existing JCR node to treat as an fcrepo object
Expand Down Expand Up @@ -435,61 +429,28 @@ public void updateProperties(final IdentifierConverter<Resource, FedoraResource>
final String sparqlUpdateStatement, final RdfStream originalTriples)
throws MalformedRdfException, AccessDeniedException {

if (!clean(sparqlUpdateStatement)) {
throw new IllegalArgumentException("Invalid SPARQL UPDATE statement:"
+ sparqlUpdateStatement);
}

final Model model = originalTriples.asModel();

final UpdateRequest request = create(sparqlUpdateStatement,
idTranslator.reverse().convert(this).toString());

final Collection<IllegalArgumentException> errors = checkInvalidPredicates(request);

if (!errors.isEmpty()) {
throw new IllegalArgumentException(errors.stream().map(Exception::getMessage).collect(joining(",\n")));
}

final JcrPropertyStatementListener listener = new JcrPropertyStatementListener(
idTranslator, getSession(), idTranslator.reverse().convert(this).asNode());

model.register(listener);

final UpdateRequest request = create(sparqlUpdateStatement,
idTranslator.reverse().convert(this).toString());
model.setNsPrefixes(request.getPrefixMapping());
execute(request, model);

listener.assertNoExceptions();
}

/**
* Helps ensure that there are no terminating slashes in the predicate.
* A terminating slash means ModeShape has trouble extracting the localName, e.g., for
* http://myurl.org/.
*
* @see <a href="https://jira.duraspace.org/browse/FCREPO-1409"> FCREPO-1409 </a> for details.
*
* @param updateStmt SPARQL Update statement specified by the user
* @return whether the statement is deemed to be not problematic for ModeShape
*/
private static boolean clean(final String updateStmt) {
final int start = updateStmt.indexOf("INSERT");
final int end = updateStmt.lastIndexOf("}");

if (start < 0 || end < 0 || end < start) {
return true;
}

final String insertStmt = updateStmt.substring(start, end);
final String[] insert = subject.split(insertStmt);
int count = 0;
final String terminatorIndicator = terminated.pattern();

for (final String s: insert) {
if (s.contains(terminatorIndicator)) {
final String[] p = terminated.split(s);
count++;
LOGGER.info("Problematic token({}):{}{} in statement:{}",
count, p[0], terminated, updateStmt);
}
}

return count == 0;
}

@Override
public RdfStream getTriples(final IdentifierConverter<Resource, FedoraResource> idTranslator,
final Class<? extends RdfStream> context) {
Expand Down Expand Up @@ -720,6 +681,32 @@ public Node getNodeVersion(final String label) {

}

/**
* Helps ensure that there are no terminating slashes in the predicate.
* A terminating slash means ModeShape has trouble extracting the localName, e.g., for
* http://myurl.org/.
*
* @see <a href="https://jira.duraspace.org/browse/FCREPO-1409"> FCREPO-1409 </a> for details.
*/
private Collection<IllegalArgumentException> checkInvalidPredicates(final UpdateRequest request) {
return request.getOperations().stream()
.flatMap(x -> {
if (x instanceof UpdateModify) {
final UpdateModify y = (UpdateModify)x;
return Stream.concat(y.getInsertQuads().stream(), y.getDeleteQuads().stream());
} else if (x instanceof UpdateData) {
return ((UpdateData)x).getQuads().stream();
} else if (x instanceof UpdateDeleteWhere) {
return ((UpdateDeleteWhere)x).getQuads().stream();
} else {
return Stream.empty();
}
})
.filter(x -> x.getPredicate().isURI() && x.getPredicate().getURI().endsWith("/"))
.map(x -> new IllegalArgumentException("Invalid predicate ends with '/': " + x.getPredicate().getURI()))
.collect(Collectors.toList());
}

private Node getFrozenNode(final String label) throws RepositoryException {
try {
final Session session = getSession();
Expand Down
Expand Up @@ -465,6 +465,17 @@ public void testInvalidSparqlUpdateValidation() throws RepositoryException {
new RdfStream());
}

@Test
public void testValidSparqlUpdateWithLiteralTrailingSlash() throws RepositoryException {
final String pid = UUID.randomUUID().toString();
final FedoraResource object = containerService.findOrCreate(session, pid);
object.updateProperties(
subjects,
"INSERT { <> <http://myurl.org/title> \"fancy title/\" . \n" +
" <> <http://myurl.org/title> \"fancy title 2<br/>\" . } WHERE { }",
new RdfStream());
}

@Test
public void testValidSparqlUpdateValidationAltSyntax() throws RepositoryException {
final String pid = UUID.randomUUID().toString();
Expand Down

0 comments on commit 6ea28b2

Please sign in to comment.