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