You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by dh...@apache.org on 2014/09/01 09:19:01 UTC

[1/3] CAMEL-7773 Initial version of Camel-Olingo2 component

Repository: camel
Updated Branches:
  refs/heads/master 89a8b35b8 -> 9a92064cf


http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/camel-olingo2/camel-olingo2-component/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/components/camel-olingo2/camel-olingo2-component/src/test/resources/log4j.properties b/components/camel-olingo2/camel-olingo2-component/src/test/resources/log4j.properties
new file mode 100644
index 0000000..1ced00f
--- /dev/null
+++ b/components/camel-olingo2/camel-olingo2-component/src/test/resources/log4j.properties
@@ -0,0 +1,39 @@
+#
+# 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.
+#
+
+#
+# The logging properties used for testing.
+#
+log4j.rootLogger=INFO, file
+
+# uncomment the following line to turn on component debug messages
+#log4j.logger.org.apache.camel.component.olingo2=DEBUG
+
+# uncomment the following line to turn on HTTP Client debug messages
+#log4j.logger.httpclient.wire=DEBUG
+
+# CONSOLE appender not used by default
+log4j.appender.out=org.apache.log4j.ConsoleAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+#log4j.appender.out.layout.ConversionPattern=[%30.30t] %-30.30c{1} %-5p %m%n
+log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
+
+# File appender
+log4j.appender.file=org.apache.log4j.FileAppender
+log4j.appender.file.layout=org.apache.log4j.PatternLayout
+log4j.appender.file.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
+log4j.appender.file.file=target/camel-olingo2-test.log

http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/camel-olingo2/camel-olingo2-component/src/test/resources/test-options.properties
----------------------------------------------------------------------
diff --git a/components/camel-olingo2/camel-olingo2-component/src/test/resources/test-options.properties b/components/camel-olingo2/camel-olingo2-component/src/test/resources/test-options.properties
new file mode 100644
index 0000000..97e37bb
--- /dev/null
+++ b/components/camel-olingo2/camel-olingo2-component/src/test/resources/test-options.properties
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+# Test values for Olingo configuration properties
+serviceUri=http://localhost:8080/MyFormula.svc
+contentType=application/json;charset=utf-8
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/camel-olingo2/pom.xml
----------------------------------------------------------------------
diff --git a/components/camel-olingo2/pom.xml b/components/camel-olingo2/pom.xml
new file mode 100644
index 0000000..9d591c9
--- /dev/null
+++ b/components/camel-olingo2/pom.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <artifactId>components</artifactId>
+    <groupId>org.apache.camel</groupId>
+    <version>2.14-SNAPSHOT</version>
+  </parent>
+
+  <groupId>org.apache.camel.component.olingo2</groupId>
+  <artifactId>camel-olingo2-parent</artifactId>
+  <packaging>pom</packaging>
+
+  <name>Camel :: Olingo2 Component Parent</name>
+  <description>Parent project for Camel {Olingo2} Component</description>
+
+  <modules>
+    <module>camel-olingo2-api</module>
+    <module>camel-olingo2-component</module>
+  </modules>
+
+  <profiles>
+    <profile>
+      <id>olingo2-test</id>
+      <build>
+        <plugins>
+          <plugin>
+            <artifactId>maven-surefire-plugin</artifactId>
+            <configuration>
+              <childDelegation>false</childDelegation>
+              <useFile>true</useFile>
+              <forkMode>once</forkMode>
+              <forkedProcessTimeoutInSeconds>300</forkedProcessTimeoutInSeconds>
+              <excludes>
+                <exclude>**/*XXXTest.java</exclude>
+              </excludes>
+              <includes>
+                <include>**/*Test.java</include>
+              </includes>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+
+</project>

http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/pom.xml
----------------------------------------------------------------------
diff --git a/components/pom.xml b/components/pom.xml
index bc980a4..962c7a6 100644
--- a/components/pom.xml
+++ b/components/pom.xml
@@ -146,6 +146,7 @@
     <module>camel-netty4</module>
     <module>camel-netty-http</module>
     <module>camel-ognl</module>
+    <module>camel-olingo</module>
     <module>camel-openshift</module>
     <module>camel-optaplanner</module>
     <module>camel-paxlogging</module>


[2/3] CAMEL-7773 Initial version of Camel-Olingo2 component

