Repository: olingo-odata4 Updated Branches: refs/heads/master b8187f530 -> 1f286fd42
[OLINGO-663] helper for conditional handling Change-Id: I48599dc609bfb53bc2903083abe235458109e20a Signed-off-by: Christian Amend <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/1f286fd4 Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/1f286fd4 Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/1f286fd4 Branch: refs/heads/master Commit: 1f286fd4240a3bbaa9fb7fb50fdb5e44ba9169f0 Parents: b8187f5 Author: Klaus Straubinger <[email protected]> Authored: Wed Jun 3 10:42:54 2015 +0200 Committer: Christian Amend <[email protected]> Committed: Wed Jun 3 11:00:16 2015 +0200 ---------------------------------------------------------------------- .../olingo/server/api/CustomETagSupport.java | 74 ----------- .../olingo/server/api/ETagInformation.java | 74 ----------- .../org/apache/olingo/server/api/OData.java | 11 +- .../olingo/server/api/ODataHttpHandler.java | 1 + .../server/api/etag/CustomETagSupport.java | 74 +++++++++++ .../olingo/server/api/etag/ETagHelper.java | 66 ++++++++++ .../api/etag/PreconditionRequiredException.java | 54 ++++++++ .../apache/olingo/server/core/ETagParser.java | 86 ------------ .../olingo/server/core/ODataDispatcher.java | 2 + .../server/core/ODataExceptionHelper.java | 1 + .../apache/olingo/server/core/ODataHandler.java | 3 +- .../server/core/ODataHttpHandlerImpl.java | 2 +- .../apache/olingo/server/core/ODataImpl.java | 23 ++-- .../core/PreconditionRequiredException.java | 52 -------- .../server/core/PreconditionsValidator.java | 127 ------------------ .../olingo/server/core/etag/ETagHelperImpl.java | 72 ++++++++++ .../server/core/etag/ETagInformation.java | 74 +++++++++++ .../olingo/server/core/etag/ETagParser.java | 86 ++++++++++++ .../core/etag/PreconditionsValidator.java | 128 ++++++++++++++++++ .../olingo/server/core/ETagParserTest.java | 131 ------------------- .../olingo/server/core/etag/ETagParserTest.java | 129 ++++++++++++++++++ .../olingo/server/tecsvc/ETagSupport.java | 2 +- .../processor/TechnicalEntityProcessor.java | 20 +-- .../TechnicalPrimitiveComplexProcessor.java | 22 +++- .../tecsvc/processor/TechnicalProcessor.java | 32 ----- .../server/core/PreconditionsValidatorTest.java | 4 +- 26 files changed, 733 insertions(+), 617 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1f286fd4/lib/server-api/src/main/java/org/apache/olingo/server/api/CustomETagSupport.java ---------------------------------------------------------------------- diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/CustomETagSupport.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/CustomETagSupport.java deleted file mode 100644 index 5e4f500..0000000 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/CustomETagSupport.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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.olingo.server.api; - -import org.apache.olingo.commons.api.edm.EdmBindingTarget; - -/** - * <p>Processors that would like to support etags for certain entity sets can implement this - * interface.</p> - * <p>If implemented this interface can be registered at the ODataHttpHandler. This will result in change request to - * require an if-match/if-none-match or an if-modified-since/if-unmodified-since header. Otherwise the request will - * result in a "Precondition Required" response</p> - */ -public interface CustomETagSupport { - - /** - * This method will be called for update requests which target an entity or a property of an entity. - * If this method returns true and an header is not specified we will return a "Precondition Required" response. - * Validation has to be performed inside the processor methods after the dispatching. - * If this method returns false and an header is specified we will ignore the header. - * @param entitySetOrSingleton - * @return true if the entity set specified needs an if-match/if-none-match header - */ - boolean hasETag(EdmBindingTarget entitySetOrSingleton); - - /** - * This method will be called for update requests which target a media entity value. - * If this method returns true and an header is not specified we will return a "Precondition Required" response. - * Validation has to be performed inside the processor methods after the dispatching. - * If this method returns false and an header is specified we will ignore the header. - * @param entitySetOrSingleton - * @return true if the entity set specified needs an if-match/if-none-match header - */ - boolean hasMediaETag(EdmBindingTarget entitySetOrSingleton); - - /** - * Since the Olingo library cannot generate a metadata document etag in a generic way we call this method to retrieve - * an application specific etag for the metadata document. If this interface is registered applications can return an - * etag or null here to provide caching support for clients. If a client sends a GET request to the metadata document - * and this method delivers an etag we will match it to the request. If there has been no modification we will return - * a 304 NOT MODIFIED status code. If this interface is not registered or delivers null we just send back the usual - * metadata response. - * @return the application generated etag for the metadata document - */ - String getMetadataETag(); - - /** - * Since the Olingo library cannot generate a service document etag in a generic way we call this method to retrieve - * an application specific etag for the service document. If this interface is registered applications can return an - * etag or null here to provide caching support for clients. If a client sends a GET request to the service document - * and this method delivers an etag we will match it to the request. If there has been no modification we will return - * a 304 NOT MODIFIED status code. If this interface is not registered or delivers null we just send back the usual - * service document response. - * @return the application generated etag for the service document - */ - String getServiceDocumentETag(); - -} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1f286fd4/lib/server-api/src/main/java/org/apache/olingo/server/api/ETagInformation.java ---------------------------------------------------------------------- diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/ETagInformation.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/ETagInformation.java deleted file mode 100644 index fa65722..0000000 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/ETagInformation.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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.olingo.server.api; - -import java.util.Collection; - -/** - * Information about the values of an ETag-relevant HTTP header. - */ -public class ETagInformation { - private final boolean all; - private final Collection<String> eTags; - - public ETagInformation(final boolean all, final Collection<String> eTags) { - this.all = all; - this.eTags = eTags; - } - - /** - * Gets the information whether the values contain "*". - */ - public boolean isAll() { - return all; - } - - /** - * Gets the collection of ETag values found. - * It is empty if {@link #isAll()} returns <code>true</code>. - */ - public Collection<String> getETags() { - return eTags; - } - - /** - * <p>Checks whether a given ETag value is matched by this ETag information, - * using weak comparison as described in - * <a href="https://www.ietf.org/rfc/rfc7232.txt">RFC 7232</a>, section 2.3.2.</p> - * <p>If the given value is <code>null</code>, or if this ETag information - * does not contain anything, the result is <code>false</code>.</p> - * @param eTag the ETag value to match - * @return a boolean match result - */ - public boolean isMatchedBy(final String eTag) { - if (eTag == null) { - return false; - } else if (all) { - return true; - } else { - for (final String candidate : eTags) { - if ((eTag.startsWith("W/") ? eTag.substring(2) : eTag) - .equals(candidate.startsWith("W/") ? candidate.substring(2) : candidate)) { - return true; - } - } - return false; - } - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1f286fd4/lib/server-api/src/main/java/org/apache/olingo/server/api/OData.java ---------------------------------------------------------------------- diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/OData.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/OData.java index 2ea9ec7..c8914ef 100644 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/OData.java +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/OData.java @@ -18,7 +18,6 @@ */ package org.apache.olingo.server.api; -import java.util.Collection; import java.util.List; import org.apache.olingo.commons.api.ODataRuntimeException; @@ -30,6 +29,7 @@ import org.apache.olingo.server.api.deserializer.DeserializerException; import org.apache.olingo.server.api.deserializer.FixedFormatDeserializer; import org.apache.olingo.server.api.deserializer.ODataDeserializer; import org.apache.olingo.server.api.edmx.EdmxReference; +import org.apache.olingo.server.api.etag.ETagHelper; import org.apache.olingo.server.api.serializer.FixedFormatSerializer; import org.apache.olingo.server.api.serializer.ODataSerializer; import org.apache.olingo.server.api.serializer.SerializerException; @@ -118,11 +118,8 @@ public abstract class OData { public abstract EdmPrimitiveType createPrimitiveTypeInstance(EdmPrimitiveTypeKind kind); /** - * Creates ETag information from the values of a HTTP header - * containing a list of entity tags or a single star character, i.e., - * <code>If-Match</code> and <code>If-None-Match</code>. - * @param values the collection of header values - * @return an {@link ETagInformation} instance + * Creates a new ETag helper object for performing ETag-related tasks. + * It can be used in Processor implementations. */ - public abstract ETagInformation createETagInformation(final Collection<String> values); + public abstract ETagHelper createETagHelper(); } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1f286fd4/lib/server-api/src/main/java/org/apache/olingo/server/api/ODataHttpHandler.java ---------------------------------------------------------------------- diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/ODataHttpHandler.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/ODataHttpHandler.java index 3700291..20ed77e 100644 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/ODataHttpHandler.java +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/ODataHttpHandler.java @@ -21,6 +21,7 @@ package org.apache.olingo.server.api; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.olingo.server.api.etag.CustomETagSupport; import org.apache.olingo.server.api.processor.Processor; import org.apache.olingo.server.api.serializer.CustomContentTypeSupport; http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1f286fd4/lib/server-api/src/main/java/org/apache/olingo/server/api/etag/CustomETagSupport.java ---------------------------------------------------------------------- diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/etag/CustomETagSupport.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/etag/CustomETagSupport.java new file mode 100644 index 0000000..c0dba78 --- /dev/null +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/etag/CustomETagSupport.java @@ -0,0 +1,74 @@ +/* + * 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.olingo.server.api.etag; + +import org.apache.olingo.commons.api.edm.EdmBindingTarget; + +/** + * <p>Processors that would like to support etags for certain entity sets can implement this + * interface.</p> + * <p>If implemented this interface can be registered at the ODataHttpHandler. This will result in change request to + * require an if-match/if-none-match or an if-modified-since/if-unmodified-since header. Otherwise the request will + * result in a "Precondition Required" response</p> + */ +public interface CustomETagSupport { + + /** + * This method will be called for update requests which target an entity or a property of an entity. + * If this method returns true and an header is not specified we will return a "Precondition Required" response. + * Validation has to be performed inside the processor methods after the dispatching. + * If this method returns false and an header is specified we will ignore the header. + * @param entitySetOrSingleton + * @return true if the entity set specified needs an if-match/if-none-match header + */ + boolean hasETag(EdmBindingTarget entitySetOrSingleton); + + /** + * This method will be called for update requests which target a media entity value. + * If this method returns true and an header is not specified we will return a "Precondition Required" response. + * Validation has to be performed inside the processor methods after the dispatching. + * If this method returns false and an header is specified we will ignore the header. + * @param entitySetOrSingleton + * @return true if the entity set specified needs an if-match/if-none-match header + */ + boolean hasMediaETag(EdmBindingTarget entitySetOrSingleton); + + /** + * Since the Olingo library cannot generate a metadata document etag in a generic way we call this method to retrieve + * an application specific etag for the metadata document. If this interface is registered applications can return an + * etag or null here to provide caching support for clients. If a client sends a GET request to the metadata document + * and this method delivers an etag we will match it to the request. If there has been no modification we will return + * a 304 NOT MODIFIED status code. If this interface is not registered or delivers null we just send back the usual + * metadata response. + * @return the application generated etag for the metadata document + */ + String getMetadataETag(); + + /** + * Since the Olingo library cannot generate a service document etag in a generic way we call this method to retrieve + * an application specific etag for the service document. If this interface is registered applications can return an + * etag or null here to provide caching support for clients. If a client sends a GET request to the service document + * and this method delivers an etag we will match it to the request. If there has been no modification we will return + * a 304 NOT MODIFIED status code. If this interface is not registered or delivers null we just send back the usual + * service document response. + * @return the application generated etag for the service document + */ + String getServiceDocumentETag(); + +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1f286fd4/lib/server-api/src/main/java/org/apache/olingo/server/api/etag/ETagHelper.java ---------------------------------------------------------------------- diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/etag/ETagHelper.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/etag/ETagHelper.java new file mode 100644 index 0000000..ae8b0e2 --- /dev/null +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/etag/ETagHelper.java @@ -0,0 +1,66 @@ +/* + * 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.olingo.server.api.etag; + +import java.util.Collection; + +/** + * Used for ETag-related tasks. + */ +public interface ETagHelper { + /** + * <p>Checks the preconditions of a read request with a given ETag value + * against the If-Match and If-None-Match HTTP headers.</p> + * <p>If the given ETag value is not matched by the ETag information in the If-Match headers, + * and there are ETags in the headers to be matched, a "Precondition Failed" exception is + * thrown.</p> + * <p>If the given ETag value is matched by the ETag information in the If-None-Match headers, + * <code>true</code> is returned, and applications are supposed to return an empty response + * with a "Not Modified" status code and the ETag header, <code>false</code> otherwise.</p> + * <p>All matching uses weak comparison as described in + * <a href="https://www.ietf.org/rfc/rfc7232.txt">RFC 7232</a>, section 2.3.2.</p> + * <p>This method does not nothing and returns <code>false</code> if the ETag value is + * <code>null</code>.</p> + * @param eTag the ETag value to match + * @param ifMatchHeaders the If-Match header values + * @param ifNoneMatchHeaders the If-None-Match header values + * @return whether a "Not Modified" response should be used + */ + public boolean checkReadPreconditions(String eTag, + Collection<String> ifMatchHeaders, Collection<String> ifNoneMatchHeaders) + throws PreconditionRequiredException; + + /** + * <p>Checks the preconditions of a change request (with HTTP methods PUT, PATCH, or DELETE) + * with a given ETag value against the If-Match and If-None-Match HTTP headers.</p> + * <p>If the given ETag value is not matched by the ETag information in the If-Match headers, + * and there are ETags in the headers to be matched, or + * if the given ETag value is matched by the ETag information in the If-None-Match headers, + * a "Precondition Failed" exception is thrown.</p> + * <p>All matching uses weak comparison as described in + * <a href="https://www.ietf.org/rfc/rfc7232.txt">RFC 7232</a>, section 2.3.2.</p> + * <p>This method does not nothing if the ETag value is <code>null</code>.</p> + * @param eTag the ETag value to match + * @param ifMatchHeaders the If-Match header values + * @param ifNoneMatchHeaders the If-None-Match header values + */ + public void checkChangePreconditions(String eTag, + Collection<String> ifMatchHeaders, Collection<String> ifNoneMatchHeaders) + throws PreconditionRequiredException; +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1f286fd4/lib/server-api/src/main/java/org/apache/olingo/server/api/etag/PreconditionRequiredException.java ---------------------------------------------------------------------- diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/etag/PreconditionRequiredException.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/etag/PreconditionRequiredException.java new file mode 100644 index 0000000..b49298f --- /dev/null +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/etag/PreconditionRequiredException.java @@ -0,0 +1,54 @@ +/* + * 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.olingo.server.api.etag; + +import org.apache.olingo.server.api.ODataLibraryException; + +public class PreconditionRequiredException extends ODataLibraryException { + private static final long serialVersionUID = -8112658467394158700L; + + public static enum MessageKeys implements MessageKey { + /** no parameter */ + MISSING_HEADER, + /** no parameter */ + FAILED, + /** no parameter */ + INVALID_URI; + + @Override + public String getKey() { + return name(); + } + } + + public PreconditionRequiredException(final String developmentMessage, final MessageKey messageKey, + final String... parameters) { + super(developmentMessage, messageKey, parameters); + } + + public PreconditionRequiredException(final String developmentMessage, final Throwable cause, + final MessageKey messageKey, final String... parameters) { + super(developmentMessage, cause, messageKey, parameters); + } + + @Override + protected String getBundleName() { + return DEFAULT_SERVER_BUNDLE_NAME; + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1f286fd4/lib/server-core/src/main/java/org/apache/olingo/server/core/ETagParser.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ETagParser.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ETagParser.java deleted file mode 100644 index 20332f6..0000000 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ETagParser.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * 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.olingo.server.core; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * <p>Parses the values of HTTP header fields that contain a list of entity tags or a - * single star character, i.e., <code>If-Match</code> and <code>If-None-Match</code>.</p> - * <p>See <a href="https://www.ietf.org/rfc/rfc7232.txt">RFC 7232</a> for details; - * there the following grammar is defined:</p> - * <pre> - * If-Match = "*" / 1#entity-tag - * If-None-Match = "*" / 1#entity-tag - * entity-tag = [ weak ] opaque-tag - * weak = %x57.2F ; "W/", case-sensitive - * opaque-tag = DQUOTE *etagc DQUOTE - * etagc = %x21 / %x23-7E / %x80-FF - * </pre> - * <p>Values with illegal syntax do not contribute to the result but no exception is thrown.</p> - */ -public class ETagParser { - - private static final Pattern ETAG = Pattern.compile("\\s*(,\\s*)+|((?:W/)?\"[!#-~\\x80-\\xFF]*\")"); - - protected static Collection<String> parse(final Collection<String> values) { - if (values == null) { - return Collections.<String> emptySet(); - } - - Set<String> result = new HashSet<String>(); - for (final String value : values) { - final Collection<String> part = parse(value); - if (part.size() == 1 && part.iterator().next().equals("*")) { - return part; - } else { - result.addAll(part); - } - } - return result; - } - - private static Collection<String> parse(final String value) { - if (value.trim().equals("*")) { - return Collections.singleton("*"); - } else { - Set<String> result = new HashSet<String>(); - String separator = ""; - int start = 0; - Matcher matcher = ETAG.matcher(value.trim()); - while (matcher.find() && matcher.start() == start) { - start = matcher.end(); - if (matcher.group(1) != null) { - separator = matcher.group(1); - } else if (separator != null) { - result.add(matcher.group(2)); - separator = null; - } else { - return Collections.<String> emptySet(); - } - } - return matcher.hitEnd() ? result : Collections.<String> emptySet(); - } - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1f286fd4/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataDispatcher.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataDispatcher.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataDispatcher.java index 30edc5a..d05a98b 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataDispatcher.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataDispatcher.java @@ -33,6 +33,7 @@ import org.apache.olingo.server.api.ODataApplicationException; import org.apache.olingo.server.api.ODataLibraryException; import org.apache.olingo.server.api.ODataRequest; import org.apache.olingo.server.api.ODataResponse; +import org.apache.olingo.server.api.etag.PreconditionRequiredException; import org.apache.olingo.server.api.processor.ActionComplexCollectionProcessor; import org.apache.olingo.server.api.processor.ActionComplexProcessor; import org.apache.olingo.server.api.processor.ActionEntityCollectionProcessor; @@ -67,6 +68,7 @@ import org.apache.olingo.server.api.uri.UriResourcePartTyped; import org.apache.olingo.server.api.uri.UriResourcePrimitiveProperty; import org.apache.olingo.server.api.uri.UriResourceProperty; import org.apache.olingo.server.core.batchhandler.BatchHandler; +import org.apache.olingo.server.core.etag.PreconditionsValidator; public class ODataDispatcher { http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1f286fd4/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataExceptionHelper.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataExceptionHelper.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataExceptionHelper.java index e91e9e2..664b50a 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataExceptionHelper.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataExceptionHelper.java @@ -26,6 +26,7 @@ import org.apache.olingo.server.api.ODataApplicationException; import org.apache.olingo.server.api.ODataLibraryException; import org.apache.olingo.server.api.ODataLibraryException.ODataErrorMessage; import org.apache.olingo.server.api.deserializer.DeserializerException; +import org.apache.olingo.server.api.etag.PreconditionRequiredException; import org.apache.olingo.server.api.serializer.SerializerException; import org.apache.olingo.server.core.uri.parser.UriParserException; import org.apache.olingo.server.core.uri.parser.UriParserSemanticException; http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1f286fd4/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java index b266549..82313f5 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java @@ -26,7 +26,6 @@ import org.apache.olingo.commons.api.format.ContentType; import org.apache.olingo.commons.api.format.ODataFormat; import org.apache.olingo.commons.api.http.HttpHeader; import org.apache.olingo.commons.api.http.HttpMethod; -import org.apache.olingo.server.api.CustomETagSupport; import org.apache.olingo.server.api.OData; import org.apache.olingo.server.api.ODataApplicationException; import org.apache.olingo.server.api.ODataLibraryException; @@ -35,6 +34,8 @@ import org.apache.olingo.server.api.ODataResponse; import org.apache.olingo.server.api.ODataServerError; import org.apache.olingo.server.api.ServiceMetadata; import org.apache.olingo.server.api.deserializer.DeserializerException; +import org.apache.olingo.server.api.etag.CustomETagSupport; +import org.apache.olingo.server.api.etag.PreconditionRequiredException; import org.apache.olingo.server.api.processor.DefaultProcessor; import org.apache.olingo.server.api.processor.ErrorProcessor; import org.apache.olingo.server.api.processor.Processor; http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1f286fd4/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java index 4fb6640..5563f1b 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java @@ -33,7 +33,6 @@ import javax.servlet.http.HttpServletResponse; import org.apache.olingo.commons.api.ODataRuntimeException; import org.apache.olingo.commons.api.http.HttpHeader; import org.apache.olingo.commons.api.http.HttpMethod; -import org.apache.olingo.server.api.CustomETagSupport; import org.apache.olingo.server.api.ODataServerError; import org.apache.olingo.server.api.OData; import org.apache.olingo.server.api.ODataHttpHandler; @@ -41,6 +40,7 @@ import org.apache.olingo.server.api.ODataRequest; import org.apache.olingo.server.api.ODataResponse; import org.apache.olingo.server.api.ODataLibraryException; import org.apache.olingo.server.api.ServiceMetadata; +import org.apache.olingo.server.api.etag.CustomETagSupport; import org.apache.olingo.server.api.processor.Processor; import org.apache.olingo.server.api.serializer.CustomContentTypeSupport; import org.apache.olingo.server.api.serializer.SerializerException; http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1f286fd4/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java index e3cbd6d..5c5229f 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java @@ -18,8 +18,6 @@ */ package org.apache.olingo.server.core; -import java.util.Collection; -import java.util.Collections; import java.util.List; import org.apache.olingo.commons.api.edm.EdmPrimitiveType; @@ -27,7 +25,6 @@ import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind; import org.apache.olingo.commons.api.edm.provider.CsdlEdmProvider; import org.apache.olingo.commons.api.format.ODataFormat; import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory; -import org.apache.olingo.server.api.ETagInformation; import org.apache.olingo.server.api.OData; import org.apache.olingo.server.api.ODataHttpHandler; import org.apache.olingo.server.api.ServiceMetadata; @@ -35,12 +32,14 @@ import org.apache.olingo.server.api.deserializer.DeserializerException; import org.apache.olingo.server.api.deserializer.FixedFormatDeserializer; import org.apache.olingo.server.api.deserializer.ODataDeserializer; import org.apache.olingo.server.api.edmx.EdmxReference; +import org.apache.olingo.server.api.etag.ETagHelper; import org.apache.olingo.server.api.serializer.FixedFormatSerializer; import org.apache.olingo.server.api.serializer.ODataSerializer; import org.apache.olingo.server.api.serializer.SerializerException; import org.apache.olingo.server.api.uri.UriHelper; import org.apache.olingo.server.core.deserializer.FixedFormatDeserializerImpl; import org.apache.olingo.server.core.deserializer.json.ODataJsonDeserializer; +import org.apache.olingo.server.core.etag.ETagHelperImpl; import org.apache.olingo.server.core.serializer.FixedFormatSerializerImpl; import org.apache.olingo.server.core.serializer.json.ODataJsonSerializer; import org.apache.olingo.server.core.serializer.xml.ODataXmlSerializerImpl; @@ -96,21 +95,20 @@ public class ODataImpl extends OData { @Override public ODataDeserializer createDeserializer(final ODataFormat format) throws DeserializerException { - ODataDeserializer serializer; + ODataDeserializer deserializer; switch (format) { case JSON: case JSON_NO_METADATA: case JSON_FULL_METADATA: - serializer = new ODataJsonDeserializer(); + deserializer = new ODataJsonDeserializer(); break; case XML: - // We do not support xml deserialization right now so this mus lead to an error + // We do not support XML deserialization right now so this must lead to an error. default: throw new DeserializerException("Unsupported format: " + format, - SerializerException.MessageKeys.UNSUPPORTED_FORMAT, format.toString()); + DeserializerException.MessageKeys.UNSUPPORTED_FORMAT, format.toString()); } - - return serializer; + return deserializer; } @Override @@ -119,10 +117,7 @@ public class ODataImpl extends OData { } @Override - public ETagInformation createETagInformation(final Collection<String> values) { - final Collection<String> eTags = ETagParser.parse(values); - final boolean isAll = eTags.size() == 1 && eTags.iterator().next().equals("*"); - return new ETagInformation(isAll, - isAll ? Collections.<String> emptySet() : Collections.unmodifiableCollection(eTags)); + public ETagHelper createETagHelper() { + return new ETagHelperImpl(); } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1f286fd4/lib/server-core/src/main/java/org/apache/olingo/server/core/PreconditionRequiredException.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/PreconditionRequiredException.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/PreconditionRequiredException.java deleted file mode 100644 index 25ba117..0000000 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/PreconditionRequiredException.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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.olingo.server.core; - -import org.apache.olingo.server.api.ODataLibraryException; - -public class PreconditionRequiredException extends ODataLibraryException { - private static final long serialVersionUID = -8112658467394158700L; - - public static enum MessageKeys implements MessageKey { - /** no parameter */ - MISSING_HEADER, - /** no parameter */ - INVALID_URI; - - @Override - public String getKey() { - return name(); - } - } - - public PreconditionRequiredException(final String developmentMessage, final MessageKey messageKey, - final String... parameters) { - super(developmentMessage, messageKey, parameters); - } - - public PreconditionRequiredException(final String developmentMessage, final Throwable cause, - final MessageKey messageKey, final String... parameters) { - super(developmentMessage, cause, messageKey, parameters); - } - - @Override - protected String getBundleName() { - return DEFAULT_SERVER_BUNDLE_NAME; - } -} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1f286fd4/lib/server-core/src/main/java/org/apache/olingo/server/core/PreconditionsValidator.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/PreconditionsValidator.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/PreconditionsValidator.java deleted file mode 100644 index 490acc1..0000000 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/PreconditionsValidator.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * 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.olingo.server.core; - -import org.apache.olingo.commons.api.edm.EdmBindingTarget; -import org.apache.olingo.commons.api.edm.EdmFunctionImport; -import org.apache.olingo.commons.api.edm.EdmNavigationProperty; -import org.apache.olingo.server.api.CustomETagSupport; -import org.apache.olingo.server.api.uri.UriInfo; -import org.apache.olingo.server.api.uri.UriResource; -import org.apache.olingo.server.api.uri.UriResourceEntitySet; -import org.apache.olingo.server.api.uri.UriResourceFunction; -import org.apache.olingo.server.api.uri.UriResourceNavigation; -import org.apache.olingo.server.api.uri.UriResourceSingleton; - -public class PreconditionsValidator { - - private final CustomETagSupport customETagSupport; - private final UriInfo uriInfo; - private final String ifMatch; - private final String ifNoneMatch; - - public PreconditionsValidator(CustomETagSupport customETagSupport, UriInfo uriInfo, String ifMatch, - String ifNoneMatch) { - this.customETagSupport = customETagSupport; - this.uriInfo = uriInfo; - this.ifMatch = ifMatch; - this.ifNoneMatch = ifNoneMatch; - } - - public void validatePreconditions(boolean isMediaValue) throws PreconditionRequiredException { - EdmBindingTarget affectedEntitySetOrSingleton = extractInformation(); - if (affectedEntitySetOrSingleton != null) { - if ((isMediaValue && customETagSupport.hasMediaETag(affectedEntitySetOrSingleton)) || - (!isMediaValue && customETagSupport.hasETag(affectedEntitySetOrSingleton))) { - checkETagHeaderPresent(); - } - } - } - - private void checkETagHeaderPresent() throws PreconditionRequiredException { - if (ifMatch == null && ifNoneMatch == null) { - throw new PreconditionRequiredException("Expected an if-match or if-none-match header", - PreconditionRequiredException.MessageKeys.MISSING_HEADER); - } - } - - private EdmBindingTarget extractInformation() throws PreconditionRequiredException { - EdmBindingTarget lastFoundEntitySetOrSingleton = null; - int counter = 0; - for (UriResource uriResourcePart : uriInfo.getUriResourceParts()) { - switch (uriResourcePart.getKind()) { - case function: - lastFoundEntitySetOrSingleton = getEntitySetFromFunctionImport((UriResourceFunction) uriResourcePart); - break; - case singleton: - lastFoundEntitySetOrSingleton = ((UriResourceSingleton) uriResourcePart).getSingleton(); - break; - case entitySet: - lastFoundEntitySetOrSingleton = getEntitySet((UriResourceEntitySet) uriResourcePart); - break; - case navigationProperty: - lastFoundEntitySetOrSingleton = getEntitySetFromNavigation(lastFoundEntitySetOrSingleton, - (UriResourceNavigation) uriResourcePart); - break; - case value: - case action: - // This should not be possible since the URI Parser validates this but to be sure we throw an exception. - if (counter != uriInfo.getUriResourceParts().size() - 1) { - throw new PreconditionRequiredException("$Value or Action must be the last segment in the URI.", - PreconditionRequiredException.MessageKeys.INVALID_URI); - } - break; - default: - lastFoundEntitySetOrSingleton = null; - break; - } - if (lastFoundEntitySetOrSingleton == null) { - // Once we loose track of the entity set there is no way to retrieve it. - break; - } - counter++; - } - return lastFoundEntitySetOrSingleton; - } - - private EdmBindingTarget getEntitySetFromFunctionImport(UriResourceFunction uriResourceFunction) { - EdmFunctionImport functionImport = uriResourceFunction.getFunctionImport(); - if (functionImport != null && functionImport.getReturnedEntitySet() != null - && !uriResourceFunction.isCollection()) { - return functionImport.getReturnedEntitySet(); - } - return null; - } - - private EdmBindingTarget getEntitySet(UriResourceEntitySet uriResourceEntitySet) { - if (!uriResourceEntitySet.isCollection()) { - return uriResourceEntitySet.getEntitySet(); - } - return null; - } - - private EdmBindingTarget getEntitySetFromNavigation(EdmBindingTarget lastFoundEntitySetOrSingleton, - UriResourceNavigation uriResourceNavigation) { - if (lastFoundEntitySetOrSingleton != null && !uriResourceNavigation.isCollection()) { - EdmNavigationProperty navProp = uriResourceNavigation.getProperty(); - return lastFoundEntitySetOrSingleton.getRelatedBindingTarget(navProp.getName()); - } - return null; - } -} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1f286fd4/lib/server-core/src/main/java/org/apache/olingo/server/core/etag/ETagHelperImpl.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/etag/ETagHelperImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/etag/ETagHelperImpl.java new file mode 100644 index 0000000..1d8c6d6 --- /dev/null +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/etag/ETagHelperImpl.java @@ -0,0 +1,72 @@ +/* + * 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.olingo.server.core.etag; + +import java.util.Collection; +import java.util.Collections; + +import org.apache.olingo.server.api.etag.ETagHelper; +import org.apache.olingo.server.api.etag.PreconditionRequiredException; + +public class ETagHelperImpl implements ETagHelper { + + @Override + public boolean checkReadPreconditions(final String eTag, + final Collection<String> ifMatchHeaders, final Collection<String> ifNoneMatchHeaders) + throws PreconditionRequiredException { + if (eTag != null) { + final ETagInformation ifMatch = createETagInformation(ifMatchHeaders); + if (!ifMatch.isMatchedBy(eTag) && !ifMatch.getETags().isEmpty()) { + throw new PreconditionRequiredException("The If-Match precondition is not fulfilled.", + PreconditionRequiredException.MessageKeys.FAILED); + } + return createETagInformation(ifNoneMatchHeaders).isMatchedBy(eTag); + } + return false; + } + + @Override + public void checkChangePreconditions(final String eTag, + final Collection<String> ifMatchHeaders, final Collection<String> ifNoneMatchHeaders) + throws PreconditionRequiredException { + if (eTag != null) { + final ETagInformation ifMatch = createETagInformation(ifMatchHeaders); + final ETagInformation ifNoneMatch = createETagInformation(ifNoneMatchHeaders); + if (!ifMatch.isMatchedBy(eTag) && !ifMatch.getETags().isEmpty() + || ifNoneMatch.isMatchedBy(eTag)) { + throw new PreconditionRequiredException("The preconditions are not fulfilled.", + PreconditionRequiredException.MessageKeys.FAILED); + } + } + } + + /** + * Creates ETag information from the values of a HTTP header + * containing a list of entity tags or a single star character, i.e., + * <code>If-Match</code> and <code>If-None-Match</code>. + * @param values the collection of header values + * @return an {@link ETagInformation} instance + */ + protected ETagInformation createETagInformation(final Collection<String> values) { + final Collection<String> eTags = ETagParser.parse(values); + final boolean isAll = eTags.size() == 1 && eTags.iterator().next().equals("*"); + return new ETagInformation(isAll, + isAll ? Collections.<String> emptySet() : Collections.unmodifiableCollection(eTags)); + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1f286fd4/lib/server-core/src/main/java/org/apache/olingo/server/core/etag/ETagInformation.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/etag/ETagInformation.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/etag/ETagInformation.java new file mode 100644 index 0000000..03310ec --- /dev/null +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/etag/ETagInformation.java @@ -0,0 +1,74 @@ +/* + * 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.olingo.server.core.etag; + +import java.util.Collection; + +/** + * Information about the values of an ETag-relevant HTTP header. + */ +public class ETagInformation { + private final boolean all; + private final Collection<String> eTags; + + public ETagInformation(final boolean all, final Collection<String> eTags) { + this.all = all; + this.eTags = eTags; + } + + /** + * Gets the information whether the values contain "*". + */ + public boolean isAll() { + return all; + } + + /** + * Gets the collection of ETag values found. + * It is empty if {@link #isAll()} returns <code>true</code>. + */ + public Collection<String> getETags() { + return eTags; + } + + /** + * <p>Checks whether a given ETag value is matched by this ETag information, + * using weak comparison as described in + * <a href="https://www.ietf.org/rfc/rfc7232.txt">RFC 7232</a>, section 2.3.2.</p> + * <p>If the given value is <code>null</code>, or if this ETag information + * does not contain anything, the result is <code>false</code>.</p> + * @param eTag the ETag value to match + * @return a boolean match result + */ + public boolean isMatchedBy(final String eTag) { + if (eTag == null) { + return false; + } else if (all) { + return true; + } else { + for (final String candidate : eTags) { + if ((eTag.startsWith("W/") ? eTag.substring(2) : eTag) + .equals(candidate.startsWith("W/") ? candidate.substring(2) : candidate)) { + return true; + } + } + return false; + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1f286fd4/lib/server-core/src/main/java/org/apache/olingo/server/core/etag/ETagParser.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/etag/ETagParser.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/etag/ETagParser.java new file mode 100644 index 0000000..b8534dd --- /dev/null +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/etag/ETagParser.java @@ -0,0 +1,86 @@ +/* + * 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.olingo.server.core.etag; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * <p>Parses the values of HTTP header fields that contain a list of entity tags or a + * single star character, i.e., <code>If-Match</code> and <code>If-None-Match</code>.</p> + * <p>See <a href="https://www.ietf.org/rfc/rfc7232.txt">RFC 7232</a> for details; + * there the following grammar is defined:</p> + * <pre> + * If-Match = "*" / 1#entity-tag + * If-None-Match = "*" / 1#entity-tag + * entity-tag = [ weak ] opaque-tag + * weak = %x57.2F ; "W/", case-sensitive + * opaque-tag = DQUOTE *etagc DQUOTE + * etagc = %x21 / %x23-7E / %x80-FF + * </pre> + * <p>Values with illegal syntax do not contribute to the result but no exception is thrown.</p> + */ +public class ETagParser { + + private static final Pattern ETAG = Pattern.compile("\\s*(,\\s*)+|((?:W/)?\"[!#-~\\x80-\\xFF]*\")"); + + protected static Collection<String> parse(final Collection<String> values) { + if (values == null) { + return Collections.<String> emptySet(); + } + + Set<String> result = new HashSet<String>(); + for (final String value : values) { + final Collection<String> part = parse(value); + if (part.size() == 1 && part.iterator().next().equals("*")) { + return part; + } else { + result.addAll(part); + } + } + return result; + } + + private static Collection<String> parse(final String value) { + if (value.trim().equals("*")) { + return Collections.singleton("*"); + } else { + Set<String> result = new HashSet<String>(); + String separator = ""; + int start = 0; + Matcher matcher = ETAG.matcher(value.trim()); + while (matcher.find() && matcher.start() == start) { + start = matcher.end(); + if (matcher.group(1) != null) { + separator = matcher.group(1); + } else if (separator != null) { + result.add(matcher.group(2)); + separator = null; + } else { + return Collections.<String> emptySet(); + } + } + return matcher.hitEnd() ? result : Collections.<String> emptySet(); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1f286fd4/lib/server-core/src/main/java/org/apache/olingo/server/core/etag/PreconditionsValidator.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/etag/PreconditionsValidator.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/etag/PreconditionsValidator.java new file mode 100644 index 0000000..3adf8af --- /dev/null +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/etag/PreconditionsValidator.java @@ -0,0 +1,128 @@ +/* + * 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.olingo.server.core.etag; + +import org.apache.olingo.commons.api.edm.EdmBindingTarget; +import org.apache.olingo.commons.api.edm.EdmFunctionImport; +import org.apache.olingo.commons.api.edm.EdmNavigationProperty; +import org.apache.olingo.server.api.etag.CustomETagSupport; +import org.apache.olingo.server.api.etag.PreconditionRequiredException; +import org.apache.olingo.server.api.uri.UriInfo; +import org.apache.olingo.server.api.uri.UriResource; +import org.apache.olingo.server.api.uri.UriResourceEntitySet; +import org.apache.olingo.server.api.uri.UriResourceFunction; +import org.apache.olingo.server.api.uri.UriResourceNavigation; +import org.apache.olingo.server.api.uri.UriResourceSingleton; + +public class PreconditionsValidator { + + private final CustomETagSupport customETagSupport; + private final UriInfo uriInfo; + private final String ifMatch; + private final String ifNoneMatch; + + public PreconditionsValidator(CustomETagSupport customETagSupport, UriInfo uriInfo, String ifMatch, + String ifNoneMatch) { + this.customETagSupport = customETagSupport; + this.uriInfo = uriInfo; + this.ifMatch = ifMatch; + this.ifNoneMatch = ifNoneMatch; + } + + public void validatePreconditions(boolean isMediaValue) throws PreconditionRequiredException { + EdmBindingTarget affectedEntitySetOrSingleton = extractInformation(); + if (affectedEntitySetOrSingleton != null) { + if ((isMediaValue && customETagSupport.hasMediaETag(affectedEntitySetOrSingleton)) || + (!isMediaValue && customETagSupport.hasETag(affectedEntitySetOrSingleton))) { + checkETagHeaderPresent(); + } + } + } + + private void checkETagHeaderPresent() throws PreconditionRequiredException { + if (ifMatch == null && ifNoneMatch == null) { + throw new PreconditionRequiredException("Expected an if-match or if-none-match header", + PreconditionRequiredException.MessageKeys.MISSING_HEADER); + } + } + + private EdmBindingTarget extractInformation() throws PreconditionRequiredException { + EdmBindingTarget lastFoundEntitySetOrSingleton = null; + int counter = 0; + for (UriResource uriResourcePart : uriInfo.getUriResourceParts()) { + switch (uriResourcePart.getKind()) { + case function: + lastFoundEntitySetOrSingleton = getEntitySetFromFunctionImport((UriResourceFunction) uriResourcePart); + break; + case singleton: + lastFoundEntitySetOrSingleton = ((UriResourceSingleton) uriResourcePart).getSingleton(); + break; + case entitySet: + lastFoundEntitySetOrSingleton = getEntitySet((UriResourceEntitySet) uriResourcePart); + break; + case navigationProperty: + lastFoundEntitySetOrSingleton = getEntitySetFromNavigation(lastFoundEntitySetOrSingleton, + (UriResourceNavigation) uriResourcePart); + break; + case value: + case action: + // This should not be possible since the URI Parser validates this but to be sure we throw an exception. + if (counter != uriInfo.getUriResourceParts().size() - 1) { + throw new PreconditionRequiredException("$Value or Action must be the last segment in the URI.", + PreconditionRequiredException.MessageKeys.INVALID_URI); + } + break; + default: + lastFoundEntitySetOrSingleton = null; + break; + } + if (lastFoundEntitySetOrSingleton == null) { + // Once we loose track of the entity set there is no way to retrieve it. + break; + } + counter++; + } + return lastFoundEntitySetOrSingleton; + } + + private EdmBindingTarget getEntitySetFromFunctionImport(UriResourceFunction uriResourceFunction) { + EdmFunctionImport functionImport = uriResourceFunction.getFunctionImport(); + if (functionImport != null && functionImport.getReturnedEntitySet() != null + && !uriResourceFunction.isCollection()) { + return functionImport.getReturnedEntitySet(); + } + return null; + } + + private EdmBindingTarget getEntitySet(UriResourceEntitySet uriResourceEntitySet) { + if (!uriResourceEntitySet.isCollection()) { + return uriResourceEntitySet.getEntitySet(); + } + return null; + } + + private EdmBindingTarget getEntitySetFromNavigation(EdmBindingTarget lastFoundEntitySetOrSingleton, + UriResourceNavigation uriResourceNavigation) { + if (lastFoundEntitySetOrSingleton != null && !uriResourceNavigation.isCollection()) { + EdmNavigationProperty navProp = uriResourceNavigation.getProperty(); + return lastFoundEntitySetOrSingleton.getRelatedBindingTarget(navProp.getName()); + } + return null; + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1f286fd4/lib/server-core/src/test/java/org/apache/olingo/server/core/ETagParserTest.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/ETagParserTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/ETagParserTest.java deleted file mode 100644 index 7931b2b..0000000 --- a/lib/server-core/src/test/java/org/apache/olingo/server/core/ETagParserTest.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * 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.olingo.server.core; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.hasItems; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - -import java.util.Arrays; -import java.util.Collections; - -import org.apache.olingo.server.api.ETagInformation; -import org.apache.olingo.server.api.OData; -import org.junit.Test; - -public class ETagParserTest { - - private static final OData odata = OData.newInstance(); - - @Test - public void empty() { - final ETagInformation eTagInformation = odata.createETagInformation(null); - assertFalse(eTagInformation.isAll()); - assertNotNull(eTagInformation.getETags()); - assertTrue(eTagInformation.getETags().isEmpty()); - } - - @Test - public void loneStar() { - final ETagInformation eTagInformation = odata.createETagInformation(Collections.singleton("*")); - assertTrue(eTagInformation.isAll()); - assertNotNull(eTagInformation.getETags()); - assertTrue(eTagInformation.getETags().isEmpty()); - } - - @Test - public void starWins() { - final ETagInformation eTagInformation = odata.createETagInformation(Arrays.asList("\"ETag\"", "*")); - assertTrue(eTagInformation.isAll()); - assertNotNull(eTagInformation.getETags()); - assertTrue(eTagInformation.getETags().isEmpty()); - } - - @Test - public void starAsEtagAndEmptyEtag() { - final ETagInformation eTagInformation = odata.createETagInformation( - Collections.singleton("\"*\", \"\"")); - assertFalse(eTagInformation.isAll()); - assertNotNull(eTagInformation.getETags()); - assertThat(eTagInformation.getETags().size(), equalTo(2)); - assertThat(eTagInformation.getETags(), hasItems("\"*\"", "\"\"")); - } - - @Test - public void severalEtags() { - final ETagInformation eTagInformation = odata.createETagInformation( - Arrays.asList("\"ETag1\"", "\"ETag2\",, , ,W/\"ETag3\", ,")); - assertFalse(eTagInformation.isAll()); - assertNotNull(eTagInformation.getETags()); - assertThat(eTagInformation.getETags().size(), equalTo(3)); - assertThat(eTagInformation.getETags(), hasItems("\"ETag1\"", "\"ETag2\"", "W/\"ETag3\"")); - } - - @Test - public void duplicateEtagValues() { - final ETagInformation eTagInformation = odata.createETagInformation( - Arrays.asList("\"ETag1\"", "\"ETag2\", W/\"ETag1\", \"ETag1\"")); - assertFalse(eTagInformation.isAll()); - assertNotNull(eTagInformation.getETags()); - assertThat(eTagInformation.getETags().size(), equalTo(3)); - assertThat(eTagInformation.getETags(), hasItems("\"ETag1\"", "\"ETag2\"", "W/\"ETag1\"")); - } - - @Test - public void specialCharacters() { - final ETagInformation eTagInformation = odata.createETagInformation( - Collections.singleton("\"!#$%&'()*+,-./:;<=>?@[]^_`{|}~¡\u00FF\", \"ETag2\"")); - assertFalse(eTagInformation.isAll()); - assertNotNull(eTagInformation.getETags()); - assertThat(eTagInformation.getETags().size(), equalTo(2)); - assertThat(eTagInformation.getETags(), hasItems( - "\"!#$%&'()*+,-./:;<=>?@[]^_`{|}~¡\u00FF\"", "\"ETag2\"")); - } - - @Test - public void wrongFormat() { - final ETagInformation eTagInformation = odata.createETagInformation( - Arrays.asList("\"ETag1\", ETag2", "w/\"ETag3\"", "W//\"ETag4\"", "W/ETag5", - "\"\"ETag6\"\"", " \"ETag7\"\"ETag7\" ", "\"ETag8\" \"ETag8\"", - "\"ETag 9\"", "\"ETag10\"")); - assertFalse(eTagInformation.isAll()); - assertNotNull(eTagInformation.getETags()); - assertThat(eTagInformation.getETags().size(), equalTo(2)); - assertThat(eTagInformation.getETags(), hasItems("\"ETag1\"", "\"ETag10\"")); - } - - @Test - public void match() { - assertFalse(odata.createETagInformation(Collections.<String> emptySet()).isMatchedBy("\"ETag\"")); - assertFalse(odata.createETagInformation(Collections.singleton("\"ETag\"")).isMatchedBy(null)); - assertTrue(odata.createETagInformation(Collections.singleton("\"ETag\"")).isMatchedBy("\"ETag\"")); - assertTrue(odata.createETagInformation(Collections.singleton("*")).isMatchedBy("\"ETag\"")); - assertTrue(odata.createETagInformation(Collections.singleton("\"ETag\"")).isMatchedBy("W/\"ETag\"")); - assertTrue(odata.createETagInformation(Collections.singleton("W/\"ETag\"")).isMatchedBy("\"ETag\"")); - assertFalse(odata.createETagInformation(Collections.singleton("\"ETag\"")).isMatchedBy("W/\"ETag2\"")); - assertFalse(odata.createETagInformation(Collections.singleton("W/\"ETag\"")).isMatchedBy("\"ETag2\"")); - assertTrue(odata.createETagInformation(Arrays.asList("\"ETag1\",\"ETag2\"", "\"ETag3\",\"ETag4\"")) - .isMatchedBy("\"ETag4\"")); - assertFalse(odata.createETagInformation(Arrays.asList("\"ETag1\",\"ETag2\"", "\"ETag3\",\"ETag4\"")) - .isMatchedBy("\"ETag5\"")); - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1f286fd4/lib/server-core/src/test/java/org/apache/olingo/server/core/etag/ETagParserTest.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/etag/ETagParserTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/etag/ETagParserTest.java new file mode 100644 index 0000000..059d53d --- /dev/null +++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/etag/ETagParserTest.java @@ -0,0 +1,129 @@ +/* + * 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.olingo.server.core.etag; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.hasItems; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.Collections; + +import org.junit.Test; + +public class ETagParserTest { + + private static final ETagHelperImpl eTagHelper = new ETagHelperImpl(); + + @Test + public void empty() { + final ETagInformation eTagInformation = eTagHelper.createETagInformation(null); + assertFalse(eTagInformation.isAll()); + assertNotNull(eTagInformation.getETags()); + assertTrue(eTagInformation.getETags().isEmpty()); + } + + @Test + public void loneStar() { + final ETagInformation eTagInformation = eTagHelper.createETagInformation(Collections.singleton("*")); + assertTrue(eTagInformation.isAll()); + assertNotNull(eTagInformation.getETags()); + assertTrue(eTagInformation.getETags().isEmpty()); + } + + @Test + public void starWins() { + final ETagInformation eTagInformation = eTagHelper.createETagInformation(Arrays.asList("\"ETag\"", "*")); + assertTrue(eTagInformation.isAll()); + assertNotNull(eTagInformation.getETags()); + assertTrue(eTagInformation.getETags().isEmpty()); + } + + @Test + public void starAsEtagAndEmptyEtag() { + final ETagInformation eTagInformation = eTagHelper.createETagInformation( + Collections.singleton("\"*\", \"\"")); + assertFalse(eTagInformation.isAll()); + assertNotNull(eTagInformation.getETags()); + assertThat(eTagInformation.getETags().size(), equalTo(2)); + assertThat(eTagInformation.getETags(), hasItems("\"*\"", "\"\"")); + } + + @Test + public void severalEtags() { + final ETagInformation eTagInformation = eTagHelper.createETagInformation( + Arrays.asList("\"ETag1\"", "\"ETag2\",, , ,W/\"ETag3\", ,")); + assertFalse(eTagInformation.isAll()); + assertNotNull(eTagInformation.getETags()); + assertThat(eTagInformation.getETags().size(), equalTo(3)); + assertThat(eTagInformation.getETags(), hasItems("\"ETag1\"", "\"ETag2\"", "W/\"ETag3\"")); + } + + @Test + public void duplicateEtagValues() { + final ETagInformation eTagInformation = eTagHelper.createETagInformation( + Arrays.asList("\"ETag1\"", "\"ETag2\", W/\"ETag1\", \"ETag1\"")); + assertFalse(eTagInformation.isAll()); + assertNotNull(eTagInformation.getETags()); + assertThat(eTagInformation.getETags().size(), equalTo(3)); + assertThat(eTagInformation.getETags(), hasItems("\"ETag1\"", "\"ETag2\"", "W/\"ETag1\"")); + } + + @Test + public void specialCharacters() { + final ETagInformation eTagInformation = eTagHelper.createETagInformation( + Collections.singleton("\"!#$%&'()*+,-./:;<=>?@[]^_`{|}~¡\u00FF\", \"ETag2\"")); + assertFalse(eTagInformation.isAll()); + assertNotNull(eTagInformation.getETags()); + assertThat(eTagInformation.getETags().size(), equalTo(2)); + assertThat(eTagInformation.getETags(), hasItems( + "\"!#$%&'()*+,-./:;<=>?@[]^_`{|}~¡\u00FF\"", "\"ETag2\"")); + } + + @Test + public void wrongFormat() { + final ETagInformation eTagInformation = eTagHelper.createETagInformation( + Arrays.asList("\"ETag1\", ETag2", "w/\"ETag3\"", "W//\"ETag4\"", "W/ETag5", + "\"\"ETag6\"\"", " \"ETag7\"\"ETag7\" ", "\"ETag8\" \"ETag8\"", + "\"ETag 9\"", "\"ETag10\"")); + assertFalse(eTagInformation.isAll()); + assertNotNull(eTagInformation.getETags()); + assertThat(eTagInformation.getETags().size(), equalTo(2)); + assertThat(eTagInformation.getETags(), hasItems("\"ETag1\"", "\"ETag10\"")); + } + + @Test + public void match() { + assertFalse(eTagHelper.createETagInformation(Collections.<String> emptySet()).isMatchedBy("\"ETag\"")); + assertFalse(eTagHelper.createETagInformation(Collections.singleton("\"ETag\"")).isMatchedBy(null)); + assertTrue(eTagHelper.createETagInformation(Collections.singleton("\"ETag\"")).isMatchedBy("\"ETag\"")); + assertTrue(eTagHelper.createETagInformation(Collections.singleton("*")).isMatchedBy("\"ETag\"")); + assertTrue(eTagHelper.createETagInformation(Collections.singleton("\"ETag\"")).isMatchedBy("W/\"ETag\"")); + assertTrue(eTagHelper.createETagInformation(Collections.singleton("W/\"ETag\"")).isMatchedBy("\"ETag\"")); + assertFalse(eTagHelper.createETagInformation(Collections.singleton("\"ETag\"")).isMatchedBy("W/\"ETag2\"")); + assertFalse(eTagHelper.createETagInformation(Collections.singleton("W/\"ETag\"")).isMatchedBy("\"ETag2\"")); + assertTrue(eTagHelper.createETagInformation(Arrays.asList("\"ETag1\",\"ETag2\"", "\"ETag3\",\"ETag4\"")) + .isMatchedBy("\"ETag4\"")); + assertFalse(eTagHelper.createETagInformation(Arrays.asList("\"ETag1\",\"ETag2\"", "\"ETag3\",\"ETag4\"")) + .isMatchedBy("\"ETag5\"")); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1f286fd4/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/ETagSupport.java ---------------------------------------------------------------------- diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/ETagSupport.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/ETagSupport.java index 9011107..09d4910 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/ETagSupport.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/ETagSupport.java @@ -19,7 +19,7 @@ package org.apache.olingo.server.tecsvc; import org.apache.olingo.commons.api.edm.EdmBindingTarget; -import org.apache.olingo.server.api.CustomETagSupport; +import org.apache.olingo.server.api.etag.CustomETagSupport; public class ETagSupport implements CustomETagSupport { http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1f286fd4/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java ---------------------------------------------------------------------- diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java index eaec6d0..086f69c 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java @@ -161,9 +161,7 @@ public class TechnicalEntityProcessor extends TechnicalProcessor .validate(edmEntitySet, deserializerResult.getEntity()); entity = dataProvider.create(edmEntitySet); - dataProvider.update(request.getRawBaseUri(), edmEntitySet, entity, deserializerResult.getEntity(), false, - - true); + dataProvider.update(request.getRawBaseUri(), edmEntitySet, entity, deserializerResult.getEntity(), false, true); expand = deserializerResult.getExpandTree(); } @@ -199,7 +197,7 @@ public class TechnicalEntityProcessor extends TechnicalProcessor } } - checkChangePreconditions(entity.getETag(), + odata.createETagHelper().checkChangePreconditions(entity.getETag(), request.getHeaders(HttpHeader.IF_MATCH), request.getHeaders(HttpHeader.IF_NONE_MATCH)); checkRequestFormat(requestFormat); @@ -232,7 +230,7 @@ public class TechnicalEntityProcessor extends TechnicalProcessor final EdmEntityType edmEntityType = edmEntitySet.getEntityType(); Entity entity = readEntity(uriInfo); - checkChangePreconditions(entity.getMediaETag(), + odata.createETagHelper().checkChangePreconditions(entity.getMediaETag(), request.getHeaders(HttpHeader.IF_MATCH), request.getHeaders(HttpHeader.IF_NONE_MATCH)); checkRequestFormat(requestFormat); @@ -251,13 +249,13 @@ public class TechnicalEntityProcessor extends TechnicalProcessor @Override public void deleteEntity(final ODataRequest request, ODataResponse response, final UriInfo uriInfo) - throws ODataApplicationException { + throws ODataLibraryException, ODataApplicationException { final EdmEntitySet edmEntitySet = getEdmEntitySet(uriInfo); final Entity entity = readEntity(uriInfo); final List<UriResource> resourcePaths = uriInfo.getUriResourceParts(); final boolean isValue = resourcePaths.get(resourcePaths.size() - 1) instanceof UriResourceValue; - checkChangePreconditions(isValue ? entity.getMediaETag() : entity.getETag(), + odata.createETagHelper().checkChangePreconditions(isValue ? entity.getMediaETag() : entity.getETag(), request.getHeaders(HttpHeader.IF_MATCH), request.getHeaders(HttpHeader.IF_NONE_MATCH)); dataProvider.delete(edmEntitySet, entity); @@ -353,9 +351,13 @@ public class TechnicalEntityProcessor extends TechnicalProcessor final Entity entity = readEntity(uriInfo); - checkReadPreconditions(entity.getETag(), + if (odata.createETagHelper().checkReadPreconditions(entity.getETag(), request.getHeaders(HttpHeader.IF_MATCH), - request.getHeaders(HttpHeader.IF_NONE_MATCH)); + request.getHeaders(HttpHeader.IF_NONE_MATCH))) { + response.setStatusCode(HttpStatusCode.NOT_MODIFIED.getStatusCode()); + response.setHeader(HttpHeader.ETAG, entity.getETag()); + return; + } final ODataFormat format = ODataFormat.fromContentType(requestedContentType); final ExpandOption expand = uriInfo.getExpandOption(); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1f286fd4/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalPrimitiveComplexProcessor.java ---------------------------------------------------------------------- diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalPrimitiveComplexProcessor.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalPrimitiveComplexProcessor.java index 4ba7081..58b38c4 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalPrimitiveComplexProcessor.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalPrimitiveComplexProcessor.java @@ -171,9 +171,13 @@ public class TechnicalPrimitiveComplexProcessor extends TechnicalProcessor final Entity entity = readEntity(uriInfo); if (entity != null && entity.getETag() != null) { - checkReadPreconditions(entity.getETag(), + if (odata.createETagHelper().checkReadPreconditions(entity.getETag(), request.getHeaders(HttpHeader.IF_MATCH), - request.getHeaders(HttpHeader.IF_NONE_MATCH)); + request.getHeaders(HttpHeader.IF_NONE_MATCH))) { + response.setStatusCode(HttpStatusCode.NOT_MODIFIED.getStatusCode()); + response.setHeader(HttpHeader.ETAG, entity.getETag()); + return; + } } final Property property = entity == null ? @@ -219,7 +223,7 @@ public class TechnicalPrimitiveComplexProcessor extends TechnicalProcessor final EdmEntitySet edmEntitySet = getEdmEntitySet(resource); Entity entity = readEntity(uriInfo); - checkChangePreconditions(entity.getETag(), + odata.createETagHelper().checkChangePreconditions(entity.getETag(), request.getHeaders(HttpHeader.IF_MATCH), request.getHeaders(HttpHeader.IF_NONE_MATCH)); @@ -252,13 +256,13 @@ public class TechnicalPrimitiveComplexProcessor extends TechnicalProcessor } private void deleteProperty(final ODataRequest request, ODataResponse response, final UriInfo uriInfo) - throws ODataApplicationException { + throws ODataLibraryException, ODataApplicationException { final UriInfoResource resource = uriInfo.asUriInfoResource(); validatePath(resource); getEdmEntitySet(uriInfo); // including checks Entity entity = readEntity(uriInfo); - checkChangePreconditions(entity.getETag(), + odata.createETagHelper().checkChangePreconditions(entity.getETag(), request.getHeaders(HttpHeader.IF_MATCH), request.getHeaders(HttpHeader.IF_NONE_MATCH)); @@ -397,9 +401,13 @@ public class TechnicalPrimitiveComplexProcessor extends TechnicalProcessor final Entity entity = readEntity(uriInfo); if (entity != null && entity.getETag() != null) { - checkReadPreconditions(entity.getETag(), + if (odata.createETagHelper().checkReadPreconditions(entity.getETag(), request.getHeaders(HttpHeader.IF_MATCH), - request.getHeaders(HttpHeader.IF_NONE_MATCH)); + request.getHeaders(HttpHeader.IF_NONE_MATCH))) { + response.setStatusCode(HttpStatusCode.NOT_MODIFIED.getStatusCode()); + response.setHeader(HttpHeader.ETAG, entity.getETag()); + return; + } } final Property property = entity == null ? http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1f286fd4/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java ---------------------------------------------------------------------- diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java index b9e32fe..c95091d 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java @@ -18,7 +18,6 @@ */ package org.apache.olingo.server.tecsvc.processor; -import java.util.Collection; import java.util.List; import java.util.Locale; @@ -32,7 +31,6 @@ import org.apache.olingo.commons.api.edm.EdmFunction; import org.apache.olingo.commons.api.edm.EdmNavigationProperty; import org.apache.olingo.commons.api.format.ContentType; import org.apache.olingo.commons.api.http.HttpStatusCode; -import org.apache.olingo.server.api.ETagInformation; import org.apache.olingo.server.api.OData; import org.apache.olingo.server.api.ODataApplicationException; import org.apache.olingo.server.api.ServiceMetadata; @@ -257,34 +255,4 @@ public abstract class TechnicalProcessor implements Processor { HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ROOT); } } - - protected void checkReadPreconditions(final String eTag, - final Collection<String> ifMatchHeaders, final Collection<String> ifNoneMatchHeaders) - throws ODataApplicationException { - if (eTag != null) { - final ETagInformation ifMatch = odata.createETagInformation(ifMatchHeaders); - if (!ifMatch.isMatchedBy(eTag) && !ifMatch.getETags().isEmpty()) { - throw new ODataApplicationException("The If-Match precondition is not fulfilled.", - HttpStatusCode.PRECONDITION_FAILED.getStatusCode(), Locale.ROOT); - } - if (odata.createETagInformation(ifNoneMatchHeaders).isMatchedBy(eTag)) { - throw new ODataApplicationException("The entity has not been modified.", - HttpStatusCode.NOT_MODIFIED.getStatusCode(), Locale.ROOT); - } - } - } - - protected void checkChangePreconditions(final String eTag, - final Collection<String> ifMatchHeaders, final Collection<String> ifNoneMatchHeaders) - throws ODataApplicationException { - if (eTag != null) { - final ETagInformation ifMatch = odata.createETagInformation(ifMatchHeaders); - final ETagInformation ifNoneMatch = odata.createETagInformation(ifNoneMatchHeaders); - if (!ifMatch.isMatchedBy(eTag) && !ifMatch.getETags().isEmpty() - || ifNoneMatch.isMatchedBy(eTag)) { - throw new ODataApplicationException("The preconditions are not fulfilled.", - HttpStatusCode.PRECONDITION_FAILED.getStatusCode(), Locale.ROOT); - } - } - } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1f286fd4/lib/server-test/src/test/java/org/apache/olingo/server/core/PreconditionsValidatorTest.java ---------------------------------------------------------------------- diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/PreconditionsValidatorTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/PreconditionsValidatorTest.java index 033f9af..7cab567 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/PreconditionsValidatorTest.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/PreconditionsValidatorTest.java @@ -25,8 +25,10 @@ import org.apache.olingo.commons.api.edm.Edm; import org.apache.olingo.commons.api.edm.EdmBindingTarget; import org.apache.olingo.commons.api.http.HttpMethod; import org.apache.olingo.commons.core.edm.EdmProviderImpl; -import org.apache.olingo.server.api.CustomETagSupport; +import org.apache.olingo.server.api.etag.CustomETagSupport; +import org.apache.olingo.server.api.etag.PreconditionRequiredException; import org.apache.olingo.server.api.uri.UriInfo; +import org.apache.olingo.server.core.etag.PreconditionsValidator; import org.apache.olingo.server.core.uri.parser.Parser; import org.apache.olingo.server.core.uri.parser.UriParserException; import org.apache.olingo.server.core.uri.parser.UriParserSemanticException;
