[OLINGO-663] conditional GET in technical service, part 1

Change-Id: I89b0e765e1d3618c318522d1c2a22988f9d017fd

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/db47760e
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/db47760e
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/db47760e

Branch: refs/heads/OLINGO-632_OSGi-Support
Commit: db47760e355be674569c1f15a7aeaa67614f4970
Parents: 6ff644d
Author: Klaus Straubinger <[email protected]>
Authored: Fri May 22 15:48:43 2015 +0200
Committer: Christian Amend <[email protected]>
Committed: Fri May 22 15:55:49 2015 +0200

----------------------------------------------------------------------
 .../fit/tecsvc/client/ConditionalITCase.java    |  80 +++++++++++
 .../olingo/server/api/EtagInformation.java      |  72 ++++++++++
 .../org/apache/olingo/server/api/OData.java     |  15 ++-
 .../apache/olingo/server/core/EtagParser.java   |  86 ++++++++++++
 .../apache/olingo/server/core/ODataImpl.java    |  11 ++
 .../olingo/server/core/EtagParserTest.java      | 131 +++++++++++++++++++
 .../processor/TechnicalEntityProcessor.java     |  14 ++
 7 files changed, 407 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/db47760e/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ConditionalITCase.java
----------------------------------------------------------------------
diff --git 
a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ConditionalITCase.java 
b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ConditionalITCase.java
new file mode 100644
index 0000000..2dcbc26
--- /dev/null
+++ 
b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ConditionalITCase.java
@@ -0,0 +1,80 @@
+/*
+ * 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.fit.tecsvc.client;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+import org.apache.olingo.client.api.ODataClient;
+import org.apache.olingo.client.api.communication.ODataClientErrorException;
+import 
org.apache.olingo.client.api.communication.request.retrieve.ODataEntityRequest;
+import 
org.apache.olingo.client.api.communication.response.ODataRetrieveResponse;
+import org.apache.olingo.client.api.domain.ClientEntity;
+import org.apache.olingo.client.core.ODataClientFactory;
+import org.apache.olingo.commons.api.ODataError;
+import org.apache.olingo.commons.api.format.ODataFormat;
+import org.apache.olingo.commons.api.http.HttpStatusCode;
+import org.apache.olingo.fit.AbstractBaseTestITCase;
+import org.apache.olingo.fit.tecsvc.TecSvcConst;
+import org.junit.Test;
+
+public final class ConditionalITCase extends AbstractBaseTestITCase {
+
+  @Test
+  public void readWithWrongIfMatch() throws Exception {
+    final ODataClient client = getClient();
+    ODataEntityRequest<ClientEntity> request = 
client.getRetrieveRequestFactory().getEntityRequest(
+        client.newURIBuilder(TecSvcConst.BASE_URI)
+        .appendEntitySetSegment("ESCompAllPrim").appendKeySegment(0).build());
+    request.setIfMatch("W/\"1\"");
+    assertNotNull(request);
+
+    try {
+      request.execute();
+      fail("Expected Exception not thrown!");
+    } catch (final ODataClientErrorException e) {
+      assertEquals(HttpStatusCode.PRECONDITION_FAILED.getStatusCode(), 
e.getStatusLine().getStatusCode());
+      final ODataError error = e.getODataError();
+      assertThat(error.getMessage(), containsString("condition"));
+    }
+  }
+
+  @Test
+  public void readNotModified() throws Exception {
+    final ODataClient client = getClient();
+    ODataEntityRequest<ClientEntity> request = 
client.getRetrieveRequestFactory().getEntityRequest(
+        client.newURIBuilder(TecSvcConst.BASE_URI)
+        .appendEntitySetSegment("ESCompAllPrim").appendKeySegment(0).build());
+    request.setIfNoneMatch("W/\"0\"");
+    assertNotNull(request);
+
+    final ODataRetrieveResponse<ClientEntity> response = request.execute();
+    assertEquals(HttpStatusCode.NOT_MODIFIED.getStatusCode(), 
response.getStatusCode());
+  }
+
+  @Override
+  protected ODataClient getClient() {
+    ODataClient odata = ODataClientFactory.getClient();
+    odata.getConfiguration().setDefaultPubFormat(ODataFormat.JSON);
+    return odata;
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/db47760e/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
new file mode 100644
index 0000000..d6be3fa
--- /dev/null
+++ 
b/lib/server-api/src/main/java/org/apache/olingo/server/api/EtagInformation.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.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.</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;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/db47760e/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 43cd3d1..1acd163 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,6 +18,7 @@
  */
 package org.apache.olingo.server.api;
 