Posted by dh...@apache.org.
http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/camel-olingo2/camel-olingo2-api/src/test/java/org/apache/camel/component/olingo2/api/Olingo2AppIntegrationTest.java
----------------------------------------------------------------------
diff --git a/components/camel-olingo2/camel-olingo2-api/src/test/java/org/apache/camel/component/olingo2/api/Olingo2AppIntegrationTest.java b/components/camel-olingo2/camel-olingo2-api/src/test/java/org/apache/camel/component/olingo2/api/Olingo2AppIntegrationTest.java
new file mode 100644
index 0000000..d1cfb2a
--- /dev/null
+++ b/components/camel-olingo2/camel-olingo2-api/src/test/java/org/apache/camel/component/olingo2/api/Olingo2AppIntegrationTest.java
@@ -0,0 +1,560 @@
+/**
+ * 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.camel.component.olingo2.api;
+
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import org.apache.camel.component.olingo2.api.batch.Olingo2BatchChangeRequest;
+import org.apache.camel.component.olingo2.api.batch.Olingo2BatchQueryRequest;
+import org.apache.camel.component.olingo2.api.batch.Olingo2BatchRequest;
+import org.apache.camel.component.olingo2.api.batch.Olingo2BatchResponse;
+import org.apache.camel.component.olingo2.api.batch.Operation;
+import org.apache.camel.component.olingo2.api.impl.AbstractFutureCallback;
+import org.apache.camel.component.olingo2.api.impl.Olingo2AppImpl;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.concurrent.FutureCallback;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.olingo.odata2.api.commons.HttpStatusCodes;
+import org.apache.olingo.odata2.api.edm.Edm;
+import org.apache.olingo.odata2.api.edm.EdmEntitySetInfo;
+import org.apache.olingo.odata2.api.ep.entry.ODataEntry;
+import org.apache.olingo.odata2.api.ep.feed.ODataDeltaFeed;
+import org.apache.olingo.odata2.api.ep.feed.ODataFeed;
+import org.apache.olingo.odata2.api.exception.ODataApplicationException;
+import org.apache.olingo.odata2.api.servicedocument.Collection;
+import org.apache.olingo.odata2.api.servicedocument.ServiceDocument;
+import org.apache.olingo.odata2.core.commons.ContentType;
+import org.apache.olingo.odata2.core.uri.SystemQueryOption;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Integration test for {@link org.apache.camel.component.olingo2.api.impl.Olingo2AppImpl}.
+ * To test run the sample Olingo2 Server as outlined at
+ * http://olingo.apache.org/doc/tutorials/Olingo2V2BasicClientSample.html
+ */
+public class Olingo2AppIntegrationTest {
+
+    private static final Logger LOG = LoggerFactory.getLogger(Olingo2AppIntegrationTest.class);
+    private static final long TIMEOUT = 10;
+
+    private static final String MANUFACTURERS = "Manufacturers";
+    private static final String ADDRESS = "Address";
+    private static final String CARS = "Cars";
+
+    private static final String TEST_KEY = "'1'";
+    private static final String TEST_CREATE_KEY = "'123'";
+    private static final String TEST_MANUFACTURER = MANUFACTURERS + "(" + TEST_KEY + ")";
+    private static final String TEST_CREATE_MANUFACTURER = MANUFACTURERS + "(" + TEST_CREATE_KEY + ")";
+
+    private static final String TEST_RESOURCE_CONTENT_ID = "1";
+    private static final String TEST_RESOURCE = "$" + TEST_RESOURCE_CONTENT_ID;
+
+    private static final char NEW_LINE = '\n';
+    private static final String TEST_CAR = "Manufacturers('1')/Cars('1')";
+    private static final String TEST_MANUFACTURER_FOUNDED_PROPERTY = "Manufacturers('1')/Founded";
+    private static final String TEST_MANUFACTURER_FOUNDED_VALUE = "Manufacturers('1')/Founded/$value";
+    private static final String FOUNDED_PROPERTY = "Founded";
+    private static final String TEST_MANUFACTURER_ADDRESS_PROPERTY = "Manufacturers('1')/Address";
+    private static final String TEST_MANUFACTURER_LINKS_CARS = "Manufacturers('1')/$links/Cars";
+    private static final String TEST_CAR_LINK_MANUFACTURER = "Cars('1')/$links/Manufacturer";
+    private static final String COUNT_OPTION = "/$count";
+
+    private static String TEST_SERVICE_URL = "http://localhost:8080/MyFormula.svc";
+    //    private static String TEST_SERVICE_URL = "http://localhost:8080/cars-annotations-sample/MyFormula.svc";
+//    private static ContentType TEST_FORMAT = ContentType.APPLICATION_XML_CS_UTF_8;
+    private static ContentType TEST_FORMAT = ContentType.APPLICATION_JSON_CS_UTF_8;
+    private static final String INDEX = "/index.jsp";
+
+    private static Olingo2App olingoApp;
+    private static final String GEN_SAMPLE_DATA = "genSampleData=true";
+    private static Edm edm;
+
+    @BeforeClass
+    public static void beforeClass() throws Exception {
+
+        olingoApp = new Olingo2AppImpl(TEST_SERVICE_URL);
+        olingoApp.setContentType(TEST_FORMAT.toString());
+
+        LOG.info("Generate sample data ");
+        generateSampleData(TEST_SERVICE_URL);
+
+        LOG.info("Read Edm ");
+        final TestOlingo2ResponseHandler<Edm> responseHandler = new TestOlingo2ResponseHandler<Edm>();
+
+        olingoApp.read(null, Olingo2AppImpl.METADATA, null, responseHandler);
+
+        edm = responseHandler.await();
+        LOG.info("Read default EntityContainer:  {}", responseHandler.await().getDefaultEntityContainer().getName());
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        olingoApp.close();
+    }
+
+    @Test
+    public void testServiceDocument() throws Exception {
+        final TestOlingo2ResponseHandler<ServiceDocument> responseHandler =
+            new TestOlingo2ResponseHandler<ServiceDocument>();
+
+        olingoApp.read(null, "", null, responseHandler);
+
+        final ServiceDocument serviceDocument = responseHandler.await();
+        final List<Collection> collections = serviceDocument.getAtomInfo().getWorkspaces().get(0).getCollections();
+        assertEquals("Service Atom Collections", 3, collections.size());
+        LOG.info("Service Atom Collections:  {}", collections);
+
+        final List<EdmEntitySetInfo> entitySetsInfo = serviceDocument.getEntitySetsInfo();
+        assertEquals("Service Entity Sets", 3, entitySetsInfo.size());
+        LOG.info("Service Document Entries:  {}", entitySetsInfo);
+    }
+
+    @Test
+    public void testReadFeed() throws Exception {
+        final TestOlingo2ResponseHandler<ODataFeed> responseHandler = new TestOlingo2ResponseHandler<ODataFeed>();
+
+        olingoApp.read(edm, MANUFACTURERS, null, responseHandler);
+
+        final ODataFeed dataFeed = responseHandler.await();
+        assertNotNull("Data feed", dataFeed);
+        LOG.info("Entries:  {}", prettyPrint(dataFeed));
+    }
+
+    @Test
+    public void testReadEntry() throws Exception {
+        final TestOlingo2ResponseHandler<ODataEntry> responseHandler = new TestOlingo2ResponseHandler<ODataEntry>();
+
+        olingoApp.read(edm, TEST_MANUFACTURER, null, responseHandler);
+        ODataEntry entry = responseHandler.await();
+        LOG.info("Single Entry:  {}", prettyPrint(entry));
+
+        responseHandler.reset();
+
+        olingoApp.read(edm, TEST_CAR, null, responseHandler);
+        entry = responseHandler.await();
+        LOG.info("Single Entry:  {}", prettyPrint(entry));
+
+        responseHandler.reset();
+        final Map<String, String> queryParams = new HashMap<String, String>();
+        queryParams.put(SystemQueryOption.$expand.toString(), CARS);
+
+        olingoApp.read(edm, TEST_MANUFACTURER, queryParams, responseHandler);
+
+        ODataEntry entryExpanded = responseHandler.await();
+        LOG.info("Single Entry with expanded Cars relation:  {}", prettyPrint(entryExpanded));
+    }
+
+    @Test
+    public void testReadUpdateProperties() throws Exception {
+        // test simple property Manufacturer.Founded
+        final TestOlingo2ResponseHandler<Map<String, Object>> propertyHandler =
+            new TestOlingo2ResponseHandler<Map<String, Object>>();
+
+        olingoApp.read(edm, TEST_MANUFACTURER_FOUNDED_PROPERTY, null, propertyHandler);
+
+        Calendar founded = (Calendar) propertyHandler.await().get(FOUNDED_PROPERTY);
+        LOG.info("Founded property {}", founded.toString());
+
+        final TestOlingo2ResponseHandler<Calendar> valueHandler = new TestOlingo2ResponseHandler<Calendar>();
+
+        olingoApp.read(edm, TEST_MANUFACTURER_FOUNDED_VALUE, null, valueHandler);
+
+        founded = valueHandler.await();
+        LOG.info("Founded property {}", founded.toString());
+
+        final TestOlingo2ResponseHandler<HttpStatusCodes> statusHandler =
+            new TestOlingo2ResponseHandler<HttpStatusCodes>();
+        final HashMap<String, Object> properties = new HashMap<String, Object>();
+        properties.put(FOUNDED_PROPERTY, new Date());
+
+//        olingoApp.update(edm, TEST_MANUFACTURER_FOUNDED_PROPERTY, properties, statusHandler);
+        // requires a plain Date for XML
+        olingoApp.update(edm, TEST_MANUFACTURER_FOUNDED_PROPERTY, new Date(), statusHandler);
+
+        LOG.info("Founded property updated with status {}", statusHandler.await().getStatusCode());
+
+        statusHandler.reset();
+
+        olingoApp.update(edm, TEST_MANUFACTURER_FOUNDED_VALUE, new Date(), statusHandler);
+
+        LOG.info("Founded property updated with status {}", statusHandler.await().getStatusCode());
+
+        // test complex property Manufacturer.Address
+        propertyHandler.reset();
+
+        olingoApp.read(edm, TEST_MANUFACTURER_ADDRESS_PROPERTY, null, propertyHandler);
+
+        final Map<String, Object> address = propertyHandler.await();
+        LOG.info("Address property {}", prettyPrint(address, 0));
+
+        statusHandler.reset();
+
+        address.clear();
+        // Olingo2 sample server MERGE/PATCH behaves like PUT!!!
+//        address.put("Street", "Main Street");
+        address.put("Street", "Star Street 137");
+        address.put("City", "Stuttgart");
+        address.put("ZipCode", "70173");
+        address.put("Country", "Germany");
+
+//        olingoApp.patch(edm, TEST_MANUFACTURER_ADDRESS_PROPERTY, address, statusHandler);
+        olingoApp.merge(edm, TEST_MANUFACTURER_ADDRESS_PROPERTY, address, statusHandler);
+
+        LOG.info("Address property updated with status {}", statusHandler.await().getStatusCode());
+    }
+
+    @Test
+    public void testReadLinks() throws Exception {
+        final TestOlingo2ResponseHandler<List<String>> linksHandler = new TestOlingo2ResponseHandler<List<String>>();
+
+        olingoApp.read(edm, TEST_MANUFACTURER_LINKS_CARS, null, linksHandler);
+
+        final List<String> links = linksHandler.await();
+        assertFalse(links.isEmpty());
+        LOG.info("Read links: {}", links);
+
+        final TestOlingo2ResponseHandler<String> linkHandler = new TestOlingo2ResponseHandler<String>();
+
+        olingoApp.read(edm, TEST_CAR_LINK_MANUFACTURER, null, linkHandler);
+
+        final String link = linkHandler.await();
+        LOG.info("Read link: {}", link);
+    }
+
+    @Test
+    public void testReadCount() throws Exception {
+        final TestOlingo2ResponseHandler<Long> countHandler = new TestOlingo2ResponseHandler<Long>();
+
+        olingoApp.read(edm, MANUFACTURERS + COUNT_OPTION, null, countHandler);
+
+        LOG.info("Manufacturers count: {}", countHandler.await());
+
+        countHandler.reset();
+        olingoApp.read(edm, TEST_MANUFACTURER + COUNT_OPTION, null, countHandler);
+
+        LOG.info("Manufacturer count: {}", countHandler.await());
+
+        countHandler.reset();
+        olingoApp.read(edm, TEST_MANUFACTURER_LINKS_CARS + COUNT_OPTION, null, countHandler);
+
+        LOG.info("Manufacturers links count: {}", countHandler.await());
+
+        countHandler.reset();
+        olingoApp.read(edm, TEST_CAR_LINK_MANUFACTURER + COUNT_OPTION, null, countHandler);
+
+        LOG.info("Manufacturer link count: {}", countHandler.await());
+    }
+
+    @Test
+    public void testCreateUpdateDeleteEntry() throws Exception {
+
+        // create entry to update
+        final TestOlingo2ResponseHandler<ODataEntry> entryHandler = new TestOlingo2ResponseHandler<ODataEntry>();
+
+        olingoApp.create(edm, MANUFACTURERS, getEntityData(), entryHandler);
+
+        ODataEntry createdEntry = entryHandler.await();
+        LOG.info("Created Entry:  {}", prettyPrint(createdEntry));
+
+        Map<String, Object> data = getEntityData();
+        @SuppressWarnings("unchecked")
+        Map<String, Object> address = (Map<String, Object>) data.get(ADDRESS);
+
+        data.put("Name", "MyCarManufacturer Renamed");
+        address.put("Street", "Main Street");
+        final TestOlingo2ResponseHandler<HttpStatusCodes> statusHandler =
+            new TestOlingo2ResponseHandler<HttpStatusCodes>();
+
+        olingoApp.update(edm, TEST_CREATE_MANUFACTURER, data, statusHandler);
+        statusHandler.await();
+
+        statusHandler.reset();
+        data.put("Name", "MyCarManufacturer Patched");
+        olingoApp.patch(edm, TEST_CREATE_MANUFACTURER, data, statusHandler);
+        statusHandler.await();
+
+        entryHandler.reset();
+        olingoApp.read(edm, TEST_CREATE_MANUFACTURER, null, entryHandler);
+
+        ODataEntry updatedEntry = entryHandler.await();
+        LOG.info("Updated Entry successfully:  {}", prettyPrint(updatedEntry));
+
+        statusHandler.reset();
+        olingoApp.delete(TEST_CREATE_MANUFACTURER,  statusHandler);
+
+        HttpStatusCodes statusCode = statusHandler.await();
+        LOG.info("Deletion of Entry was successful:  {}: {}", statusCode.getStatusCode(), statusCode.getInfo());
+
+        try {
+            LOG.info("Verify Delete Entry");
+
+            entryHandler.reset();
+            olingoApp.read(edm, TEST_CREATE_MANUFACTURER, null, entryHandler);
+
+            entryHandler.await();
+            fail("Entry not deleted!");
+        } catch (Exception e) {
+            LOG.info("Deleted entry not found: {}", e.getMessage());
+        }
+    }
+
+    @Test
+    public void testBatchRequest() throws Exception {
+
+        final List<Olingo2BatchRequest> batchParts = new ArrayList<Olingo2BatchRequest>();
+
+        // Edm query
+        batchParts.add(Olingo2BatchQueryRequest.resourcePath(Olingo2AppImpl.METADATA).build());
+
+        // feed query
+        batchParts.add(Olingo2BatchQueryRequest.resourcePath(MANUFACTURERS).build());
+
+        // read
+        batchParts.add(Olingo2BatchQueryRequest.resourcePath(TEST_MANUFACTURER).build());
+
+        // read with expand
+        final HashMap<String, String> queryParams = new HashMap<String, String>();
+        queryParams.put(SystemQueryOption.$expand.toString(), CARS);
+        batchParts.add(Olingo2BatchQueryRequest.resourcePath(TEST_MANUFACTURER).queryParams(queryParams).build());
+
+        // create
+        final Map<String, Object> data = getEntityData();
+        batchParts.add(Olingo2BatchChangeRequest.resourcePath(MANUFACTURERS).
+            contentId(TEST_RESOURCE_CONTENT_ID).operation(Operation.CREATE).body(data).build());
+
+        // update
+        final Map<String, Object> updateData = new HashMap<String, Object>(data);
+        @SuppressWarnings("unchecked")
+        Map<String, Object> address = (Map<String, Object>) updateData.get(ADDRESS);
+        updateData.put("Name", "MyCarManufacturer Renamed");
+        address.put("Street", "Main Street");
+
+        batchParts.add(Olingo2BatchChangeRequest.resourcePath(TEST_RESOURCE).operation(Operation.UPDATE)
+            .body(updateData).build());
+
+        // delete
+        batchParts.add(Olingo2BatchChangeRequest.resourcePath(TEST_RESOURCE).operation(Operation.DELETE).build());
+
+        final TestOlingo2ResponseHandler<List<Olingo2BatchResponse>> responseHandler =
+            new TestOlingo2ResponseHandler<List<Olingo2BatchResponse>>();
+
+        // read to verify delete
+        batchParts.add(Olingo2BatchQueryRequest.resourcePath(TEST_CREATE_MANUFACTURER).build());
+
+        olingoApp.batch(edm, batchParts, responseHandler);
+
+        final List<Olingo2BatchResponse> responseParts = responseHandler.await(15, TimeUnit.MINUTES);
+        assertEquals("Batch responses expected", 8, responseParts.size());
+
+        assertNotNull(responseParts.get(0).getBody());
+        final ODataFeed feed = (ODataFeed) responseParts.get(1).getBody();
+        assertNotNull(feed);
+        LOG.info("Batch feed:  {}", prettyPrint(feed));
+
+        ODataEntry dataEntry = (ODataEntry) responseParts.get(2).getBody();
+        assertNotNull(dataEntry);
+        LOG.info("Batch read entry:  {}", prettyPrint(dataEntry));
+
+        dataEntry = (ODataEntry) responseParts.get(3).getBody();
+        assertNotNull(dataEntry);
+        LOG.info("Batch read entry with expand:  {}", prettyPrint(dataEntry));
+
+        dataEntry = (ODataEntry) responseParts.get(4).getBody();
+        assertNotNull(dataEntry);
+        LOG.info("Batch create entry:  {}", prettyPrint(dataEntry));
+
+        assertEquals(HttpStatusCodes.NO_CONTENT.getStatusCode(), responseParts.get(5).getStatusCode());
+        assertEquals(HttpStatusCodes.NO_CONTENT.getStatusCode(), responseParts.get(6).getStatusCode());
+
+        assertEquals(HttpStatusCodes.NOT_FOUND.getStatusCode(), responseParts.get(7).getStatusCode());
+        final Exception exception = (Exception) responseParts.get(7).getBody();
+        assertNotNull(exception);
+        LOG.info("Batch retrieve deleted entry:  {}", exception);
+    }
+
+    private Map<String, Object> getEntityData() {
+        Map<String, Object> data = new HashMap<String, Object>();
+        data.put("Id", "123");
+        data.put("Name", "MyCarManufacturer");
+        data.put(FOUNDED_PROPERTY, new Date());
+        Map<String, Object> address = new HashMap<String, Object>();
+        address.put("Street", "Main");
+        address.put("ZipCode", "42421");
+        address.put("City", "Fairy City");
+        address.put("Country", "FarFarAway");
+        data.put(ADDRESS, address);
+        return data;
+    }
+
+    private static String prettyPrint(ODataFeed dataFeed) {
+        StringBuilder builder = new StringBuilder();
+        builder.append("[\n");
+        for (ODataEntry entry : dataFeed.getEntries()) {
+            builder.append(prettyPrint(entry.getProperties(), 1)).append('\n');
+        }
+        builder.append("]\n");
+        return builder.toString();
+    }
+
+    private static String prettyPrint(ODataEntry createdEntry) {
+        return prettyPrint(createdEntry.getProperties(), 0);
+    }
+
+    private static String prettyPrint(Map<String, Object> properties, int level) {
+        StringBuilder b = new StringBuilder();
+        Set<Map.Entry<String, Object>> entries = properties.entrySet();
+
+        for (Map.Entry<String, Object> entry : entries) {
+            indent(b, level);
+            b.append(entry.getKey()).append(": ");
+            Object value = entry.getValue();
+            if (value instanceof Map) {
+                @SuppressWarnings("unchecked")
+                final Map<String, Object> objectMap = (Map<String, Object>) value;
+                value = prettyPrint(objectMap, level + 1);
+                b.append(value).append(NEW_LINE);
+            } else if (value instanceof Calendar) {
+                Calendar cal = (Calendar) value;
+                value = SimpleDateFormat.getInstance().format(cal.getTime());
+                b.append(value).append(NEW_LINE);
+            } else if (value instanceof ODataDeltaFeed) {
+                ODataDeltaFeed feed = (ODataDeltaFeed) value;
+                List<ODataEntry> inlineEntries = feed.getEntries();
+                b.append("{");
+                for (ODataEntry oDataEntry : inlineEntries) {
+                    value = prettyPrint(oDataEntry.getProperties(), level + 1);
+                    b.append("\n[\n").append(value).append("\n],");
+                }
+                b.deleteCharAt(b.length() - 1);
+                indent(b, level);
+                b.append("}\n");
+            } else {
+                b.append(value).append(NEW_LINE);
+            }
+        }
+        // remove last line break
+        b.deleteCharAt(b.length() - 1);
+        return b.toString();
+    }
+
+    private static void indent(StringBuilder builder, int indentLevel) {
+        for (int i = 0; i < indentLevel; i++) {
+            builder.append("  ");
+        }
+    }
+
+    private static void generateSampleData(String serviceUrl) throws IOException {
+        final HttpPost httpUriRequest = new HttpPost(serviceUrl.substring(0, serviceUrl.lastIndexOf('/')) + INDEX);
+        httpUriRequest.setEntity(new ByteArrayEntity(GEN_SAMPLE_DATA.getBytes()));
+        ((Olingo2AppImpl)olingoApp).execute(httpUriRequest, Olingo2AppImpl.APPLICATION_FORM_URL_ENCODED,
+            new FutureCallback<HttpResponse>() {
+                @Override
+                public void completed(HttpResponse result) {
+                    try {
+                        AbstractFutureCallback.checkStatus(result);
+                        LOG.info("Sample data generated  {}", result.getStatusLine());
+                    } catch (ODataApplicationException e) {
+                        LOG.error("Sample data generation error: " + e.getMessage(), e);
+                    }
+                }
+
+                @Override
+                public void failed(Exception ex) {
+                    LOG.error("Error generating sample data " + ex.getMessage(), ex);
+                }
+
+                @Override
+                public void cancelled() {
+                    LOG.error("Sample data generation canceled!");
+                }
+            });
+    }
+
+    private static final class TestOlingo2ResponseHandler<T> implements Olingo2ResponseHandler<T> {
+
+        private T response;
+        private Exception error;
+        private CountDownLatch latch = new CountDownLatch(1);
+
+        @Override
+        public void onResponse(T response) {
+            this.response = response;
+            if (LOG.isDebugEnabled()) {
+                if (response instanceof ODataFeed) {
+                    LOG.debug("Received response: {}", prettyPrint((ODataFeed) response));
+                } else if (response instanceof ODataEntry) {
+                    LOG.debug("Received response: {}", prettyPrint((ODataEntry) response));
+                } else {
+                    LOG.debug("Received response: {}", response);
+                }
+            }
+            latch.countDown();
+        }
+
+        @Override
+        public void onException(Exception ex) {
+            error = ex;
+            latch.countDown();
+        }
+
+        @Override
+        public void onCanceled() {
+            error = new IllegalStateException("Request Canceled");
+            latch.countDown();
+        }
+
+        public T await() throws Exception {
+            return await(TIMEOUT, TimeUnit.SECONDS);
+        }
+
+        public T await(long timeout, TimeUnit unit) throws Exception {
+            assertTrue("Timeout waiting for response", latch.await(timeout, unit));
+            if (error != null) {
+                throw error;
+            }
+            assertNotNull("Response", response);
+            return response;
+        }
+
+        public void reset() {
+            latch.countDown();
+            latch = new CountDownLatch(1);
+            response = null;
+            error = null;
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/camel-olingo2/camel-olingo2-api/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/components/camel-olingo2/camel-olingo2-api/src/test/resources/log4j.properties b/components/camel-olingo2/camel-olingo2-api/src/test/resources/log4j.properties
new file mode 100644
index 0000000..af87137
--- /dev/null
+++ b/components/camel-olingo2/camel-olingo2-api/src/test/resources/log4j.properties
@@ -0,0 +1,39 @@
+#
+# 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.
+#
+
+#
+# The logging properties used for testing.
+#
+log4j.rootLogger=INFO, file
+
+# uncomment the following line to turn on API debug messages
+#log4j.logger.org.apache.camel.component.olingo2.api=DEBUG
+
+# uncomment the following line to turn on HTTP Client debug messages
+#log4j.logger.httpclient.wire=DEBUG
+
+# CONSOLE appender not used by default
+log4j.appender.out=org.apache.log4j.ConsoleAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+#log4j.appender.out.layout.ConversionPattern=[%30.30t] %-30.30c{1} %-5p %m%n
+log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
+
+# File appender
+log4j.appender.file=org.apache.log4j.FileAppender
+log4j.appender.file.layout=org.apache.log4j.PatternLayout
+log4j.appender.file.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
+log4j.appender.file.file=target/camel-olingo2-test.log

http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/camel-olingo2/camel-olingo2-component/pom.xml
----------------------------------------------------------------------
diff --git a/components/camel-olingo2/camel-olingo2-component/pom.xml b/components/camel-olingo2/camel-olingo2-component/pom.xml
new file mode 100644
index 0000000..fc9942c
--- /dev/null
+++ b/components/camel-olingo2/camel-olingo2-component/pom.xml
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.camel.component.olingo2</groupId>
+    <artifactId>camel-olingo2-parent</artifactId>
+    <version>2.14-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>camel-olingo2</artifactId>
+  <packaging>bundle</packaging>
+  <name>Camel :: Olingo2 Component</name>
+  <description>Camel Component for Apache Olingo2</description>
+
+  <properties>
+    <schemeName>olingo2</schemeName>
+    <componentName>Olingo2</componentName>
+    <componentPackage>org.apache.camel.component.olingo2</componentPackage>
+    <outPackage>org.apache.camel.component.olingo2.internal</outPackage>
+
+    <camel.osgi.export.pkg>${componentPackage}</camel.osgi.export.pkg>
+    <camel.osgi.private.pkg>${outPackage}</camel.osgi.private.pkg>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.camel.component.olingo2</groupId>
+      <artifactId>camel-olingo2-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>commons-lang</groupId>
+      <artifactId>commons-lang</artifactId>
+      <version>${commons-lang-version}</version>
+    </dependency>
+
+    <!-- Camel annotations in provided scope to avoid compile errors in IDEs -->
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>spi-annotations</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+    </dependency>
+
+    <!-- Component API javadoc in provided scope to read API signatures -->
+    <dependency>
+      <groupId>org.apache.camel.component.olingo2</groupId>
+      <artifactId>camel-olingo2-api</artifactId>
+      <version>${project.version}</version>
+      <classifier>javadoc</classifier>
+      <scope>provided</scope>
+    </dependency>
+
+    <!-- logging -->
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <version>1.7.7</version>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <version>1.7.7</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+      <version>1.2.17</version>
+      <scope>test</scope>
+    </dependency>
+
+    <!-- testing -->
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <defaultGoal>install</defaultGoal>
+
+    <plugins>
+      
+      <!-- generate Component source and test source -->
+      <plugin>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>camel-api-component-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>generate-test-component-classes</id>
+            <goals>
+              <goal>fromApis</goal>
+            </goals>
+            <configuration>
+              <apis>
+                <api>
+                  <apiName/>
+                  <proxyClass>org.apache.camel.component.olingo2.api.Olingo2App</proxyClass>
+                  <fromSignatureFile>src/signatures/olingo-api-signature.txt</fromSignatureFile>
+                  <excludeConfigNames>edm|responseHandler</excludeConfigNames>
+                  <extraOptions>
+                    <extraOption>
+                      <name>keyPredicate</name>
+                      <type>java.lang.String</type>
+                    </extraOption>
+                  </extraOptions>
+                  <nullableOptions>
+                    <nullableOption>queryParams</nullableOption>
+                  </nullableOptions>
+                </api>
+              </apis>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+      <!-- add generated source and test source to build -->
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>add-generated-sources</id>
+            <goals>
+              <goal>add-source</goal>
+            </goals>
+            <configuration>
+              <sources>
+                <source>${project.build.directory}/generated-sources/camel-component</source>
+              </sources>
+            </configuration>
+          </execution>
+          <execution>
+            <id>add-generated-test-sources</id>
+            <goals>
+              <goal>add-test-source</goal>
+            </goals>
+            <configuration>
+              <sources>
+                <source>${project.build.directory}/generated-test-sources/camel-component</source>
+              </sources>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+    </plugins>
+
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <groupId>org.apache.camel</groupId>
+          <artifactId>camel-api-component-maven-plugin</artifactId>
+          <version>${project.version}</version>
+          <configuration>
+            <scheme>${schemeName}</scheme>
+            <componentName>${componentName}</componentName>
+            <componentPackage>${componentPackage}</componentPackage>
+            <outPackage>${outPackage}</outPackage>
+          </configuration>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+
+  </build>
+
+  <reporting>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>camel-api-component-maven-plugin</artifactId>
+        <version>${project.version}</version>
+        <configuration>
+          <scheme>${schemeName}</scheme>
+          <componentName>${componentName}</componentName>
+          <componentPackage>${componentPackage}</componentPackage>
+          <outPackage>${outPackage}</outPackage>
+        </configuration>
+      </plugin>
+    </plugins>
+  </reporting>
+
+</project>

http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/camel-olingo2/camel-olingo2-component/src/main/java/org/apache/camel/component/olingo2/Olingo2Component.java
----------------------------------------------------------------------
diff --git a/components/camel-olingo2/camel-olingo2-component/src/main/java/org/apache/camel/component/olingo2/Olingo2Component.java b/components/camel-olingo2/camel-olingo2-component/src/main/java/org/apache/camel/component/olingo2/Olingo2Component.java
new file mode 100644
index 0000000..771a3ec
--- /dev/null
+++ b/components/camel-olingo2/camel-olingo2-component/src/main/java/org/apache/camel/component/olingo2/Olingo2Component.java
@@ -0,0 +1,152 @@
+/**
+ * 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.camel.component.olingo2;
+
+import java.util.Map;
+
+import javax.net.ssl.SSLContext;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Endpoint;
+import org.apache.camel.component.olingo2.api.Olingo2App;
+import org.apache.camel.component.olingo2.api.impl.Olingo2AppImpl;
+import org.apache.camel.component.olingo2.internal.Olingo2ApiCollection;
+import org.apache.camel.component.olingo2.internal.Olingo2ApiName;
+import org.apache.camel.spi.UriEndpoint;
+import org.apache.camel.util.component.AbstractApiComponent;
+import org.apache.http.HttpHost;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
+
+/**
+ * Represents the component that manages {@link Olingo2Endpoint}.
+ */
+@UriEndpoint(scheme = "olingo2", consumerClass = Olingo2Consumer.class, consumerPrefix = "consumer")
+public class Olingo2Component extends AbstractApiComponent<Olingo2ApiName, Olingo2Configuration, Olingo2ApiCollection> {
+
+    // component level shared proxy
+    private Olingo2App apiProxy;
+
+    public Olingo2Component() {
+        super(Olingo2Endpoint.class, Olingo2ApiName.class, Olingo2ApiCollection.getCollection());
+    }
+
+    public Olingo2Component(CamelContext context) {
+        super(context, Olingo2Endpoint.class, Olingo2ApiName.class, Olingo2ApiCollection.getCollection());
+    }
+
+    @Override
+    protected Olingo2ApiName getApiName(String apiNameStr) throws IllegalArgumentException {
+        return Olingo2ApiName.fromValue(apiNameStr);
+    }
+
+    @Override
+    protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception {
+        // parse remaining to extract resourcePath and queryParams
+        final String[] pathSegments = remaining.split("/", -1);
+        final String methodName = pathSegments[0];
+
+        if (pathSegments.length > 1) {
+            final StringBuilder resourcePath = new StringBuilder();
+            for (int i = 1; i < pathSegments.length; i++) {
+                resourcePath.append(pathSegments[i]);
+                if (i < (pathSegments.length - 1)) {
+                    resourcePath.append('/');
+                }
+            }
+            // This will override any URI supplied ?resourcePath=... param
+            parameters.put(Olingo2Endpoint.RESOURCE_PATH_PROPERTY, resourcePath.toString());
+        }
+
+        final Olingo2Configuration endpointConfiguration = createEndpointConfiguration(Olingo2ApiName.DEFAULT);
+        final Endpoint endpoint = createEndpoint(uri, methodName, Olingo2ApiName.DEFAULT, endpointConfiguration);
+
+        // set endpoint property inBody
+        setProperties(endpoint, parameters);
+
+        // configure endpoint properties and initialize state
+        endpoint.configureProperties(parameters);
+
+        return endpoint;
+    }
+
+    @Override
+    protected Endpoint createEndpoint(String uri, String methodName, Olingo2ApiName apiName,
+                                      Olingo2Configuration endpointConfiguration) {
+        return new Olingo2Endpoint(uri, this, apiName, methodName, endpointConfiguration);
+    }
+
+    public Olingo2App createApiProxy(Olingo2Configuration endpointConfiguration) {
+        final Olingo2App result;
+        if (endpointConfiguration.equals(this.configuration)) {
+            synchronized (this) {
+                if (apiProxy == null) {
+                    apiProxy = createOlingo2App(this.configuration);
+                }
+            }
+            result = apiProxy;
+        } else {
+            result = createOlingo2App(endpointConfiguration);
+        }
+        return result;
+    }
+
+    private Olingo2App createOlingo2App(Olingo2Configuration configuration) {
+
+        HttpAsyncClientBuilder clientBuilder = configuration.getHttpAsyncClientBuilder();
+        if (clientBuilder == null) {
+            clientBuilder = HttpAsyncClientBuilder.create();
+
+            // apply simple configuration properties
+            final RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
+            requestConfigBuilder.setConnectTimeout(configuration.getConnectTimeout());
+            requestConfigBuilder.setSocketTimeout(configuration.getSocketTimeout());
+
+            final HttpHost proxy = configuration.getProxy();
+            if (proxy != null) {
+                requestConfigBuilder.setProxy(proxy);
+            }
+
+            // set default request config
+            clientBuilder.setDefaultRequestConfig(requestConfigBuilder.build());
+
+            final SSLContext context = configuration.getSslContext();
+            if (context != null) {
+                clientBuilder.setSSLContext(context);
+            }
+        }
+
+        apiProxy = new Olingo2AppImpl(configuration.getServiceUri(), clientBuilder);
+        apiProxy.setContentType(configuration.getContentType());
+
+        return apiProxy;
+    }
+
+    public void closeApiProxy(Olingo2App apiProxy) {
+        if (this.apiProxy != apiProxy) {
+            // not a shared proxy
+            apiProxy.close();
+        }
+    }
+
+    @Override
+    protected void doStop() throws Exception {
+        if (apiProxy != null) {
+            apiProxy.close();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/camel-olingo2/camel-olingo2-component/src/main/java/org/apache/camel/component/olingo2/Olingo2Configuration.java
----------------------------------------------------------------------
diff --git a/components/camel-olingo2/camel-olingo2-component/src/main/java/org/apache/camel/component/olingo2/Olingo2Configuration.java b/components/camel-olingo2/camel-olingo2-component/src/main/java/org/apache/camel/component/olingo2/Olingo2Configuration.java
new file mode 100644
index 0000000..e6ec348
--- /dev/null
+++ b/components/camel-olingo2/camel-olingo2-component/src/main/java/org/apache/camel/component/olingo2/Olingo2Configuration.java
@@ -0,0 +1,158 @@
+/**
+ * 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.camel.component.olingo2;
+
+import java.util.Map;
+import javax.net.ssl.SSLContext;
+
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.spi.UriParams;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.apache.http.HttpHost;
+import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
+import org.apache.olingo.odata2.core.commons.ContentType;
+
+/**
+ * Component configuration for Olingo2 component.
+ */
+@UriParams
+public class Olingo2Configuration {
+
+    private static final String DEFAULT_CONTENT_TYPE = ContentType.APPLICATION_JSON_CS_UTF_8.toString();
+    private static final int DEFAULT_TIMEOUT = 30 * 1000;
+
+    @UriParam
+    private String serviceUri;
+
+    @UriParam
+    private String contentType = DEFAULT_CONTENT_TYPE;
+
+    @UriParam
+    private Map<String, String> httpHeaders;
+
+    // common connection parameters for convenience
+    @UriParam
+    private int connectTimeout = DEFAULT_TIMEOUT;
+
+    @UriParam
+    private int socketTimeout = DEFAULT_TIMEOUT;
+
+    @UriParam
+    private HttpHost proxy;
+
+    @UriParam
+    private SSLContext sslContext;
+
+    // for more complex configuration, use a client builder
+    @UriParam
+    private HttpAsyncClientBuilder httpAsyncClientBuilder;
+
+    public String getServiceUri() {
+        return serviceUri;
+    }
+
+    public void setServiceUri(String serviceUri) {
+        this.serviceUri = serviceUri;
+    }
+
+    public String getContentType() {
+        return contentType;
+    }
+
+    public void setContentType(String contentType) {
+        this.contentType = contentType;
+    }
+
+    public Map<String, String> getHttpHeaders() {
+        return httpHeaders;
+    }
+
+    public void setHttpHeaders(Map<String, String> httpHeaders) {
+        this.httpHeaders = httpHeaders;
+    }
+
+    public int getConnectTimeout() {
+        return connectTimeout;
+    }
+
+    public void setConnectTimeout(int connectTimeout) {
+        this.connectTimeout = connectTimeout;
+    }
+
+    public int getSocketTimeout() {
+        return socketTimeout;
+    }
+
+    public void setSocketTimeout(int socketTimeout) {
+        this.socketTimeout = socketTimeout;
+    }
+
+    public HttpHost getProxy() {
+        return proxy;
+    }
+
+    public void setProxy(HttpHost proxy) {
+        this.proxy = proxy;
+    }
+
+    public SSLContext getSslContext() {
+        return sslContext;
+    }
+
+    public void setSslContext(SSLContext sslContext) {
+        this.sslContext = sslContext;
+    }
+
+    public HttpAsyncClientBuilder getHttpAsyncClientBuilder() {
+        return httpAsyncClientBuilder;
+    }
+
+    public void setHttpAsyncClientBuilder(HttpAsyncClientBuilder httpAsyncClientBuilder) {
+        this.httpAsyncClientBuilder = httpAsyncClientBuilder;
+    }
+
+    @Override
+    public int hashCode() {
+        return new HashCodeBuilder()
+            .append(serviceUri)
+            .append(contentType)
+            .append(httpHeaders)
+            .append(connectTimeout)
+            .append(socketTimeout)
+            .append(proxy)
+            .append(sslContext)
+            .append(httpAsyncClientBuilder)
+            .hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof Olingo2Configuration) {
+            Olingo2Configuration other = (Olingo2Configuration) obj;
+            return serviceUri == null ? other.serviceUri == null : serviceUri.equals(other.serviceUri)
+                && contentType == null ? other.contentType == null : contentType.equals(other.contentType)
+                && httpHeaders == null ? other.httpHeaders == null : httpHeaders.equals(other.httpHeaders)
+                && connectTimeout == other.connectTimeout
+                && socketTimeout == other.socketTimeout
+                && proxy == null ? other.proxy == null : proxy.equals(other.proxy)
+                && sslContext == null ? other.sslContext == null : sslContext.equals(other.sslContext)
+                && httpAsyncClientBuilder == null ? other.httpAsyncClientBuilder == null
+                : httpAsyncClientBuilder.equals(other.httpAsyncClientBuilder);
+        }
+        return false;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/camel-olingo2/camel-olingo2-component/src/main/java/org/apache/camel/component/olingo2/Olingo2Consumer.java
----------------------------------------------------------------------
diff --git a/components/camel-olingo2/camel-olingo2-component/src/main/java/org/apache/camel/component/olingo2/Olingo2Consumer.java b/components/camel-olingo2/camel-olingo2-component/src/main/java/org/apache/camel/component/olingo2/Olingo2Consumer.java
new file mode 100644
index 0000000..72206d1
--- /dev/null
+++ b/components/camel-olingo2/camel-olingo2-component/src/main/java/org/apache/camel/component/olingo2/Olingo2Consumer.java
@@ -0,0 +1,93 @@
+/**
+ * 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.camel.component.olingo2;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+
+import org.apache.camel.Processor;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.component.olingo2.api.Olingo2ResponseHandler;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.component.AbstractApiConsumer;
+
+import org.apache.camel.component.olingo2.internal.Olingo2ApiName;
+import org.apache.camel.util.component.ApiConsumerHelper;
+
+/**
+ * The Olingo2 consumer.
+ */
+public class Olingo2Consumer extends AbstractApiConsumer<Olingo2ApiName, Olingo2Configuration> {
+
+    public Olingo2Consumer(Olingo2Endpoint endpoint, Processor processor) {
+        super(endpoint, processor);
+    }
+
+    @Override
+    protected int poll() throws Exception {
+        // invoke the consumer method
+        final Map<String, Object> args = new HashMap<String, Object>();
+        args.putAll(endpoint.getEndpointProperties());
+
+        // let the endpoint and the Consumer intercept properties
+        endpoint.interceptProperties(args);
+        interceptProperties(args);
+
+        try {
+            // create responseHandler
+            final CountDownLatch latch = new CountDownLatch(1);
+            final Object[] result = new Object[1];
+            final Exception[] error = new Exception[1];
+
+            args.put(Olingo2Endpoint.RESPONSE_HANDLER_PROPERTY, new Olingo2ResponseHandler<Object>() {
+                @Override
+                public void onResponse(Object response) {
+                    result[0] = response;
+                    latch.countDown();
+                }
+
+                @Override
+                public void onException(Exception ex) {
+                    error[0] = ex;
+                    latch.countDown();
+                }
+
+                @Override
+                public void onCanceled() {
+                    error[0] = new RuntimeCamelException("Http Request cancelled");
+                    latch.countDown();
+                }
+            });
+
+            doInvokeMethod(args);
+
+            // guaranteed to return, since an exception on timeout is expected!!!
+            latch.await();
+
+            if (error[0] != null) {
+                throw error[0];
+            }
+
+            return ApiConsumerHelper.getResultsProcessed(this, result[0], isSplitResult());
+
+        } catch (Throwable t) {
+            throw ObjectHelper.wrapRuntimeCamelException(t);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/camel-olingo2/camel-olingo2-component/src/main/java/org/apache/camel/component/olingo2/Olingo2Endpoint.java
----------------------------------------------------------------------
diff --git a/components/camel-olingo2/camel-olingo2-component/src/main/java/org/apache/camel/component/olingo2/Olingo2Endpoint.java b/components/camel-olingo2/camel-olingo2-component/src/main/java/org/apache/camel/component/olingo2/Olingo2Endpoint.java
new file mode 100644
index 0000000..170eb49
--- /dev/null
+++ b/components/camel-olingo2/camel-olingo2-component/src/main/java/org/apache/camel/component/olingo2/Olingo2Endpoint.java
@@ -0,0 +1,281 @@
+/**
+ * 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.camel.component.olingo2;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+
+import org.apache.camel.Consumer;
+import org.apache.camel.Processor;
+import org.apache.camel.Producer;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.component.olingo2.api.Olingo2App;
+import org.apache.camel.component.olingo2.api.Olingo2ResponseHandler;
+import org.apache.camel.component.olingo2.internal.Olingo2ApiCollection;
+import org.apache.camel.component.olingo2.internal.Olingo2ApiName;
+import org.apache.camel.component.olingo2.internal.Olingo2Constants;
+import org.apache.camel.component.olingo2.internal.Olingo2PropertiesHelper;
+import org.apache.camel.spi.UriEndpoint;
+import org.apache.camel.util.component.AbstractApiEndpoint;
+import org.apache.camel.util.component.ApiMethod;
+import org.apache.camel.util.component.ApiMethodPropertiesHelper;
+import org.apache.olingo.odata2.api.edm.Edm;
+
+/**
+ * Represents a Olingo2 endpoint.
+ */
+@UriEndpoint(scheme = "olingo2", consumerClass = Olingo2Consumer.class, consumerPrefix = "consumer")
+public class Olingo2Endpoint extends AbstractApiEndpoint<Olingo2ApiName, Olingo2Configuration> {
+
+    protected static final String RESOURCE_PATH_PROPERTY = "resourcePath";
+    protected static final String RESPONSE_HANDLER_PROPERTY = "responseHandler";
+
+    private static final String KEY_PREDICATE_PROPERTY = "keyPredicate";
+    private static final String QUERY_PARAMS_PROPERTY = "queryParams";
+
+    private static final String READ_METHOD = "read";
+    private static final String EDM_PROPERTY = "edm";
+    private static final String DATA_PROPERTY = "data";
+    private static final String DELETE_METHOD = "delete";
+
+    private final Set<String> endpointPropertyNames;
+
+    private Olingo2App apiProxy;
+
+    private volatile Edm edm;
+
+    public Olingo2Endpoint(String uri, Olingo2Component component,
+                           Olingo2ApiName apiName, String methodName, Olingo2Configuration endpointConfiguration) {
+        super(uri, component, apiName, methodName, Olingo2ApiCollection.getCollection().getHelper(apiName), endpointConfiguration);
+
+        // get all endpoint property names
+        endpointPropertyNames = new HashSet<String>(getPropertiesHelper().getValidEndpointProperties(configuration));
+        // avoid adding edm as queryParam
+        endpointPropertyNames.add(EDM_PROPERTY);
+    }
+
+    public Producer createProducer() throws Exception {
+        return new Olingo2Producer(this);
+    }
+
+    public Consumer createConsumer(Processor processor) throws Exception {
+        // make sure inBody is not set for consumers
+        if (inBody != null) {
+            throw new IllegalArgumentException("Option inBody is not supported for consumer endpoint");
+        }
+        // only read method is supported
+        if (!READ_METHOD.equals(methodName)) {
+            throw new IllegalArgumentException("Only read method is supported for consumer endpoints");
+        }
+        final Olingo2Consumer consumer = new Olingo2Consumer(this, processor);
+        // also set consumer.* properties
+        configureConsumer(consumer);
+        return consumer;
+    }
+
+    @Override
+    protected ApiMethodPropertiesHelper<Olingo2Configuration> getPropertiesHelper() {
+        return Olingo2PropertiesHelper.getHelper();
+    }
+
+    protected String getThreadProfileName() {
+        return Olingo2Constants.THREAD_PROFILE_NAME;
+    }
+
+    @Override
+    public void configureProperties(Map<String, Object> options) {
+        // handle individual query params
+        parseQueryParams(options);
+
+        super.configureProperties(options);
+    }
+
+    @Override
+    protected void afterConfigureProperties() {
+        // set default inBody
+        if (!(READ_METHOD.equals(methodName) || DELETE_METHOD.equals(methodName))
+            && inBody == null) {
+            inBody = DATA_PROPERTY;
+        }
+        createProxy();
+    }
+
+    @Override
+    public synchronized Object getApiProxy(ApiMethod method, Map<String, Object> args) {
+        return apiProxy;
+    }
+
+    @Override
+    public Olingo2Component getComponent() {
+        return (Olingo2Component) super.getComponent();
+    }
+
+    @Override
+    protected void doStart() throws Exception {
+        if (apiProxy == null) {
+            createProxy();
+        }
+    }
+
+    @Override
+    protected void doStop() throws Exception {
+        if (apiProxy != null) {
+            // close the apiProxy
+            getComponent().closeApiProxy(apiProxy);
+            apiProxy = null;
+        }
+    }
+
+    @Override
+    public void interceptPropertyNames(Set<String> propertyNames) {
+        // add edm, and responseHandler property names
+        // edm is computed on first call to getApiProxy(), and responseHandler is provided by consumer and producer
+        if (!DELETE_METHOD.equals(methodName)) {
+            propertyNames.add(EDM_PROPERTY);
+        }
+        propertyNames.add(RESPONSE_HANDLER_PROPERTY);
+    }
+
+    @Override
+    public void interceptProperties(Map<String, Object> properties) {
+
+        // read Edm if not set yet
+        properties.put(EDM_PROPERTY, readEdm());
+
+        // handle keyPredicate
+        final String keyPredicate = (String) properties.get(KEY_PREDICATE_PROPERTY);
+        if (keyPredicate != null) {
+
+            // make sure a resource path is provided
+            final String resourcePath = (String) properties.get(RESOURCE_PATH_PROPERTY);
+            if (resourcePath == null) {
+                throw new IllegalArgumentException("Resource path must be provided in endpoint URI, or URI parameter '"
+                    + RESOURCE_PATH_PROPERTY + "', or exchange header '"
+                    + Olingo2Constants.PROPERTY_PREFIX + RESOURCE_PATH_PROPERTY + "'");
+            }
+
+            // append keyPredicate to dynamically create resource path
+            properties.put(RESOURCE_PATH_PROPERTY, resourcePath + '(' + keyPredicate + ')');
+        }
+
+        // handle individual queryParams
+        parseQueryParams(properties);
+    }
+
+    private void createProxy() {
+        apiProxy = getComponent().createApiProxy(getConfiguration());
+    }
+
+    private void parseQueryParams(Map<String, Object> options) {
+        // extract non-endpoint properties as query params
+        final Map<String, String> queryParams = new HashMap<String, String>();
+        for (Iterator<Map.Entry<String, Object>> it = options.entrySet().iterator(); it.hasNext();) {
+
+            final Map.Entry<String, Object> entry = it.next();
+            final String paramName = entry.getKey();
+
+            if (!endpointPropertyNames.contains(paramName)) {
+
+                // add to query params
+                final Object value = entry.getValue();
+                if (value == null) {
+                    throw new IllegalArgumentException("Null value for query parameter " + paramName);
+                }
+                queryParams.put(paramName, value.toString());
+
+                // remove entry from supplied options
+                it.remove();
+            }
+        }
+        if (!queryParams.isEmpty()) {
+
+            @SuppressWarnings("unchecked")
+            final Map<String, String> oldParams = (Map<String, String>) options.get(QUERY_PARAMS_PROPERTY);
+            if (oldParams == null) {
+                // set queryParams property
+                options.put(QUERY_PARAMS_PROPERTY, queryParams);
+            } else {
+                // overwrite old params in supplied map
+                oldParams.putAll(queryParams);
+            }
+
+        }
+    }
+
+    /// double checked locking based singleton Edm reader
+    private Edm readEdm() {
+
+        Edm localEdm = edm;
+        if (localEdm == null) {
+
+            synchronized (this) {
+
+                localEdm = edm;
+                if (localEdm == null) {
+
+                    final CountDownLatch latch = new CountDownLatch(1);
+                    final Exception[] error = new Exception[1];
+                    apiProxy.read(null, "$metadata", null, new Olingo2ResponseHandler<Edm>() {
+
+                        @Override
+                        public void onResponse(Edm response) {
+                            edm = response;
+                            latch.countDown();
+                        }
+
+                        @Override
+                        public void onException(Exception ex) {
+                            error[0] = ex;
+                            latch.countDown();
+                        }
+
+                        @Override
+                        public void onCanceled() {
+                            error[0] = new RuntimeCamelException("OData HTTP request cancelled");
+                            latch.countDown();
+                        }
+                    });
+
+                    try {
+                        // wait until response or timeout
+                        latch.await();
+
+                        final Exception ex = error[0];
+                        if (ex != null) {
+                            if (ex instanceof RuntimeCamelException) {
+                                throw (RuntimeCamelException) ex;
+                            } else {
+                                throw new RuntimeCamelException("Error reading EDM " + ex.getMessage(), ex);
+                            }
+                        }
+
+                    } catch (InterruptedException e) {
+                        throw new RuntimeCamelException(e.getMessage(), e);
+                    }
+
+                    localEdm = edm;
+                }
+            }
+        }
+
+        return localEdm;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/camel-olingo2/camel-olingo2-component/src/main/java/org/apache/camel/component/olingo2/Olingo2Producer.java
----------------------------------------------------------------------
diff --git a/components/camel-olingo2/camel-olingo2-component/src/main/java/org/apache/camel/component/olingo2/Olingo2Producer.java b/components/camel-olingo2/camel-olingo2-component/src/main/java/org/apache/camel/component/olingo2/Olingo2Producer.java
new file mode 100644
index 0000000..5286af6
--- /dev/null
+++ b/components/camel-olingo2/camel-olingo2-component/src/main/java/org/apache/camel/component/olingo2/Olingo2Producer.java
@@ -0,0 +1,106 @@
+/**
+ * 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.camel.component.olingo2;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.camel.AsyncCallback;
+import org.apache.camel.Exchange;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.component.olingo2.api.Olingo2ResponseHandler;
+import org.apache.camel.component.olingo2.internal.Olingo2ApiName;
+import org.apache.camel.component.olingo2.internal.Olingo2PropertiesHelper;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.component.AbstractApiProducer;
+import org.apache.camel.util.component.ApiMethod;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The Olingo2 producer.
+ */
+public class Olingo2Producer extends AbstractApiProducer<Olingo2ApiName, Olingo2Configuration> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(Olingo2Producer.class);
+
+    public Olingo2Producer(Olingo2Endpoint endpoint) {
+        super(endpoint, Olingo2PropertiesHelper.getHelper());
+    }
+
+    @Override
+    public boolean process(final Exchange exchange, final AsyncCallback callback) {
+        // properties for method arguments
+        final Map<String, Object> properties = new HashMap<String, Object>();
+        properties.putAll(endpoint.getEndpointProperties());
+        propertiesHelper.getExchangeProperties(exchange, properties);
+
+        // let the endpoint and the Producer intercept properties
+        endpoint.interceptProperties(properties);
+        interceptProperties(properties);
+
+        // create response handler
+        properties.put(Olingo2Endpoint.RESPONSE_HANDLER_PROPERTY, new Olingo2ResponseHandler<Object>() {
+            @Override
+            public void onResponse(Object response) {
+                // producer returns a single response, even for methods with List return types
+                exchange.getOut().setBody(response);
+                // copy headers
+                exchange.getOut().setHeaders(exchange.getIn().getHeaders());
+
+                interceptResult(response, exchange);
+
+                callback.done(false);
+            }
+
+            @Override
+            public void onException(Exception ex) {
+                exchange.setException(ex);
+                callback.done(false);
+            }
+
+            @Override
+            public void onCanceled() {
+                exchange.setException(new RuntimeCamelException("HTTP Request cancelled for "
+                    + endpoint.getEndpointUri()));
+                callback.done(false);
+            }
+        });
+
+        // decide which method to invoke
+        final ApiMethod method = findMethod(exchange, properties);
+        if (method == null) {
+            // synchronous failure
+            callback.done(true);
+            return true;
+        }
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Invoking operation {} with {}", method.getName(), properties.keySet());
+        }
+
+        try {
+            doInvokeMethod(method, properties);
+        } catch (Throwable t) {
+            exchange.setException(ObjectHelper.wrapRuntimeCamelException(t));
+            callback.done(true);
+            return true;
+        }
+
+        return false;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/camel-olingo2/camel-olingo2-component/src/main/java/org/apache/camel/component/olingo2/internal/Olingo2Constants.java
----------------------------------------------------------------------
diff --git a/components/camel-olingo2/camel-olingo2-component/src/main/java/org/apache/camel/component/olingo2/internal/Olingo2Constants.java b/components/camel-olingo2/camel-olingo2-component/src/main/java/org/apache/camel/component/olingo2/internal/Olingo2Constants.java
new file mode 100644
index 0000000..96644ba
--- /dev/null
+++ b/components/camel-olingo2/camel-olingo2-component/src/main/java/org/apache/camel/component/olingo2/internal/Olingo2Constants.java
@@ -0,0 +1,29 @@
+/**
+ * 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.camel.component.olingo2.internal;
+
+/**
+ * Constants for Olingo2 component.
+ */
+public interface Olingo2Constants {
+
+    // prefix for parameters when passed as exchange header properties
+    String PROPERTY_PREFIX = "CamelOlingo2.";
+
+    // thread profile name for this component
+    String THREAD_PROFILE_NAME = "CamelOlingo2";
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/camel-olingo2/camel-olingo2-component/src/main/java/org/apache/camel/component/olingo2/internal/Olingo2PropertiesHelper.java
----------------------------------------------------------------------
diff --git a/components/camel-olingo2/camel-olingo2-component/src/main/java/org/apache/camel/component/olingo2/internal/Olingo2PropertiesHelper.java b/components/camel-olingo2/camel-olingo2-component/src/main/java/org/apache/camel/component/olingo2/internal/Olingo2PropertiesHelper.java
new file mode 100644
index 0000000..a8110de
--- /dev/null
+++ b/components/camel-olingo2/camel-olingo2-component/src/main/java/org/apache/camel/component/olingo2/internal/Olingo2PropertiesHelper.java
@@ -0,0 +1,39 @@
+/**
+ * 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.camel.component.olingo2.internal;
+
+import org.apache.camel.component.olingo2.Olingo2Configuration;
+import org.apache.camel.util.component.ApiMethodPropertiesHelper;
+
+/**
+ * Singleton {@link ApiMethodPropertiesHelper} for Olingo2 component.
+ */
+public final class Olingo2PropertiesHelper extends ApiMethodPropertiesHelper<Olingo2Configuration> {
+
+    private static Olingo2PropertiesHelper helper;
+
+    private Olingo2PropertiesHelper() {
+        super(Olingo2Configuration.class, Olingo2Constants.PROPERTY_PREFIX);
+    }
+
+    public static synchronized Olingo2PropertiesHelper getHelper() {
+        if (helper == null) {
+            helper = new Olingo2PropertiesHelper();
+        }
+        return helper;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/camel-olingo2/camel-olingo2-component/src/main/resources/META-INF/services/org/apache/camel/component/olingo2
----------------------------------------------------------------------
diff --git a/components/camel-olingo2/camel-olingo2-component/src/main/resources/META-INF/services/org/apache/camel/component/olingo2 b/components/camel-olingo2/camel-olingo2-component/src/main/resources/META-INF/services/org/apache/camel/component/olingo2
new file mode 100644
index 0000000..a715916
--- /dev/null
+++ b/components/camel-olingo2/camel-olingo2-component/src/main/resources/META-INF/services/org/apache/camel/component/olingo2
@@ -0,0 +1 @@
+class=org.apache.camel.component.olingo2.Olingo2Component

http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/camel-olingo2/camel-olingo2-component/src/signatures/olingo-api-signature.txt
----------------------------------------------------------------------
diff --git a/components/camel-olingo2/camel-olingo2-component/src/signatures/olingo-api-signature.txt b/components/camel-olingo2/camel-olingo2-component/src/signatures/olingo-api-signature.txt
new file mode 100644
index 0000000..f4bdace
--- /dev/null
+++ b/components/camel-olingo2/camel-olingo2-component/src/signatures/olingo-api-signature.txt
@@ -0,0 +1,7 @@
+void read(org.apache.olingo.odata2.api.edm.Edm edm, String resourcePath, java.util.Map<String, String> queryParams, org.apache.camel.component.olingo2.api.Olingo2ResponseHandler responseHandler);
+void delete(String resourcePath, org.apache.camel.component.olingo2.api.Olingo2ResponseHandler responseHandler);
+void create(org.apache.olingo.odata2.api.edm.Edm edm, String resourcePath, Object data, org.apache.camel.component.olingo2.api.Olingo2ResponseHandler responseHandler);
+void update(org.apache.olingo.odata2.api.edm.Edm edm, String resourcePath, Object data, org.apache.camel.component.olingo2.api.Olingo2ResponseHandler responseHandler);
+void patch(org.apache.olingo.odata2.api.edm.Edm edm, String resourcePath, Object data, org.apache.camel.component.olingo2.api.Olingo2ResponseHandler responseHandler);
+void merge(org.apache.olingo.odata2.api.edm.Edm edm, String resourcePath, Object data, org.apache.camel.component.olingo2.api.Olingo2ResponseHandler responseHandler);
+void batch(org.apache.olingo.odata2.api.edm.Edm edm, Object data, org.apache.camel.component.olingo2.api.Olingo2ResponseHandler responseHandler);

http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/camel-olingo2/camel-olingo2-component/src/test/java/org/apache/camel/component/olingo2/AbstractOlingo2TestSupport.java
----------------------------------------------------------------------
diff --git a/components/camel-olingo2/camel-olingo2-component/src/test/java/org/apache/camel/component/olingo2/AbstractOlingo2TestSupport.java b/components/camel-olingo2/camel-olingo2-component/src/test/java/org/apache/camel/component/olingo2/AbstractOlingo2TestSupport.java
new file mode 100644
index 0000000..094f0b1
--- /dev/null
+++ b/components/camel-olingo2/camel-olingo2-component/src/test/java/org/apache/camel/component/olingo2/AbstractOlingo2TestSupport.java
@@ -0,0 +1,82 @@
+/**
+ * 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.camel.component.olingo2;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.apache.camel.util.IntrospectionSupport;
+
+/**
+ * Abstract base class for Olingo Integration tests generated by Camel API component maven plugin.
+ */
+public class AbstractOlingo2TestSupport extends CamelTestSupport {
+
+    private static final String TEST_OPTIONS_PROPERTIES = "/test-options.properties";
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+
+        final CamelContext context = super.createCamelContext();
+
+        // read Olingo component configuration from TEST_OPTIONS_PROPERTIES
+        final Properties properties = new Properties();
+        try {
+            properties.load(getClass().getResourceAsStream(TEST_OPTIONS_PROPERTIES));
+        } catch (Exception e) {
+            throw new IOException(String.format("%s could not be loaded: %s", TEST_OPTIONS_PROPERTIES, e.getMessage()),
+                e);
+        }
+
+        Map<String, Object> options = new HashMap<String, Object>();
+        for (Map.Entry<Object, Object> entry : properties.entrySet()) {
+            options.put(entry.getKey().toString(), entry.getValue());
+        }
+
+        final Olingo2Configuration configuration = new Olingo2Configuration();
+        IntrospectionSupport.setProperties(configuration, options);
+
+        // add OlingoComponent to Camel context
+        final Olingo2Component component = new Olingo2Component(context);
+        component.setConfiguration(configuration);
+        context.addComponent("olingo2", component);
+
+        return context;
+    }
+
+    @Override
+    public boolean isCreateCamelContextPerClass() {
+        // only create the context once for this class
+        return true;
+    }
+
+    @SuppressWarnings("unchecked")
+    protected <T> T requestBodyAndHeaders(String endpointUri, Object body, Map<String, Object> headers)
+        throws CamelExecutionException {
+        return (T) template().requestBodyAndHeaders(endpointUri, body, headers);
+    }
+
+    @SuppressWarnings("unchecked")
+    protected <T> T requestBody(String endpoint, Object body) throws CamelExecutionException {
+        return (T) template().requestBody(endpoint, body);
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/camel-olingo2/camel-olingo2-component/src/test/java/org/apache/camel/component/olingo2/Olingo2AppIntegrationTest.java
----------------------------------------------------------------------
diff --git a/components/camel-olingo2/camel-olingo2-component/src/test/java/org/apache/camel/component/olingo2/Olingo2AppIntegrationTest.java b/components/camel-olingo2/camel-olingo2-component/src/test/java/org/apache/camel/component/olingo2/Olingo2AppIntegrationTest.java
new file mode 100644
index 0000000..87ff736
--- /dev/null
+++ b/components/camel-olingo2/camel-olingo2-component/src/test/java/org/apache/camel/component/olingo2/Olingo2AppIntegrationTest.java
@@ -0,0 +1,235 @@
+/**
+ * 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.camel.component.olingo2;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.olingo2.api.batch.Olingo2BatchChangeRequest;
+import org.apache.camel.component.olingo2.api.batch.Olingo2BatchQueryRequest;
+import org.apache.camel.component.olingo2.api.batch.Olingo2BatchRequest;
+import org.apache.camel.component.olingo2.api.batch.Olingo2BatchResponse;
+import org.apache.camel.component.olingo2.api.batch.Operation;
+import org.apache.camel.component.olingo2.api.impl.Olingo2AppImpl;
+import org.apache.camel.component.olingo2.internal.Olingo2Constants;
+import org.apache.olingo.odata2.api.commons.HttpStatusCodes;
+import org.apache.olingo.odata2.api.edm.Edm;
+import org.apache.olingo.odata2.api.ep.entry.ODataEntry;
+import org.apache.olingo.odata2.api.ep.feed.ODataFeed;
+import org.apache.olingo.odata2.api.servicedocument.ServiceDocument;
+import org.apache.olingo.odata2.core.uri.SystemQueryOption;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Test class for {@link org.apache.camel.component.olingo2.api.Olingo2App} APIs.
+ * The integration test runs against Apache Olingo 2.0 sample server
+ * described at http://olingo.apache.org/doc/sample-setup.html
+ */
+public class Olingo2AppIntegrationTest extends AbstractOlingo2TestSupport {
+
+    private static final Logger LOG = LoggerFactory.getLogger(Olingo2AppIntegrationTest.class);
+    private static final String ID_PROPERTY = "Id";
+    private static final String MANUFACTURERS = "Manufacturers";
+    private static final String TEST_MANUFACTURER = "Manufacturers('1')";
+    private static final String CARS = "Cars";
+    private static final String TEST_RESOURCE_CONTENT_ID = "1";
+    private static final String ADDRESS = "Address";
+    private static final String TEST_RESOURCE = "$1";
+    private static final String TEST_CREATE_MANUFACTURER = "Manufacturers('123')";
+
+    @Test
+    public void testRead() throws Exception {
+        final Map<String, Object> headers = new HashMap<String, Object>();
+
+        // read ServiceDocument
+        final ServiceDocument document = requestBodyAndHeaders("direct://READSERVICEDOC", null, headers);
+        assertNotNull(document);
+        assertFalse("ServiceDocument entity sets", document.getEntitySetsInfo().isEmpty());
+
+        // parameter type is java.util.Map
+        final HashMap<String, String> queryParams = new HashMap<String, String>();
+        queryParams.put(SystemQueryOption.$top.name(), "5");
+        headers.put("CamelOlingo2.queryParams", queryParams);
+
+        // read ODataFeed
+        final ODataFeed manufacturers = requestBodyAndHeaders("direct://READFEED", null, headers);
+        assertNotNull(manufacturers);
+        assertEquals("Manufacturers feed size", 5, manufacturers.getEntries().size());
+
+        // read ODataEntry
+        headers.clear();
+        headers.put(Olingo2Constants.PROPERTY_PREFIX + "keyPredicate", "'1'");
+        final ODataEntry manufacturer = requestBodyAndHeaders("direct://READENTRY", null, headers);
+        assertNotNull(manufacturer);
+        assertEquals("Manufacturer Id", "1", manufacturer.getProperties().get(ID_PROPERTY));
+    }
+
+    @Test
+    public void testCreateUpdateDelete() throws Exception {
+        final Map<String, Object> data = getEntityData();
+        Map<String, Object> address;
+
+        final ODataEntry manufacturer = requestBody("direct://CREATE", data);
+        assertNotNull("Created Manufacturer", manufacturer);
+        assertEquals("Created Manufacturer Id", "123", manufacturer.getProperties().get(ID_PROPERTY));
+
+        // update
+        data.put("Name", "MyCarManufacturer Renamed");
+        address = (Map<String, Object>)data.get("Address");
+        address.put("Street", "Main Street");
+
+        HttpStatusCodes status = requestBody("direct://UPDATE", data);
+        assertNotNull("Update status", status);
+        assertEquals("Update status", HttpStatusCodes.NO_CONTENT.getStatusCode(), status.getStatusCode());
+
+        // delete
+        status = requestBody("direct://DELETE", null);
+        assertNotNull("Delete status", status);
+        assertEquals("Delete status", HttpStatusCodes.NO_CONTENT.getStatusCode(), status.getStatusCode());
+    }
+
+    private Map<String, Object> getEntityData() {
+        final Map<String, Object> data = new HashMap<String, Object>();
+        data.put("Id", "123");
+        data.put("Name", "MyCarManufacturer");
+        data.put("Founded", new Date());
+        Map<String, Object> address = new HashMap<String, Object>();
+        address.put("Street", "Main");
+        address.put("ZipCode", "42421");
+        address.put("City", "Fairy City");
+        address.put("Country", "FarFarAway");
+        data.put("Address", address);
+        return data;
+    }
+
+    @Test
+    public void testBatch() throws Exception {
+        final List<Olingo2BatchRequest> batchParts = new ArrayList<Olingo2BatchRequest>();
+
+        // 1. Edm query
+        batchParts.add(Olingo2BatchQueryRequest.resourcePath(Olingo2AppImpl.METADATA).build());
+
+        // 2. feed query
+        batchParts.add(Olingo2BatchQueryRequest.resourcePath(MANUFACTURERS).build());
+
+        // 3. read
+        batchParts.add(Olingo2BatchQueryRequest.resourcePath(TEST_MANUFACTURER).build());
+
+        // 4. read with expand
+        final HashMap<String, String> queryParams = new HashMap<String, String>();
+        queryParams.put(SystemQueryOption.$expand.toString(), CARS);
+        batchParts.add(Olingo2BatchQueryRequest.resourcePath(TEST_MANUFACTURER).queryParams(queryParams).build());
+
+        // 5. create
+        final Map<String, Object> data = getEntityData();
+        batchParts.add(Olingo2BatchChangeRequest.resourcePath(MANUFACTURERS).
+            contentId(TEST_RESOURCE_CONTENT_ID).operation(Operation.CREATE).body(data).build());
+
+        // 6. update
+        final Map<String, Object> updateData = new HashMap<String, Object>(data);
+        @SuppressWarnings("unchecked")
+        Map<String, Object> address = (Map<String, Object>) updateData.get(ADDRESS);
+        updateData.put("Name", "MyCarManufacturer Renamed");
+        address.put("Street", "Main Street");
+
+        batchParts.add(Olingo2BatchChangeRequest.resourcePath(TEST_RESOURCE).operation(Operation.UPDATE)
+            .body(updateData).build());
+
+        // 7. delete
+        batchParts.add(Olingo2BatchChangeRequest.resourcePath(TEST_RESOURCE).operation(Operation.DELETE).build());
+
+        // 8. read to verify delete
+        batchParts.add(Olingo2BatchQueryRequest.resourcePath(TEST_CREATE_MANUFACTURER).build());
+
+        // execute batch request
+        final List<Olingo2BatchResponse> responseParts = requestBody("direct://BATCH", batchParts);
+        assertNotNull("Batch response", responseParts);
+        assertEquals("Batch responses expected", 8, responseParts.size());
+
+        final Edm edm = (Edm) responseParts.get(0).getBody();
+        assertNotNull(edm);
+
+        final ODataFeed feed = (ODataFeed) responseParts.get(1).getBody();
+        assertNotNull(feed);
+
+        ODataEntry dataEntry = (ODataEntry) responseParts.get(2).getBody();
+        assertNotNull(dataEntry);
+
+        dataEntry = (ODataEntry) responseParts.get(3).getBody();
+        assertNotNull(dataEntry);
+
+        dataEntry = (ODataEntry) responseParts.get(4).getBody();
+        assertNotNull(dataEntry);
+
+        assertEquals(HttpStatusCodes.NO_CONTENT.getStatusCode(), responseParts.get(5).getStatusCode());
+        assertEquals(HttpStatusCodes.NO_CONTENT.getStatusCode(), responseParts.get(6).getStatusCode());
+
+        assertEquals(HttpStatusCodes.NOT_FOUND.getStatusCode(), responseParts.get(7).getStatusCode());
+        final Exception exception = (Exception) responseParts.get(7).getBody();
+        assertNotNull(exception);
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            public void configure() {
+                // test routes for read
+                from("direct://READSERVICEDOC")
+                    .to("olingo2://read/");
+
+                from("direct://READFEED")
+                    .to("olingo2://read/Manufacturers?$orderBy=Name%20asc");
+
+                from("direct://READENTRY")
+                    .to("olingo2://read/Manufacturers");
+
+                // test route for create
+                from("direct://CREATE")
+                  .to("olingo2://create/Manufacturers");
+
+                // test route for update
+                from("direct://UPDATE")
+                  .to("olingo2://update/Manufacturers('123')");
+
+                // test route for delete
+                from("direct://DELETE")
+                  .to("olingo2://delete/Manufacturers('123')");
+
+/*
+                // test route for merge
+                from("direct://MERGE")
+                  .to("olingo2://merge");
+
+                // test route for patch
+                from("direct://PATCH")
+                  .to("olingo2://patch");
+*/
+
+                // test route for batch
+                from("direct://BATCH")
+                    .to("olingo2://batch");
+
+            }
+        };
+    }
+}


[3/3] git commit: CAMEL-7773 Initial version of Camel-Olingo2 component

Posted by dh...@apache.org.
CAMEL-7773 Initial version of Camel-Olingo2 component


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/9a92064c
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/9a92064c
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/9a92064c

Branch: refs/heads/master
Commit: 9a92064cf9977a1cafa916441995e5e7804ec19e
Parents: 89a8b35
Author: Dhiraj Bokde <dh...@yahoo.com>
Authored: Mon Sep 1 00:18:26 2014 -0700
Committer: Dhiraj Bokde <dh...@yahoo.com>
Committed: Mon Sep 1 00:18:26 2014 -0700

----------------------------------------------------------------------
 .../camel-olingo2/camel-olingo2-api/pom.xml     | 141 +++
 .../camel/component/olingo2/api/Olingo2App.java | 134 +++
 .../olingo2/api/Olingo2ResponseHandler.java     |  43 +
 .../api/batch/Olingo2BatchChangeRequest.java    |  94 ++
 .../api/batch/Olingo2BatchQueryRequest.java     |  71 ++
 .../olingo2/api/batch/Olingo2BatchRequest.java  |  39 +
 .../olingo2/api/batch/Olingo2BatchResponse.java |  65 ++
 .../component/olingo2/api/batch/Operation.java  |  35 +
 .../api/impl/AbstractFutureCallback.java        | 104 +++
 .../component/olingo2/api/impl/HttpMerge.java   |  47 +
 .../olingo2/api/impl/Olingo2AppImpl.java        | 901 +++++++++++++++++++
 .../olingo2/api/Olingo2AppIntegrationTest.java  | 560 ++++++++++++
 .../src/test/resources/log4j.properties         |  39 +
 .../camel-olingo2-component/pom.xml             | 208 +++++
 .../component/olingo2/Olingo2Component.java     | 152 ++++
 .../component/olingo2/Olingo2Configuration.java | 158 ++++
 .../component/olingo2/Olingo2Consumer.java      |  93 ++
 .../component/olingo2/Olingo2Endpoint.java      | 281 ++++++
 .../component/olingo2/Olingo2Producer.java      | 106 +++
 .../olingo2/internal/Olingo2Constants.java      |  29 +
 .../internal/Olingo2PropertiesHelper.java       |  39 +
 .../services/org/apache/camel/component/olingo2 |   1 +
 .../src/signatures/olingo-api-signature.txt     |   7 +
 .../olingo2/AbstractOlingo2TestSupport.java     |  82 ++
 .../olingo2/Olingo2AppIntegrationTest.java      | 235 +++++
 .../src/test/resources/log4j.properties         |  39 +
 .../src/test/resources/test-options.properties  |  19 +
 components/camel-olingo2/pom.xml                |  65 ++
 components/pom.xml                              |   1 +
 29 files changed, 3788 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/camel-olingo2/camel-olingo2-api/pom.xml
----------------------------------------------------------------------
diff --git a/components/camel-olingo2/camel-olingo2-api/pom.xml b/components/camel-olingo2/camel-olingo2-api/pom.xml
new file mode 100644
index 0000000..971e402
--- /dev/null
+++ b/components/camel-olingo2/camel-olingo2-api/pom.xml
@@ -0,0 +1,141 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.camel.component.olingo2</groupId>
+    <artifactId>camel-olingo2-parent</artifactId>
+    <version>2.14-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>camel-olingo2-api</artifactId>
+  <name>Camel :: Olingo2 Component API</name>
+  <description>Camel API for Apache Olingo2</description>
+  <packaging>bundle</packaging>
+
+  <properties>
+    <camel.osgi.export.pkg>org.apache.camel.component.olingo2.api*</camel.osgi.export.pkg>
+    <olingo2-version>2.0.0</olingo2-version>
+    <http-asyncclient-version>4.0.2</http-asyncclient-version>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.olingo</groupId>
+      <artifactId>olingo-odata2-api</artifactId>
+      <version>${olingo2-version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.olingo</groupId>
+      <artifactId>olingo-odata2-core</artifactId>
+      <version>${olingo2-version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>javax.ws.rs</groupId>
+          <artifactId>javax.ws.rs-api</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpasyncclient</artifactId>
+      <version>${http-asyncclient-version}</version>
+    </dependency>
+
+    <!-- logging -->
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <version>1.7.7</version>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <version>1.7.7</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+      <version>1.2.17</version>
+      <scope>test</scope>
+    </dependency>
+
+    <!-- testing -->
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <defaultGoal>install</defaultGoal>
+
+    <plugins>
+      
+      <!-- to generate API Javadoc -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+        <version>2.9.1</version>
+        <executions>
+          <execution>
+            <id>add-javadoc</id>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+            <configuration>
+              <attach>true</attach>
+              <source>1.6</source>
+              <quiet>true</quiet>
+              <detectOfflineLinks>false</detectOfflineLinks>
+              <javadocVersion>1.6</javadocVersion>
+              <encoding>UTF-8</encoding>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+    </plugins>
+  </build>
+
+  <!-- Disable Java 8 doclint checks to avoid Javadoc plugin failures -->
+  <profiles>
+    <profile>
+      <id>doclint-java8-disable</id>
+      <activation>
+        <jdk>[1.8,</jdk>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-javadoc-plugin</artifactId>
+            <configuration>
+              <additionalparam>-Xdoclint:none</additionalparam>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+
+</project>

http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/Olingo2App.java
----------------------------------------------------------------------
diff --git a/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/Olingo2App.java b/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/Olingo2App.java
new file mode 100644
index 0000000..751b798
--- /dev/null
+++ b/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/Olingo2App.java
@@ -0,0 +1,134 @@
+/**
+ * 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.camel.component.olingo2.api;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.component.olingo2.api.batch.Olingo2BatchResponse;
+import org.apache.olingo.odata2.api.commons.HttpStatusCodes;
+import org.apache.olingo.odata2.api.edm.Edm;
+
+/**
+ * Olingo2 Client Api Interface.
+ */
+public interface Olingo2App {
+
+    /**
+     * Sets Service base URI.
+     * @param serviceUri
+     */
+    void setServiceUri(String serviceUri);
+
+    /**
+     * Returns Service base URI.
+     * @return service base URI.
+     */
+    String getServiceUri();
+
+    /**
+     * Sets custom Http headers to add to every service request.
+     * @param httpHeaders custom Http headers.
+     */
+    void setHttpHeaders(Map<String, String> httpHeaders);
+
+    /**
+     * Returns custom Http headers.
+     * @return custom Http headers.
+     */
+    Map<String, String> getHttpHeaders();
+
+    /**
+     * Returns content type for service calls. Defaults to <code>application/json;charset=utf-8</code>.
+     * @return content type.
+     */
+    String getContentType();
+
+    /**
+     * Set default service call content type.
+     * @param contentType content type.
+     */
+    void setContentType(String contentType);
+
+    /**
+     * Closes resources.
+     */
+    void close();
+
+    /**
+     * Reads an OData resource and invokes callback with appropriate result.
+     * @param edm Service Edm, read from calling <code>read(null, "$metdata", null, responseHandler)</code>
+     * @param resourcePath OData Resource path
+     * @param queryParams OData query params
+     *                    from http://www.odata.org/documentation/odata-version-2-0/uri-conventions#SystemQueryOptions
+     * @param responseHandler callback handler
+     */
+    <T> void read(Edm edm, String resourcePath, Map<String, String> queryParams,
+                  Olingo2ResponseHandler<T> responseHandler);
+
+    /**
+     * Deletes an OData resource and invokes callback
+     * with {@link org.apache.olingo.odata2.api.commons.HttpStatusCodes} on success, or with exception on failure.
+     * @param resourcePath resource path for Entry
+     * @param responseHandler {@link org.apache.olingo.odata2.api.commons.HttpStatusCodes} callback handler
+     */
+    void delete(String resourcePath, Olingo2ResponseHandler<HttpStatusCodes> responseHandler);
+
+    /**
+     * Creates a new OData resource.
+     * @param edm service Edm
+     * @param resourcePath resource path to create
+     * @param data request data
+     * @param responseHandler callback handler
+     */
+    <T> void create(Edm edm, String resourcePath, Object data, Olingo2ResponseHandler<T> responseHandler);
+
+    /**
+     * Updates an OData resource.
+     * @param edm service Edm
+     * @param resourcePath resource path to update
+     * @param data updated data
+     * @param responseHandler {@link org.apache.olingo.odata2.api.ep.entry.ODataEntry} callback handler
+     */
+    <T> void update(Edm edm, String resourcePath, Object data, Olingo2ResponseHandler<T> responseHandler);
+
+    /**
+     * Patches/merges an OData resource using HTTP PATCH.
+     * @param edm service Edm
+     * @param resourcePath resource path to update
+     * @param data patch/merge data
+     * @param responseHandler {@link org.apache.olingo.odata2.api.ep.entry.ODataEntry} callback handler
+     */
+    <T> void patch(Edm edm, String resourcePath, Object data, Olingo2ResponseHandler<T> responseHandler);
+
+    /**
+     * Patches/merges an OData resource using HTTP MERGE.
+     * @param edm service Edm
+     * @param resourcePath resource path to update
+     * @param data patch/merge data
+     * @param responseHandler {@link org.apache.olingo.odata2.api.ep.entry.ODataEntry} callback handler
+     */
+    <T> void merge(Edm edm, String resourcePath, Object data, Olingo2ResponseHandler<T> responseHandler);
+
+    /**
+     * Executes a batch request.
+     * @param edm service Edm
+     * @param data ordered {@link org.apache.camel.component.olingo2.api.batch.Olingo2BatchRequest} list
+     * @param responseHandler callback handler
+     */
+    void batch(Edm edm, Object data, Olingo2ResponseHandler<List<Olingo2BatchResponse>> responseHandler);
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/Olingo2ResponseHandler.java
----------------------------------------------------------------------
diff --git a/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/Olingo2ResponseHandler.java b/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/Olingo2ResponseHandler.java
new file mode 100644
index 0000000..e96e345
--- /dev/null
+++ b/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/Olingo2ResponseHandler.java
@@ -0,0 +1,43 @@
+/**
+ * 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.camel.component.olingo2.api;
+
+/**
+ * Callback interface to asynchronously process Olingo2 response.
+ */
+public interface Olingo2ResponseHandler<T> {
+
+    /**
+     * Handle response data on successful completion of Olingo2 request.
+     * @param response response data from Olingo2, may be NULL for Olingo2 operations with no response data.
+     */
+    void onResponse(T response);
+
+    /**
+     * Handle exception raised from Olingo2 request.
+     * @param ex exception from Olingo2 request.
+     *           May be an instance of {@link org.apache.olingo.odata2.api.exception.ODataException} or
+     *           some other exception, such as {@link java.io.IOException}
+     */
+    void onException(Exception ex);
+
+    /**
+     * Handle Olingo2 request cancellation.
+     * May be caused by the underlying HTTP connection being shutdown asynchronously.
+     */
+    void onCanceled();
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/batch/Olingo2BatchChangeRequest.java
----------------------------------------------------------------------
diff --git a/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/batch/Olingo2BatchChangeRequest.java b/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/batch/Olingo2BatchChangeRequest.java
new file mode 100644
index 0000000..d2d06ad
--- /dev/null
+++ b/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/batch/Olingo2BatchChangeRequest.java
@@ -0,0 +1,94 @@
+/**
+ * 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.camel.component.olingo2.api.batch;
+
+import java.util.Map;
+
+/**
+ * Batch Change part.
+ */
+public class Olingo2BatchChangeRequest extends Olingo2BatchRequest {
+
+    protected String contentId;
+    protected Operation operation;
+    protected Object body;
+
+    public Operation getOperation() {
+        return operation;
+    }
+
+    public Object getBody() {
+        return body;
+    }
+
+    public String getContentId() {
+        return contentId;
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder("Batch Change Request{ ")
+            .append(resourcePath)
+            .append(", headers=").append(headers)
+            .append(", contentId=").append(contentId)
+            .append(", operation=").append(operation)
+            .append(", body=").append(body)
+            .append('}')
+            .toString();
+    }
+
+    public static Olingo2BatchChangeRequestBuilder resourcePath(String resourcePath) {
+        if (resourcePath == null) {
+            throw new IllegalArgumentException("resourcePath");
+        }
+        return new Olingo2BatchChangeRequestBuilder().resourcePath(resourcePath);
+    }
+
+    public static class Olingo2BatchChangeRequestBuilder {
+
+        private Olingo2BatchChangeRequest request = new Olingo2BatchChangeRequest();
+
+        public Olingo2BatchChangeRequestBuilder resourcePath(String resourcePath) {
+            request.resourcePath = resourcePath;
+            return this;
+        }
+
+        public Olingo2BatchChangeRequestBuilder headers(Map<String, String> headers) {
+            request.headers = headers;
+            return this;
+        }
+
+        public Olingo2BatchChangeRequestBuilder contentId(String contentId) {
+            request.contentId = contentId;
+            return this;
+        }
+
+        public Olingo2BatchChangeRequestBuilder operation(Operation operation) {
+            request.operation = operation;
+            return this;
+        }
+
+        public Olingo2BatchChangeRequestBuilder body(Object body) {
+            request.body = body;
+            return this;
+        }
+
+        public Olingo2BatchChangeRequest build() {
+            return request;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/batch/Olingo2BatchQueryRequest.java
----------------------------------------------------------------------
diff --git a/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/batch/Olingo2BatchQueryRequest.java b/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/batch/Olingo2BatchQueryRequest.java
new file mode 100644
index 0000000..cac47ff
--- /dev/null
+++ b/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/batch/Olingo2BatchQueryRequest.java
@@ -0,0 +1,71 @@
+/**
+ * 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.camel.component.olingo2.api.batch;
+
+import java.util.Map;
+
+/**
+ * Batch Query part.
+ */
+public class Olingo2BatchQueryRequest extends Olingo2BatchRequest {
+
+    private Map<String, String> queryParams;
+
+    public Map<String, String> getQueryParams() {
+        return queryParams;
+    }
+
+    public static Olingo2BatchQueryRequestBuilder resourcePath(String resourcePath) {
+        if (resourcePath == null) {
+            throw new IllegalArgumentException("resourcePath");
+        }
+        return new Olingo2BatchQueryRequestBuilder().resourcePath(resourcePath);
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder("Batch Query Request{ ")
+            .append(resourcePath)
+            .append(", headers=").append(headers)
+            .append(", queryParams=").append(queryParams)
+            .append('}')
+            .toString();
+    }
+
+    public static class Olingo2BatchQueryRequestBuilder {
+        private Olingo2BatchQueryRequest request = new Olingo2BatchQueryRequest();
+
+        public Olingo2BatchQueryRequest build() {
+            return request;
+        }
+
+        public Olingo2BatchQueryRequestBuilder resourcePath(String resourcePath) {
+            request.resourcePath = resourcePath;
+            return this;
+        }
+
+        public Olingo2BatchQueryRequestBuilder headers(Map<String, String> headers) {
+            request.headers = headers;
+            return this;
+        }
+
+        public Olingo2BatchQueryRequestBuilder queryParams(Map<String, String> queryParams) {
+            queryParams = queryParams;
+            return this;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/batch/Olingo2BatchRequest.java
----------------------------------------------------------------------
diff --git a/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/batch/Olingo2BatchRequest.java b/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/batch/Olingo2BatchRequest.java
new file mode 100644
index 0000000..1da4ee2
--- /dev/null
+++ b/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/batch/Olingo2BatchRequest.java
@@ -0,0 +1,39 @@
+/**
+ * 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.camel.component.olingo2.api.batch;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Base part in a multipart Batch request.
+ */
+public abstract class Olingo2BatchRequest {
+
+    protected String resourcePath;
+    protected Map<String, String> headers = new HashMap<String, String>();
+
+    public String getResourcePath() {
+        return resourcePath;
+    }
+
+    public Map<String, String> getHeaders() {
+        return headers;
+    }
+
+    public abstract String toString();
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/batch/Olingo2BatchResponse.java
----------------------------------------------------------------------
diff --git a/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/batch/Olingo2BatchResponse.java b/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/batch/Olingo2BatchResponse.java
new file mode 100644
index 0000000..830ac46
--- /dev/null
+++ b/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/batch/Olingo2BatchResponse.java
@@ -0,0 +1,65 @@
+/**
+ * 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.camel.component.olingo2.api.batch;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Batch Response part.
+ */
+public class Olingo2BatchResponse {
+
+    private final int statusCode;
+    private final String statusInfo;
+
+    private final String contentId;
+
+    private final Map<String, String> headers;
+    private final Object body;
+
+
+    public Olingo2BatchResponse(int statusCode, String statusInfo,
+                               String contentId, Map<String, String> headers, Object body) {
+        this.statusCode = statusCode;
+        this.statusInfo = statusInfo;
+        this.contentId = contentId;
+        this.headers = Collections.unmodifiableMap(new HashMap<String, String>(headers));
+        this.body = body;
+    }
+
+    public int getStatusCode() {
+        return statusCode;
+    }
+
+    public String getStatusInfo() {
+        return statusInfo;
+    }
+
+    public String getContentId() {
+        return contentId;
+    }
+
+    public Map<String, String> getHeaders() {
+        return headers;
+    }
+
+    public Object getBody() {
+        return body;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/batch/Operation.java
----------------------------------------------------------------------
diff --git a/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/batch/Operation.java b/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/batch/Operation.java
new file mode 100644
index 0000000..c08df52
--- /dev/null
+++ b/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/batch/Operation.java
@@ -0,0 +1,35 @@
+/**
+ * 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.camel.component.olingo2.api.batch;
+
+/**
+* OData operation used by {@link org.apache.camel.component.olingo2.api.batch.Olingo2BatchChangeRequest}.
+*/
+public enum Operation {
+
+    CREATE("POST"), UPDATE("PUT"), PATCH("PATCH"), MERGE("MERGE"), DELETE("DELETE");
+
+    private final String httpMethod;
+
+    Operation(String httpMethod) {
+        this.httpMethod = httpMethod;
+    }
+
+    public String getHttpMethod() {
+        return httpMethod;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/impl/AbstractFutureCallback.java
----------------------------------------------------------------------
diff --git a/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/impl/AbstractFutureCallback.java b/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/impl/AbstractFutureCallback.java
new file mode 100644
index 0000000..04be21a
--- /dev/null
+++ b/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/impl/AbstractFutureCallback.java
@@ -0,0 +1,104 @@
+/**
+ * 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.camel.component.olingo2.api.impl;
+
+import java.io.IOException;
+
+import org.apache.camel.component.olingo2.api.Olingo2ResponseHandler;
+import org.apache.http.HttpHeaders;
+import org.apache.http.HttpResponse;
+import org.apache.http.StatusLine;
+import org.apache.http.concurrent.FutureCallback;
+import org.apache.olingo.odata2.api.commons.HttpStatusCodes;
+import org.apache.olingo.odata2.api.ep.EntityProvider;
+import org.apache.olingo.odata2.api.ep.EntityProviderException;
+import org.apache.olingo.odata2.api.exception.ODataApplicationException;
+import org.apache.olingo.odata2.api.exception.ODataException;
+import org.apache.olingo.odata2.api.processor.ODataErrorContext;
+import org.apache.olingo.odata2.core.commons.ContentType;
+
+/**
+* Helper implementation of {@link org.apache.http.concurrent.FutureCallback}
+ * for {@link org.apache.camel.component.olingo2.api.impl.Olingo2AppImpl}
+*/
+public abstract class AbstractFutureCallback<T> implements FutureCallback<HttpResponse> {
+
+    private final Olingo2ResponseHandler<T> responseHandler;
+
+    AbstractFutureCallback(Olingo2ResponseHandler<T> responseHandler) {
+        this.responseHandler = responseHandler;
+    }
+
+    public static HttpStatusCodes checkStatus(HttpResponse response) throws ODataApplicationException {
+        final StatusLine statusLine = response.getStatusLine();
+        HttpStatusCodes httpStatusCode = HttpStatusCodes.fromStatusCode(statusLine.getStatusCode());
+        if (400 <= httpStatusCode.getStatusCode() && httpStatusCode.getStatusCode() <= 599) {
+            if (response.getEntity() != null) {
+                try {
+                    final ContentType responseContentType = ContentType.create(
+                        response.getFirstHeader(HttpHeaders.CONTENT_TYPE).getValue());
+
+                    switch (responseContentType.getODataFormat()) {
+                    case ATOM:
+                    case XML:
+                    case JSON:
+                        final ODataErrorContext errorContext = EntityProvider.readErrorDocument(
+                            response.getEntity().getContent(),
+                            responseContentType.toString());
+                        throw new ODataApplicationException(errorContext.getMessage(),
+                            errorContext.getLocale(), httpStatusCode, errorContext.getErrorCode(),
+                            errorContext.getException());
+                    default:
+                        // fall through to default exception with status line information
+                    }
+                } catch (EntityProviderException e) {
+                    throw new ODataApplicationException(e.getMessage(), response.getLocale(), httpStatusCode, e);
+                } catch (IOException e) {
+                    throw new ODataApplicationException(e.getMessage(), response.getLocale(), httpStatusCode, e);
+                }
+            }
+
+            throw new ODataApplicationException(statusLine.getReasonPhrase(), response.getLocale(), httpStatusCode);
+        }
+
+        return httpStatusCode;
+    }
+
+    @Override
+    public final void completed(HttpResponse result) {
+        try {
+            // check response status
+            checkStatus(result);
+
+            onCompleted(result);
+        } catch (Exception e) {
+            failed(e);
+        }
+    }
+
+    protected abstract void onCompleted(HttpResponse result) throws ODataException, IOException;
+
+    @Override
+    public final void failed(Exception ex) {
+        responseHandler.onException(ex);
+    }
+
+    @Override
+    public final void cancelled() {
+        responseHandler.onCanceled();
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/impl/HttpMerge.java
----------------------------------------------------------------------
diff --git a/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/impl/HttpMerge.java b/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/impl/HttpMerge.java
new file mode 100644
index 0000000..b002b4e
--- /dev/null
+++ b/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/impl/HttpMerge.java
@@ -0,0 +1,47 @@
+/**
+ * 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.camel.component.olingo2.api.impl;
+
+import java.net.URI;
+
+import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
+
+/**
+ * HTTP MERGE method.
+ */
+public class HttpMerge extends HttpEntityEnclosingRequestBase {
+
+    public static final String METHOD_NAME = "MERGE";
+
+    public HttpMerge() {
+    }
+
+    public HttpMerge(final URI uri) {
+        super();
+        setURI(uri);
+    }
+
+    public HttpMerge(final String uri) {
+        super();
+        setURI(URI.create(uri));
+    }
+
+    @Override
+    public String getMethod() {
+        return METHOD_NAME;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/9a92064c/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/impl/Olingo2AppImpl.java
----------------------------------------------------------------------
diff --git a/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/impl/Olingo2AppImpl.java b/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/impl/Olingo2AppImpl.java
new file mode 100644
index 0000000..125f7cf
--- /dev/null
+++ b/components/camel-olingo2/camel-olingo2-api/src/main/java/org/apache/camel/component/olingo2/api/impl/Olingo2AppImpl.java
@@ -0,0 +1,901 @@
+/**
+ * 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.camel.component.olingo2.api.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.camel.component.olingo2.api.Olingo2App;
+import org.apache.camel.component.olingo2.api.Olingo2ResponseHandler;
+import org.apache.camel.component.olingo2.api.batch.Olingo2BatchChangeRequest;
+import org.apache.camel.component.olingo2.api.batch.Olingo2BatchQueryRequest;
+import org.apache.camel.component.olingo2.api.batch.Olingo2BatchRequest;
+import org.apache.camel.component.olingo2.api.batch.Olingo2BatchResponse;
+import org.apache.camel.component.olingo2.api.batch.Operation;
+import org.apache.http.HttpHeaders;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpVersion;
+import org.apache.http.StatusLine;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPatch;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.concurrent.FutureCallback;
+import org.apache.http.entity.InputStreamEntity;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
+import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
+import org.apache.http.impl.nio.client.HttpAsyncClients;
+import org.apache.http.message.BasicHttpResponse;
+import org.apache.http.message.BasicStatusLine;
+import org.apache.http.nio.client.util.HttpAsyncClientUtils;
+import org.apache.olingo.odata2.api.ODataServiceVersion;
+import org.apache.olingo.odata2.api.batch.BatchException;
+import org.apache.olingo.odata2.api.client.batch.BatchChangeSet;
+import org.apache.olingo.odata2.api.client.batch.BatchChangeSetPart;
+import org.apache.olingo.odata2.api.client.batch.BatchPart;
+import org.apache.olingo.odata2.api.client.batch.BatchQueryPart;
+import org.apache.olingo.odata2.api.client.batch.BatchSingleResponse;
+import org.apache.olingo.odata2.api.commons.HttpStatusCodes;
+import org.apache.olingo.odata2.api.commons.ODataHttpHeaders;
+import org.apache.olingo.odata2.api.edm.Edm;
+import org.apache.olingo.odata2.api.edm.EdmEntitySet;
+import org.apache.olingo.odata2.api.edm.EdmException;
+import org.apache.olingo.odata2.api.edm.EdmProperty;
+import org.apache.olingo.odata2.api.ep.EntityProvider;
+import org.apache.olingo.odata2.api.ep.EntityProviderException;
+import org.apache.olingo.odata2.api.ep.EntityProviderReadProperties;
+import org.apache.olingo.odata2.api.ep.EntityProviderWriteProperties;
+import org.apache.olingo.odata2.api.exception.ODataApplicationException;
+import org.apache.olingo.odata2.api.exception.ODataException;
+import org.apache.olingo.odata2.api.processor.ODataResponse;
+import org.apache.olingo.odata2.api.uri.PathSegment;
+import org.apache.olingo.odata2.api.uri.UriParser;
+import org.apache.olingo.odata2.core.ODataPathSegmentImpl;
+import org.apache.olingo.odata2.core.commons.ContentType;
+import org.apache.olingo.odata2.core.uri.UriInfoImpl;
+
+/**
+ * Application API used by Olingo2 Component.
+ */
+public final class Olingo2AppImpl implements Olingo2App {
+
+    public static final ContentType APPLICATION_FORM_URL_ENCODED = ContentType.create("application", "x-www-form-urlencoded");
+
+    public static final String METADATA = "$metadata";
+
+    private static final String SEPARATOR = "/";
+
+    private static final String BOUNDARY_PREFIX = "batch_";
+    private static final String BOUNDARY_PARAMETER = "boundary";
+
+    private static final ContentType METADATA_CONTENT_TYPE = ContentType.APPLICATION_XML_CS_UTF_8;
+    private static final ContentType SERVICE_DOCUMENT_CONTENT_TYPE = ContentType.APPLICATION_ATOM_SVC_CS_UTF_8;
+    private static final ContentType BATCH_CONTENT_TYPE =
+        ContentType.MULTIPART_MIXED.receiveWithCharsetParameter(ContentType.CHARSET_UTF_8);
+    private static final String BATCH = "$batch";
+    private static final String MAX_DATA_SERVICE_VERSION = "Max" + ODataHttpHeaders.DATASERVICEVERSION;
+
+    private final CloseableHttpAsyncClient client;
+
+    private String serviceUri;
+    private ContentType contentType;
+    private Map<String, String> httpHeaders;
+
+    /**
+     * Create Olingo2 Application with default HTTP configuration.
+     */
+    public Olingo2AppImpl(String serviceUri) {
+        this(serviceUri, null);
+    }
+
+    /**
+     * Create Olingo2 Application with custom HTTP client builder.
+     *
+     * @param serviceUri Service Application base URI.
+     * @param builder custom HTTP client builder.
+     */
+    public Olingo2AppImpl(String serviceUri, HttpAsyncClientBuilder builder) {
+        if (serviceUri == null) {
+            throw new IllegalArgumentException("serviceUri");
+        }
+        this.serviceUri = serviceUri;
+
+        if (builder == null) {
+            this.client = HttpAsyncClients.createDefault();
+        } else {
+            this.client = builder.build();
+        }
+        this.client.start();
+        this.contentType = ContentType.APPLICATION_JSON_CS_UTF_8;
+    }
+
+    @Override
+    public void setServiceUri(String serviceUri) {
+        this.serviceUri = serviceUri;
+    }
+
+    @Override
+    public String getServiceUri() {
+        return serviceUri;
+    }
+
+    @Override
+    public Map<String, String> getHttpHeaders() {
+        return httpHeaders;
+    }
+
+    @Override
+    public void setHttpHeaders(Map<String, String> httpHeaders) {
+        this.httpHeaders = httpHeaders;
+    }
+
+    @Override
+    public String getContentType() {
+        return contentType.toString();
+    }
+
+    @Override
+    public void setContentType(String contentType) {
+        this.contentType = ContentType.parse(contentType);
+    }
+
+    @Override
+    public void close() {
+        HttpAsyncClientUtils.closeQuietly(client);
+    }
+
+    @Override
+    public <T> void read(final Edm edm, final String resourcePath, final Map<String, String> queryParams,
+                         final Olingo2ResponseHandler<T> responseHandler) {
+
+        final UriInfoImpl uriInfo = parseUri(edm, resourcePath, queryParams);
+
+        execute(new HttpGet(createUri(resourcePath, queryParams)), getResourceContentType(uriInfo),
+            new AbstractFutureCallback<T>(responseHandler) {
+
+                @Override
+                @SuppressWarnings("unchecked")
+                public void onCompleted(HttpResponse result) throws IOException {
+
+                    readContent(uriInfo, result.getEntity() != null ? result.getEntity().getContent() : null,
+                        responseHandler);
+                }
+
+            });
+    }
+
+    private ContentType getResourceContentType(UriInfoImpl uriInfo) {
+        ContentType resourceContentType;
+        switch (uriInfo.getUriType()) {
+        case URI0:
+            // service document
+            resourceContentType = SERVICE_DOCUMENT_CONTENT_TYPE;
+            break;
+        case URI8:
+            // metadata
+            resourceContentType = METADATA_CONTENT_TYPE;
+            break;
+        case URI4:
+        case URI5:
+            // is it a $value URI??
+            if (uriInfo.isValue()) {
+                // property value and $count
+                resourceContentType = ContentType.TEXT_PLAIN_CS_UTF_8;
+            } else {
+                resourceContentType = contentType;
+            }
+            break;
+        case URI15:
+        case URI16:
+        case URI50A:
+        case URI50B:
+            // $count
+            resourceContentType = ContentType.TEXT_PLAIN_CS_UTF_8;
+            break;
+        default:
+            resourceContentType = contentType;
+        }
+        return resourceContentType;
+    }
+
+    @Override
+    public <T> void create(Edm edm, String resourcePath, Object data, Olingo2ResponseHandler<T> responseHandler) {
+        final UriInfoImpl uriInfo = parseUri(edm, resourcePath, null);
+
+        writeContent(edm, new HttpPost(createUri(resourcePath, null)), uriInfo, data, responseHandler);
+    }
+
+    @Override
+    public <T> void update(Edm edm, String resourcePath, Object data, Olingo2ResponseHandler<T> responseHandler) {
+        final UriInfoImpl uriInfo = parseUri(edm, resourcePath, null);
+
+        writeContent(edm, new HttpPut(createUri(resourcePath, null)), uriInfo, data, responseHandler);
+    }
+
+    @Override
+    public <T> void patch(Edm edm, String resourcePath, Object data, Olingo2ResponseHandler<T> responseHandler) {
+        final UriInfoImpl uriInfo = parseUri(edm, resourcePath, null);
+
+        writeContent(edm, new HttpPatch(createUri(resourcePath, null)), uriInfo, data, responseHandler);
+    }
+
+    @Override
+    public <T> void merge(Edm edm, String resourcePath, Object data, Olingo2ResponseHandler<T> responseHandler) {
+        final UriInfoImpl uriInfo = parseUri(edm, resourcePath, null);
+
+        writeContent(edm, new HttpMerge(createUri(resourcePath, null)), uriInfo, data, responseHandler);
+    }
+
+    @Override
+    public void batch(Edm edm, Object data, Olingo2ResponseHandler<List<Olingo2BatchResponse>> responseHandler) {
+        final UriInfoImpl uriInfo = parseUri(edm, BATCH, null);
+
+        writeContent(edm, new HttpPost(createUri(BATCH, null)), uriInfo, data, responseHandler);
+    }
+
+    @Override
+    public void delete(String resourcePath, final Olingo2ResponseHandler<HttpStatusCodes> responseHandler) {
+
+        execute(new HttpDelete(createUri(resourcePath)), contentType,
+            new AbstractFutureCallback<HttpStatusCodes>(responseHandler) {
+                @Override
+                public void onCompleted(HttpResponse result) {
+                    final StatusLine statusLine = result.getStatusLine();
+                    responseHandler.onResponse(HttpStatusCodes.fromStatusCode(statusLine.getStatusCode()));
+                }
+            });
+    }
+
+    private <T> void readContent(UriInfoImpl uriInfo, InputStream content, Olingo2ResponseHandler<T> responseHandler) {
+        try {
+            responseHandler.onResponse(this.<T>readContent(uriInfo, content));
+        } catch (EntityProviderException e) {
+            responseHandler.onException(e);
+        } catch (ODataApplicationException e) {
+            responseHandler.onException(e);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T> T readContent(UriInfoImpl uriInfo, InputStream content)
+        throws EntityProviderException, ODataApplicationException {
+        T response;
+        switch (uriInfo.getUriType()) {
+        case URI0:
+            // service document
+            response = (T) EntityProvider.readServiceDocument(content, SERVICE_DOCUMENT_CONTENT_TYPE.toString());
+            break;
+
+        case URI8:
+            // $metadata
+            response = (T) EntityProvider.readMetadata(content, false);
+            break;
+
+        case URI7A:
+            // link
+            response = (T) EntityProvider.readLink(getContentType(), uriInfo.getTargetEntitySet(), content);
+            break;
+
+        case URI7B:
+            // links
+            response = (T) EntityProvider.readLinks(getContentType(), uriInfo.getTargetEntitySet(), content);
+            break;
+
+        case URI3:
+            // complex property
+            final List<EdmProperty> complexPropertyPath = uriInfo.getPropertyPath();
+            final EdmProperty complexProperty = complexPropertyPath.get(complexPropertyPath.size() - 1);
+            response = (T) EntityProvider.readProperty(getContentType(),
+                complexProperty, content, EntityProviderReadProperties.init().build());
+            break;
+
+        case URI4:
+        case URI5:
+            // simple property
+            final List<EdmProperty> simplePropertyPath = uriInfo.getPropertyPath();
+            final EdmProperty simpleProperty = simplePropertyPath.get(simplePropertyPath.size() - 1);
+            if (uriInfo.isValue()) {
+                response = (T) EntityProvider.readPropertyValue(simpleProperty, content);
+            } else {
+                response = (T) EntityProvider.readProperty(getContentType(),
+                    simpleProperty, content, EntityProviderReadProperties.init().build());
+            }
+            break;
+
+        case URI15:
+        case URI16:
+        case URI50A:
+        case URI50B:
+            // $count
+            try {
+                final String stringCount = new String(EntityProvider.readBinary(content), ContentType.CHARSET_UTF_8);
+                response = (T) Long.valueOf(stringCount);
+            } catch (UnsupportedEncodingException e) {
+                throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED, e);
+            }
+            break;
+
+        case URI1:
+        case URI6B:
+            if (uriInfo.getCustomQueryOptions().containsKey("!deltaToken")) {
+                // ODataDeltaFeed
+                response = (T) EntityProvider.readDeltaFeed(
+                    getContentType(),
+                    uriInfo.getTargetEntitySet(), content,
+                    EntityProviderReadProperties.init().build());
+            } else {
+                // ODataFeed
+                response = (T) EntityProvider.readFeed(
+                    getContentType(),
+                    uriInfo.getTargetEntitySet(), content,
+                    EntityProviderReadProperties.init().build());
+            }
+            break;
+
+        case URI2:
+        case URI6A:
+            response = (T) EntityProvider.readEntry(
+                getContentType(),
+                uriInfo.getTargetEntitySet(),
+                content,
+                EntityProviderReadProperties.init().build());
+            break;
+
+        default:
+            throw new ODataApplicationException("Unsupported resource type " + uriInfo.getTargetType(),
+                Locale.ENGLISH);
+        }
+
+        return response;
+    }
+
+    private <T> void writeContent(final Edm edm, HttpEntityEnclosingRequestBase httpEntityRequest,
+                                  final UriInfoImpl uriInfo, final Object content,
+                                  final Olingo2ResponseHandler<T> responseHandler) {
+
+        try {
+            // process resource by UriType
+            final ODataResponse response = writeContent(edm, uriInfo, content);
+
+            // copy all response headers
+            for (String header : response.getHeaderNames()) {
+                httpEntityRequest.setHeader(header, response.getHeader(header));
+            }
+
+            // get (http) entity which is for default Olingo2 implementation an InputStream
+            if (response.getEntity() instanceof InputStream) {
+                httpEntityRequest.setEntity(new InputStreamEntity((InputStream) response.getEntity()));
+/*
+                // avoid sending it without a header field set
+                if (!httpEntityRequest.containsHeader(HttpHeaders.CONTENT_TYPE)) {
+                    httpEntityRequest.addHeader(HttpHeaders.CONTENT_TYPE, getContentType());
+                }
+*/
+            }
+
+            // execute HTTP request
+            execute(httpEntityRequest, contentType, new AbstractFutureCallback<T>(responseHandler) {
+                @SuppressWarnings("unchecked")
+                @Override
+                public void onCompleted(HttpResponse result)
+                    throws IOException, EntityProviderException, BatchException, ODataApplicationException {
+
+                    // if a entity is created (via POST request) the response body contains the new created entity
+                    HttpStatusCodes statusCode = HttpStatusCodes.fromStatusCode(result.getStatusLine().getStatusCode());
+                    if (statusCode != HttpStatusCodes.NO_CONTENT) {
+
+                        // TODO do we need to handle response based on other UriTypes???
+                        switch (uriInfo.getUriType()) {
+                        case URI9:
+                            // $batch
+                            final List<BatchSingleResponse> singleResponses = EntityProvider.parseBatchResponse(
+                                result.getEntity().getContent(),
+                                result.getFirstHeader(HttpHeaders.CONTENT_TYPE).getValue());
+
+                            // parse batch response bodies
+                            final List<Olingo2BatchResponse> responses = new ArrayList<Olingo2BatchResponse>();
+                            Map<String, String> contentIdLocationMap = new HashMap<String, String>();
+
+                            final List<Olingo2BatchRequest> batchRequests = (List<Olingo2BatchRequest>) content;
+                            final Iterator<Olingo2BatchRequest> iterator = batchRequests.iterator();
+
+                            for (BatchSingleResponse response : singleResponses) {
+                                final Olingo2BatchRequest request = iterator.next();
+
+                                if (request instanceof Olingo2BatchChangeRequest
+                                    && ((Olingo2BatchChangeRequest)request).getContentId() != null) {
+
+                                    contentIdLocationMap.put("$" + ((Olingo2BatchChangeRequest)request).getContentId(),
+                                        response.getHeader(HttpHeaders.LOCATION));
+                                }
+
+                                try {
+                                    responses.add(parseResponse(edm, contentIdLocationMap, request, response));
+                                } catch (Exception e) {
+                                    // report any parsing errors as error response
+                                    responses.add(new Olingo2BatchResponse(
+                                        Integer.parseInt(response.getStatusCode()),
+                                        response.getStatusInfo(), response.getContentId(), response.getHeaders(),
+                                        new ODataApplicationException(
+                                            "Error parsing response for " + request + ": " + e.getMessage(),
+                                            Locale.ENGLISH, e)));
+                                }
+                            }
+                            responseHandler.onResponse((T) responses);
+                            break;
+
+                        default:
+                            // get the response content as an ODataEntry object
+                            responseHandler.onResponse((T) EntityProvider.readEntry(response.getContentHeader(),
+                                uriInfo.getTargetEntitySet(),
+                                result.getEntity().getContent(),
+                                EntityProviderReadProperties.init().build()));
+                            break;
+                        }
+
+                    } else {
+                        responseHandler.onResponse(
+                            (T) HttpStatusCodes.fromStatusCode(result.getStatusLine().getStatusCode()));
+                    }
+                }
+            });
+        } catch (ODataException e) {
+            responseHandler.onException(e);
+        } catch (URISyntaxException e) {
+            responseHandler.onException(e);
+        } catch (UnsupportedEncodingException e) {
+            responseHandler.onException(e);
+        } catch (IOException e) {
+            responseHandler.onException(e);
+        }
+    }
+
+    private ODataResponse writeContent(Edm edm, UriInfoImpl uriInfo, Object content)
+        throws ODataApplicationException, EdmException, EntityProviderException, URISyntaxException, IOException {
+
+        String responseContentType = getContentType();
+        ODataResponse response;
+
+        switch (uriInfo.getUriType()) {
+        case URI4:
+        case URI5:
+            // simple property
+            final List<EdmProperty> simplePropertyPath = uriInfo.getPropertyPath();
+            final EdmProperty simpleProperty = simplePropertyPath.get(simplePropertyPath.size() - 1);
+            responseContentType = simpleProperty.getMimeType();
+            if (uriInfo.isValue()) {
+                response = EntityProvider.writePropertyValue(simpleProperty, content);
+                responseContentType = ContentType.TEXT_PLAIN_CS_UTF_8.toString();
+            } else {
+                response = EntityProvider.writeProperty(getContentType(), simpleProperty, content);
+            }
+            break;
+
+        case URI3:
+            // complex property
+            final List<EdmProperty> complexPropertyPath = uriInfo.getPropertyPath();
+            final EdmProperty complexProperty = complexPropertyPath.get(complexPropertyPath.size() - 1);
+            response = EntityProvider.writeProperty(responseContentType, complexProperty, content);
+            break;
+
+        case URI7A:
+            // $links with 0..1 cardinality property
+            final EdmEntitySet targetLinkEntitySet = uriInfo.getTargetEntitySet();
+            final URI rootLinkUri = new URI(targetLinkEntitySet.getName());
+            EntityProviderWriteProperties linkProperties =
+                EntityProviderWriteProperties.serviceRoot(rootLinkUri).build();
+            @SuppressWarnings("unchecked")
+            final Map<String, Object> linkMap = (Map<String, Object>) content;
+            response = EntityProvider.writeLink(responseContentType, targetLinkEntitySet, linkMap, linkProperties);
+            break;
+
+        case URI7B:
+            // $links with * cardinality property
+            final EdmEntitySet targetLinksEntitySet = uriInfo.getTargetEntitySet();
+            final URI rootLinksUri = new URI(targetLinksEntitySet.getName());
+            EntityProviderWriteProperties linksProperties =
+                EntityProviderWriteProperties.serviceRoot(rootLinksUri).build();
+            @SuppressWarnings("unchecked")
+            final Map<String, Object> linksMap = (Map<String, Object>) content;
+            response = EntityProvider.writeLink(responseContentType, targetLinksEntitySet, linksMap, linksProperties);
+            break;
+
+        case URI1:
+        case URI2:
+        case URI6A:
+        case URI6B:
+            // Entity
+            final EdmEntitySet targetEntitySet = uriInfo.getTargetEntitySet();
+            final URI rootUri = new URI(targetEntitySet.getName());
+            EntityProviderWriteProperties properties = EntityProviderWriteProperties.serviceRoot(rootUri).build();
+            @SuppressWarnings("unchecked")
+            final Map<String, Object> objectMap = (Map<String, Object>) content;
+            response = EntityProvider.writeEntry(responseContentType, targetEntitySet, objectMap, properties);
+            break;
+
+        case URI9:
+            // $batch
+            @SuppressWarnings("unchecked")
+            final List<Olingo2BatchRequest> batchParts = (List<Olingo2BatchRequest>) content;
+            response = parseBatchRequest(edm, batchParts);
+            break;
+
+        default:
+            // notify exception and return!!!
+            throw new ODataApplicationException("Unsupported resource type " + uriInfo.getTargetType(),
+                Locale.ENGLISH);
+        }
+
+        return response.getContentHeader() != null ? response
+            : ODataResponse.fromResponse(response).contentHeader(responseContentType).build();
+    }
+
+    private ODataResponse parseBatchRequest(final Edm edm, final List<Olingo2BatchRequest> batchParts)
+        throws IOException, EntityProviderException, ODataApplicationException, EdmException, URISyntaxException {
+
+        // create Batch request from parts
+        final ArrayList<BatchPart> parts = new ArrayList<BatchPart>();
+        final ArrayList<BatchChangeSetPart> changeSetParts = new ArrayList<BatchChangeSetPart>();
+
+        final Map<String, String> contentIdMap = new HashMap<String, String>();
+
+        for (Olingo2BatchRequest batchPart : batchParts) {
+
+            if (batchPart instanceof Olingo2BatchQueryRequest) {
+
+                // need to add change set parts collected so far??
+                if (!changeSetParts.isEmpty()) {
+                    addChangeSetParts(parts, changeSetParts);
+                    changeSetParts.clear();
+                    contentIdMap.clear();
+                }
+
+                // add to request parts
+                final UriInfoImpl uriInfo = parseUri(edm, batchPart.getResourcePath(), null);
+                parts.add(createBatchQueryPart(uriInfo, (Olingo2BatchQueryRequest) batchPart));
+
+            } else {
+
+                // add to change set parts
+                final BatchChangeSetPart changeSetPart = createBatchChangeSetPart(
+                    edm, contentIdMap, (Olingo2BatchChangeRequest) batchPart);
+                changeSetParts.add(changeSetPart);
+            }
+        }
+
+        // add any remaining change set parts
+        if (!changeSetParts.isEmpty()) {
+            addChangeSetParts(parts, changeSetParts);
+        }
+
+        final String boundary = BOUNDARY_PREFIX + UUID.randomUUID();
+        InputStream batchRequest = EntityProvider.writeBatchRequest(parts, boundary);
+        // add two blank lines before all --batch boundaries
+        // otherwise Olingo2 EntityProvider parser barfs in the server!!!
+        final byte[] bytes = EntityProvider.readBinary(batchRequest);
+        final String batchRequestBody = new String(bytes, ContentType.CHARSET_UTF_8);
+        batchRequest = new ByteArrayInputStream(batchRequestBody.replaceAll(
+            "--(batch_)", "\r\n\r\n--$1").getBytes(ContentType.CHARSET_UTF_8));
+
+        final String contentHeader = ContentType.create(BATCH_CONTENT_TYPE, BOUNDARY_PARAMETER, boundary).toString();
+        return ODataResponse.entity(batchRequest).contentHeader(contentHeader).build();
+    }
+
+    private void addChangeSetParts(ArrayList<BatchPart> parts, ArrayList<BatchChangeSetPart> changeSetParts) {
+        final BatchChangeSet changeSet = BatchChangeSet.newBuilder().build();
+        for (BatchChangeSetPart changeSetPart : changeSetParts) {
+            changeSet.add(changeSetPart);
+        }
+        parts.add(changeSet);
+    }
+
+    private BatchChangeSetPart createBatchChangeSetPart(Edm edm, Map<String, String> contentIdMap,
+                                                        Olingo2BatchChangeRequest batchRequest)
+        throws EdmException, URISyntaxException, EntityProviderException, IOException, ODataApplicationException {
+
+        // build body string
+        String resourcePath = batchRequest.getResourcePath();
+        // is it a referenced entity?
+        if (resourcePath.startsWith("$")) {
+            resourcePath = replaceContentId(edm, resourcePath, contentIdMap);
+        }
+
+        final UriInfoImpl uriInfo = parseUri(edm, resourcePath, null);
+
+        // serialize data into ODataResponse object, if set in request and this is not a DELETE request
+        final Map<String, String> headers = new HashMap<String, String>();
+        byte[] body = null;
+
+        if (batchRequest.getBody() != null
+            && !Operation.DELETE.equals(batchRequest.getOperation())) {
+
+            final ODataResponse response = writeContent(edm, uriInfo, batchRequest.getBody());
+            // copy response headers
+            for (String header : response.getHeaderNames()) {
+                headers.put(header, response.getHeader(header));
+            }
+
+            // get (http) entity which is for default Olingo2 implementation an InputStream
+            body = response.getEntity() instanceof InputStream
+                ? EntityProvider.readBinary((InputStream) response.getEntity()) : null;
+            if (body != null) {
+                headers.put(HttpHeaders.CONTENT_LENGTH, String.valueOf(body.length));
+            }
+        }
+
+        headers.put(HttpHeaders.ACCEPT, getResourceContentType(uriInfo).toString());
+        if (!headers.containsKey(HttpHeaders.CONTENT_TYPE)) {
+            headers.put(HttpHeaders.CONTENT_TYPE, getContentType());
+        }
+
+        // add request headers
+        headers.putAll(batchRequest.getHeaders());
+
+        final String contentId = batchRequest.getContentId();
+        if (contentId != null) {
+            contentIdMap.put("$" + contentId, resourcePath);
+        }
+        return BatchChangeSetPart.uri(createBatchUri(batchRequest))
+            .method(batchRequest.getOperation().getHttpMethod())
+            .contentId(contentId)
+            .headers(headers)
+            .body(body == null ? null : new String(body, ContentType.CHARSET_UTF_8)).build();
+    }
+
+    private BatchQueryPart createBatchQueryPart(UriInfoImpl uriInfo, Olingo2BatchQueryRequest batchRequest) {
+
+        final Map<String, String> headers = new HashMap<String, String>(batchRequest.getHeaders());
+        if (!headers.containsKey(HttpHeaders.ACCEPT)) {
+            headers.put(HttpHeaders.ACCEPT, getResourceContentType(uriInfo).toString());
+        }
+
+        return BatchQueryPart.method("GET")
+            .uri(createBatchUri(batchRequest))
+            .headers(headers)
+            .build();
+    }
+
+    private static String replaceContentId(Edm edm, String entityReference, Map<String, String> contentIdMap) throws EdmException {
+        final int pathSeparator = entityReference.indexOf('/');
+        final StringBuilder referencedEntity;
+        if (pathSeparator == -1) {
+            referencedEntity = new StringBuilder(contentIdMap.get(entityReference));
+        } else {
+            referencedEntity = new StringBuilder(contentIdMap.get(entityReference.substring(0, pathSeparator)));
+        }
+
+        // create a dummy entity location by adding a dummy key predicate
+        final EdmEntitySet entitySet = edm.getDefaultEntityContainer().getEntitySet(referencedEntity.toString());
+        final List<EdmProperty> keyProperties = entitySet.getEntityType().getKeyProperties();
+
+        if (keyProperties.size() == 1) {
+            referencedEntity.append("('dummy')");
+        } else {
+            referencedEntity.append("(");
+            for (EdmProperty keyProperty : keyProperties) {
+                referencedEntity.append(keyProperty.getName()).append('=').append("'dummy',");
+            }
+            referencedEntity.deleteCharAt(referencedEntity.length() - 1);
+            referencedEntity.append(')');
+        }
+
+        return pathSeparator == -1 ? referencedEntity.toString()
+            : referencedEntity.append(entityReference.substring(pathSeparator)).toString();
+    }
+
+    private Olingo2BatchResponse parseResponse(Edm edm, Map<String, String> contentIdLocationMap,
+                                              Olingo2BatchRequest request, BatchSingleResponse response)
+        throws EntityProviderException, ODataApplicationException {
+
+        // validate HTTP status
+        final int statusCode = Integer.parseInt(response.getStatusCode());
+        final String statusInfo = response.getStatusInfo();
+
+        final BasicHttpResponse httpResponse = new BasicHttpResponse(new BasicStatusLine(HttpVersion.HTTP_1_1,
+            statusCode, statusInfo));
+        final Map<String, String> headers = response.getHeaders();
+        for (Map.Entry<String, String> entry : headers.entrySet()) {
+            httpResponse.setHeader(entry.getKey(), entry.getValue());
+        }
+
+        ByteArrayInputStream content = null;
+        try {
+            if (response.getBody() != null) {
+                final ContentType partContentType = ContentType.create(
+                    headers.get(HttpHeaders.CONTENT_TYPE)).receiveWithCharsetParameter(ContentType.CHARSET_UTF_8);
+                final String charset = partContentType.getParameters().get(ContentType.PARAMETER_CHARSET);
+
+                final String body = response.getBody();
+                content = body != null ? new ByteArrayInputStream(body.getBytes(charset)) : null;
+
+                httpResponse.setEntity(new StringEntity(body, charset));
+            }
+
+            AbstractFutureCallback.checkStatus(httpResponse);
+        } catch (ODataApplicationException e) {
+            return new Olingo2BatchResponse(
+                statusCode, statusInfo, response.getContentId(),
+                response.getHeaders(), e);
+        } catch (UnsupportedEncodingException e) {
+            return new Olingo2BatchResponse(
+                statusCode, statusInfo, response.getContentId(),
+                response.getHeaders(), e);
+        }
+
+        // resolve resource path and query params and parse batch part uri
+        final String resourcePath = request.getResourcePath();
+        final String resolvedResourcePath;
+        if (resourcePath.startsWith("$") && !(METADATA.equals(resourcePath) || BATCH.equals(resourcePath))) {
+            resolvedResourcePath = findLocation(resourcePath, contentIdLocationMap);
+        } else {
+            final String resourceLocation = response.getHeader(HttpHeaders.LOCATION);
+            resolvedResourcePath = resourceLocation != null
+                ? resourceLocation.substring(serviceUri.length()) : resourcePath;
+        }
+        final Map<String, String> resolvedQueryParams = request instanceof Olingo2BatchQueryRequest
+            ? ((Olingo2BatchQueryRequest) request).getQueryParams() : null;
+        final UriInfoImpl uriInfo = parseUri(edm, resolvedResourcePath, resolvedQueryParams);
+
+        // resolve response content
+        final Object resolvedContent = content != null ? readContent(uriInfo, content) : null;
+
+        return new Olingo2BatchResponse(statusCode, statusInfo, response.getContentId(), response.getHeaders(),
+            resolvedContent);
+    }
+
+    private String findLocation(String resourcePath, Map<String, String> contentIdLocationMap) {
+        final int pathSeparator = resourcePath.indexOf('/');
+        if (pathSeparator == -1) {
+            return contentIdLocationMap.get(resourcePath);
+        } else {
+            return contentIdLocationMap.get(resourcePath.substring(0, pathSeparator))
+                + resourcePath.substring(pathSeparator);
+        }
+
+    }
+
+    private String createBatchUri(Olingo2BatchRequest part) {
+        String result;
+        if (part instanceof Olingo2BatchQueryRequest) {
+            final Olingo2BatchQueryRequest queryPart = (Olingo2BatchQueryRequest) part;
+            result = createUri(queryPart.getResourcePath(), queryPart.getQueryParams());
+        } else {
+            result = createUri(part.getResourcePath());
+        }
+        // strip base URI
+        return result.substring(serviceUri.length() + 1);
+    }
+
+    private String createUri(String resourcePath) {
+        return createUri(resourcePath, null);
+    }
+
+    private String createUri(String resourcePath, Map<String, String> queryParams) {
+
+        final StringBuilder absolutUri = new StringBuilder(serviceUri).append(SEPARATOR).append(resourcePath);
+        if (queryParams != null && !queryParams.isEmpty()) {
+            absolutUri.append("/?");
+            int nParams = queryParams.size();
+            int index = 0;
+            for (Map.Entry<String, String> entry : queryParams.entrySet()) {
+                absolutUri.append(entry.getKey()).append('=').append(entry.getValue());
+                if (++index < nParams) {
+                    absolutUri.append('&');
+                }
+            }
+        }
+        return absolutUri.toString();
+    }
+
+    private static UriInfoImpl parseUri(Edm edm, String resourcePath, Map<String, String> queryParams) {
+        UriInfoImpl result;
+        try {
+            final List<PathSegment> pathSegments = new ArrayList<PathSegment>();
+            final String[] segments = new URI(resourcePath).getPath().split(SEPARATOR);
+            if (queryParams == null) {
+                queryParams = Collections.emptyMap();
+            }
+            for (String segment : segments) {
+                if (segment.indexOf(';') == -1) {
+
+                    pathSegments.add(new ODataPathSegmentImpl(segment, null));
+                } else {
+
+                    // handle matrix params in path segment
+                    final String[] splitSegment = segment.split(";");
+                    segment = splitSegment[0];
+
+                    Map<String, List<String>> matrixParams = new HashMap<String, List<String>>();
+                    for (int i = 1; i < splitSegment.length; i++) {
+                        final String[] param = splitSegment[i].split("=");
+                        List<String> values = matrixParams.get(param[0]);
+                        if (values == null) {
+                            values = new ArrayList<String>();
+                            matrixParams.put(param[0], values);
+                        }
+                        if (param[1].indexOf(',') == -1) {
+                            values.add(param[1]);
+                        } else {
+                            values.addAll(Arrays.asList(param[1].split(",")));
+                        }
+                    }
+                    pathSegments.add(new ODataPathSegmentImpl(segment, matrixParams));
+                }
+            }
+            result = (UriInfoImpl) UriParser.parse(edm, pathSegments, queryParams);
+        } catch (URISyntaxException e) {
+            throw new IllegalArgumentException("resourcePath: " + e.getMessage(), e);
+        } catch (ODataException e) {
+            throw new IllegalArgumentException("resourcePath: " + e.getMessage(), e);
+        }
+
+        return result;
+    }
+
+    // public for unit test, not to be used otherwise
+    public void execute(HttpUriRequest httpUriRequest, ContentType contentType,
+                        FutureCallback<HttpResponse> callback) {
+
+        // add accept header when its not a form or multipart
+        final String contentTypeString = contentType.toString();
+        if (!APPLICATION_FORM_URL_ENCODED.equals(contentType)
+            && !contentType.getType().equals(ContentType.MULTIPART_MIXED.getType())) {
+            // otherwise accept what is being sent
+            httpUriRequest.addHeader(HttpHeaders.ACCEPT, contentTypeString);
+        }
+        // is something being sent?
+        if (httpUriRequest instanceof HttpEntityEnclosingRequestBase) {
+            httpUriRequest.addHeader(HttpHeaders.CONTENT_TYPE, contentTypeString);
+        }
+
+        // set user specified custom headers
+        if (httpHeaders != null && !httpHeaders.isEmpty()) {
+            for (Map.Entry<String, String> entry : httpHeaders.entrySet()) {
+                httpUriRequest.setHeader(entry.getKey(), entry.getValue());
+            }
+        }
+
+        // add client protocol version if not specified
+        if (!httpUriRequest.containsHeader(ODataHttpHeaders.DATASERVICEVERSION)) {
+            httpUriRequest.addHeader(ODataHttpHeaders.DATASERVICEVERSION, ODataServiceVersion.V20);
+        }
+        if (!httpUriRequest.containsHeader(MAX_DATA_SERVICE_VERSION)) {
+            httpUriRequest.addHeader(MAX_DATA_SERVICE_VERSION, ODataServiceVersion.V30);
+        }
+
+        // execute request
+        client.execute(httpUriRequest, callback);
+    }
+
+}