Batch handler clean up
Signed-off-by: Christian Amend <chrisam@apache.org>
This commit is contained in:
parent
0bd32951b8
commit
6c7d11f4d7
|
@ -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:
|
||||
|
|
|
@ -38,7 +38,7 @@ public class BatchChangeSetSorter {
|
|||
final List<ODataRequest> orderdList = new ArrayList<ODataRequest>();
|
||||
|
||||
private static Pattern referencePattern = Pattern.compile(REG_EX_REFERENCE);
|
||||
private Set<String> knownContentId = new HashSet<String>();
|
||||
private Set<String> knownContentIds = new HashSet<String>();
|
||||
private Map<String, List<ODataRequest>> requestReferenceMapping = new HashMap<String, List<ODataRequest>>();
|
||||
|
||||
public BatchChangeSetSorter(List<ODataRequest> requests) throws BatchException {
|
||||
|
@ -52,15 +52,19 @@ public class BatchChangeSetSorter {
|
|||
private List<ODataRequest> sort(final List<ODataRequest> requests) throws BatchException {
|
||||
extractUrlReference(requests);
|
||||
|
||||
// Add requests without reference (roots)
|
||||
final List<ODataRequest> 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<ODataRequest> orderdList) {
|
||||
private boolean processNextLevel() {
|
||||
final List<ODataRequest> addedRequests = getRemainingRequestsWithKownContentId();
|
||||
addRequestsToKnownContentIds(addedRequests);
|
||||
orderdList.addAll(addedRequests);
|
||||
|
@ -83,10 +87,10 @@ public class BatchChangeSetSorter {
|
|||
private List<ODataRequest> getRemainingRequestsWithKownContentId() {
|
||||
List<ODataRequest> result = new ArrayList<ODataRequest>();
|
||||
|
||||
for (String contextId : knownContentId) {
|
||||
List<ODataRequest> processedRequests = requestReferenceMapping.get(contextId);
|
||||
if (processedRequests != null && processedRequests.size() != 0) {
|
||||
result.addAll(processedRequests);
|
||||
for (String contextId : knownContentIds) {
|
||||
final List<ODataRequest> 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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String, String> uriMapping = new HashMap<String, String>();
|
||||
|
||||
public void addMapping(final String contentId, final String uri) {
|
||||
uriMapping.put(contentId, uri);
|
||||
}
|
||||
|
||||
public String getUri(final String contentId) {
|
||||
return uriMapping.get(contentId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String, String> uriMapping = new HashMap<String, String>();
|
||||
|
||||
public void addMapping(final String contentId, final String uri) {
|
||||
uriMapping.put(contentId, uri);
|
||||
}
|
||||
|
||||
public String getUri(final String contentId) {
|
||||
return uriMapping.get(contentId);
|
||||
}
|
||||
}
|
|
@ -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<String, List<String>> 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<String, List<String>> 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<String, List<String>> 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<String> 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<String, List<String>> getMimeHeader() {
|
||||
final Map<String, List<String>> header = new HashMap<String, List<String>>();
|
||||
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<String, List<String>> 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<String, List<String>> header)
|
||||
private ODataRequest buildODataRequest(final String content, final Map<String, List<String>> 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<ODataResponse> responses = new ArrayList<ODataResponse>();
|
||||
|
||||
for (ODataRequest request : requests) {
|
||||
// Mock the processor of the changeset requests
|
||||
when(handler.process(request)).then(new Answer<ODataResponse>() {
|
||||
// Mock the processor for a given requests
|
||||
when(oDataHandler.process(request)).then(new Answer<ODataResponse>() {
|
||||
@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")) {
|
||||
|
|
Loading…
Reference in New Issue