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");
+
+            }
+        };
+    }
+}

Reply via email to