+import java.util.Collection;
 import java.util.List;
 
 import org.apache.olingo.commons.api.ODataRuntimeException;
@@ -110,8 +111,18 @@ public abstract class OData {
   public abstract ODataDeserializer createDeserializer(ODataFormat format) 
throws DeserializerException;
 
   /**
-   * @param kind
-   * @return a {@link EdmPrimitiveType} instance for the type kind
+   * Creates a primitive-type instance.
+   * @param kind the kind of the primitive type
+   * @return an {@link EdmPrimitiveType} instance for the type kind
    */
   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
+   */
+  public abstract EtagInformation createEtagInformation(final 
Collection<String> values);
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/db47760e/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
new file mode 100644
index 0000000..f5477c0
--- /dev/null
+++ 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/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;
+
+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();
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/db47760e/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 ea936fd..3b13717 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,6 +18,8 @@
  */
 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;
@@ -25,6 +27,7 @@ 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;
@@ -114,4 +117,12 @@ public class ODataImpl extends OData {
   public EdmPrimitiveType createPrimitiveTypeInstance(final 
EdmPrimitiveTypeKind kind) {
     return EdmPrimitiveTypeFactory.getInstance(kind);
   }
+
+  @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));
+  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/db47760e/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
new file mode 100644
index 0000000..51c89d3
--- /dev/null
+++ 
b/lib/server-core/src/test/java/org/apache/olingo/server/core/EtagParserTest.java
@@ -0,0 +1,131 @@
+/*
+ * 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\""));
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/db47760e/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 322d13b..0ad21bb 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
@@ -34,6 +34,7 @@ import org.apache.olingo.commons.api.http.HttpContentType;
 import org.apache.olingo.commons.api.http.HttpHeader;
 import org.apache.olingo.commons.api.http.HttpMethod;
 import org.apache.olingo.commons.api.http.HttpStatusCode;
+import org.apache.olingo.server.api.EtagInformation;
 import org.apache.olingo.server.api.ODataApplicationException;
 import org.apache.olingo.server.api.ODataRequest;
 import org.apache.olingo.server.api.ODataResponse;
@@ -409,6 +410,19 @@ public class TechnicalEntityProcessor extends 
TechnicalProcessor
 
     final Entity entity = readEntity(uriInfo);
 
+    if (entity.getETag() != null) {
+      final EtagInformation ifMatch = 
odata.createEtagInformation(request.getHeaders(HttpHeader.IF_MATCH));
+      if (!ifMatch.isMatchedBy(entity.getETag()) && 
!ifMatch.getEtags().isEmpty()) {
+        throw new ODataApplicationException("The If-Match precondition is not 
fulfilled.",
+            HttpStatusCode.PRECONDITION_FAILED.getStatusCode(), Locale.ROOT);
+      }
+      if 
(odata.createEtagInformation(request.getHeaders(HttpHeader.IF_NONE_MATCH))
+          .isMatchedBy(entity.getETag())) {
+        throw new ODataApplicationException("The entity has not been 
modified.",
+            HttpStatusCode.NOT_MODIFIED.getStatusCode(), Locale.ROOT);
+      }
+    }
+
     final ODataFormat format = 
ODataFormat.fromContentType(requestedContentType);
     final ExpandOption expand = uriInfo.getExpandOption();
     final SelectOption select = uriInfo.getSelectOption();

Reply via email to