You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by fm...@apache.org on 2014/03/13 17:30:25 UTC

[37/37] git commit: OLINGO-175 moved ODataJClient test-service into fit module

OLINGO-175 moved ODataJClient test-service into fit module


Project: http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/commit/70f06578
Tree: http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/tree/70f06578
Diff: http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/diff/70f06578

Branch: refs/heads/olingo200
Commit: 70f065789834184a2af16be7381dd3fc5a5d6247
Parents: 119627f
Author: fmartelli <fa...@gmail.com>
Authored: Thu Mar 13 17:29:27 2014 +0100
Committer: fmartelli <fa...@gmail.com>
Committed: Thu Mar 13 17:29:27 2014 +0100

----------------------------------------------------------------------
 fit/pom.xml                                     |  169 +-
 .../org/apache/olingo/fit/AbstractServices.java |  776 ++++++++++
 .../fit/UnsupportedMediaTypeException.java      |   41 +
 .../java/org/apache/olingo/fit/V3Services.java  |   38 +
 .../java/org/apache/olingo/fit/V4Services.java  |   38 +
 .../org/apache/olingo/fit/methods/MERGE.java    |   31 +
 .../org/apache/olingo/fit/methods/PATCH.java    |   31 +
 .../apache/olingo/fit/rproxy/LinkRewrite.java   |   54 +
 .../olingo/fit/rproxy/LinkRewriteRenderer.java  |   56 +
 .../olingo/fit/utils/AbstractUtilities.java     |  721 +++++++++
 .../org/apache/olingo/fit/utils/Accept.java     |   78 +
 .../org/apache/olingo/fit/utils/Commons.java    |  268 ++++
 .../org/apache/olingo/fit/utils/Constants.java  |  103 ++
 .../org/apache/olingo/fit/utils/FSManager.java  |  165 ++
 .../apache/olingo/fit/utils/JSONUtilities.java  |  428 ++++++
 .../org/apache/olingo/fit/utils/LinkInfo.java   |   54 +
 .../olingo/fit/utils/MetadataLinkInfo.java      |  175 +++
 .../olingo/fit/utils/NavigationLinks.java       |  120 ++
 .../apache/olingo/fit/utils/ODataVersion.java   |   35 +
 .../fit/utils/XHTTPMethodInterceptor.java       |   43 +
 .../olingo/fit/utils/XMLEventReaderWrapper.java |  144 ++
 .../apache/olingo/fit/utils/XMLUtilities.java   | 1190 +++++++++++++++
 .../org/apache/olingo/fit/utils/XmlElement.java |   99 ++
 .../main/resources/META-INF/vfs-providers.xml   |   27 +
 fit/src/main/resources/context.xml              |   25 +
 fit/src/main/resources/esigate.properties       |   26 +
 .../org/esigate/rewrite-proxy.properties        |   23 +
 fit/src/main/resources/tomcat-users.xml         |   27 +
 .../main/resources/v3/Car/14/entity.full.json   |   13 +
 fit/src/main/resources/v3/Car/14/entity.xml     |   39 +
 .../main/resources/v3/Car/16/entity.full.json   |   14 +
 fit/src/main/resources/v3/Car/16/entity.xml     |   45 +
 fit/src/main/resources/v3/Car/feed.full.json    |  297 ++++
 fit/src/main/resources/v3/Car/feed.xml          |  459 ++++++
 .../v3/Car/filter/((1 add VIN) eq 16).full.json |    1 +
 .../v3/Car/filter/((1 add VIN) eq 16).xml       |   45 +
 .../v3/Car/filter/((VIN add 1) eq 16).full.json |    1 +
 .../v3/Car/filter/((VIN add 1) eq 16).xml       |   45 +
 .../((VIN lt 16) and (VIN gt 12)).full.json     |    1 +
 .../filter/((VIN lt 16) and (VIN gt 12)).xml    |   81 +
 .../v3/Car/filter/(16 eq (1 add VIN)).full.json |    1 +
 .../v3/Car/filter/(16 eq (1 add VIN)).xml       |   45 +
 .../v3/Car/filter/(VIN lt 16).full.json         |    1 +
 .../resources/v3/Car/filter/(VIN lt 16).xml     |  117 ++
 ...ngth(Description) gt (VIN add 10)).full.json |    1 +
 .../(length(Description) gt (VIN add 10)).xml   |  117 ++
 .../v3/Car/filter/VIN add 5 lt 11.full.json     |    1 +
 .../resources/v3/Car/filter/VIN add 5 lt 11.xml |   41 +
 .../v3/Car/filter/VIN div 2 le 8.full.json      |    1 +
 .../resources/v3/Car/filter/VIN div 2 le 8.xml  |   41 +
 .../filter/VIN le 18 and VIN gt 12.full.json    |    1 +
 .../v3/Car/filter/VIN le 18 and VIN gt 12.xml   |   41 +
 .../v3/Car/filter/VIN mul 2 le 30.full.json     |    1 +
 .../resources/v3/Car/filter/VIN mul 2 le 30.xml |   41 +
 ...not (((VIN ge 16) or (VIN le 12))).full.json |    1 +
 .../not (((VIN ge 16) or (VIN le 12))).xml      |   81 +
 .../startswith(Description,'cen').full.json     |    1 +
 .../filter/startswith(Description,'cen').xml    |   45 +
 .../VIN desc/filter/(VIN lt 16).full.json       |    9 +
 .../Car/orderby/VIN desc/filter/(VIN lt 16).xml |  117 ++
 .../v3/ComputerDetail/-10/entity.full.json      |   23 +
 .../resources/v3/ComputerDetail/-10/entity.xml  |   44 +
 .../(month(PurchaseDate) eq 12).full.json       |    1 +
 .../filter/(month(PurchaseDate) eq 12).xml      |   64 +
 .../filter/day(PurchaseDate) eq 15.full.json    |    1 +
 .../filter/day(PurchaseDate) eq 15.xml          |   41 +
 .../filter/hour(PurchaseDate) eq 1.full.json    |    1 +
 .../filter/hour(PurchaseDate) eq 1.xml          |   41 +
 .../filter/minute(PurchaseDate) eq 33.full.json |    1 +
 .../filter/minute(PurchaseDate) eq 33.xml       |   41 +
 .../filter/month(PurchaseDate) eq 12.full.json  |    1 +
 .../filter/month(PurchaseDate) eq 12.xml        |   41 +
 .../filter/second(PurchaseDate) eq 35.full.json |    1 +
 .../filter/second(PurchaseDate) eq 35.xml       |   41 +
 .../filter/year(PurchaseDate) eq 2020.full.json |    1 +
 .../filter/year(PurchaseDate) eq 2020.xml       |   41 +
 .../resources/v3/Customer/-10/entity.full.json  |  673 ++++++++
 .../main/resources/v3/Customer/-10/entity.xml   |  516 +++++++
 .../v3/Customer/-10/links/Info.full.json        |    4 +
 .../resources/v3/Customer/-10/links/Info.xml    |   22 +
 .../v3/Customer/-10/links/Logins('3').full.json |    4 +
 .../v3/Customer/-10/links/Logins.full.json      |   12 +
 .../resources/v3/Customer/-10/links/Logins.xml  |   25 +
 .../v3/Customer/-10/links/Orders(-10).full.json |    4 +
 .../v3/Customer/-10/links/Orders.full.json      |   13 +
 .../resources/v3/Customer/-10/links/Orders.xml  |   26 +
 .../main/resources/v3/Customer/-7/entity.xml    |  381 +++++
 .../main/resources/v3/Customer/feed.full.json   |  893 +++++++++++
 fit/src/main/resources/v3/Customer/feed.xml     |  704 +++++++++
 ....PhoneNumber,'ODataJClient') eq 1).full.json |    1 +
 ...ePhone.PhoneNumber,'ODataJClient') eq 1).xml |   30 +
 ...ne.PhoneNumber,'lccvussrv') ne -1).full.json |    1 +
 ...omePhone.PhoneNumber,'lccvussrv') ne -1).xml |  600 ++++++++
 .../Customer/filter/CustomerId eq -10.full.json |  678 ++++++++
 .../v3/Customer/filter/CustomerId eq -10.xml    |  522 +++++++
 .../Customer/filter/CustomerId gt -10.full.json |    1 +
 .../v3/Customer/filter/CustomerId gt -10.xml    |  740 +++++++++
 .../Customer/filter/CustomerId lt -10.full.json |    1 +
 .../v3/Customer/filter/CustomerId lt -10.xml    |   30 +
 .../isof(Name,'Edm.String') eq true.full.json   |    1 +
 .../filter/isof(Name,'Edm.String') eq true.xml  | 1085 +++++++++++++
 .../not endswith(Name,'Chandan').full.json      |    1 +
 .../filter/not endswith(Name,'Chandan').xml     | 1065 +++++++++++++
 .../v3/Customer/skiptoken/-1.full.json          |    6 +
 .../main/resources/v3/Customer/skiptoken/-1.xml |   30 +
 .../v3/Customer/skiptoken/-10.full.json         |  978 ++++++++++++
 .../resources/v3/Customer/skiptoken/-10.xml     |  953 ++++++++++++
 .../v3/Customer/skiptoken/-3.full.json          |  776 ++++++++++
 .../main/resources/v3/Customer/skiptoken/-3.xml |  613 ++++++++
 .../v3/Customer/skiptoken/-5.full.json          | 1444 ++++++++++++++++++
 .../main/resources/v3/Customer/skiptoken/-5.xml | 1104 +++++++++++++
 .../v3/Customer/skiptoken/-7.full.json          | 1296 ++++++++++++++++
 .../main/resources/v3/Customer/skiptoken/-7.xml |  990 ++++++++++++
 .../v3/Customer/skiptoken/-9.full.json          |  715 +++++++++
 .../main/resources/v3/Customer/skiptoken/-9.xml |  559 +++++++
 .../v3/CustomerInfo/11/entity.full.json         |   11 +
 .../resources/v3/CustomerInfo/11/entity.xml     |   37 +
 .../v3/CustomerInfo/12/entity.full.json         |   11 +
 .../resources/v3/CustomerInfo/12/entity.xml     |   37 +
 .../v3/EdmBooleanSet/true/entity.full.json      |    1 +
 .../resources/v3/EdmBooleanSet/true/entity.xml  |   36 +
 .../v3/EdmByteSet/255/entity.full.json          |    1 +
 .../main/resources/v3/EdmByteSet/255/entity.xml |   36 +
 .../entity.full.json                            |    1 +
 .../79228162514264337593543950335M/entity.xml   |   36 +
 .../1.7976931348623157E308D/entity.full.json    |    1 +
 .../1.7976931348623157E308D/entity.xml          |   36 +
 .../entity.full.json                            |    1 +
 .../entity.xml                                  |   36 +
 .../v3/EdmInt16Set/32767/entity.full.json       |    1 +
 .../resources/v3/EdmInt16Set/32767/entity.xml   |   36 +
 .../v3/EdmInt32Set/-2147483648/entity.full.json |    1 +
 .../v3/EdmInt32Set/-2147483648/entity.xml       |   36 +
 .../9223372036854775807L/entity.full.json       |    1 +
 .../EdmInt64Set/9223372036854775807L/entity.xml |   36 +
 .../EdmSingleSet/3.4028235E38f/entity.full.json |    1 +
 .../v3/EdmSingleSet/3.4028235E38f/entity.xml    |   36 +
 .../v3/EdmStringSet/'$'/entity.full.json        |    1 +
 .../resources/v3/EdmStringSet/'$'/entity.xml    |   36 +
 .../entity.full.json                            |    1 +
 .../entity.xml                                  |   36 +
 .../v3/InStreamErrorGetCustomer.full.json       |    1 +
 .../resources/v3/InStreamErrorGetCustomer.xml   |  525 +++++++
 .../resources/v3/Login/'3'/entity.full.json     |   13 +
 fit/src/main/resources/v3/Login/'3'/entity.xml  |   42 +
 .../resources/v3/Message/1 -10/entity.full.json |   17 +
 .../main/resources/v3/Message/1 -10/entity.xml  |   46 +
 .../resources/v3/Order/-10/entity.full.json     |   11 +
 fit/src/main/resources/v3/Order/-10/entity.xml  |   40 +
 .../main/resources/v3/Order/-7/entity.full.json |   17 +
 fit/src/main/resources/v3/Order/-7/entity.xml   |   43 +
 .../main/resources/v3/Order/-9/entity.full.json |   17 +
 fit/src/main/resources/v3/Order/-9/entity.xml   |   43 +
 .../v3/OrderLine/-10 -10/entity.full.json       |   14 +
 .../resources/v3/OrderLine/-10 -10/entity.xml   |   42 +
 .../resources/v3/OrderLine/-10 -10/etag.txt     |    1 +
 .../filter/PersonId sub 2 lt -10.full.json      |    1 +
 .../v3/Person/filter/PersonId sub 2 lt -10.xml  |   79 +
 ...iaDefaultService.SpecialEmployee').full.json |    1 +
 ....AstoriaDefaultService.SpecialEmployee').xml |  131 ++
 .../resources/v3/Product/-10/entity.full.json   |   51 +
 .../main/resources/v3/Product/-10/entity.xml    |   61 +
 fit/src/main/resources/v3/Product/-10/etag.txt  |    1 +
 .../resources/v3/Product/-6/entity.full.json    |   51 +
 fit/src/main/resources/v3/Product/-6/entity.xml |   61 +
 fit/src/main/resources/v3/Product/-6/etag.txt   |    1 +
 .../resources/v3/Product/-7/entity.full.json    |   51 +
 fit/src/main/resources/v3/Product/-7/entity.xml |   61 +
 fit/src/main/resources/v3/Product/-7/etag.txt   |    1 +
 .../v3/Product/-7/links/Photos.full.json        |   12 +
 .../resources/v3/Product/-9/entity.full.json    |   60 +
 fit/src/main/resources/v3/Product/-9/entity.xml |   68 +
 fit/src/main/resources/v3/Product/-9/etag.txt   |    1 +
 .../main/resources/v3/Product/feed.full.json    |  452 ++++++
 fit/src/main/resources/v3/Product/feed.xml      |  410 +++++
 .../ceiling(Dimensions.Width) eq 7338.full.json |    1 +
 .../ceiling(Dimensions.Width) eq 7338.xml       |   68 +
 ...', newname') eq 'kdcuklu, newname'.full.json |    1 +
 ...tion, ', newname') eq 'kdcuklu, newname'.xml |   74 +
 .../floor(Dimensions.Width) eq 7337.full.json   |    1 +
 .../filter/floor(Dimensions.Width) eq 7337.xml  |   68 +
 .../indexof(Description, 'k') eq 0.full.json    |    1 +
 .../filter/indexof(Description, 'k') eq 0.xml   |  117 ++
 .../filter/length(Description) eq 7.full.json   |    1 +
 .../Product/filter/length(Description) eq 7.xml |   74 +
 .../round(Dimensions.Width) eq 7338.full.json   |    1 +
 .../filter/round(Dimensions.Width) eq 7338.xml  |   68 +
 ...artswith(Description, 'k') eq true.full.json |    1 +
 .../startswith(Description, 'k') eq true.xml    |  117 ++
 ...of('kdcuklu', Description) eq true.full.json |    1 +
 ...stringof('kdcuklu', Description) eq true.xml |   74 +
 .../toupper(Description) eq 'KDCUKLU'.full.json |    1 +
 .../toupper(Description) eq 'KDCUKLU'.xml       |   74 +
 .../v3/ProductPhoto/-2 -2/entity.full.json      |   10 +
 .../resources/v3/ProductPhoto/-2 -2/entity.xml  |   39 +
 .../v3/ProductPhoto/-3 -3/entity.full.json      |   10 +
 .../resources/v3/ProductPhoto/-3 -3/entity.xml  |   38 +
 fit/src/main/resources/v3/badRequest.json       |   17 +
 fit/src/main/resources/v3/badRequest.xml        |   30 +
 fit/src/main/resources/v3/largeMetadata.xml     |   42 +
 fit/src/main/resources/v3/metadata.xml          |  719 +++++++++
 fit/src/main/resources/v3/notFound.json         |   11 +
 fit/src/main/resources/v3/notFound.xml          |   25 +
 fit/src/main/resources/v3/services.full.json    |  102 ++
 fit/src/main/resources/v3/services.xml          |   98 ++
 .../main/resources/v3/unsupportedMediaType.json |   17 +
 .../main/resources/v3/unsupportedMediaType.xml  |   34 +
 fit/src/main/resources/v4/metadata.xml          |  519 +++++++
 .../main/webapp/WEB-INF/applicationContext.xml  |   50 +
 fit/src/main/webapp/WEB-INF/web.xml             |   75 +
 pom.xml                                         |   44 +
 211 files changed, 30162 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/70f06578/fit/pom.xml
