Changeset Sorter

Signed-off-by: Christian Amend <chrisam@apache.org>
This commit is contained in:
Christian Holzer 2014-10-28 18:18:21 +01:00 committed by Christian Amend
parent 15bd15267a
commit ee2451cbd0
9 changed files with 279 additions and 62 deletions

View File

@ -27,9 +27,9 @@ import org.apache.olingo.server.api.ODataResponse;
public interface BatchOperation { public interface BatchOperation {
public List<BatchRequestPart> parseBatchRequest(InputStream in) throws BatchException; public List<BatchRequestPart> 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<ODataResponsePart> batchResponses, ODataResponse response) throws BatchException, public void writeResponseParts(List<ODataResponsePart> batchResponses, ODataResponse response) throws BatchException,
IOException; IOException;

View File

@ -23,9 +23,11 @@ import java.util.List;
import org.apache.olingo.server.api.ODataRequest; import org.apache.olingo.server.api.ODataRequest;
import org.apache.olingo.server.api.ODataResponse; import org.apache.olingo.server.api.ODataResponse;
import org.apache.olingo.server.api.batch.BatchOperation; import org.apache.olingo.server.api.batch.BatchOperation;
import org.apache.olingo.server.api.batch.BatchRequestPart;
public interface BatchProcessor extends Processor { public interface BatchProcessor extends Processor {
void executeBatch(BatchOperation operation, ODataRequest requst, ODataResponse response); void executeBatch(BatchOperation operation, ODataRequest requests, ODataResponse response);
List<ODataResponse> executeChangeSet(BatchOperation operation, List<ODataRequest> requests); List<ODataResponse> executeChangeSet(BatchOperation operation, List<ODataRequest> requests,
BatchRequestPart requestPart);
} }

View File

@ -53,12 +53,12 @@ public class BatchOperationImpl implements BatchOperation {
} }
@Override @Override
public ODataResponse handleODataRequest(ODataRequest request) { public ODataResponse handleODataRequest(ODataRequest request, BatchRequestPart requestPart) {
return partHandler.handleODataRequest(request); return partHandler.handleODataRequest(request, requestPart);
} }
@Override @Override
public ODataResponsePart handleBatchRequest(BatchRequestPart request) { public ODataResponsePart handleBatchRequest(BatchRequestPart request) throws BatchException {
return partHandler.handleBatchRequest(request); return partHandler.handleBatchRequest(request);
} }

View File

@ -19,11 +19,14 @@
package org.apache.olingo.server.core.batch.handler; package org.apache.olingo.server.core.batch.handler;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import org.apache.olingo.commons.api.http.HttpHeader; import org.apache.olingo.commons.api.http.HttpHeader;
import org.apache.olingo.server.api.ODataRequest; import org.apache.olingo.server.api.ODataRequest;
import org.apache.olingo.server.api.ODataResponse; 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.BatchOperation;
import org.apache.olingo.server.api.batch.BatchRequestPart; import org.apache.olingo.server.api.batch.BatchRequestPart;
import org.apache.olingo.server.api.batch.ODataResponsePart; import org.apache.olingo.server.api.batch.ODataResponsePart;
@ -36,6 +39,7 @@ public class BatchPartHandler {
private ODataHandler oDataHandler; private ODataHandler oDataHandler;
private BatchProcessor batchProcessor; private BatchProcessor batchProcessor;
private BatchOperation batchOperation; private BatchOperation batchOperation;
private Map<BatchRequestPart, UriMapping> uriMapping = new HashMap<BatchRequestPart, UriMapping>();
public BatchPartHandler(final ODataHandler oDataHandler, final BatchProcessor processor, public BatchPartHandler(final ODataHandler oDataHandler, final BatchProcessor processor,
final BatchOperation batchOperation) { final BatchOperation batchOperation) {
@ -44,23 +48,62 @@ public class BatchPartHandler {
this.batchOperation = batchOperation; this.batchOperation = batchOperation;
} }
public ODataResponse handleODataRequest(ODataRequest request) { public ODataResponse handleODataRequest(ODataRequest request, BatchRequestPart requestPart) {
final ODataResponse response = oDataHandler.process(request); final ODataResponse response;
response.setHeader(BatchParserCommon.HTTP_CONTENT_ID, request.getHeader(BatchParserCommon.HTTP_CONTENT_ID));
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; return response;
} }
public ODataResponsePart handleBatchRequest(BatchRequestPart request) { private UriMapping getUriMappingOrDefault(final BatchRequestPart requestPart) {
final List<ODataResponse> responses = new ArrayList<ODataResponse>(); 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()) { if (request.isChangeSet()) {
responses.addAll(batchProcessor.executeChangeSet(batchOperation, request.getRequests())); return handleChangeSet(request);
return new ODataResponsePartImpl(responses, true);
} else { } else {
responses.add(handleODataRequest(request.getRequests().get(0))); final List<ODataResponse> responses = new ArrayList<ODataResponse>();
responses.add(handleODataRequest(request.getRequests().get(0), request));
return new ODataResponsePartImpl(responses, false); return new ODataResponsePartImpl(responses, false);
} }
} }
private ODataResponsePart handleChangeSet(BatchRequestPart request) throws BatchException {
final List<ODataResponse> responses = new ArrayList<ODataResponse>();
final BatchChangeSetSorter sorter = new BatchChangeSetSorter(request.getRequests());
responses.addAll(batchProcessor.executeChangeSet(batchOperation, sorter.getOrderdRequests(), request));
return new ODataResponsePartImpl(responses, true);
}
} }

