Repository: marmotta Updated Branches: refs/heads/develop 4daf7bdf7 -> bf7ee37eb
MARMOTTA-558: Improved Generation of new URIs when resources are POSTed. Project: http://git-wip-us.apache.org/repos/asf/marmotta/repo Commit: http://git-wip-us.apache.org/repos/asf/marmotta/commit/bf7ee37e Tree: http://git-wip-us.apache.org/repos/asf/marmotta/tree/bf7ee37e Diff: http://git-wip-us.apache.org/repos/asf/marmotta/diff/bf7ee37e Branch: refs/heads/develop Commit: bf7ee37ebd6d85fdea746199fd4cd1dda1961f77 Parents: 406ce98 Author: Jakob Frank <[email protected]> Authored: Thu Oct 30 17:15:41 2014 +0100 Committer: Jakob Frank <[email protected]> Committed: Fri Oct 31 09:56:38 2014 +0100 ---------------------------------------------------------------------- .../ldp/util/AbstractResourceUriGenerator.java | 63 ++++++++++++++++ .../platform/ldp/util/RandomUriGenerator.java | 77 ++++++++++++++++++++ .../platform/ldp/util/SlugUriGenerator.java | 50 +++++++++++++ .../platform/ldp/util/UuidUriGenerator.java | 38 ++++++++++ .../platform/ldp/webservices/LdpWebService.java | 46 ++++-------- 5 files changed, 244 insertions(+), 30 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/marmotta/blob/bf7ee37e/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/util/AbstractResourceUriGenerator.java ---------------------------------------------------------------------- diff --git a/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/util/AbstractResourceUriGenerator.java b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/util/AbstractResourceUriGenerator.java new file mode 100644 index 0000000..c3fe05f --- /dev/null +++ b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/util/AbstractResourceUriGenerator.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.marmotta.platform.ldp.util; + +import org.apache.commons.lang3.StringUtils; +import org.apache.marmotta.platform.ldp.api.LdpService; +import org.openrdf.repository.RepositoryConnection; +import org.openrdf.repository.RepositoryException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Abstract baseclass to generate new URIs for a created resource. + */ +public abstract class AbstractResourceUriGenerator { + + protected final Logger log = LoggerFactory.getLogger(this.getClass()); + + private final LdpService ldpService; + private final String container; + private final RepositoryConnection connection; + + protected AbstractResourceUriGenerator(LdpService ldpService, String container, RepositoryConnection connection) { + this.ldpService = ldpService; + this.container = StringUtils.removeEnd(container, "/"); + this.connection = connection; + } + + public String generateResourceUri() throws RepositoryException { + String newResource = String.format("%s/%s", container, generateNextLocalName()); + log.trace("Checking possible name clash for new resource <{}>", newResource); + if (ldpService.exists(connection, newResource) || ldpService.isReusedURI(connection, newResource)) { + do { + final String candidate = String.format("%s/%s", container, generateNextLocalName()); + log.trace("<{}> already exists, trying <{}>", newResource, candidate); + newResource = candidate; + } while (ldpService.exists(connection, newResource) || ldpService.isReusedURI(connection, newResource)); + log.debug("resolved name clash, new resource will be <{}>", newResource); + } else { + log.debug("no name clash for <{}>", newResource); + } + + return newResource; + } + + protected abstract String generateNextLocalName(); + +} http://git-wip-us.apache.org/repos/asf/marmotta/blob/bf7ee37e/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/util/RandomUriGenerator.java ---------------------------------------------------------------------- diff --git a/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/util/RandomUriGenerator.java b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/util/RandomUriGenerator.java new file mode 100644 index 0000000..fe5ef6d --- /dev/null +++ b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/util/RandomUriGenerator.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.marmotta.platform.ldp.util; + +import org.apache.marmotta.platform.ldp.api.LdpService; +import org.openrdf.repository.RepositoryConnection; + +import java.util.Random; + +/** + * Random-Based URI Generator + */ +public class RandomUriGenerator extends AbstractResourceUriGenerator { + + private static final char[] symbols; + + static { + StringBuilder sb = new StringBuilder(); + for (char ch = '0'; ch <= '9'; ++ch) { + sb.append(ch); + } + for (char ch = 'a'; ch <= 'z'; ++ch) { + sb.append(ch); + } + for (char ch = 'A'; ch <= 'Z'; ++ch) { + sb.append(ch); + } + symbols = sb.toString().toCharArray(); + } + + private final StringBuilder builder; + private final Random random; + + + public RandomUriGenerator(LdpService ldpService, String container, RepositoryConnection connection) { + super(ldpService, container, connection); + builder = new StringBuilder(); + random = new Random(); + } + + @Override + protected String generateNextLocalName() { + return builder.append(symbols[random.nextInt(symbols.length)]).toString(); + } +} http://git-wip-us.apache.org/repos/asf/marmotta/blob/bf7ee37e/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/util/SlugUriGenerator.java ---------------------------------------------------------------------- diff --git a/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/util/SlugUriGenerator.java b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/util/SlugUriGenerator.java new file mode 100644 index 0000000..e07a10f --- /dev/null +++ b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/util/SlugUriGenerator.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.marmotta.platform.ldp.util; + +import org.apache.marmotta.platform.ldp.api.LdpService; +import org.openrdf.repository.RepositoryConnection; + +/** + * Slug-Header based Generator for resource URIs. + */ +public class SlugUriGenerator extends AbstractResourceUriGenerator { + + private final String slug; + private int i = 0; + + public SlugUriGenerator(LdpService ldpService, String container, String slug, RepositoryConnection connection) { + super(ldpService, container, connection); + + String localName = LdpUtils.urify(slug); + log.trace("Slug urified: {}", localName); + + this.slug = localName; + } + + @Override + protected String generateNextLocalName() { + if (i < 1) { + i++; + return slug; + } else { + return String.format("%s-%d", slug, i++); + } + } + +} http://git-wip-us.apache.org/repos/asf/marmotta/blob/bf7ee37e/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/util/UuidUriGenerator.java ---------------------------------------------------------------------- diff --git a/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/util/UuidUriGenerator.java b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/util/UuidUriGenerator.java new file mode 100644 index 0000000..75860cb --- /dev/null +++ b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/util/UuidUriGenerator.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.marmotta.platform.ldp.util; + +import org.apache.marmotta.platform.ldp.api.LdpService; +import org.openrdf.repository.RepositoryConnection; + +import java.util.UUID; + +/** + * UUID-based URI generator. + */ +public class UuidUriGenerator extends AbstractResourceUriGenerator { + + public UuidUriGenerator(LdpService ldpService, String container, RepositoryConnection connection) { + super(ldpService, container, connection); + } + + @Override + protected String generateNextLocalName() { + return UUID.randomUUID().toString(); + } +} http://git-wip-us.apache.org/repos/asf/marmotta/blob/bf7ee37e/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/webservices/LdpWebService.java ---------------------------------------------------------------------- diff --git a/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/webservices/LdpWebService.java b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/webservices/LdpWebService.java index f504885..98b5883 100644 --- a/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/webservices/LdpWebService.java +++ b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/webservices/LdpWebService.java @@ -33,7 +33,10 @@ import org.apache.marmotta.platform.ldp.exceptions.InvalidModificationException; import org.apache.marmotta.platform.ldp.patch.InvalidPatchDocumentException; import org.apache.marmotta.platform.ldp.patch.parser.ParseException; import org.apache.marmotta.platform.ldp.patch.parser.RdfPatchParser; +import org.apache.marmotta.platform.ldp.util.AbstractResourceUriGenerator; import org.apache.marmotta.platform.ldp.util.LdpUtils; +import org.apache.marmotta.platform.ldp.util.RandomUriGenerator; +import org.apache.marmotta.platform.ldp.util.SlugUriGenerator; import org.jboss.resteasy.spi.NoLogWebApplicationException; import org.openrdf.model.Statement; import org.openrdf.model.URI; @@ -386,21 +389,6 @@ public class LdpWebService { // Get the LDP-Interaction Model (Sec. 5.2.3.4 and Sec. 4.2.1.4) final LdpService.InteractionModel ldpInteractionModel = ldpService.getInteractionModel(linkHeaders); - final String localName; - if (StringUtils.isBlank(slug)) { - /* Sec. 5.2.3.8) */ - // FIXME: Maybe us a shorter uid? - localName = UUID.randomUUID().toString(); - } else { - // Honor client wishes from Slug-header (Sec. 5.2.3.10) - // http://www.ietf.org/rfc/rfc5023.txt - log.trace("Slug-Header is '{}'", slug); - localName = LdpUtils.urify(slug); - log.trace("Slug-Header urified: {}", localName); - } - - String newResource = uriInfo.getRequestUriBuilder().path(localName).build().toString(); - if (ldpService.isNonRdfSourceResource(conn, container)) { log.info("POSTing to a NonRdfSource is not allowed ({})", container); final Response.ResponseBuilder response = createResponse(conn, Response.Status.METHOD_NOT_ALLOWED, container).entity("POST to NonRdfSource is not allowed\n"); @@ -408,20 +396,19 @@ public class LdpWebService { return response.build(); } - log.trace("Checking possible name clash for new resource <{}>", newResource); - if (ldpService.exists(conn, newResource) || ldpService.isReusedURI(conn, newResource)) { - int i = 0; - final String base = newResource; - do { - final String candidate = base + "-" + (++i); - log.trace("<{}> already exists, trying <{}>", newResource, candidate); - newResource = candidate; - } while (ldpService.exists(conn, newResource) || ldpService.isReusedURI(conn, newResource)); - log.debug("resolved name clash, new resource will be <{}>", newResource); + final AbstractResourceUriGenerator uriGenerator; + if (StringUtils.isBlank(slug)) { + /* Sec. 5.2.3.8) */ + uriGenerator = new RandomUriGenerator(ldpService, container, conn); } else { - log.debug("no name clash for <{}>", newResource); + // Honor client wishes from Slug-header (Sec. 5.2.3.10) + // http://www.ietf.org/rfc/rfc5023.txt + log.trace("Slug-Header is '{}'", slug); + uriGenerator = new SlugUriGenerator(ldpService, container, slug, conn); } + String newResource = uriGenerator.generateResourceUri(); + log.debug("POST to <{}> will create new LDP-R <{}>", container, newResource); // connection is closed by buildPostResponse return buildPostResponse(conn, container, newResource, ldpInteractionModel, postBody, type); @@ -449,7 +436,7 @@ public class LdpWebService { String location = ldpService.addResource(connection, container, newResource, interactionModel, mimeType, requestBody); final Response.ResponseBuilder response = createResponse(connection, Response.Status.CREATED, container).location(java.net.URI.create(location)); if (newResource.compareTo(location) != 0) { - response.links(Link.fromUri(newResource).rel(LINK_REL_DESCRIBEDBY).param(LINK_PARAM_ANCHOR, location).build()); //FIXME: Sec. 5.2.3.12, see also http://www.w3.org/2012/ldp/track/issues/15 + response.links(Link.fromUri(newResource).rel(LINK_REL_DESCRIBEDBY).param(LINK_PARAM_ANCHOR, location).build()); } connection.commit(); return response.build(); @@ -769,14 +756,13 @@ public class LdpWebService { final URI rdfSource = ldpService.getRdfSourceForNonRdfSource(connection, resource); if (rdfSource != null) { // Sec. 5.2.8.1 and 5.2.3.12 - // FIXME: Sec. 5.2.3.12, see also http://www.w3.org/2012/ldp/track/issues/15 rb.link(rdfSource.stringValue(), LINK_REL_DESCRIBEDBY); - // TODO: Propose to LDP-WG? + // This is not covered by the Spec, but is very convenient to have rb.link(rdfSource.stringValue(), LINK_REL_META); } final URI nonRdfSource = ldpService.getNonRdfSourceForRdfSource(connection, resource); if (nonRdfSource != null) { - // TODO: Propose to LDP-WG? + // This is not covered by the Spec, but is very convenient to have rb.link(nonRdfSource.stringValue(), LINK_REL_CONTENT); }