----------------------------------------------------------------------
diff --git a/fit/pom.xml b/fit/pom.xml
index 3cd2f74..f4578c2 100644
--- a/fit/pom.xml
+++ b/fit/pom.xml
@@ -24,7 +24,7 @@
   <modelVersion>4.0.0</modelVersion>
 
   <artifactId>olingo-fit-incubating</artifactId>
-  <packaging>jar</packaging>
+  <packaging>war</packaging>
   <name>${project.artifactId}</name>
 
   <parent>
@@ -33,6 +33,22 @@
     <version>0.1.0-SNAPSHOT</version>
     <relativePath>..</relativePath>
   </parent>
+  
+  <properties>
+    <main.basedir>${project.parent.basedir}</main.basedir>
+    
+    <log.directory>${project.build.directory}/log</log.directory>
+        
+    <war.maven.plugin.version>2.4</war.maven.plugin.version>
+    
+    <cargo.maven.plugin.version>1.4.7</cargo.maven.plugin.version>
+    <cargo.servlet.port>9080</cargo.servlet.port>
+    <cargo.tomcat.ajp.port>9889</cargo.tomcat.ajp.port>
+    <cargo.rmi.port>9805</cargo.rmi.port>
+    <cargo.log>${log.directory}/cargo.log</cargo.log>
+    <cargo.output>${log.directory}/cargo-output.log</cargo.output>
+    <tomcat.version>7.0.50</tomcat.version>
+  </properties>
 	
   <dependencies>
     <dependency>
