You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by mi...@apache.org on 2014/08/12 10:49:34 UTC

git commit: [OLINGO-388] Duplicate system query options throw exception

Repository: olingo-odata2
Updated Branches:
  refs/heads/master d9e90388f -> 575c59ff7


[OLINGO-388] Duplicate system query options throw exception


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata2/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata2/commit/575c59ff
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata2/tree/575c59ff
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata2/diff/575c59ff

Branch: refs/heads/master
Commit: 575c59ff7106ddcb542c9e806c0302775f83c140
Parents: d9e9038
Author: Christian Holzer <c....@sap.com>
Authored: Mon Aug 11 14:25:08 2014 +0200
Committer: Michael Bolz <mi...@apache.org>
Committed: Tue Aug 12 10:33:06 2014 +0200

----------------------------------------------------------------------
 .../odata2/api/processor/ODataRequest.java      | 14 ++++-
 .../apache/olingo/odata2/api/uri/UriParser.java | 16 +++++
 .../odata2/api/uri/UriSyntaxException.java      |  2 +
 .../olingo/odata2/core/ODataRequestHandler.java |  2 +-
 .../olingo/odata2/core/ODataRequestImpl.java    | 45 +++++++++++---
 .../odata2/core/rest/ODataSubLocator.java       |  2 +-
 .../odata2/core/servlet/ODataServlet.java       |  2 +-
 .../olingo/odata2/core/servlet/RestUtil.java    | 30 ++++++++++
 .../olingo/odata2/core/uri/UriParserImpl.java   | 62 +++++++++++++++-----
 .../src/main/resources/i18n.properties          |  1 +
 .../core/ODataRequestHandlerValidationTest.java | 16 +++++
 .../olingo/odata2/core/uri/UriParserTest.java   | 42 +++++++++----
 .../fit/basic/HttpExceptionResponseTest.java    | 13 +++-
 .../fit/basic/LanguageNegotiationTest.java      |  6 +-
 .../src/test/resources/i18n.properties          | 22 -------
 .../src/test/resources/i18n_it.properties       |  1 +
 16 files changed, 212 insertions(+), 64 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/575c59ff/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/processor/ODataRequest.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/processor/ODataRequest.java b/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/processor/ODataRequest.java
