You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by mi...@apache.org on 2015/07/08 10:15:06 UTC
[12/12] olingo-odata4 git commit: [OLINGO-708] Merge branch 'master'
into OLINGO-708_AsyncSupportTec
[OLINGO-708] Merge branch 'master' into OLINGO-708_AsyncSupportTec
Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/1476b034
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/1476b034
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/1476b034
Branch: refs/heads/OLINGO-708_AsyncSupportTec
Commit: 1476b034b5501121fc097de7b79090c6755326a3
Parents: 2080f23 3a103d5
Author: mibo <mi...@apache.org>
Authored: Wed Jul 8 10:03:43 2015 +0200
Committer: mibo <mi...@apache.org>
Committed: Wed Jul 8 10:14:15 2015 +0200
----------------------------------------------------------------------
ext/karaf/karaf-fit/pom.xml | 2 +-
.../osgi/itests/OlingoOSGiTestSupport.java | 2 +-
.../fit/tecsvc/client/ActionImportITCase.java | 70 +-
.../fit/tecsvc/client/AsyncSupportITCase.java | 156 +++-
.../BasicHttpExceptionHandlingITCase.java | 4 +-
.../olingo/fit/tecsvc/client/BasicITCase.java | 21 +-
.../fit/tecsvc/client/BatchClientITCase.java | 96 +-
.../tecsvc/client/EdmEnabledClientITCase.java | 63 --
.../tecsvc/client/EntityReferencesITCase.java | 43 +
.../fit/tecsvc/client/ExpandSelectITCase.java | 12 +
.../fit/tecsvc/client/FunctionImportITCase.java | 76 ++
.../tecsvc/client/PrimitiveComplexITCase.java | 3 +-
.../tecsvc/client/SystemQueryOptionITCase.java | 4 +-
.../fit/tecsvc/http/BasicBatchITCase.java | 15 +-
.../olingo/fit/tecsvc/http/BasicHttpITCase.java | 89 +-
.../fit/tecsvc/http/EntityReferenceITCase.java | 54 +-
.../olingo/fit/tecsvc/http/PingITCase.java | 12 +-
.../olingo/fit/v4/AuthBatchTestITCase.java | 4 +-
.../apache/olingo/fit/v4/BatchTestITCase.java | 20 +-
.../olingo/fit/v4/ConformanceTestITCase.java | 15 +-
.../olingo/fit/v4/EntityRetrieveTestITCase.java | 3 +-
.../olingo/fit/v4/MediaEntityTestITCase.java | 4 +-
.../api/communication/header/HeaderName.java | 194 ----
.../api/communication/header/ODataHeaders.java | 21 -
.../communication/header/ODataPreferences.java | 28 +-
.../api/communication/request/ODataRequest.java | 33 +-
.../communication/response/ODataResponse.java | 9 -
.../api/domain/ClientEntitySetIterator.java | 14 +-
.../client/api/domain/ClientPrimitiveValue.java | 2 +
.../olingo/client/core/ODataClientImpl.java | 18 +-
.../communication/header/ODataHeadersImpl.java | 17 -
.../request/AbstractODataRequest.java | 54 +-
.../request/AsyncBatchRequestWrapperImpl.java | 8 +-
.../request/AsyncRequestWrapperImpl.java | 29 +-
.../request/batch/ODataBatchRequestImpl.java | 3 +-
.../request/batch/ODataBatchUtilities.java | 10 +-
.../request/batch/ODataChangesetImpl.java | 4 +-
.../invoke/AbstractODataInvokeRequest.java | 7 +-
.../retrieve/EdmMetadataRequestImpl.java | 6 -
.../response/AbstractODataResponse.java | 60 +-
.../batch/ODataBatchResponseManager.java | 12 +-
.../core/domain/ClientPrimitiveValueImpl.java | 5 +
.../ClientODataDeserializerImpl.java | 11 +-
.../apache/olingo/client/core/AbstractTest.java | 5 +-
.../olingo/client/core/v4/EntityTest.java | 3 +-
.../olingo/commons/api/format/AcceptType.java | 26 +-
.../olingo/commons/api/format/ContentType.java | 85 +-
.../olingo/commons/api/format/TypeUtil.java | 36 +-
.../olingo/commons/api/http/HttpHeader.java | 20 +-
.../commons/api/format/ContentTypeTest.java | 1 -
.../olingo/server/core/uri/antlr/UriLexer.g4 | 16 +-
.../olingo/server/core/uri/antlr/UriParser.g4 | 2 +-
.../json/ODataJsonDeserializer.java | 3 +-
.../serializer/json/ODataJsonSerializer.java | 5 +-
.../core/uri/parser/UriParseTreeVisitor.java | 3 +-
.../tecsvc/async/TechnicalAsyncService.java | 13 +-
.../olingo/server/tecsvc/data/ActionData.java | 59 +-
.../olingo/server/tecsvc/data/DataCreator.java | 903 +++++++++++--------
.../olingo/server/tecsvc/data/DataProvider.java | 22 +-
.../olingo/server/tecsvc/data/FunctionData.java | 82 +-
.../processor/TechnicalActionProcessor.java | 9 +-
.../processor/TechnicalEntityProcessor.java | 39 +-
.../tecsvc/processor/TechnicalProcessor.java | 9 +-
.../tecsvc/provider/ContainerProvider.java | 12 +-
.../tecsvc/provider/FunctionProvider.java | 20 +-
.../server/tecsvc/provider/SchemaProvider.java | 4 +-
.../server/tecsvc/data/DataProviderTest.java | 6 +-
.../olingo/server/core/ODataHandlerTest.java | 4 +-
...ataJsonDeserializerActionParametersTest.java | 18 +-
.../json/ODataJsonSerializerTest.java | 13 +-
.../core/uri/antlr/TestFullResourcePath.java | 26 +-
.../olingo/server/core/uri/antlr/TestLexer.java | 35 +-
.../core/uri/antlr/TestUriParserImpl.java | 6 +-
pom.xml | 10 +-
samples/client/pom.xml | 2 +-
75 files changed, 1507 insertions(+), 1303 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1476b034/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/AsyncSupportITCase.java
----------------------------------------------------------------------
diff --cc fit/src/test/java/org/apache/olingo/fit/tecsvc/client/AsyncSupportITCase.java
index db7dc5c,0000000..fb0cfd1
mode 100644,000000..100644
--- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/AsyncSupportITCase.java
+++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/AsyncSupportITCase.java
@@@ -1,97 -1,0 +1,245 @@@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.fit.tecsvc.client;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
++import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
++import static org.junit.Assert.fail;
+
+import java.net.URI;
++import java.util.List;
+import java.util.concurrent.TimeUnit;
+
++import junit.framework.Assert;
+import org.apache.olingo.client.api.ODataClient;
+import org.apache.olingo.client.api.communication.request.ODataRequest;
++import org.apache.olingo.client.api.communication.request.cud.ODataEntityCreateRequest;
+import org.apache.olingo.client.api.communication.response.AsyncResponseWrapper;
++import org.apache.olingo.client.api.communication.response.ODataEntityCreateResponse;
+import org.apache.olingo.client.api.communication.response.ODataResponse;
+import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse;
+import org.apache.olingo.client.api.domain.ClientEntity;
++import org.apache.olingo.client.api.domain.ClientEntitySet;
++import org.apache.olingo.client.api.domain.ClientObjectFactory;
++import org.apache.olingo.client.api.domain.ClientProperty;
++import org.apache.olingo.client.api.domain.ClientValue;
+import org.apache.olingo.client.core.ODataClientFactory;
+import org.apache.olingo.commons.api.data.Entity;
+import org.apache.olingo.commons.api.data.ResWrap;
++import org.apache.olingo.commons.api.edm.FullQualifiedName;
+import org.apache.olingo.commons.api.format.ContentType;
++import org.apache.olingo.commons.api.http.HttpHeader;
++import org.apache.olingo.commons.api.http.HttpStatusCode;
+import org.apache.olingo.fit.AbstractBaseTestITCase;
+import org.apache.olingo.fit.tecsvc.TecSvcConst;
+import org.apache.olingo.server.tecsvc.async.TechnicalAsyncService;
+import org.junit.Test;
+
+public final class AsyncSupportITCase extends AbstractBaseTestITCase {
+ private static final String ES_ALL_PRIM = "ESAllPrim";
+ private static final String SERVICE_URI = TecSvcConst.BASE_URI;
++ public static final int SLEEP_TIMEOUT_IN_MS = 100;
+
+ @Test
- public void testSimple() throws Exception {
++ public void testReadEntity() throws Exception {
+ ODataClient client = getClient();
+ URI uri = client.newURIBuilder(SERVICE_URI)
+ .appendEntitySetSegment(ES_ALL_PRIM)
+ .appendKeySegment(32767).build();
+
+ //
+ final ODataRetrieveResponse<ClientEntity> response = getClient().getRetrieveRequestFactory()
+ .getEntityRequest(uri).execute();
+ assertEquals(32767, response.getBody().getProperty("PropertyInt16").getPrimitiveValue().toValue());
+ assertEquals(200, response.getStatusCode());
+
+ // first async request
++
+ ODataRequest re1 = getClient().getRetrieveRequestFactory()
+ .getEntityRequest(uri)
- .addCustomHeader("Prefer", "respond-async, " + TechnicalAsyncService.TEC_ASYNC_SLEEP + "=1");
++ .addCustomHeader(HttpHeader.PREFER, "respond-async; " + TechnicalAsyncService.TEC_ASYNC_SLEEP + "=1");
+ AsyncResponseWrapper<ODataResponse> first = client.getAsyncRequestFactory().getAsyncRequestWrapper(re1).execute();
+
+ assertTrue(first.isPreferenceApplied());
+
+ // second async request
+ ODataRequest re2 = getClient().getRetrieveRequestFactory()
+ .getEntityRequest(uri)
- .addCustomHeader("Prefer", "respond-async, " + TechnicalAsyncService.TEC_ASYNC_SLEEP + "=1");
++ .addCustomHeader(HttpHeader.PREFER, "respond-async; " + TechnicalAsyncService.TEC_ASYNC_SLEEP + "=1");
+ AsyncResponseWrapper<ODataResponse> second = client.getAsyncRequestFactory().getAsyncRequestWrapper(re2).execute();
+ assertTrue(second.isPreferenceApplied());
+
+ // get result of first async request
+ assertFalse(first.isDone());
+
- TimeUnit.SECONDS.sleep(2);
++ waitTillDone(first, 2);
+ assertTrue(first.isDone());
+
+ assertNotNull(first.getODataResponse());
+ ODataResponse firstResponse = first.getODataResponse();
+ assertEquals(200, firstResponse.getStatusCode());
+ ResWrap<Entity> entity = getClient().getDeserializer(ContentType.APPLICATION_JSON)
+ .toEntity(firstResponse.getRawResponse());
+ assertEquals(32767, entity.getPayload().getProperty("PropertyInt16").asPrimitive());
+ assertEquals("First Resource - positive values", entity.getPayload().getProperty("PropertyString").asPrimitive());
+ }
+
++ @Test
++ public void testReadEntitySet() throws Exception {
++ ODataClient client = getClient();
++ URI uri = client.newURIBuilder(SERVICE_URI)
++ .appendEntitySetSegment(ES_ALL_PRIM)
++ .build();
++
++ //
++ final ODataRetrieveResponse<ClientEntitySet> response = getClient().getRetrieveRequestFactory()
++ .getEntitySetRequest(uri).execute();
++ assertEquals(200, response.getStatusCode());
++ ClientEntitySet responseBody = response.getBody();
++ assertEquals(3, responseBody.getEntities().size());
++ checkEntityAvailableWith(responseBody, "PropertyInt16", 32767);
++
++ // first async request
++ ODataRequest re1 = getClient().getRetrieveRequestFactory()
++ .getEntitySetRequest(uri)
++ .addCustomHeader(HttpHeader.PREFER, "respond-async; " + TechnicalAsyncService.TEC_ASYNC_SLEEP + "=1");
++ AsyncResponseWrapper<ODataResponse> first = client.getAsyncRequestFactory().getAsyncRequestWrapper(re1).execute();
++
++ assertTrue(first.isPreferenceApplied());
++
++ // second async request
++ ODataRequest re2 = getClient().getRetrieveRequestFactory()
++ .getEntitySetRequest(uri)
++ .addCustomHeader(HttpHeader.PREFER, "respond-async; " + TechnicalAsyncService.TEC_ASYNC_SLEEP + "=1");
++ AsyncResponseWrapper<ODataResponse> second = client.getAsyncRequestFactory().getAsyncRequestWrapper(re2).execute();
++ assertTrue(second.isPreferenceApplied());
++
++ // get result of first async request
++ assertFalse(first.isDone());
++
++ waitTillDone(first, 2);
++ assertTrue(first.isDone());
++
++ assertNotNull(first.getODataResponse());
++ ODataResponse firstResponse = first.getODataResponse();
++ assertEquals(200, firstResponse.getStatusCode());
++ ResWrap<Entity> entity = getClient().getDeserializer(ContentType.APPLICATION_JSON)
++ .toEntity(firstResponse.getRawResponse());
++ assertEquals(32767, entity.getPayload().getProperty("PropertyInt16").asPrimitive());
++ assertEquals("First Resource - positive values", entity.getPayload().getProperty("PropertyString").asPrimitive());
++ }
++
++ private void checkEntityAvailableWith(ClientEntitySet entitySet, String property, Object value) {
++ List<ClientEntity> entities = entitySet.getEntities();
++ for (ClientEntity entity : entities) {
++ ClientProperty ep = entity.getProperty("PropertyInt16");
++ if(ep != null) {
++ assertEquals(value, ep.getPrimitiveValue().toValue());
++ return;
++ }
++ }
++ fail("Entity with property '" + property +
++ "' and value '" + value + "' not found in entitySet '" + entitySet + "'");
++ }
++
++ private static final String NAV_PROPERTY_ET_TWO_PRIM_ONE = "NavPropertyETTwoPrimOne";
++
++ @Test
++ public void testCreateEntity() throws Exception {
++ ODataClient client = getClient();
++ URI uri = client.newURIBuilder(SERVICE_URI)
++ .appendEntitySetSegment(ES_ALL_PRIM).build();
++
++ //
++ final ClientObjectFactory factory = client.getObjectFactory();
++ ClientEntity newEntity = factory.newEntity(new FullQualifiedName("olingo.odata.test1", "ETAllPrim"));
++ newEntity.getProperties().add(factory.newPrimitiveProperty("PropertyInt64",
++ factory.newPrimitiveValueBuilder().buildInt32(42)));
++ newEntity.addLink(factory.newEntityNavigationLink(NAV_PROPERTY_ET_TWO_PRIM_ONE,
++ client.newURIBuilder(SERVICE_URI)
++ .appendEntitySetSegment("ESTwoPrim")
++ .appendKeySegment(32766)
++ .build()));
++
++ final ODataEntityCreateRequest<ClientEntity> createRequest =
++ client.getCUDRequestFactory().getEntityCreateRequest(uri, newEntity);
++ assertNotNull(createRequest);
++ final ODataEntityCreateResponse<ClientEntity> createResponse = createRequest.execute();
++
++ assertEquals(HttpStatusCode.CREATED.getStatusCode(), createResponse.getStatusCode());
++ assertEquals(SERVICE_URI + "/ESAllPrim(1)", createResponse.getHeader(HttpHeader.LOCATION).iterator().next());
++ final ClientEntity createdEntity = createResponse.getBody();
++ assertNotNull(createdEntity);
++ final ClientProperty property1 = createdEntity.getProperty("PropertyInt64");
++ assertNotNull(property1);
++ assertEquals(42, property1.getPrimitiveValue().toValue());
++ final ClientProperty property2 = createdEntity.getProperty("PropertyDecimal");
++ assertNotNull(property2);
++ assertNull(property2.getPrimitiveValue()); //
++
++
++// // first async request
++// ODataRequest re1 = getClient().getRetrieveRequestFactory()
++// .getEntityRequest(uri)
++// .addCustomHeader(HttpHeader.PREFER, "respond-async; " + TechnicalAsyncService.TEC_ASYNC_SLEEP + "=1");
++// AsyncResponseWrapper<ODataResponse> first = client.getAsyncRequestFactory().getAsyncRequestWrapper(re1).execute();
++//
++// assertTrue(first.isPreferenceApplied());
++//
++// // second async request
++// ODataRequest re2 = getClient().getRetrieveRequestFactory()
++// .getEntityRequest(uri)
++// .addCustomHeader(HttpHeader.PREFER, "respond-async; " + TechnicalAsyncService.TEC_ASYNC_SLEEP + "=1");
++// AsyncResponseWrapper<ODataResponse> second = client.getAsyncRequestFactory().getAsyncRequestWrapper(re2).execute();
++// assertTrue(second.isPreferenceApplied());
++//
++// // get result of first async request
++// assertFalse(first.isDone());
++//
++// waitTillDone(first, 2);
++// assertTrue(first.isDone());
++//
++// assertNotNull(first.getODataResponse());
++// ODataResponse firstResponse = first.getODataResponse();
++// assertEquals(200, firstResponse.getStatusCode());
++// ResWrap<Entity> entity = getClient().getDeserializer(ContentType.APPLICATION_JSON)
++// .toEntity(firstResponse.getRawResponse());
++// assertEquals(32767, entity.getPayload().getProperty("PropertyInt16").asPrimitive());
++// assertEquals("First Resource - positive values", entity.getPayload().getProperty("PropertyString").asPrimitive());
++ }
++
++
++ private void waitTillDone(AsyncResponseWrapper async, int maxWaitInSeconds) throws InterruptedException {
++ int waitCounter = maxWaitInSeconds * 1000;
++ while(!async.isDone() && waitCounter > 0) {
++ TimeUnit.MILLISECONDS.sleep(SLEEP_TIMEOUT_IN_MS);
++ waitCounter -= SLEEP_TIMEOUT_IN_MS;
++ }
++ }
++
+ @Override
+ protected ODataClient getClient() {
+ ODataClient odata = ODataClientFactory.getClient();
+ odata.getConfiguration().setDefaultPubFormat(ContentType.JSON);
+ return odata;
+ }
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1476b034/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/async/TechnicalAsyncService.java
----------------------------------------------------------------------
diff --cc lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/async/TechnicalAsyncService.java
index 2b752f0,0000000..6a1cede
mode 100644,000000..100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/async/TechnicalAsyncService.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/async/TechnicalAsyncService.java
@@@ -1,250 -1,0 +1,259 @@@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.server.tecsvc.async;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.olingo.commons.api.ODataRuntimeException;
+import org.apache.olingo.commons.api.format.ContentType;
+import org.apache.olingo.commons.api.http.HttpHeader;
+import org.apache.olingo.commons.api.http.HttpStatusCode;
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.ODataApplicationException;
+import org.apache.olingo.server.api.ODataLibraryException;
+import org.apache.olingo.server.api.ODataRequest;
+import org.apache.olingo.server.api.ODataResponse;
+import org.apache.olingo.server.api.processor.Processor;
+import org.apache.olingo.server.api.serializer.SerializerException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class TechnicalAsyncService {
+
+ public static final String TEC_ASYNC_SLEEP = "tec.sleep";
+
+ private static final Map<String, AsyncRunner> LOCATION_2_ASYNC_RUNNER =
+ Collections.synchronizedMap(new HashMap<String, AsyncRunner>());
+ private static final ExecutorService ASYNC_REQUEST_EXECUTOR = Executors.newFixedThreadPool(10);
+ private static final AtomicInteger ID_GENERATOR = new AtomicInteger();
+ public static final String STATUS_MONITOR_TOKEN = "status";
+
+
-
-
+ public <T extends Processor> AsyncProcessor<T> register(T processor, Class<T> processorInterface) {
+ return new AsyncProcessor<T>(processor, processorInterface, this);
+ }
+
++ public static void updateHeader(ODataResponse response, HttpStatusCode status, String location) {
++ response.setStatusCode(status.getStatusCode());
++ response.setHeader(HttpHeader.LOCATION, location);
++ response.setHeader(HttpHeader.PREFERENCE_APPLIED, "respond-async");
++
++ }
++
++ public static void acceptedResponse(ODataResponse response, String location) {
++ updateHeader(response, HttpStatusCode.ACCEPTED, location);
++ }
++
+
+ private static final class AsyncProcessorHolder {
+ private static final TechnicalAsyncService INSTANCE = new TechnicalAsyncService();
+ }
+
+ public static TechnicalAsyncService getInstance() {
+ return AsyncProcessorHolder.INSTANCE;
+ }
+
+ public void shutdownThreadPool() {
+ ASYNC_REQUEST_EXECUTOR.shutdown();
+ }
+
+ public boolean isStatusMonitorResource(HttpServletRequest request) {
+ return request.getRequestURL() != null && request.getRequestURL().toString().contains(STATUS_MONITOR_TOKEN);
+ }
+
+ String processAsynchronous(AsyncProcessor dispatchedProcessor)
+ throws ODataApplicationException, ODataLibraryException {
+ // use executor thread pool
+ String location = createNewAsyncLocation(dispatchedProcessor.getRequest());
+ dispatchedProcessor.setLocation(location);
+ AsyncRunner run = new AsyncRunner(dispatchedProcessor);
+ LOCATION_2_ASYNC_RUNNER.put(location, run);
+ ASYNC_REQUEST_EXECUTOR.execute(run);
+ //
+ return location;
+ }
+
+ public void handle(HttpServletRequest request, HttpServletResponse response) throws SerializerException, IOException {
+ String location = getAsyncLocation(request);
+ AsyncRunner runner = LOCATION_2_ASYNC_RUNNER.get(location);
+
+ if(runner == null) {
+ response.setStatus(HttpStatusCode.NOT_FOUND.getStatusCode());
+ } else {
+ if(runner.isFinished()) {
+ ODataResponse wrapResult = runner.getDispatched().getProcessResponse();
+ wrapToAsyncHttpResponse(wrapResult, response);
+ LOCATION_2_ASYNC_RUNNER.remove(location);
+ } else {
+ response.setStatus(HttpStatusCode.ACCEPTED.getStatusCode());
+ response.setHeader(HttpHeader.LOCATION, location);
+ String content = "In progress for async location = " + location;
+ writeToResponse(response, content);
+ }
+ }
+ }
+
+ private static void writeToResponse(HttpServletResponse response, InputStream input) throws IOException {
+ copy(input, response.getOutputStream());
+ }
+
+ private void writeToResponse(HttpServletResponse response, String content) {
+ writeToResponse(response, content.getBytes());
+ }
+
+ private static void writeToResponse(HttpServletResponse response, byte[] content) {
+ OutputStream output = null;
+ try {
+ output = response.getOutputStream();
+ output.write(content);
+ } catch (IOException e) {
+ throw new ODataRuntimeException(e);
+ } finally {
+ closeStream(output);
+ }
+ }
+
+ static void wrapToAsyncHttpResponse(final ODataResponse odResponse, final HttpServletResponse response)
+ throws SerializerException, IOException {
+ OData odata = OData.newInstance();
+ InputStream odResponseStream = odata.createFixedFormatSerializer().asyncResponse(odResponse);
+
+ response.setHeader(HttpHeader.CONTENT_TYPE, ContentType.APPLICATION_HTTP.toContentTypeString());
+ response.setHeader(HttpHeader.CONTENT_ENCODING, "binary");
+ response.setStatus(HttpStatusCode.OK.getStatusCode());
+
+ writeToResponse(response, odResponseStream);
+ }
+
+ static void writeHttpResponse(final ODataResponse odResponse, final HttpServletResponse response) throws IOException {
+ response.setStatus(odResponse.getStatusCode());
+
+ for (Map.Entry<String, String> entry : odResponse.getHeaders().entrySet()) {
+ response.setHeader(entry.getKey(), entry.getValue());
+ }
+
+ copy(odResponse.getContent(), response.getOutputStream());
+ }
+
+ static void copy(final InputStream input, final OutputStream output) {
+ if(output == null || input == null) {
+ return;
+ }
+
+ try {
+ byte[] buffer = new byte[1024];
+ int n;
+ while (-1 != (n = input.read(buffer))) {
+ output.write(buffer, 0, n);
+ }
+ } catch (IOException e) {
+ throw new ODataRuntimeException(e);
+ } finally {
+ closeStream(output);
+ closeStream(input);
+ }
+ }
+
+
+ private static void closeStream(final Closeable closeable) {
+ if (closeable != null) {
+ try {
+ closeable.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+
+ private String createNewAsyncLocation(ODataRequest request) {
+ int pos = request.getRawBaseUri().lastIndexOf("/") + 1;
+ return request.getRawBaseUri().substring(0, pos) + STATUS_MONITOR_TOKEN + "/" + ID_GENERATOR.incrementAndGet();
+ }
+
+ private String getAsyncLocation(HttpServletRequest request) {
+ return request.getRequestURL().toString();
+ }
+
+ private static class AsyncRunner implements Runnable {
+ private final AsyncProcessor dispatched;
+ private int defaultSleepTimeInSeconds = 0;
+ private Exception exception;
+ boolean finished = false;
+
+ public AsyncRunner(AsyncProcessor wrap) {
+ this(wrap, 0);
+ }
+
+ public AsyncRunner(AsyncProcessor wrap, int defaultSleepTimeInSeconds) {
+ this.dispatched = wrap;
+ if(defaultSleepTimeInSeconds > 0) {
+ this.defaultSleepTimeInSeconds = defaultSleepTimeInSeconds;
+ }
+ }
+
+ @Override
+ public void run() {
+ try {
+ int sleep = getSleepTime(dispatched);
+ TimeUnit.SECONDS.sleep(sleep);
+ dispatched.process();
+ } catch (Exception e) {
+ exception = e;
+ }
+ finished = true;
+ }
+
+ private int getSleepTime(AsyncProcessor wrap) {
+ String preferHeader = wrap.getPreferHeader();
+ Matcher matcher = Pattern.compile("(" + TEC_ASYNC_SLEEP +
+ "=)(\\d*)").matcher(preferHeader);
+ if (matcher.find()) {
+ String waitTimeAsString = matcher.group(2);
+ return Integer.parseInt(waitTimeAsString);
+ }
+ return defaultSleepTimeInSeconds;
+ }
+
+ public Exception getException() {
+ return exception;
+ }
+
+ public boolean isFinished() {
+ return finished;
+ }
+
+ public AsyncProcessor getDispatched() {
+ return dispatched;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1476b034/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
----------------------------------------------------------------------
diff --cc lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
index 60d7538,2045704..fc37b40
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
@@@ -357,8 -339,9 +360,9 @@@ public class TechnicalEntityProcessor e
final Entity entity = readEntity(uriInfo, true);
final UriResourceNavigation navigationProperty = getLastNavigation(uriInfo);
+ ensureNavigationPropertyNotNull(navigationProperty);
dataProvider.createReference(entity, navigationProperty.getProperty(), references.getEntityReferences().get(0),
- request.getRawBaseUri());
+ request.getRawBaseUri());
response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode());
}
@@@ -398,23 -382,6 +403,20 @@@
private void readEntity(final ODataRequest request, final ODataResponse response, final UriInfo uriInfo,
final ContentType requestedFormat, final boolean isReference)
throws ODataApplicationException, ODataLibraryException {
+ //
+ if(odata.createPreferences(request.getHeaders(HttpHeader.PREFER)).hasRespondAsync()) {
+ TechnicalAsyncService asyncService = TechnicalAsyncService.getInstance();
+ TechnicalEntityProcessor processor = new TechnicalEntityProcessor(dataProvider, serviceMetadata);
+ processor.init(odata, serviceMetadata);
+ AsyncProcessor<EntityProcessor> asyncProcessor = asyncService.register(processor, EntityProcessor.class);
+ asyncProcessor.prepareFor().readEntity(request, response, uriInfo, requestedFormat);
+ String location = asyncProcessor.processAsync();
- //
- response.setStatusCode(HttpStatusCode.ACCEPTED.getStatusCode());
- response.setHeader(HttpHeader.LOCATION, location);
- response.setHeader(HttpHeader.PREFERENCE_APPLIED, "respond-async");
++ TechnicalAsyncService.acceptedResponse(response, location);
+ //
+ return;
+ }
+ //
+
final EdmEntitySet edmEntitySet = getEdmEntitySet(uriInfo);
final EdmEntityType edmEntityType = edmEntitySet == null ?
(EdmEntityType) ((UriResourcePartTyped) uriInfo.getUriResourceParts()
@@@ -453,6 -420,6 +455,21 @@@
private void readEntityCollection(final ODataRequest request, final ODataResponse response,
final UriInfo uriInfo, final ContentType requestedContentType, final boolean isReference)
throws ODataApplicationException, ODataLibraryException {
++ //
++ if(odata.createPreferences(request.getHeaders(HttpHeader.PREFER)).hasRespondAsync()) {
++ TechnicalAsyncService asyncService = TechnicalAsyncService.getInstance();
++ TechnicalEntityProcessor processor = new TechnicalEntityProcessor(dataProvider, serviceMetadata);
++ processor.init(odata, serviceMetadata);
++ AsyncProcessor<EntityCollectionProcessor> asyncProcessor =
++ asyncService.register(processor, EntityCollectionProcessor.class);
++ asyncProcessor.prepareFor().readEntityCollection(request, response, uriInfo, requestedContentType);
++ String location = asyncProcessor.processAsync();
++ TechnicalAsyncService.acceptedResponse(response, location);
++ //
++ return;
++ }
++ //
++
final EdmEntitySet edmEntitySet = getEdmEntitySet(uriInfo.asUriInfoResource());
final EdmEntityType edmEntityType = edmEntitySet == null ?
(EdmEntityType) ((UriResourcePartTyped) uriInfo.getUriResourceParts()