@@ -45,5 +61,156 @@
       <artifactId>olingo-client-core-incubating</artifactId>
       <version>${project.version}</version>
     </dependency>
+    
+    <!-- Stax -->
+    <dependency>
+      <groupId>stax</groupId>
+      <artifactId>stax-api</artifactId>
+      <version>1.0.1</version>
+    </dependency>
+    <!-- /Stax -->
+
+    <!-- REST services CXF -->
+    <dependency>
+      <groupId>org.apache.cxf</groupId>
+      <artifactId>cxf-rt-frontend-jaxrs</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-databind</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.jaxrs</groupId>
+      <artifactId>jackson-jaxrs-json-provider</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-web</artifactId>
+    </dependency>
+    <!-- /REST services CXF -->
+    
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>javax.servlet-api</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.esigate</groupId>
+      <artifactId>esigate-core</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>commons-logging</groupId>
+      <artifactId>commons-logging</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-vfs2</artifactId>
+    </dependency>
   </dependencies>
+  
+  <build>
+    <defaultGoal>clean package cargo:run</defaultGoal>
+    
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-war-plugin</artifactId>
+        <version>${war.maven.plugin.version}</version>
+        <configuration>
+          <webResources>
+            <!--
+                 HACK: Include legal files explicity, otherwise they will end up in the wrong path
+                       or in another jar file in the war.
+            -->
+            <resource>
+              <directory>${project.build.outputDirectory}</directory>
+              <includes>
+                <include>META-INF/DEPENDENCIES*</include>
+              </includes>
+            </resource>
+            <resource>
+              <directory>${basedir}/../</directory>
+              <targetPath>META-INF</targetPath>
+              <includes>
+                <include>LICENSE</include>
+              </includes>
+            </resource>
+          </webResources>
+          <packagingExcludes>WEB-INF/classes/esigate.properties,WEB-INF/classes/META-INF/LICENSE*,WEB-INF/classes/META-INF/DEPENDENCIES*</packagingExcludes>
+        </configuration>
+      </plugin>
+          
+      <plugin>
+        <groupId>org.codehaus.cargo</groupId>
+        <artifactId>cargo-maven2-plugin</artifactId>
+        <version>${cargo.maven.plugin.version}</version>
+        <configuration>
+          <container>
+            <containerId>tomcat7x</containerId>
+            <zipUrlInstaller>
+              <url>http://archive.apache.org/dist/tomcat/tomcat-7/v${tomcat.version}/bin/apache-tomcat-${tomcat.version}.zip</url>
+              <downloadDir>${settings.localRepository}/org/codehaus/cargo/cargo-container-archives</downloadDir>
+              <extractDir>${project.build.directory}/cargo/extract</extractDir>
+            </zipUrlInstaller>
+            <log>${cargo.log}</log>
+            <output>${cargo.output}</output>
+          </container>
+          <configuration>
+            <type>standalone</type>
+            <properties>
+              <cargo.servlet.port>${cargo.servlet.port}</cargo.servlet.port>
+              <cargo.tomcat.ajp.port>${cargo.tomcat.ajp.port}</cargo.tomcat.ajp.port>
+              <cargo.rmi.port>${cargo.rmi.port}</cargo.rmi.port>
+
+              <!--<cargo.jvmargs>-Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n</cargo.jvmargs>-->
+              <cargo.jvmargs>-noverify -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC -XX:MaxPermSize=256m</cargo.jvmargs>
+            </properties>
+            <files>
+              <file>
+                <file>${project.build.directory}/classes/esigate.properties</file>
+                <todir>lib</todir>
+              </file>
+            </files>
+            <configfiles>
+              <configfile>
+                <file>${project.build.directory}/classes/context.xml</file>
+                <todir>conf/</todir>
+                <tofile>context.xml</tofile>
+              </configfile>
+              <configfile>
+                <file>${project.build.directory}/classes/tomcat-users.xml</file>
+                <todir>conf/</todir>
+                <tofile>tomcat-users.xml</tofile>
+              </configfile>
+            </configfiles>
+          </configuration>
+          <deployables>
+            <deployable>
+              <properties>
+                <context>/</context>
+              </properties>
+            </deployable>
+          </deployables>
+        </configuration>
+      </plugin>
+    </plugins>
+    
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>true</filtering>
+      </resource>
+      <resource>
+        <directory>..</directory>
+        <targetPath>META-INF</targetPath>
+        <includes>
+          <include>LICENSE</include>
+        </includes>
+      </resource>
+    </resources>
+  </build>
 </project>

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/70f06578/fit/src/main/java/org/apache/olingo/fit/AbstractServices.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/AbstractServices.java b/fit/src/main/java/org/apache/olingo/fit/AbstractServices.java
new file mode 100644
index 0000000..ec13050
--- /dev/null
+++ b/fit/src/main/java/org/apache/olingo/fit/AbstractServices.java
@@ -0,0 +1,776 @@
+/*
+ * 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;
+
+import org.apache.olingo.fit.utils.Accept;
+import org.apache.olingo.fit.utils.XMLUtilities;
+import org.apache.olingo.fit.utils.JSONUtilities;
+import org.apache.olingo.fit.utils.ODataVersion;
+import org.apache.olingo.fit.utils.FSManager;
+
+import static org.apache.olingo.fit.utils.Constants.*;
+
+import org.apache.olingo.fit.methods.MERGE;
+import org.apache.olingo.fit.methods.PATCH;
+import org.apache.olingo.fit.utils.AbstractUtilities;
+import org.apache.olingo.fit.utils.Commons;
+import org.apache.olingo.fit.utils.LinkInfo;
+import java.io.File;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractServices {
+
+  /**
+   * Logger.
+   */
+  protected static final Logger LOG = LoggerFactory.getLogger(AbstractServices.class);
+
+  protected abstract ODataVersion getVersion();
+  protected final XMLUtilities xml;
+
+  protected final JSONUtilities json;
+
+  public AbstractServices() throws Exception {
+    this.xml = new XMLUtilities(getVersion());
+    this.json = new JSONUtilities(getVersion());
+  }
+
+  /**
+   * Provide sample services.
+   *
+   * @param accept Accept header.
+   * @return OData services.
+   */
+  @GET
+  public Response getSevices(@HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept) {
+    try {
+      final Accept acceptType = Accept.parse(accept, getVersion());
+
+      if (acceptType == Accept.ATOM) {
+        throw new UnsupportedMediaTypeException("Unsupported media type");
+      }
+
+      return xml.createResponse(
+              FSManager.instance(getVersion()).readFile(SERVICES, acceptType), null, acceptType);
+    } catch (Exception e) {
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  /**
+   * Provide sample metadata.
+   *
+   * @return metadata.
+   */
+  @GET
+  @Path("/$metadata")
+  @Produces("application/xml")
+  public Response getMetadata() {
+    return getMetadata(METADATA);
+  }
+
+  /**
+   * Provide sample lartge metadata.
+   *
+   * @return metadata.
+   */
+  @GET
+  @Path("/large/$metadata")
+  @Produces("application/xml")
+  public Response getLargeMetadata() {
+    return getMetadata("large" + StringUtils.capitalize(METADATA));
+  }
+
+  private Response getMetadata(final String filename) {
+    try {
+      return xml.
+              createResponse(FSManager.instance(getVersion()).readFile(filename, Accept.XML), null, Accept.XML);
+    } catch (Exception e) {
+      return xml.createFaultResponse(Accept.XML.toString(), e);
+    }
+  }
+
+  @MERGE
+  @Path("/{entitySetName}({entityId})")
+  @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON})
+  @Consumes({MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON})
+  public Response mergeEntity(
+          @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept,
+          @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) String prefer,
+          @HeaderParam("If-Match") @DefaultValue(StringUtils.EMPTY) String ifMatch,
+          @PathParam("entitySetName") String entitySetName,
+          @PathParam("entityId") String entityId,
+          final String changes) {
+
+    return patchEntity(accept, prefer, ifMatch, entitySetName, entityId, changes);
+  }
+
+  @PATCH
+  @Path("/{entitySetName}({entityId})")
+  @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON})
+  @Consumes({MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON})
+  public Response patchEntity(
+          @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept,
+          @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) String prefer,
+          @HeaderParam("If-Match") @DefaultValue(StringUtils.EMPTY) String ifMatch,
+          @PathParam("entitySetName") String entitySetName,
+          @PathParam("entityId") String entityId,
+          final String changes) {
+
+    try {
+      final Accept acceptType = Accept.parse(accept, getVersion());
+
+      if (acceptType == Accept.XML || acceptType == Accept.TEXT) {
+        throw new UnsupportedMediaTypeException("Unsupported media type");
+      }
+
+      final AbstractUtilities util = acceptType == Accept.ATOM ? xml : json;
+      InputStream res =
+              util.patchEntity(entitySetName, entityId, IOUtils.toInputStream(changes), acceptType, ifMatch);
+
+      final Response response;
+      if ("return-content".equalsIgnoreCase(prefer)) {
+        response = xml.createResponse(res, null, acceptType, Response.Status.OK);
+      } else {
+        res.close();
+        response = xml.createResponse(null, null, acceptType, Response.Status.NO_CONTENT);
+      }
+
+      if (StringUtils.isNotBlank(prefer)) {
+        response.getHeaders().put("Preference-Applied", Collections.<Object>singletonList(prefer));
+      }
+
+      return response;
+    } catch (Exception e) {
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  @PUT
+  @Path("/{entitySetName}({entityId})")
+  @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON})
+  @Consumes({MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON})
+  public Response putNewEntity(
+          @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept,
+          @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) String prefer,
+          @PathParam("entitySetName") String entitySetName,
+          @PathParam("entityId") String entityId,
+          final String entity) {
+    try {
+      final Accept acceptType = Accept.parse(accept, getVersion());
+
+      if (acceptType == Accept.XML || acceptType == Accept.TEXT) {
+        throw new UnsupportedMediaTypeException("Unsupported media type");
+      }
+
+      InputStream res;
+      if (acceptType == Accept.ATOM) {
+        res = xml.addOrReplaceEntity(entityId, entitySetName, IOUtils.toInputStream(entity));
+      } else {
+        res = json.addOrReplaceEntity(entityId, entitySetName, IOUtils.toInputStream(entity));
+      }
+
+      final Response response;
+      if ("return-content".equalsIgnoreCase(prefer)) {
+        response = xml.createResponse(res, null, acceptType, Response.Status.OK);
+      } else {
+        res.close();
+        response = xml.createResponse(null, null, acceptType, Response.Status.NO_CONTENT);
+      }
+
+      if (StringUtils.isNotBlank(prefer)) {
+        response.getHeaders().put("Preference-Applied", Collections.<Object>singletonList(prefer));
+      }
+
+      return response;
+    } catch (Exception e) {
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  @POST
+  @Path("/{entitySetName}")
+  @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON})
+  @Consumes({MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON})
+  public Response postNewEntity(
+          @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept,
+          @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) String prefer,
+          @PathParam("entitySetName") String entitySetName,
+          final String entity) {
+
+    try {
+      final Accept acceptType = Accept.parse(accept, getVersion());
+
+      if (acceptType == Accept.XML || acceptType == Accept.TEXT) {
+        throw new UnsupportedMediaTypeException("Unsupported media type");
+      }
+
+      final InputStream res;
+      if (acceptType == Accept.ATOM) {
+        res = xml.addOrReplaceEntity(entitySetName, IOUtils.toInputStream(entity));
+      } else {
+        res = json.addOrReplaceEntity(entitySetName, IOUtils.toInputStream(entity));
+      }
+
+      final Response response;
+      if ("return-no-content".equalsIgnoreCase(prefer)) {
+        res.close();
+        response = xml.createResponse(null, null, acceptType, Response.Status.NO_CONTENT);
+      } else {
+        response = xml.createResponse(res, null, acceptType, Response.Status.CREATED);
+      }
+
+      if (StringUtils.isNotBlank(prefer)) {
+        response.getHeaders().put("Preference-Applied", Collections.<Object>singletonList(prefer));
+      }
+
+      return response;
+    } catch (Exception e) {
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  /**
+   * Retrieve entity set or function execution sample.
+   *
+   * @param accept Accept header.
+   * @param name entity set or function name.
+   * @param format format query option.
+   * @param inlinecount inlinecount query option.
+   * @param filter filter query option.
+   * @param orderby orderby query option.
+   * @param skiptoken skiptoken query option.
+   * @return entity set or function result.
+   */
+  @GET
+  @Path("/{name}")
+  public Response getEntitySet(
+          @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept,
+          @PathParam("name") String name,
+          @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) String format,
+          @QueryParam("$inlinecount") @DefaultValue(StringUtils.EMPTY) String inlinecount,
+          @QueryParam("$filter") @DefaultValue(StringUtils.EMPTY) String filter,
+          @QueryParam("$orderby") @DefaultValue(StringUtils.EMPTY) String orderby,
+          @QueryParam("$skiptoken") @DefaultValue(StringUtils.EMPTY) String skiptoken) {
+
+    try {
+      final Accept acceptType;
+      if (StringUtils.isNotBlank(format)) {
+        acceptType = Accept.valueOf(format.toUpperCase());
+      } else {
+        acceptType = Accept.parse(accept, getVersion());
+      }
+
+      if (acceptType == Accept.XML || acceptType == Accept.TEXT) {
+        throw new UnsupportedMediaTypeException("Unsupported media type");
+      }
+
+      try {
+        // search for function ...
+        final InputStream func = FSManager.instance(getVersion()).readFile(name, acceptType);
+        return xml.createResponse(func, null, acceptType);
+      } catch (NotFoundException e) {
+        // search for entitySet ...
+        final String basePath = name + File.separatorChar;
+
+        final StringBuilder builder = new StringBuilder();
+        builder.append(basePath);
+
+        if (StringUtils.isNotBlank(orderby)) {
+          builder.append(ORDERBY).append(File.separatorChar).append(orderby).append(File.separatorChar);
+        }
+
+        if (StringUtils.isNotBlank(filter)) {
+          builder.append(FILTER).append(File.separatorChar).append(filter.replaceAll("/", "."));
+        } else if (StringUtils.isNotBlank(skiptoken)) {
+          builder.append(SKIP_TOKEN).append(File.separatorChar).append(skiptoken);
+        } else {
+          builder.append(FEED);
+        }
+
+        InputStream feed = FSManager.instance(getVersion()).readFile(builder.toString(), acceptType);
+        if ("allpages".equals(inlinecount)) {
+          int count = xml.countAllElements(name);
+          feed.close();
+          if (acceptType == Accept.ATOM) {
+            feed = xml.addAtomInlinecount(
+                    FSManager.instance(getVersion()).readFile(builder.toString(), acceptType),
+                    count,
+                    acceptType);
+          } else {
+            feed = json.addJsonInlinecount(
+                    FSManager.instance(getVersion()).readFile(builder.toString(), acceptType),
+                    count,
+                    acceptType);
+          }
+        }
+
+        return xml.createResponse(feed, Commons.getETag(basePath, getVersion()), acceptType);
+      }
+    } catch (Exception e) {
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  /**
+   * Retrieve entity sample.
+   *
+   * @param accept Accept header.
+   * @param entitySetName Entity set name.
+   * @param entityId entity id.
+   * @param format format query option.
+   * @param expand expand query option.
+   * @param select select query option.
+   * @return entity.
+   */
+  @GET
+  @Path("/{entitySetName}({entityId})")
+  public Response getEntity(
+          @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept,
+          @PathParam("entitySetName") String entitySetName,
+          @PathParam("entityId") String entityId,
+          @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) String format,
+          @QueryParam("$expand") @DefaultValue(StringUtils.EMPTY) String expand,
+          @QueryParam("$select") @DefaultValue(StringUtils.EMPTY) String select) {
+
+    try {
+
+      final Accept acceptType;
+      if (StringUtils.isNotBlank(format)) {
+        acceptType = Accept.valueOf(format.toUpperCase());
+      } else {
+        acceptType = Accept.parse(accept, getVersion());
+      }
+
+      final Map.Entry<String, InputStream> entityInfo = xml.readEntity(entitySetName, entityId, acceptType);
+
+      InputStream entity = entityInfo.getValue();
+
+      if (StringUtils.isNotBlank(select)) {
+        if (acceptType == Accept.ATOM) {
+          entity = xml.selectEntity(entity, select.split(","));
+        } else {
+          entity = json.selectEntity(entity, select.split(","));
+        }
+      }
+
+      if (StringUtils.isNotBlank(expand)) {
+        if (acceptType == Accept.XML || acceptType == Accept.TEXT) {
+          throw new UnsupportedMediaTypeException("Unsupported media type");
+        } else if (acceptType == Accept.ATOM) {
+          for (String exp : expand.split(",")) {
+            entity = xml.expandEntity(
+                    entitySetName,
+                    entityId,
+                    entity,
+                    exp);
+          }
+        } else {
+          for (String exp : expand.split(",")) {
+            entity = json.expandEntity(
+                    entitySetName,
+                    entityId,
+                    entity,
+                    exp);
+          }
+        }
+      }
+
+      return xml.createResponse(entity, Commons.getETag(entityInfo.getKey(), getVersion()), acceptType);
+    } catch (Exception e) {
+      LOG.error("Error retrieving entity", e);
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  @DELETE
+  @Path("/{entitySetName}({entityId})")
+  public Response removeEntity(
+          @PathParam("entitySetName") String entitySetName,
+          @PathParam("entityId") String entityId) {
+
+    try {
+      final String basePath =
+              entitySetName + File.separatorChar + Commons.getEntityKey(entityId) + File.separatorChar;
+
+      FSManager.instance(getVersion()).deleteFile(basePath + ENTITY);
+
+      return xml.createResponse(null, null, null, Response.Status.NO_CONTENT);
+    } catch (Exception e) {
+      return xml.createFaultResponse(Accept.XML.toString(), e);
+    }
+  }
+
+  /**
+   * Retrieve property sample.
+   *
+   * @param accept Accept header.
+   * @param entitySetName Entity set name.
+   * @param entityId entity id.
+   * @param path path.
+   * @param format format query option.
+   * @return property.
+   */
+  @GET
+  @Path("/{entitySetName}({entityId})/{path:.*}")
+  public Response getPath(
+          @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept,
+          @PathParam("entitySetName") String entitySetName,
+          @PathParam("entityId") String entityId,
+          @PathParam("path") String path,
+          @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) String format) {
+
+    try {
+      boolean searchForValue = path.endsWith("$value");
+      Accept acceptType = null;
+      if (StringUtils.isNotBlank(format)) {
+        acceptType = Accept.valueOf(format.toUpperCase());
+      } else if (StringUtils.isNotBlank(accept)) {
+        acceptType = Accept.parse(accept, getVersion(), null);
+      }
+
+      final String basePath =
+              entitySetName + File.separatorChar + Commons.getEntityKey(entityId) + File.separatorChar;
+
+      InputStream stream;
+
+      try {
+        final LinkInfo linkInfo = xml.readLinks(entitySetName, entityId, path, Accept.XML);
+        final Map.Entry<String, List<String>> links = XMLUtilities.extractLinkURIs(linkInfo.getLinks());
+
+        switch (acceptType) {
+          case JSON:
+          case JSON_FULLMETA:
+          case JSON_NOMETA:
+            stream = json.readEntities(links.getValue(), path, links.getKey(), linkInfo.isFeed());
+            stream = json.wrapJsonEntities(stream);
+            break;
+          default:
+            stream = xml.readEntities(links.getValue(), path, links.getKey(), linkInfo.isFeed());
+        }
+      } catch (NotFoundException e) {
+        // if the given path is not about any link then search for property
+        LOG.info("Retrieve property {}", path);
+
+        stream = FSManager.instance(getVersion()).readFile(
+                basePath + ENTITY, acceptType == null || acceptType == Accept.TEXT
+                ? Accept.XML : acceptType);
+
+        if (searchForValue) {
+          stream = xml.getAtomPropertyValue(stream, path.split("/"));
+        } else {
+          if (acceptType == null || acceptType == Accept.XML || acceptType == Accept.ATOM) {
+            // retrieve xml
+            stream = xml.getAtomProperty(stream, path.split("/"));
+          } else {
+            // retrieve Edm type from xml
+            final String edmType = xml.getEdmTypeFromXML(
+                    FSManager.instance(getVersion()).readFile(basePath + ENTITY, Accept.XML),
+                    path.split("/"));
+            // retrieve json property
+            stream = json.getJsonProperty(stream, path.split("/"), edmType);
+          }
+        }
+
+        if ((searchForValue && acceptType != null && acceptType != Accept.TEXT) || acceptType == Accept.ATOM) {
+          throw new UnsupportedMediaTypeException("Unsupported media type " + acceptType);
+        }
+      }
+
+      return xml.createResponse(stream, Commons.getETag(basePath, getVersion()), acceptType);
+    } catch (Exception e) {
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  /**
+   * Retrieve links sample.
+   *
+   * @param accept Accept header.
+   * @param entitySetName Entity set name.
+   * @param entityId entity id.
+   * @param linkName link name.
+   * @param format format query option.
+   * @return links.
+   */
+  @GET
+  @Path("/{entitySetName}({entityId})/$links/{linkName}")
+  public Response getLinks(
+          @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept,
+          @PathParam("entitySetName") String entitySetName,
+          @PathParam("entityId") String entityId,
+          @PathParam("linkName") String linkName,
+          @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) String format) {
+    try {
+      final Accept acceptType;
+      if (StringUtils.isNotBlank(format)) {
+        acceptType = Accept.valueOf(format.toUpperCase());
+      } else {
+        acceptType = Accept.parse(accept, getVersion());
+      }
+
+      if (acceptType == Accept.ATOM) {
+        throw new UnsupportedMediaTypeException("Unsupported media type");
+      }
+
+      final LinkInfo links = xml.readLinks(entitySetName, entityId, linkName, acceptType);
+
+      return xml.createResponse(
+              links.getLinks(),
+              links.getEtag(),
+              acceptType);
+    } catch (Exception e) {
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  @POST
+  @Path("/{entitySetName}({entityId})/$links/{linkName}")
+  public Response postLink(
+          @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept,
+          @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) String contentType,
+          @PathParam("entitySetName") String entitySetName,
+          @PathParam("entityId") String entityId,
+          @PathParam("linkName") String linkName,
+          String link,
+          @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) String format) {
+    try {
+      final Accept acceptType;
+      if (StringUtils.isNotBlank(format)) {
+        acceptType = Accept.valueOf(format.toUpperCase());
+      } else {
+        acceptType = Accept.parse(accept, getVersion());
+      }
+
+      if (acceptType == Accept.ATOM) {
+        throw new UnsupportedMediaTypeException("Unsupported media type");
+      }
+
+      final Accept content;
+      if (StringUtils.isNotBlank(contentType)) {
+        content = Accept.parse(contentType, getVersion());
+      } else {
+        content = acceptType;
+      }
+
+      final AbstractUtilities utils = getUtilities(acceptType);
+
+      final List<String> links;
+      if (content == Accept.XML || content == Accept.TEXT || content == Accept.ATOM) {
+        links = XMLUtilities.extractLinkURIs(IOUtils.toInputStream(link)).getValue();
+      } else {
+        links = JSONUtilities.extractLinkURIs(IOUtils.toInputStream(link)).getValue();
+      }
+
+      utils.putLinksInMemory(
+              Commons.getEntityBasePath(entitySetName, entityId),
+              entitySetName,
+              entityId,
+              linkName,
+              links);
+
+      return xml.createResponse(null, null, null, Response.Status.NO_CONTENT);
+    } catch (Exception e) {
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  @MERGE
+  @Path("/{entitySetName}({entityId})/$links/{linkName}")
+  public Response mergeLink(
+          @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept,
+          @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) String contentType,
+          @PathParam("entitySetName") String entitySetName,
+          @PathParam("entityId") String entityId,
+          @PathParam("linkName") String linkName,
+          String link,
+          @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) String format) {
+    return putLink(accept, contentType, entitySetName, entityId, linkName, link, format);
+  }
+
+  @PATCH
+  @Path("/{entitySetName}({entityId})/$links/{linkName}")
+  public Response patchLink(
+          @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept,
+          @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) String contentType,
+          @PathParam("entitySetName") String entitySetName,
+          @PathParam("entityId") String entityId,
+          @PathParam("linkName") String linkName,
+          String link,
+          @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) String format) {
+    return putLink(accept, contentType, entitySetName, entityId, linkName, link, format);
+  }
+
+  @PUT
+  @Path("/{entitySetName}({entityId})/$links/{linkName}")
+  public Response putLink(
+          @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept,
+          @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) String contentType,
+          @PathParam("entitySetName") String entitySetName,
+          @PathParam("entityId") String entityId,
+          @PathParam("linkName") String linkName,
+          String link,
+          @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) String format) {
+    try {
+      final Accept acceptType;
+      if (StringUtils.isNotBlank(format)) {
+        acceptType = Accept.valueOf(format.toUpperCase());
+      } else {
+        acceptType = Accept.parse(accept, getVersion());
+      }
+
+      if (acceptType == Accept.ATOM) {
+        throw new UnsupportedMediaTypeException("Unsupported media type");
+      }
+
+      final Accept content;
+      if (StringUtils.isNotBlank(contentType)) {
+        content = Accept.parse(contentType, getVersion());
+      } else {
+        content = acceptType;
+      }
+
+      final AbstractUtilities utils = getUtilities(acceptType);
+
+      final List<String> links;
+      if (content == Accept.XML || content == Accept.TEXT || content == Accept.ATOM) {
+        links = XMLUtilities.extractLinkURIs(IOUtils.toInputStream(link)).getValue();
+      } else {
+        links = JSONUtilities.extractLinkURIs(IOUtils.toInputStream(link)).getValue();
+      }
+
+      utils.putLinksInMemory(
+              Commons.getEntityBasePath(entitySetName, entityId),
+              entitySetName,
+              linkName,
+              links);
+
+      return xml.createResponse(null, null, null, Response.Status.NO_CONTENT);
+    } catch (Exception e) {
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  @DELETE
+  @Path("/{entitySetName}({entityId})/$links/{linkName}({linkId})")
+  public Response deleteLink(
+          @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept,
+          @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) String contentType,
+          @PathParam("entitySetName") String entitySetName,
+          @PathParam("entityId") String entityId,
+          @PathParam("linkName") String linkName,
+          @PathParam("linkId") String linkId,
+          @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) String format) {
+    try {
+      final Accept acceptType;
+      if (StringUtils.isNotBlank(format)) {
+        acceptType = Accept.valueOf(format.toUpperCase());
+      } else {
+        acceptType = Accept.parse(accept, getVersion());
+      }
+
+      if (acceptType == Accept.ATOM) {
+        throw new UnsupportedMediaTypeException("Unsupported media type");
+      }
+
+      final AbstractUtilities utils = getUtilities(acceptType);
+
+      final Map.Entry<String, List<String>> currents = JSONUtilities.extractLinkURIs(utils.readLinks(
+              entitySetName, entityId, linkName, Accept.JSON_FULLMETA).getLinks());
+
+      final Map.Entry<String, List<String>> toBeRemoved = JSONUtilities.extractLinkURIs(utils.readLinks(
+              entitySetName, entityId, linkName + "(" + linkId + ")", Accept.JSON_FULLMETA).getLinks());
+
+      final List<String> remains = currents.getValue();
+      remains.removeAll(toBeRemoved.getValue());
+
+      utils.putLinksInMemory(
+              Commons.getEntityBasePath(entitySetName, entityId),
+              entitySetName,
+              linkName,
+              remains);
+
+      return xml.createResponse(null, null, null, Response.Status.NO_CONTENT);
+    } catch (Exception e) {
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  /**
+   * Count sample.
+   *
+   * @param accept Accept header.
+   * @param entitySetName entity set name.
+   * @return count.
+   */
+  @GET
+  @Path("/{entitySetName}/$count")
+  public Response count(
+          @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept,
+          @PathParam("entitySetName") String entitySetName) {
+    try {
+      final Accept acceptType = Accept.parse(accept, getVersion(), Accept.TEXT);
+
+      if (acceptType != Accept.TEXT) {
+        throw new UnsupportedMediaTypeException("Unsupported type " + accept);
+      }
+
+      int count = xml.countAllElements(entitySetName);
+
+      final Response.ResponseBuilder builder = Response.ok();
+      builder.entity(count);
+
+      return builder.build();
+    } catch (Exception e) {
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  private AbstractUtilities getUtilities(final Accept accept) {
+    final AbstractUtilities utils;
+    if (accept == Accept.XML || accept == Accept.TEXT || accept == Accept.ATOM) {
+      utils = xml;
+    } else {
+      utils = json;
+    }
+
+    return utils;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/70f06578/fit/src/main/java/org/apache/olingo/fit/UnsupportedMediaTypeException.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/UnsupportedMediaTypeException.java b/fit/src/main/java/org/apache/olingo/fit/UnsupportedMediaTypeException.java
new file mode 100644
index 0000000..ad7e4b4
--- /dev/null
+++ b/fit/src/main/java/org/apache/olingo/fit/UnsupportedMediaTypeException.java
@@ -0,0 +1,41 @@
+/*
+ * 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;
+
+public class UnsupportedMediaTypeException extends RuntimeException {
+
+  private static final long serialVersionUID = 9076398602010056960L;
+
+  /**
+   * Creates a new instance of
+   * <code>UnsupportedMediaTypeException</code> without detail message.
+   */
+  public UnsupportedMediaTypeException() {
+  }
+
+  /**
+   * Constructs an instance of
+   * <code>UnsupportedMediaTypeException</code> with the specified detail message.
+   *
+   * @param msg the detail message.
+   */
+  public UnsupportedMediaTypeException(String msg) {
+    super(msg);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/70f06578/fit/src/main/java/org/apache/olingo/fit/V3Services.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/V3Services.java b/fit/src/main/java/org/apache/olingo/fit/V3Services.java
new file mode 100644
index 0000000..7b4ca56
--- /dev/null
+++ b/fit/src/main/java/org/apache/olingo/fit/V3Services.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.fit;
+
+import org.apache.olingo.fit.utils.ODataVersion;
+import org.apache.olingo.fit.utils.XHTTPMethodInterceptor;
+import javax.ws.rs.Path;
+import org.apache.cxf.interceptor.InInterceptors;
+
+@Path("/V3/Static.svc")
+@InInterceptors(classes = XHTTPMethodInterceptor.class)
+public class V3Services extends AbstractServices {
+
+  public V3Services() throws Exception {
+    super();
+  }
+
+  @Override
+  protected ODataVersion getVersion() {
+    return ODataVersion.v3;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/70f06578/fit/src/main/java/org/apache/olingo/fit/V4Services.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/V4Services.java b/fit/src/main/java/org/apache/olingo/fit/V4Services.java
new file mode 100644
index 0000000..8906c63
--- /dev/null
+++ b/fit/src/main/java/org/apache/olingo/fit/V4Services.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.fit;
+
+import org.apache.olingo.fit.utils.ODataVersion;
+import org.apache.olingo.fit.utils.XHTTPMethodInterceptor;
+import javax.ws.rs.Path;
+import org.apache.cxf.interceptor.InInterceptors;
+
+@Path("/V4/Static.svc")
+@InInterceptors(classes = XHTTPMethodInterceptor.class)
+public class V4Services extends AbstractServices {
+
+  public V4Services() throws Exception {
+    super();
+  }
+
+  @Override
+  protected ODataVersion getVersion() {
+    return ODataVersion.v4;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/70f06578/fit/src/main/java/org/apache/olingo/fit/methods/MERGE.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/methods/MERGE.java b/fit/src/main/java/org/apache/olingo/fit/methods/MERGE.java
new file mode 100644
index 0000000..2966c11
--- /dev/null
+++ b/fit/src/main/java/org/apache/olingo/fit/methods/MERGE.java
@@ -0,0 +1,31 @@
+/*
+ * 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.methods;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import javax.ws.rs.HttpMethod;
+
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@HttpMethod("MERGE")
+public @interface MERGE {
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/70f06578/fit/src/main/java/org/apache/olingo/fit/methods/PATCH.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/methods/PATCH.java b/fit/src/main/java/org/apache/olingo/fit/methods/PATCH.java
new file mode 100644
index 0000000..6fd07b8
--- /dev/null
+++ b/fit/src/main/java/org/apache/olingo/fit/methods/PATCH.java
@@ -0,0 +1,31 @@
+/*
+ * 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.methods;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import javax.ws.rs.HttpMethod;
+
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@HttpMethod("PATCH")
+public @interface PATCH {
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/70f06578/fit/src/main/java/org/apache/olingo/fit/rproxy/LinkRewrite.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/rproxy/LinkRewrite.java b/fit/src/main/java/org/apache/olingo/fit/rproxy/LinkRewrite.java
new file mode 100644
index 0000000..082708f
--- /dev/null
+++ b/fit/src/main/java/org/apache/olingo/fit/rproxy/LinkRewrite.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.fit.rproxy;
+
+import java.util.Properties;
+import org.esigate.Driver;
+import org.esigate.DriverConfiguration;
+import org.esigate.events.Event;
+import org.esigate.events.EventDefinition;
+import org.esigate.events.EventManager;
+import org.esigate.events.IEventListener;
+import org.esigate.events.impl.RenderEvent;
+import org.esigate.extension.Extension;
+import org.esigate.util.HttpRequestHelper;
+
+public class LinkRewrite implements Extension, IEventListener {
+
+  private DriverConfiguration config;
+
+  @Override
+  public void init(final Driver driver, final Properties properties) {
+    this.config = driver.getConfiguration();
+    driver.getEventManager().register(EventManager.EVENT_RENDER_PRE, this);
+  }
+
+  @Override
+  public boolean event(final EventDefinition eventDef, final Event event) {
+    final RenderEvent renderEvent = (RenderEvent) event;
+    final String baseUrl = HttpRequestHelper.getBaseUrl(renderEvent.originalRequest).toString();
+    final LinkRewriteRenderer fixup = new LinkRewriteRenderer(baseUrl, config.getVisibleBaseURL(baseUrl));
+
+    // Add fixup renderer as first renderer.
+    renderEvent.renderers.add(0, fixup);
+
+    // Continue processing
+    return true;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/70f06578/fit/src/main/java/org/apache/olingo/fit/rproxy/LinkRewriteRenderer.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/rproxy/LinkRewriteRenderer.java b/fit/src/main/java/org/apache/olingo/fit/rproxy/LinkRewriteRenderer.java
new file mode 100644
index 0000000..f3447ac
--- /dev/null
+++ b/fit/src/main/java/org/apache/olingo/fit/rproxy/LinkRewriteRenderer.java
@@ -0,0 +1,56 @@
+/*
+ * 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.rproxy;
+
+import java.io.IOException;
+import java.io.Writer;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.esigate.Renderer;
+
+public class LinkRewriteRenderer implements Renderer {
+
+  private static final char SLASH = '/';
+
+  private String baseUrl;
+
+  private String replacementUrl;
+
+  public LinkRewriteRenderer(final String baseUrl, final String visibleBaseUrl) {
+    if (visibleBaseUrl == null || visibleBaseUrl.isEmpty()) {
+      throw new IllegalArgumentException("Need to specify baseUrl and visibleBaseUrl");
+    }
+
+    this.baseUrl = removeLeadingSlash(baseUrl);
+    this.replacementUrl = removeLeadingSlash(visibleBaseUrl);
+  }
+
+  private String removeLeadingSlash(final String src) {
+    final int lastCharPosition = src.length() - 1;
+    return src.charAt(lastCharPosition) == SLASH
+            ? src.substring(0, lastCharPosition)
+            : src;
+  }
+
+  @Override
+  public void render(final HttpEntityEnclosingRequest httpRequest, final String src, final Writer out)
+          throws IOException {
+
+    out.write(src.replaceAll(baseUrl, replacementUrl));
+  }
+}