From ee2451cbd064e3a5b4585f37192c983f32fe5c55 Mon Sep 17 00:00:00 2001 From: Christian Holzer Date: Tue, 28 Oct 2014 18:18:21 +0100 Subject: [PATCH] Changeset Sorter Signed-off-by: Christian Amend --- .../server/api/batch/BatchOperation.java | 4 +- .../server/api/processor/BatchProcessor.java | 10 +- .../batch/handler/BatchOperationImpl.java | 16 +- .../core/batch/handler/BatchPartHandler.java | 65 +++++-- .../server/core/batch/handler/UriMapping.java | 34 ++++ .../batch/parser/BatchRequestPartImpl.java | 4 + .../olingo/server/core/batch/StringUtil.java | 0 .../handler/BatchChangeSetSorterTest.java | 164 ++++++++++++++++++ .../tecsvc/processor/TechnicalProcessor.java | 44 +---- 9 files changed, 279 insertions(+), 62 deletions(-) create mode 100644 lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/UriMapping.java rename lib/server-core/src/{main => test}/java/org/apache/olingo/server/core/batch/StringUtil.java (100%) create mode 100644 lib/server-core/src/test/java/org/apache/olingo/server/core/batch/handler/BatchChangeSetSorterTest.java diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/batch/BatchOperation.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/batch/BatchOperation.java index 8e438cfa3..af7d9c11d 100644 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/batch/BatchOperation.java +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/batch/BatchOperation.java @@ -27,9 +27,9 @@ import org.apache.olingo.server.api.ODataResponse; public interface BatchOperation { public List parseBatchRequest(InputStream in) throws BatchException; - public ODataResponse handleODataRequest(ODataRequest request); + public ODataResponse handleODataRequest(ODataRequest request, BatchRequestPart requestPart); - public ODataResponsePart handleBatchRequest(BatchRequestPart request); + public ODataResponsePart handleBatchRequest(BatchRequestPart request) throws BatchException; public void writeResponseParts(List batchResponses, ODataResponse response) throws BatchException, IOException; diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/BatchProcessor.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/BatchProcessor.java index 5a9518d94..221b38ae1 100644 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/BatchProcessor.java +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/BatchProcessor.java @@ -6,9 +6,9 @@ * 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 @@ -23,9 +23,11 @@ import java.util.List; import org.apache.olingo.server.api.ODataRequest; import org.apache.olingo.server.api.ODataResponse; import org.apache.olingo.server.api.batch.BatchOperation; +import org.apache.olingo.server.api.batch.BatchRequestPart; public interface BatchProcessor extends Processor { - void executeBatch(BatchOperation operation, ODataRequest requst, ODataResponse response); + void executeBatch(BatchOperation operation, ODataRequest requests, ODataResponse response); - List executeChangeSet(BatchOperation operation, List requests); + List executeChangeSet(BatchOperation operation, List requests, + BatchRequestPart requestPart); } diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/BatchOperationImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/BatchOperationImpl.java index bc148f00b..1f5e243ab 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/BatchOperationImpl.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/BatchOperationImpl.java @@ -6,9 +6,9 @@ * 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 @@ -44,21 +44,21 @@ public class BatchOperationImpl implements BatchOperation { partHandler = new BatchPartHandler(oDataHandler, batchProcessor, this); writer = new BatchResponseWriter(); parser = new BatchParser(getContentType(request), request.getRawBaseUri(), - request.getRawServiceResolutionUri(), isStrict); + request.getRawServiceResolutionUri(), isStrict); } @Override public List parseBatchRequest(InputStream in) throws BatchException { return parser.parseBatchRequest(in); } - + @Override - public ODataResponse handleODataRequest(ODataRequest request) { - return partHandler.handleODataRequest(request); + public ODataResponse handleODataRequest(ODataRequest request, BatchRequestPart requestPart) { + return partHandler.handleODataRequest(request, requestPart); } - + @Override - public ODataResponsePart handleBatchRequest(BatchRequestPart request) { + public ODataResponsePart handleBatchRequest(BatchRequestPart request) throws BatchException { return partHandler.handleBatchRequest(request); } diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/BatchPartHandler.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/BatchPartHandler.java index a78d5859d..df23c5548 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/BatchPartHandler.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/BatchPartHandler.java @@ -19,11 +19,14 @@ package org.apache.olingo.server.core.batch.handler; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.apache.olingo.commons.api.http.HttpHeader; import org.apache.olingo.server.api.ODataRequest; import org.apache.olingo.server.api.ODataResponse; +import org.apache.olingo.server.api.batch.BatchException; import org.apache.olingo.server.api.batch.BatchOperation; import org.apache.olingo.server.api.batch.BatchRequestPart; import org.apache.olingo.server.api.batch.ODataResponsePart; @@ -36,7 +39,8 @@ public class BatchPartHandler { private ODataHandler oDataHandler; private BatchProcessor batchProcessor; private BatchOperation batchOperation; - + private Map uriMapping = new HashMap(); + public BatchPartHandler(final ODataHandler oDataHandler, final BatchProcessor processor, final BatchOperation batchOperation) { this.oDataHandler = oDataHandler; @@ -44,23 +48,62 @@ public class BatchPartHandler { this.batchOperation = batchOperation; } - public ODataResponse handleODataRequest(ODataRequest request) { - final ODataResponse response = oDataHandler.process(request); - response.setHeader(BatchParserCommon.HTTP_CONTENT_ID, request.getHeader(BatchParserCommon.HTTP_CONTENT_ID)); + public ODataResponse handleODataRequest(ODataRequest request, BatchRequestPart requestPart) { + final ODataResponse response; + + if(requestPart.isChangeSet()) { + final UriMapping mapping = getUriMappingOrDefault(requestPart); + final String reference = BatchChangeSetSorter.getReferenceInURI(request); + if(reference != null) { + BatchChangeSetSorter.replaceContentIdReference(request, reference, mapping.getUri(reference)); + } + + response = oDataHandler.process(request); + + final String contentId = request.getHeader(BatchParserCommon.HTTP_CONTENT_ID); + final String locationHeader = request.getHeader(HttpHeader.LOCATION); + mapping.addMapping(contentId, locationHeader); + } else { + response = oDataHandler.process(request); + } + + final String contentId = request.getHeader(BatchParserCommon.HTTP_CONTENT_ID); + if(contentId != null) { + response.setHeader(BatchParserCommon.HTTP_CONTENT_ID, contentId); + } return response; } - - public ODataResponsePart handleBatchRequest(BatchRequestPart request) { - final List responses = new ArrayList(); - + + private UriMapping getUriMappingOrDefault(final BatchRequestPart requestPart) { + UriMapping mapping = uriMapping.get(requestPart); + + if(uriMapping == null) { + mapping = new UriMapping(); + } + uriMapping.put(requestPart, mapping); + + return mapping; + } + + public ODataResponsePart handleBatchRequest(BatchRequestPart request) throws BatchException { if (request.isChangeSet()) { - responses.addAll(batchProcessor.executeChangeSet(batchOperation, request.getRequests())); - return new ODataResponsePartImpl(responses, true); + return handleChangeSet(request); } else { - responses.add(handleODataRequest(request.getRequests().get(0))); + final List responses = new ArrayList(); + responses.add(handleODataRequest(request.getRequests().get(0), request)); + return new ODataResponsePartImpl(responses, false); } } + private ODataResponsePart handleChangeSet(BatchRequestPart request) throws BatchException { + final List responses = new ArrayList(); + final BatchChangeSetSorter sorter = new BatchChangeSetSorter(request.getRequests()); + + responses.addAll(batchProcessor.executeChangeSet(batchOperation, sorter.getOrderdRequests(), request)); + + return new ODataResponsePartImpl(responses, true); + } + } diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/UriMapping.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/UriMapping.java new file mode 100644 index 000000000..012332094 --- /dev/null +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/UriMapping.java @@ -0,0 +1,34 @@ +/* + * 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.core.batch.handler; + +import java.util.HashMap; +import java.util.Map; + +public class UriMapping { + private Map uriMapping = new HashMap(); + + public void addMapping(final String contentId, final String uri) { + uriMapping.put(contentId, uri); + } + + public String getUri(final String contentId) { + return uriMapping.get(contentId); + } +} diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/parser/BatchRequestPartImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/parser/BatchRequestPartImpl.java index 6c80216c4..9765b373c 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/parser/BatchRequestPartImpl.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/parser/BatchRequestPartImpl.java @@ -25,6 +25,10 @@ import java.util.List; import org.apache.olingo.server.api.ODataRequest; import org.apache.olingo.server.api.batch.BatchRequestPart; +/** + * Has to be immutable! + * + */ public class BatchRequestPartImpl implements BatchRequestPart { private List requests = new ArrayList(); diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/StringUtil.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/StringUtil.java similarity index 100% rename from lib/server-core/src/main/java/org/apache/olingo/server/core/batch/StringUtil.java rename to lib/server-core/src/test/java/org/apache/olingo/server/core/batch/StringUtil.java diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/handler/BatchChangeSetSorterTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/handler/BatchChangeSetSorterTest.java new file mode 100644 index 000000000..9196514f3 --- /dev/null +++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/handler/BatchChangeSetSorterTest.java @@ -0,0 +1,164 @@ +/* + * 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.core.batch.handler; + +import static org.junit.Assert.*; + +import java.io.ByteArrayInputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.olingo.commons.api.http.HttpMethod; +import org.apache.olingo.server.api.ODataRequest; +import org.apache.olingo.server.api.batch.BatchException; +import org.apache.olingo.server.core.batch.parser.BatchParserCommon; +import org.junit.Test; + +public class BatchChangeSetSorterTest { + + private static final String BASE_URI = "http://localhost/odata.src"; + + @Test + public void test() throws BatchException { + final List changeSet = new ArrayList(); + changeSet.add(createRequest(HttpMethod.POST, BASE_URI, "$1/Adress", "2")); + changeSet.add(createRequest(HttpMethod.POST, BASE_URI, "Employees", "1")); + + BatchChangeSetSorter sorter = new BatchChangeSetSorter(changeSet); + final List sortedChangeSet = sorter.getOrderdRequests(); + + assertEquals(2, sortedChangeSet.size()); + assertEquals("1", getContentId(sortedChangeSet.get(0))); + assertEquals("2", getContentId(sortedChangeSet.get(1))); + } + + private String getContentId(ODataRequest request) { + return request.getHeader(BatchParserCommon.HTTP_CONTENT_ID); + } + + @Test + public void testNoContentId() throws BatchException { + final List changeSet = new ArrayList(); + changeSet.add(createRequest(HttpMethod.POST, BASE_URI, "$1/Department", "2")); + changeSet.add(createRequest(HttpMethod.POST, BASE_URI, "Employees", "1")); + changeSet.add(createRequest(HttpMethod.POST, BASE_URI, "Employee('2')/Address")); + changeSet.add(createRequest(HttpMethod.POST, BASE_URI, "Employee('3')/Address")); + changeSet.add(createRequest(HttpMethod.POST, BASE_URI, "$2/Manager", "3")); + + BatchChangeSetSorter sorter = new BatchChangeSetSorter(changeSet); + final List sortedChangeSet = sorter.getOrderdRequests(); + + assertEquals(5, sortedChangeSet.size()); + assertEquals("1", getContentId(sortedChangeSet.get(0))); + assertEquals(null, getContentId(sortedChangeSet.get(1))); + assertEquals(null, getContentId(sortedChangeSet.get(2))); + assertEquals("2", getContentId(sortedChangeSet.get(3))); + assertEquals("3", getContentId(sortedChangeSet.get(4))); + } + + @SuppressWarnings("unused") + @Test(expected=BatchException.class) + public void testContentIdNotAvailable() throws BatchException { + final List changeSet = new ArrayList(); + changeSet.add(createRequest(HttpMethod.POST, BASE_URI, "$1/Department", "2")); + changeSet.add(createRequest(HttpMethod.POST, BASE_URI, "Employees", "1")); + changeSet.add(createRequest(HttpMethod.POST, BASE_URI, "Employee('2')/Address")); + changeSet.add(createRequest(HttpMethod.POST, BASE_URI, "Employee('3')/Address")); + changeSet.add(createRequest(HttpMethod.POST, BASE_URI, "$4/Manager", "3")); //4 is not available + + BatchChangeSetSorter sorter = new BatchChangeSetSorter(changeSet); + final List sortedChangeSet = sorter.getOrderdRequests(); + } + + @Test + public void testStringAsContentId() throws BatchException { + final List changeSet = new ArrayList(); + changeSet.add(createRequest(HttpMethod.POST, BASE_URI, "$One/Department", "Two")); + changeSet.add(createRequest(HttpMethod.POST, BASE_URI, "Employees", "One")); + changeSet.add(createRequest(HttpMethod.POST, BASE_URI, "Employee('2')/Address")); + changeSet.add(createRequest(HttpMethod.POST, BASE_URI, "Employee('3')/Address")); + changeSet.add(createRequest(HttpMethod.POST, BASE_URI, "$Two/Manager", "Three")); + + BatchChangeSetSorter sorter = new BatchChangeSetSorter(changeSet); + final List sortedChangeSet = sorter.getOrderdRequests(); + + assertEquals(5, sortedChangeSet.size()); + assertEquals("One", getContentId(sortedChangeSet.get(0))); + assertEquals(null, getContentId(sortedChangeSet.get(1))); + assertEquals(null, getContentId(sortedChangeSet.get(2))); + assertEquals("Two", getContentId(sortedChangeSet.get(3))); + assertEquals("Three", getContentId(sortedChangeSet.get(4))); + } + + @Test + public void testRewriting() { + final String CONTENT_ID = "1"; + final String ODATA_PATH ="$" + CONTENT_ID + "/Address"; + final String RESOURCE_URI = "Employee('1')"; + final ODataRequest request = createRequest(HttpMethod.POST, BASE_URI, ODATA_PATH); + + BatchChangeSetSorter.replaceContentIdReference(request, CONTENT_ID, RESOURCE_URI); + assertEquals(BASE_URI + "/" + "Employee('1')/Address", request.getRawRequestUri()); + assertEquals("Employee('1')/Address", request.getRawODataPath()); + } + + @Test + public void testRewritingNoContentId() { + final String CONTENT_ID = "1"; + final String ODATA_PATH = /* "$" + CONTENT_ID + */ "Address"; + final String RESOURCE_URI = "Employee('1')"; + final ODataRequest request = createRequest(HttpMethod.POST, BASE_URI, ODATA_PATH); + + BatchChangeSetSorter.replaceContentIdReference(request, CONTENT_ID, RESOURCE_URI); + assertEquals(BASE_URI + "/" + "Address", request.getRawRequestUri()); + assertEquals("Address", request.getRawODataPath()); + } + + @Test + public void testWrongRewriting() { + final String CONTENT_ID = "1"; + final String ODATA_PATH = /*"$1" */ "$2" + "/Address"; + final String RESOURCE_URI = "Employee('1')"; + final ODataRequest request = createRequest(HttpMethod.POST, BASE_URI, ODATA_PATH); + + BatchChangeSetSorter.replaceContentIdReference(request, CONTENT_ID, RESOURCE_URI); + assertEquals(BASE_URI + "/" + "$2/Address", request.getRawRequestUri()); + assertEquals("$2/Address", request.getRawODataPath()); + } + + private ODataRequest createRequest(HttpMethod method, String baseUrl, String oDataPath) { + return createRequest(method, baseUrl, oDataPath, null); + } + + private ODataRequest createRequest(HttpMethod method, String baseUrl, String oDataPath, String contentId) { + final ODataRequest request = new ODataRequest(); + request.setBody(new ByteArrayInputStream(new byte[0])); + request.setMethod(HttpMethod.GET); + request.setRawBaseUri(baseUrl); + request.setRawODataPath(oDataPath); + request.setRawRequestUri(baseUrl + "/" + oDataPath); + request.setRawQueryPath(""); + + if(contentId != null) { + request.addHeader(BatchParserCommon.HTTP_CONTENT_ID, Arrays.asList(new String[] { contentId })); + } + return request; + } +} diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java index b59c92edf..e5a0d4439 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java @@ -21,7 +21,6 @@ package org.apache.olingo.server.tecsvc.processor; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; -import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Locale; @@ -74,7 +73,7 @@ import org.apache.olingo.server.tecsvc.data.DataProvider; /** * Technical Processor which provides currently implemented processor functionality. */ -public class TechnicalProcessor implements EntitySetProcessor, EntityProcessor, PropertyProcessor, BatchProcessor { +public class TechnicalProcessor implements EntitySetProcessor, EntityProcessor, PropertyProcessor { private OData odata; private DataProvider dataProvider; @@ -198,12 +197,12 @@ public class TechnicalProcessor implements EntitySetProcessor, EntityProcessor, List subResPaths = resourcePaths.subList(1, resourcePaths.size()); for (UriResource subResPath : subResPaths) { UriResourceKind kind = subResPath.getKind(); - if(kind != UriResourceKind.primitiveProperty - && kind != UriResourceKind.complexProperty - && kind != UriResourceKind.count - && kind != UriResourceKind.value) { + if (kind != UriResourceKind.primitiveProperty + && kind != UriResourceKind.complexProperty + && kind != UriResourceKind.count + && kind != UriResourceKind.value) { throw new ODataApplicationException("Invalid resource type.", - HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); + HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); } } @@ -219,6 +218,7 @@ public class TechnicalProcessor implements EntitySetProcessor, EntityProcessor, final EdmEntitySet entitySet, final boolean isSingleEntity, final ExpandOption expand, final SelectOption select, final List keys, final String propertyPath) throws SerializerException { + return ContextURL.with().entitySet(entitySet) .selectList(serializer.buildContextURLSelectList(entitySet, expand, select)) .suffix(isSingleEntity && propertyPath == null ? Suffix.ENTITY : null) @@ -226,7 +226,6 @@ public class TechnicalProcessor implements EntitySetProcessor, EntityProcessor, .navOrPropertyPath(propertyPath) .build(); } - @Override public void readProperty(final ODataRequest request, ODataResponse response, final UriInfo uriInfo, final ContentType contentType) throws ODataApplicationException, SerializerException { @@ -334,33 +333,4 @@ public class TechnicalProcessor implements EntitySetProcessor, EntityProcessor, } } } - - @Override - public void executeBatch(BatchOperation operation, ODataRequest requst, ODataResponse response) { - try { - final List parts = operation.parseBatchRequest(requst.getBody()); - final List responseParts = new ArrayList(); - - for(BatchRequestPart part : parts) { - responseParts.add(operation.handleBatchRequest(part)); - } - - operation.writeResponseParts(responseParts, response); - } catch (BatchException e) { - throw new ODataRuntimeException(e); - } catch (IOException e) { - throw new ODataRuntimeException(e); - } - } - - @Override - public List executeChangeSet(BatchOperation operation, List requests) { - List responses = new ArrayList(); - - for(ODataRequest request : requests) { - responses.add(operation.handleODataRequest(request)); - } - - return responses; - } }