diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java index 48d75ded4..af092cf2b 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java @@ -162,10 +162,10 @@ public class ODataHandler { handleResourceDispatching(request, response); break; case batch: - BatchProcessor bp = selectProcessor(BatchProcessor.class); + final BatchProcessor bp = selectProcessor(BatchProcessor.class); + final BatchHandler handler = new BatchHandler(this, bp); - final BatchHandler handler = new BatchHandler(this, request, bp, true); - handler.process(response); + handler.process(request, response, true); break; default: diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/BatchChangeSetSorter.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/BatchChangeSetSorter.java index f8ac65308..408159eb3 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/BatchChangeSetSorter.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/BatchChangeSetSorter.java @@ -38,7 +38,7 @@ public class BatchChangeSetSorter { final List orderdList = new ArrayList(); private static Pattern referencePattern = Pattern.compile(REG_EX_REFERENCE); - private Set knownContentId = new HashSet(); + private Set knownContentIds = new HashSet(); private Map> requestReferenceMapping = new HashMap>(); public BatchChangeSetSorter(List requests) throws BatchException { @@ -52,15 +52,19 @@ public class BatchChangeSetSorter { private List sort(final List requests) throws BatchException { extractUrlReference(requests); + // Add requests without reference (roots) final List requestsWithoutReferences = getRequestsWithoutReferences(); orderdList.addAll(requestsWithoutReferences); addRequestsToKnownContentIds(requestsWithoutReferences); + // Find all requests which can be processed (parent request has been processed) + // Compare level order boolean areRequestsProcessed = true; while (requestsToProcessAvailable() && areRequestsProcessed) { - areRequestsProcessed = processRemainingRequests(orderdList); + areRequestsProcessed = processNextLevel(); } + // Check if there are some requests which are not connected to a tree if (requestsToProcessAvailable()) { throw new BatchException("Invalid content id", MessageKeys.INVALID_CONTENT_ID, 0); } @@ -72,7 +76,7 @@ public class BatchChangeSetSorter { return requestReferenceMapping.keySet().size() != 0; } - private boolean processRemainingRequests(List orderdList) { + private boolean processNextLevel() { final List addedRequests = getRemainingRequestsWithKownContentId(); addRequestsToKnownContentIds(addedRequests); orderdList.addAll(addedRequests); @@ -83,10 +87,10 @@ public class BatchChangeSetSorter { private List getRemainingRequestsWithKownContentId() { List result = new ArrayList(); - for (String contextId : knownContentId) { - List processedRequests = requestReferenceMapping.get(contextId); - if (processedRequests != null && processedRequests.size() != 0) { - result.addAll(processedRequests); + for (String contextId : knownContentIds) { + final List requestsToProcess = requestReferenceMapping.get(contextId); + if (requestsToProcess != null && requestsToProcess.size() != 0) { + result.addAll(requestsToProcess); requestReferenceMapping.remove(contextId); } } @@ -105,7 +109,7 @@ public class BatchChangeSetSorter { for (ODataRequest request : requestsWithoutReference) { final String contentId = getContentIdFromHeader(request); if (contentId != null) { - knownContentId.add(contentId); + knownContentIds.add(contentId); } } } @@ -130,18 +134,17 @@ public class BatchChangeSetSorter { } public static String getReferenceInURI(ODataRequest request) { - Matcher matcher = referencePattern.matcher(removeFollingPathSegments(removeFirstSplash(request.getRawODataPath()))); + Matcher matcher = referencePattern.matcher(removeSlash(removeSlash(request.getRawODataPath(), true), false)); return (matcher.matches()) ? matcher.group(1) : null; } - private static String removeFirstSplash(String rawODataPath) { + private static String removeSlash(String rawODataPath, boolean first) { final int indexOfSlash = rawODataPath.indexOf("/"); - return (indexOfSlash == 0) ? rawODataPath.substring(1) : rawODataPath; - } - - private static String removeFollingPathSegments(String rawODataPath) { - final int indexOfSlash = rawODataPath.indexOf("/"); - return (indexOfSlash != -1) ? rawODataPath.substring(0, indexOfSlash) : rawODataPath; + if(first) { + return (indexOfSlash == 0) ? rawODataPath.substring(1) : rawODataPath; + } else { + return (indexOfSlash != -1) ? rawODataPath.substring(0, indexOfSlash) : rawODataPath; + } } public static void replaceContentIdReference(ODataRequest request, String contentId, String resourceUri) { diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/BatchHandler.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/BatchHandler.java index 01a149e6e..df16c9066 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/BatchHandler.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/BatchHandler.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 @@ -30,38 +30,38 @@ import org.apache.olingo.server.core.ODataHandler; import org.apache.olingo.server.core.batch.parser.BatchParserCommon; public class BatchHandler { - private final BatchOperation operation; private final BatchProcessor batchProcessor; - private ODataRequest request; + private final ODataHandler oDataHandler; + + public BatchHandler(final ODataHandler oDataHandler, final BatchProcessor batchProcessor) { - public BatchHandler(final ODataHandler oDataHandler, final ODataRequest request, final BatchProcessor batchProcessor, - final boolean isStrict) { - - this.request = request; this.batchProcessor = batchProcessor; - operation = new BatchOperationImpl(oDataHandler, request, batchProcessor, isStrict); + this.oDataHandler = oDataHandler; } - public void process(ODataResponse response) throws BatchException { - validateRequest(); + public void process(final ODataRequest request, final ODataResponse response, final boolean isStrict) + throws BatchException { + validateRequest(request); + + final BatchOperation operation = new BatchOperationImpl(oDataHandler, request, batchProcessor, isStrict); batchProcessor.executeBatch(operation, request, response); } - - private void validateRequest() throws BatchException { - validateHttpMethod(); - validateContentType(); + + private void validateRequest(final ODataRequest request) throws BatchException { + validateHttpMethod(request); + validateContentType(request); } - private void validateContentType() throws BatchException { + private void validateContentType(final ODataRequest request) throws BatchException { final String contentType = request.getHeader(HttpHeader.CONTENT_TYPE); - - if(contentType == null || !BatchParserCommon.PATTERN_MULTIPART_BOUNDARY.matcher(contentType).matches()) { + + if (contentType == null || !BatchParserCommon.PATTERN_MULTIPART_BOUNDARY.matcher(contentType).matches()) { throw new BatchException("Invalid content type", MessageKeys.INVALID_CONTENT_TYPE, 0); } } - private void validateHttpMethod() throws BatchException { - if(request.getMethod() != HttpMethod.POST) { + private void validateHttpMethod(final ODataRequest request) throws BatchException { + if (request.getMethod() != HttpMethod.POST) { throw new BatchException("Invalid HTTP method", MessageKeys.INVALID_METHOD, 0); } } 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 c678355b7..5bec30b99 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 @@ -58,7 +58,8 @@ public class BatchPartHandler { final UriMapping mapping = replaceReference(request, requestPart); response = oDataHandler.process(request); - + + // Store resource URI final String resourceUri = getODataPath(request, response); final String contentId = request.getHeader(BatchParserCommon.HTTP_CONTENT_ID); @@ -67,6 +68,7 @@ public class BatchPartHandler { response = oDataHandler.process(request); } + // Add content id to response final String contentId = request.getHeader(BatchParserCommon.HTTP_CONTENT_ID); if(contentId != null) { response.setHeader(BatchParserCommon.HTTP_CONTENT_ID, contentId); @@ -85,7 +87,7 @@ public class BatchPartHandler { resourceUri = uri.getRawODataPath(); } else { // Update, Upsert (PUT, PATCH, Delete) - // These methods still addresses a given URI, so we use the URI given by the request + // These methods still addresses a given resource, so we use the URI given by the request resourceUri = request.getRawODataPath(); } @@ -140,4 +142,15 @@ public class BatchPartHandler { return new ODataResponsePartImpl(responses, true); } + private static 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/handler/UriMapping.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/UriMapping.java deleted file mode 100644 index 012332094..000000000 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/UriMapping.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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/test/java/org/apache/olingo/server/core/batch/handler/MockedBatchHandlerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/handler/MockedBatchHandlerTest.java index ac111e0ba..d772fbc6a 100644 --- a/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/handler/MockedBatchHandlerTest.java +++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/handler/MockedBatchHandlerTest.java @@ -61,9 +61,19 @@ public class MockedBatchHandlerTest { private static final String BATCH_REQUEST_URI = "http://localhost:8080/odata/$batch"; private static final String BASE_URI = "http://localhost:8080/odata"; private static final String CRLF = "\r\n"; - private ODataHandler handler; + private ODataHandler oDataHandler; + private BatchHandler batchHandler; private int entityCounter = 1; + @Before + public void setup() { + final BatchProcessor batchProcessor = new BatchTestProcessorImpl(); + + entityCounter = 1; + oDataHandler = mock(ODataHandler.class); + batchHandler = new BatchHandler(oDataHandler, batchProcessor); + } + @Test public void test() throws BatchException, IOException { final String content = "--batch_12345" + CRLF @@ -130,9 +140,9 @@ public class MockedBatchHandlerTest { + "--batch_12345--"; final Map> header = getMimeHeader(); final ODataResponse response = new ODataResponse(); - final BatchHandler batchHandler = buildBatchHandler(content, header); + final ODataRequest request = buildODataRequest(content, header); - batchHandler.process(response); + batchHandler.process(request, response, true); BufferedReaderIncludingLineEndings reader = new BufferedReaderIncludingLineEndings(new InputStreamReader(response.getContent())); @@ -244,9 +254,9 @@ public class MockedBatchHandlerTest { + "--batch_12345--"; final Map> header = getMimeHeader(); final ODataResponse response = new ODataResponse(); - final BatchHandler batchHandler = buildBatchHandler(content, header); + final ODataRequest request = buildODataRequest(content, header); - batchHandler.process(response); + batchHandler.process(request, response, true); BufferedReaderIncludingLineEndings reader = new BufferedReaderIncludingLineEndings(new InputStreamReader(response.getContent())); @@ -365,9 +375,9 @@ public class MockedBatchHandlerTest { final Map> header = getMimeHeader(); final ODataResponse response = new ODataResponse(); - final BatchHandler batchHandler = buildBatchHandler(content, header); + final ODataRequest request = buildODataRequest(content, header); - batchHandler.process(response); + batchHandler.process(request, response, true); BufferedReaderIncludingLineEndings reader = new BufferedReaderIncludingLineEndings(new InputStreamReader(response.getContent())); @@ -418,12 +428,6 @@ public class MockedBatchHandlerTest { assertEquals(45, line); } - @Before - public void setup() { - handler = null; - entityCounter = 1; - } - private String checkChangeSetPartHeader(final List response, int line) { assertEquals(CRLF, response.get(line++)); assertTrue(response.get(line++).contains("--changeset_")); @@ -442,7 +446,6 @@ public class MockedBatchHandlerTest { /* * Helper methods */ - private Map> getMimeHeader() { final Map> header = new HashMap>(); header.put(HttpHeader.CONTENT_TYPE, Arrays.asList(new String[] { BATCH_CONTENT_TYPE })); @@ -450,24 +453,7 @@ public class MockedBatchHandlerTest { return header; } - private BatchHandler buildBatchHandler(final String content, Map> header) throws BatchException, - UnsupportedEncodingException { - - final ODataRequest request = buildODataRequest(content, header); - final ODataHandler oDataHandler = buildODataHandler(request); - final BatchProcessor batchProcessor = new BatchProcessorImpl(); - - return new BatchHandler(oDataHandler, request, batchProcessor, true); - } - - private ODataHandler buildODataHandler(ODataRequest request) { - handler = mock(ODataHandler.class); - when(handler.process(request)).thenCallRealMethod(); - - return handler; - } - - private ODataRequest buildODataRequest(String content, Map> header) + private ODataRequest buildODataRequest(final String content, final Map> header) throws UnsupportedEncodingException { final ODataRequest request = new ODataRequest(); @@ -490,7 +476,7 @@ public class MockedBatchHandlerTest { /** * Batch processor */ - private class BatchProcessorImpl implements BatchProcessor { + private class BatchTestProcessorImpl implements BatchProcessor { @Override public void init(OData odata, ServiceMetadata serviceMetadata) {} @@ -500,8 +486,8 @@ public class MockedBatchHandlerTest { List responses = new ArrayList(); for (ODataRequest request : requests) { - // Mock the processor of the changeset requests - when(handler.process(request)).then(new Answer() { + // Mock the processor for a given requests + when(oDataHandler.process(request)).then(new Answer() { @Override public ODataResponse answer(InvocationOnMock invocation) throws Throwable { Object[] arguments = invocation.getArguments(); @@ -565,7 +551,7 @@ public class MockedBatchHandlerTest { // Entity Collection oDataPath = parts[1]; } else { - // Navigationproperty + // Navigation property final String navProperty = parts[parts.length - 1]; if (navProperty.equals("NavPropertyETTwoPrimMany")) {