index 7dfa43e..8c3190f 100644
--- a/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/processor/ODataRequest.java
+++ b/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/processor/ODataRequest.java
@@ -50,7 +50,9 @@ public abstract class ODataRequest {
   public abstract List<String> getAcceptHeaders();
 
   public abstract Map<String, String> getQueryParameters();
-
+  
+  public abstract Map<String, List<String>> getAllQueryParameters();
+  
   public static ODataRequestBuilder requestHeaders(final Map<String, List<String>> headers) {
     return newBuilder().requestHeaders(headers);
   }
@@ -82,7 +84,11 @@ public abstract class ODataRequest {
   public static ODataRequestBuilder queryParameters(final Map<String, String> queryParameters) {
     return newBuilder().queryParameters(queryParameters);
   }
-
+  
+  public static ODataRequestBuilder allQueryParameters(final Map<String, List<String>> allQueryParameters) {
+    return newBuilder().allQueryParameters(allQueryParameters);
+  }
+  
   public static ODataRequestBuilder fromRequest(final ODataRequest request) {
     return newBuilder().fromRequest(request);
   }
@@ -119,7 +125,9 @@ public abstract class ODataRequest {
     public abstract ODataRequestBuilder acceptHeaders(List<String> acceptHeaders);
 
     public abstract ODataRequestBuilder queryParameters(Map<String, String> queryParameters);
-
+    
+    public abstract ODataRequestBuilder allQueryParameters(Map<String, List<String>> queryParameters);
+    
     public abstract ODataRequestBuilder fromRequest(ODataRequest request);
 
   }

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/575c59ff/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/uri/UriParser.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/uri/UriParser.java b/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/uri/UriParser.java
index 8db63e8..4032306 100644
--- a/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/uri/UriParser.java
+++ b/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/uri/UriParser.java
@@ -55,6 +55,8 @@ public abstract class UriParser {
 
   /**
    * Parses path segments and query parameters.
+   * This method ignores redundant system query parameters.
+   * 
    * @param pathSegments list of path segments
    * @param queryParameters query parameters
    * @return {@link UriInfo} information about the parsed URI
@@ -66,6 +68,20 @@ public abstract class UriParser {
       throws UriSyntaxException, UriNotMatchingException, EdmException;
 
   /**
+   * Parses path segments and query parameters.
+   * Throws an exception if there are redundant system query parameters.
+   * 
+   * @param pathSegments list of path segments
+   * @param queryParameters query parameters
+   * @return {@link UriInfo} information about the parsed URI
+   * @throws UriSyntaxException
+   * @throws UriNotMatchingException
+   * @throws EdmException
+   */
+  public abstract UriInfo parseAll(List<PathSegment> pathSegments, Map<String, List<String>> allQueryParameters)
+      throws UriSyntaxException, UriNotMatchingException, EdmException;
+  
+  /**
    * Parses a $filter expression string and create an expression tree.
    * <p>The current expression parser supports expressions as defined in the
    * OData specification 2.0 with the following restrictions:

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/575c59ff/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/uri/UriSyntaxException.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/uri/UriSyntaxException.java b/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/uri/UriSyntaxException.java
index f719343..480f38d 100644
--- a/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/uri/UriSyntaxException.java
+++ b/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/uri/UriSyntaxException.java
@@ -44,6 +44,8 @@ public class UriSyntaxException extends ODataBadRequestException {
       "MISSINGKEYPREDICATENAME");
   public static final MessageReference DUPLICATEKEYNAMES = createMessageReference(UriSyntaxException.class,
       "DUPLICATEKEYNAMES");
+  public static final MessageReference DUPLICATESYSTEMQUERYPARAMETES = createMessageReference(UriSyntaxException.class,
+      "DUPLICATESYSTEMQUERYPARAMETES");
   public static final MessageReference EMPTYSEGMENT = createMessageReference(UriSyntaxException.class, "EMPTYSEGMENT");
   public static final MessageReference MUSTNOTBELASTSEGMENT = createMessageReference(UriSyntaxException.class,
       "MUSTNOTBELASTSEGMENT");

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/575c59ff/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ODataRequestHandler.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ODataRequestHandler.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ODataRequestHandler.java
index 0b1da19..2f169aa 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ODataRequestHandler.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ODataRequestHandler.java
@@ -104,7 +104,7 @@ public class ODataRequestHandler {
 
       final List<PathSegment> pathSegments = context.getPathInfo().getODataSegments();
       int timingHandle2 = context.startRuntimeMeasurement("UriParserImpl", "parse");
-      uriInfo = (UriInfoImpl) uriParser.parse(pathSegments, request.getQueryParameters());
+      uriInfo = (UriInfoImpl) uriParser.parseAll(pathSegments, request.getAllQueryParameters());
       context.stopRuntimeMeasurement(timingHandle2);
 
       final ODataHttpMethod method = request.getMethod();

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/575c59ff/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ODataRequestImpl.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ODataRequestImpl.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ODataRequestImpl.java
index 67e77b4..41a45a7 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ODataRequestImpl.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ODataRequestImpl.java
@@ -21,6 +21,7 @@ package org.apache.olingo.odata2.core;
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -41,6 +42,7 @@ public class ODataRequestImpl extends ODataRequest {
   private InputStream body;
   private PathInfo pathInfo;
   private Map<String, String> queryParameters;
+  private Map<String, List<String>> allQueryParameters;
   private List<String> acceptHeaders;
   private ContentType contentType;
   private List<Locale> acceptableLanguages;
@@ -91,12 +93,17 @@ public class ODataRequestImpl extends ODataRequest {
     return pathInfo;
   }
 
+  @Override
+  public Map<String, List<String>> getAllQueryParameters() {
+    return allQueryParameters;
+  }
+
   public class ODataRequestBuilderImpl extends ODataRequestBuilder {
     private ODataHttpMethod method;
     private CaseInsensitiveMap requestHeaders = new CaseInsensitiveMap();
     private InputStream body;
     private PathInfo pathInfo;
-    private Map<String, String> queryParameters;
+    private Map<String, List<String>> allQueryParameters = new HashMap<String, List<String>>();
     private List<String> acceptHeaders;
     private ContentType contentType;
     private List<Locale> acceptableLanguages;
@@ -107,10 +114,12 @@ public class ODataRequestImpl extends ODataRequest {
       ODataRequestImpl.this.requestHeaders = requestHeaders;
       ODataRequestImpl.this.body = body;
       ODataRequestImpl.this.pathInfo = pathInfo;
-      ODataRequestImpl.this.queryParameters = queryParameters;
+      queryParameters = convertMultiMaptoSingleMap(allQueryParameters);
+      ODataRequestImpl.this.allQueryParameters = allQueryParameters;
       ODataRequestImpl.this.acceptHeaders = acceptHeaders;
       ODataRequestImpl.this.contentType = contentType;
       ODataRequestImpl.this.acceptableLanguages = acceptableLanguages;
+
       return ODataRequestImpl.this;
     }
 
@@ -156,7 +165,18 @@ public class ODataRequestImpl extends ODataRequest {
 
     @Override
     public ODataRequestBuilder queryParameters(final Map<String, String> queryParameters) {
-      this.queryParameters = queryParameters;
+      for (String key : queryParameters.keySet()) {
+        List<String> parameterValues = new LinkedList<String>();
+        parameterValues.add(queryParameters.get(key));
+
+        allQueryParameters.put(key, parameterValues);
+      }
+      return this;
+    }
+
+    @Override
+    public ODataRequestBuilder allQueryParameters(final Map<String, List<String>> allQueryParameters) {
+      this.allQueryParameters = new HashMap<String, List<String>>(allQueryParameters);
       return this;
     }
 
@@ -192,16 +212,25 @@ public class ODataRequestImpl extends ODataRequest {
           acceptableLanguages.add(acceptLanguage);
         }
       }
-      if (request.getQueryParameters() != null) {
-        queryParameters = new HashMap<String, String>();
-        for (Map.Entry<String, String> queryParameter : request.getQueryParameters().entrySet()) {
+      if (request.getAllQueryParameters() != null) {
+        allQueryParameters = new HashMap<String, List<String>>();
+        for (Map.Entry<String, List<String>> queryParameter : request.getAllQueryParameters().entrySet()) {
           String queryParameterName = queryParameter.getKey();
-          queryParameters.put(queryParameterName, queryParameter.getValue());
+          allQueryParameters.put(queryParameterName, request.getAllQueryParameters().get(queryParameterName));
         }
       }
       return this;
     }
 
+    private <T, K> Map<T, K> convertMultiMaptoSingleMap(final Map<T, List<K>> multiMap) {
+      final Map<T, K> singleMap = new HashMap<T, K>();
+
+      for (T key : multiMap.keySet()) {
+        singleMap.put(key, multiMap.get(key).get(0));
+      }
+
+      return singleMap;
+    }
   }
 
   private class CaseInsensitiveMap extends HashMap<String, List<String>> {
@@ -224,4 +253,4 @@ public class ODataRequestImpl extends ODataRequest {
       return super.get(skey.toLowerCase());
     }
   }
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/575c59ff/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/rest/ODataSubLocator.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/rest/ODataSubLocator.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/rest/ODataSubLocator.java
index 2c207bb..7ab9ee1 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/rest/ODataSubLocator.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/rest/ODataSubLocator.java
@@ -166,7 +166,7 @@ public final class ODataSubLocator {
         .acceptHeaders(RestUtil.extractAcceptHeaders(param))
         .body(RestUtil.contentAsStream(RestUtil.extractRequestContent(param)))
         .pathInfo(RestUtil.buildODataPathInfo(param))
-        .queryParameters(RestUtil.convertToSinglevaluedMap(param.getUriInfo().getQueryParameters()))
+        .allQueryParameters(param.getUriInfo().getQueryParameters())
         .requestHeaders(param.getHttpHeaders().getRequestHeaders())
         .contentType(RestUtil.extractRequestContentType(param).toContentTypeString())
         .build();

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/575c59ff/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/servlet/ODataServlet.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/servlet/ODataServlet.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/servlet/ODataServlet.java
index 5078e4b..1d4dfe3 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/servlet/ODataServlet.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/servlet/ODataServlet.java
@@ -166,7 +166,7 @@ public class ODataServlet extends HttpServlet {
           .acceptHeaders(RestUtil.extractAcceptHeaders(req.getHeader(HttpHeaders.ACCEPT)))
           .acceptableLanguages(RestUtil.extractAcceptableLanguage(req.getHeader(HttpHeaders.ACCEPT_LANGUAGE)))
           .pathInfo(RestUtil.buildODataPathInfo(req, pathSplit))
-          .queryParameters(RestUtil.extractQueryParameters(req.getQueryString()))
+          .allQueryParameters(RestUtil.extractAllQueryParameters(req.getQueryString()))
           .requestHeaders(RestUtil.extractHeaders(req))
           .body(req.getInputStream())
           .build();

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/575c59ff/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/servlet/RestUtil.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/servlet/RestUtil.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/servlet/RestUtil.java
index 36a1f65..6e06bc1 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/servlet/RestUtil.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/servlet/RestUtil.java
@@ -26,6 +26,7 @@ import java.util.Collections;
 import java.util.Comparator;
 import java.util.Enumeration;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -104,6 +105,35 @@ public class RestUtil {
     return queryParametersMap;
   }
 
+  public static Map<String, List<String>> extractAllQueryParameters(final String queryString) {
+    Map<String, List<String>> allQueryParameterMap = new HashMap<String, List<String>>();
+    
+    if (queryString != null) {
+      // At first the queryString will be decoded.
+      List<String> queryParameters = Arrays.asList(Decoder.decode(queryString).split("\\&"));
+      for (String param : queryParameters) {
+        int indexOfEqualSign = param.indexOf("=");
+        
+        if (indexOfEqualSign < 0) {
+          final List<String> parameterList = allQueryParameterMap.containsKey(param) ? allQueryParameterMap.get(param) 
+              : new LinkedList<String>();
+         allQueryParameterMap.put(param, parameterList);
+          
+          parameterList.add("");
+        } else {
+          final String key = param.substring(0, indexOfEqualSign);
+          final List<String> parameterList = allQueryParameterMap.containsKey(key) ? allQueryParameterMap.get(key) 
+              : new LinkedList<String>();
+          
+          allQueryParameterMap.put(key, parameterList);
+          parameterList.add(param.substring(indexOfEqualSign + 1));
+        }
+      }
+    }
+    
+    return allQueryParameterMap;
+  }
+  
   /*
    * Parses Accept-Language header. Returns a list sorted by quality parameter
    */

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/575c59ff/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/UriParserImpl.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/UriParserImpl.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/UriParserImpl.java
index 2f0a286..c58f7ae 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/UriParserImpl.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/uri/UriParserImpl.java
@@ -21,6 +21,7 @@ package org.apache.olingo.odata2.core.uri;
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.regex.Matcher;
@@ -102,6 +103,14 @@ public class UriParserImpl extends UriParser {
   @Override
   public UriInfo parse(final List<PathSegment> pathSegments, final Map<String, String> queryParameters)
       throws UriSyntaxException, UriNotMatchingException, EdmException {
+
+    return parseAll(pathSegments, convertFromSingleMapToMultiMap(queryParameters));
+  }
+
+  @Override
+  public UriInfo parseAll(final List<PathSegment> pathSegments, final Map<String, List<String>> allQueryParameters)
+      throws UriSyntaxException, UriNotMatchingException, EdmException {
+
     this.pathSegments = copyPathSegmentList(pathSegments);
     systemQueryOptions = new HashMap<SystemQueryOption, String>();
     otherQueryParameters = new HashMap<String, String>();
@@ -111,7 +120,7 @@ public class UriParserImpl extends UriParser {
 
     handleResourcePath();
 
-    distributeQueryParameters(queryParameters);
+    distributeQueryParameters(allQueryParameters);
     checkSystemQueryOptionsCompatibility();
     handleSystemQueryOptions();
     handleOtherQueryParameters();
@@ -119,6 +128,19 @@ public class UriParserImpl extends UriParser {
     return uriResult;
   }
 
+  private <T, K> Map<T, List<K>> convertFromSingleMapToMultiMap(final Map<T, K> singleMap) {
+    Map<T, List<K>> multiMap = new HashMap<T, List<K>>();
+
+    for (T key : singleMap.keySet()) {
+      List<K> valueList = new LinkedList<K>();
+      valueList.add(singleMap.get(key));
+
+      multiMap.put(key, valueList);
+    }
+
+    return multiMap;
+  }
+
   private void preparePathSegments() throws UriSyntaxException {
     // Remove an empty path segment at the start of the OData part of the resource path.
     if (!pathSegments.isEmpty() && pathSegments.get(0).equals("")) {
@@ -508,23 +530,35 @@ public class UriParserImpl extends UriParser {
     ensureLastSegment();
   }
 
-  private void distributeQueryParameters(final Map<String, String> queryParameters) throws UriSyntaxException {
+  private void distributeQueryParameters(final Map<String, List<String>> queryParameters) throws UriSyntaxException {
     for (final String queryOptionString : queryParameters.keySet()) {
-      final String value = queryParameters.get(queryOptionString);
-      if (queryOptionString.startsWith("$")) {
-        SystemQueryOption queryOption;
-        try {
-          queryOption = SystemQueryOption.valueOf(queryOptionString);
-        } catch (IllegalArgumentException e) {
-          throw new UriSyntaxException(UriSyntaxException.INVALIDSYSTEMQUERYOPTION.addContent(queryOptionString), e);
-        }
-        if ("".equals(value)) {
-          throw new UriSyntaxException(UriSyntaxException.INVALIDNULLVALUE.addContent(queryOptionString));
+      final List<String> valueList = queryParameters.get(queryOptionString);
+
+      if (valueList.size() >= 1) {
+        String value = valueList.get(0);
+
+        if (queryOptionString.startsWith("$")) {
+          SystemQueryOption queryOption;
+          try {
+            queryOption = SystemQueryOption.valueOf(queryOptionString);
+          } catch (IllegalArgumentException e) {
+            throw new UriSyntaxException(UriSyntaxException.INVALIDSYSTEMQUERYOPTION.addContent(queryOptionString), e);
+          }
+          if ("".equals(value)) {
+            throw new UriSyntaxException(UriSyntaxException.INVALIDNULLVALUE.addContent(queryOptionString));
+          } else {
+            if (valueList.size() == 1 && !systemQueryOptions.containsKey(queryOption)) {
+              systemQueryOptions.put(queryOption, value);
+            } else {
+              throw new UriSyntaxException(UriSyntaxException.DUPLICATESYSTEMQUERYPARAMETES
+                  .addContent(queryOptionString));
+            }
+          }
         } else {
-          systemQueryOptions.put(queryOption, value);
+          otherQueryParameters.put(queryOptionString, value);
         }
       } else {
-        otherQueryParameters.put(queryOptionString, value);
+        throw new UriSyntaxException(UriSyntaxException.INVALIDNULLVALUE.addContent(queryOptionString));
       }
     }
   }

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/575c59ff/odata2-lib/odata-core/src/main/resources/i18n.properties
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/resources/i18n.properties b/odata2-lib/odata-core/src/main/resources/i18n.properties
index cc2ca5f..90b7045 100644
--- a/odata2-lib/odata-core/src/main/resources/i18n.properties
+++ b/odata2-lib/odata-core/src/main/resources/i18n.properties
@@ -40,6 +40,7 @@ org.apache.olingo.odata2.api.uri.UriSyntaxException.NONAVIGATIONPROPERTY=Propert
 org.apache.olingo.odata2.api.uri.UriSyntaxException.MISSINGPARAMETER=Missing parameter.
 org.apache.olingo.odata2.api.uri.UriSyntaxException.MISSINGKEYPREDICATENAME=Missing key predicate name for key: '%1$s'.
 org.apache.olingo.odata2.api.uri.UriSyntaxException.DUPLICATEKEYNAMES=Duplicate key names: '%1$s'.
+org.apache.olingo.odata2.api.uri.UriSyntaxException.DUPLICATESYSTEMQUERYPARAMETES=Duplicate system query parameter names: '%1$s'.
 org.apache.olingo.odata2.api.uri.UriSyntaxException.EMPTYSEGMENT=No empty segment allowed.
 org.apache.olingo.odata2.api.uri.UriSyntaxException.MUSTNOTBELASTSEGMENT='%1$s' must not be the last segment. 
 org.apache.olingo.odata2.api.uri.UriSyntaxException.MUSTBELASTSEGMENT='%1$s' must be the last segment.

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/575c59ff/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/ODataRequestHandlerValidationTest.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/ODataRequestHandlerValidationTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/ODataRequestHandlerValidationTest.java
index b46da46..7105c93 100644
--- a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/ODataRequestHandlerValidationTest.java
+++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/ODataRequestHandlerValidationTest.java
@@ -29,6 +29,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
@@ -232,6 +233,8 @@ public class ODataRequestHandlerValidationTest extends BaseTest {
     when(request.getPathInfo()).thenReturn(pathInfo);
     when(request.getQueryParameters()).thenReturn(
         queryParameters == null ? Collections.<String, String> emptyMap() : queryParameters);
+    when(request.getAllQueryParameters()).thenReturn(queryParameters == null ?
+        Collections.<String, List<String>> emptyMap() : convertToMultiMap(queryParameters));
     when(request.getContentType()).thenReturn(
         requestContentType == null ? HttpContentType.APPLICATION_JSON : requestContentType);
     when(request.getRequestHeaderValue(httpHeaderName)).thenReturn(httpHeaderValue);
@@ -241,6 +244,19 @@ public class ODataRequestHandlerValidationTest extends BaseTest {
     return request;
   }
 
+  private Map<String, List<String>> convertToMultiMap(final Map<String, String> queryParameters) {
+    Map<String, List<String>> multiMap = new HashMap<String, List<String>>();
+
+    for (final String key : queryParameters.keySet()) {
+      List<String> parameterList = new LinkedList<String>();
+      parameterList.add(queryParameters.get(key));
+
+      multiMap.put(key, parameterList);
+    }
+
+    return multiMap;
+  }
+
   private ODataService mockODataService(final ODataServiceFactory serviceFactory) throws ODataException {
     ODataService service = DispatcherTest.getMockService();
     when(service.getEntityDataModel()).thenReturn(edm);

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/575c59ff/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/uri/UriParserTest.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/uri/UriParserTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/uri/UriParserTest.java
index c3e457d..0c64844 100644
--- a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/uri/UriParserTest.java
+++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/uri/UriParserTest.java
@@ -31,6 +31,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
@@ -81,19 +82,25 @@ public class UriParserTest extends BaseTest {
 
     final List<PathSegment> pathSegments =
         MockFacade.getPathSegmentsAsODataPathSegmentMock(Arrays.asList(path[0].split("/", -1)));
-    final Map<String, String> queryParameters =
-        getQueryParameters(path.length == 2 ? unescape(path[1]) : "");
+    final Map<String, List<String>> queryParameters = getQueryParameters(path.length == 2 ? unescape(path[1]) : "");
 
-    return (UriInfoImpl) new UriParserImpl(edm).parse(pathSegments, queryParameters);
+    return (UriInfoImpl) new UriParserImpl(edm).parseAll(pathSegments, queryParameters);
   }
 
-  private Map<String, String> getQueryParameters(final String uri) {
-    Map<String, String> queryParameters = new HashMap<String, String>();
+  private Map<String, List<String>> getQueryParameters(final String uri) {
+    Map<String, List<String>> allQueryParameters = new HashMap<String, List<String>>();
+
     for (final String option : uri.split("&")) {
       final String[] keyAndValue = option.split("=");
-      queryParameters.put(keyAndValue[0], keyAndValue.length == 2 ? keyAndValue[1] : "");
+      List<String> list = allQueryParameters.containsKey(keyAndValue[0]) ?
+          allQueryParameters.get(keyAndValue[0]) : new LinkedList<String>();
+
+      list.add(keyAndValue.length == 2 ? keyAndValue[1] : "");
+
+      allQueryParameters.put(keyAndValue[0], list);
     }
-    return queryParameters;
+
+    return allQueryParameters;
   }
 
   private String unescape(final String s) throws UriSyntaxException {
@@ -667,9 +674,6 @@ public class UriParserTest extends BaseTest {
     assertEquals("xml", result.getFormat());
     assertNull(result.getTop());
 
-    result = parse("Employees?$format=xml&$format=json");
-    assertEquals("json", result.getFormat());
-
     result = parse("Employees?$format=custom/*");
     assertNotNull(result.getFormat());
     assertEquals("custom/*", result.getFormat().toString());
@@ -708,6 +712,24 @@ public class UriParserTest extends BaseTest {
   }
 
   @Test
+  public void parseWrongRedundantSystemQueryOptions() throws Exception {
+    parseWrongUri("Employees?$top=1&$top=2", UriSyntaxException.DUPLICATESYSTEMQUERYPARAMETES);
+    parseWrongUri("Employees?$top=1&$skip=1&$top=2", UriSyntaxException.DUPLICATESYSTEMQUERYPARAMETES);
+    parseWrongUri("Employees?$top=1&$top=1", UriSyntaxException.DUPLICATESYSTEMQUERYPARAMETES);
+    parseWrongUri("Employees?$skip=1&$skip=2", UriSyntaxException.DUPLICATESYSTEMQUERYPARAMETES);
+    parseWrongUri("Employees?$expand=ne_Manager&$expand=ne_Manager", UriSyntaxException.DUPLICATESYSTEMQUERYPARAMETES);
+    parseWrongUri("Employees?$orderby=Name%20desc&$orderby=Birthday%20desc",
+        UriSyntaxException.DUPLICATESYSTEMQUERYPARAMETES);
+    parseWrongUri("Employees?$select=EmployeeName&$select=EmployeeName",
+        UriSyntaxException.DUPLICATESYSTEMQUERYPARAMETES);
+    parseWrongUri("Employees?$filter=EmployeeName%20eq%20'Foo'&$filter=EmployeeName%20ne%20'Bar'",
+        UriSyntaxException.DUPLICATESYSTEMQUERYPARAMETES);
+    parseWrongUri("Employees?$inlinecount=allpages&$inlinecount=none",
+        UriSyntaxException.DUPLICATESYSTEMQUERYPARAMETES);
+    parseWrongUri("Employees?$format=xml&$format=json", UriSyntaxException.DUPLICATESYSTEMQUERYPARAMETES);
+  }
+
+  @Test
   public void parseWrongSystemQueryOptionSkip() throws Exception {
     parseWrongUri("Employees?$skip=-1", UriSyntaxException.INVALIDNEGATIVEVALUE);
     parseWrongUri("Employees?$skip=-0", UriSyntaxException.INVALIDNEGATIVEVALUE);

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/575c59ff/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/basic/HttpExceptionResponseTest.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/basic/HttpExceptionResponseTest.java b/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/basic/HttpExceptionResponseTest.java
index 8fbba2f..1bb8f69 100644
--- a/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/basic/HttpExceptionResponseTest.java
+++ b/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/basic/HttpExceptionResponseTest.java
@@ -95,7 +95,18 @@ public class HttpExceptionResponseTest extends AbstractBasicTest {
     assertXpathValuesEqual("\"" + MessageService.getMessage(Locale.ENGLISH, ODataNotFoundException.ENTITY).getText()
         + "\"", "/a:error/a:message", content);
   }
-
+  
+  @Test
+  public void test400BadRequestRedundantSystemQueryOptions() throws Exception {
+    HttpResponse response = executeGetRequest("Employees?$top=1&$top=3");
+    assertEquals(HttpStatusCodes.BAD_REQUEST.getStatusCode(), response.getStatusLine().getStatusCode());
+    
+    final String content = StringHelper.inputStreamToString(response.getEntity().getContent());
+    assertEquals("<?xml version='1.0' encoding='UTF-8'?><error xmlns=\"http://schemas.microsoft.com/ado/2007/"
+        + "08/dataservices/metadata\"><code/><message xml:lang=\"en\">Duplicate system query parameter names: "
+        + "'$top'.</message></error>", content);
+  }
+  
   @Test
   public void genericHttpExceptions() throws Exception {
     disableLogging();

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/575c59ff/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/basic/LanguageNegotiationTest.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/basic/LanguageNegotiationTest.java b/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/basic/LanguageNegotiationTest.java
index 3d46b45..ecbd893 100644
--- a/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/basic/LanguageNegotiationTest.java
+++ b/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/basic/LanguageNegotiationTest.java
@@ -100,7 +100,7 @@ public class LanguageNegotiationTest extends AbstractBasicTest {
 
     assertXpathExists("/m:error/m:message", content);
     assertXpathExists("/m:error/m:message[@xml:lang=\"it\"]", content);
-    assertXpathEvaluatesTo("itLanguage", "/m:error/m:message/text()", content);
+    assertXpathEvaluatesTo("eccezione comune", "/m:error/m:message/text()", content);
 
   }
 
@@ -114,11 +114,11 @@ public class LanguageNegotiationTest extends AbstractBasicTest {
 
     assertXpathExists("/m:error/m:message", content);
     assertXpathExists("/m:error/m:message[@xml:lang=\"en\"]", content);
-    assertXpathEvaluatesTo("fallbackLanguage", "/m:error/m:message/text()", content);
+    assertXpathEvaluatesTo("Common exception", "/m:error/m:message/text()", content);
   }
 
   private static class MyException extends ODataMessageException {
-    private static final MessageReference TEST = createMessageReference(MyException.class, "TEST");
+    private static final MessageReference TEST = createMessageReference(ODataMessageException.class, "COMMON");
     private static final long serialVersionUID = 1L;
 
     /**

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/575c59ff/odata2-lib/odata-fit/src/test/resources/i18n.properties
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-fit/src/test/resources/i18n.properties b/odata2-lib/odata-fit/src/test/resources/i18n.properties
deleted file mode 100644
index ae7c98b..0000000
--- a/odata2-lib/odata-fit/src/test/resources/i18n.properties
+++ /dev/null
@@ -1,22 +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.
-#-------------------------------------------------------------------------------
-# Language Negotiation Test
-#
-
-org.apache.olingo.odata2.fit.basic.LanguageNegotiationTest$MyException.TEST=fallbackLanguage

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/575c59ff/odata2-lib/odata-fit/src/test/resources/i18n_it.properties
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-fit/src/test/resources/i18n_it.properties b/odata2-lib/odata-fit/src/test/resources/i18n_it.properties
index 12d072b..1320e74 100644
--- a/odata2-lib/odata-fit/src/test/resources/i18n_it.properties
+++ b/odata2-lib/odata-fit/src/test/resources/i18n_it.properties
@@ -20,3 +20,4 @@
 #
 
 org.apache.olingo.odata2.fit.basic.LanguageNegotiationTest$MyException.TEST=itLanguage
+org.apache.olingo.odata2.api.exception.ODataMessageException.COMMON=eccezione comune