View File

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

View File

@ -25,6 +25,10 @@ import java.util.List;
import org.apache.olingo.server.api.ODataRequest; import org.apache.olingo.server.api.ODataRequest;
import org.apache.olingo.server.api.batch.BatchRequestPart; import org.apache.olingo.server.api.batch.BatchRequestPart;
/**
* Has to be immutable!
*
*/
public class BatchRequestPartImpl implements BatchRequestPart { public class BatchRequestPartImpl implements BatchRequestPart {
private List<ODataRequest> requests = new ArrayList<ODataRequest>(); private List<ODataRequest> requests = new ArrayList<ODataRequest>();

View File

@ -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<ODataRequest> changeSet = new ArrayList<ODataRequest>();
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<ODataRequest> 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<ODataRequest> changeSet = new ArrayList<ODataRequest>();
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<ODataRequest> 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<ODataRequest> changeSet = new ArrayList<ODataRequest>();
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<ODataRequest> sortedChangeSet = sorter.getOrderdRequests();
}
@Test
public void testStringAsContentId() throws BatchException {
final List<ODataRequest> changeSet = new ArrayList<ODataRequest>();
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<ODataRequest> 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;
}
}

View File

@ -21,7 +21,6 @@ package org.apache.olingo.server.tecsvc.processor;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -74,7 +73,7 @@ import org.apache.olingo.server.tecsvc.data.DataProvider;
/** /**
* Technical Processor which provides currently implemented processor functionality. * 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 OData odata;
private DataProvider dataProvider; private DataProvider dataProvider;
@ -198,7 +197,7 @@ public class TechnicalProcessor implements EntitySetProcessor, EntityProcessor,
List<UriResource> subResPaths = resourcePaths.subList(1, resourcePaths.size()); List<UriResource> subResPaths = resourcePaths.subList(1, resourcePaths.size());
for (UriResource subResPath : subResPaths) { for (UriResource subResPath : subResPaths) {
UriResourceKind kind = subResPath.getKind(); UriResourceKind kind = subResPath.getKind();
if(kind != UriResourceKind.primitiveProperty if (kind != UriResourceKind.primitiveProperty
&& kind != UriResourceKind.complexProperty && kind != UriResourceKind.complexProperty
&& kind != UriResourceKind.count && kind != UriResourceKind.count
&& kind != UriResourceKind.value) { && kind != UriResourceKind.value) {
@ -219,6 +218,7 @@ public class TechnicalProcessor implements EntitySetProcessor, EntityProcessor,
final EdmEntitySet entitySet, final boolean isSingleEntity, final EdmEntitySet entitySet, final boolean isSingleEntity,
final ExpandOption expand, final SelectOption select, final ExpandOption expand, final SelectOption select,
final List<UriParameter> keys, final String propertyPath) throws SerializerException { final List<UriParameter> keys, final String propertyPath) throws SerializerException {
return ContextURL.with().entitySet(entitySet) return ContextURL.with().entitySet(entitySet)
.selectList(serializer.buildContextURLSelectList(entitySet, expand, select)) .selectList(serializer.buildContextURLSelectList(entitySet, expand, select))
.suffix(isSingleEntity && propertyPath == null ? Suffix.ENTITY : null) .suffix(isSingleEntity && propertyPath == null ? Suffix.ENTITY : null)
@ -226,7 +226,6 @@ public class TechnicalProcessor implements EntitySetProcessor, EntityProcessor,
.navOrPropertyPath(propertyPath) .navOrPropertyPath(propertyPath)
.build(); .build();
} }
@Override @Override
public void readProperty(final ODataRequest request, ODataResponse response, final UriInfo uriInfo, public void readProperty(final ODataRequest request, ODataResponse response, final UriInfo uriInfo,
final ContentType contentType) throws ODataApplicationException, SerializerException { 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<BatchRequestPart> parts = operation.parseBatchRequest(requst.getBody());
final List<ODataResponsePart> responseParts = new ArrayList<ODataResponsePart>();
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<ODataResponse> executeChangeSet(BatchOperation operation, List<ODataRequest> requests) {
List<ODataResponse> responses = new ArrayList<ODataResponse>();
for(ODataRequest request : requests) {
responses.add(operation.handleODataRequest(request));
}
return responses;
}
} }