[OLINGO-832] First idea of PoC

This commit is contained in:
Michael Bolz 2015-12-08 14:49:26 +01:00
parent d88913fc6b
commit f4ad8892ad
6 changed files with 500 additions and 7 deletions

View File

@ -0,0 +1,28 @@
/*
* 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.commons.api.data;
/**
* Data representation for a collection of single entities.
*/
public abstract class EntityStreamCollection extends EntityCollection {
public abstract boolean hasNext();
public abstract Entity nextEntity();
}

View File

@ -49,6 +49,7 @@ import org.apache.olingo.server.core.etag.ETagHelperImpl;
import org.apache.olingo.server.core.prefer.PreferencesImpl;
import org.apache.olingo.server.core.serializer.FixedFormatSerializerImpl;
import org.apache.olingo.server.core.serializer.json.ODataJsonSerializer;
import org.apache.olingo.server.core.serializer.json.ODataJsonStreamSerializer;
import org.apache.olingo.server.core.serializer.xml.ODataXmlSerializer;
import org.apache.olingo.server.core.uri.UriHelperImpl;
@ -63,7 +64,8 @@ public class ODataImpl extends OData {
if (metadata == null
|| ContentType.VALUE_ODATA_METADATA_MINIMAL.equals(metadata)
|| ContentType.VALUE_ODATA_METADATA_NONE.equals(metadata)) {
serializer = new ODataJsonSerializer(contentType);
// serializer = new ODataJsonSerializer(contentType);
serializer = new ODataJsonStreamSerializer(contentType);
}
} else if (contentType.isCompatible(ContentType.APPLICATION_XML)
|| contentType.isCompatible(ContentType.APPLICATION_ATOM_XML)) {

View File

@ -0,0 +1,166 @@
/*
* 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.serializer;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.EntityStreamCollection;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.server.api.ServiceMetadata;
import org.apache.olingo.server.api.serializer.EntitySerializerOptions;
import org.apache.olingo.server.api.serializer.SerializerException;
import org.apache.olingo.server.api.serializer.SerializerResult;
import org.apache.olingo.server.core.serializer.json.ODataJsonStreamSerializer;
import org.apache.olingo.server.core.serializer.utils.CircleStreamBuffer;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class StreamSerializerResult implements SerializerResult {
private InputStream content;
private static class StreamInputStream extends InputStream {
private String head;
private String tail;
private int tailCount = 0;
private int headCount = 0;
private int entityCount = 0;
private InputStream inputStream = null;
private ODataJsonStreamSerializer jsonSerializer;
private EntityStreamCollection coll;
private ServiceMetadata metadata;
private EdmEntityType entityType;
private EntitySerializerOptions options;
public StreamInputStream(EntityStreamCollection coll, EdmEntityType entityType, String head,
ODataJsonStreamSerializer jsonSerializer, ServiceMetadata metadata,
EntitySerializerOptions options, String tail) {
this.coll = coll;
this.entityType = entityType;
this.head = head;
this.jsonSerializer = jsonSerializer;
this.metadata = metadata;
this.options = options;
this.tail = tail;
}
@Override
public int read() throws IOException {
if (headCount < head.length()) {
return head.charAt(headCount++);
}
if (inputStream == null && coll.hasNext()) {
try {
inputStream = serEntity(coll.nextEntity());
entityCount++;
if (entityCount > 1) {
return (int) ',';
}
} catch (SerializerException e) {
inputStream = null;
return read();
}
}
if (inputStream != null) {
int read = inputStream.read();
if (read == -1) {
inputStream = null;
return read();
}
return read;
}
if (tailCount < tail.length()) {
return tail.charAt(tailCount++);
}
return -1;
}
private InputStream serEntity(Entity entity) throws SerializerException {
try {
CircleStreamBuffer buffer = new CircleStreamBuffer();
OutputStream outputStream = buffer.getOutputStream();
JsonGenerator json = new JsonFactory().createGenerator(outputStream);
jsonSerializer.writeEntity(metadata, entityType, entity, null,
options == null ? null : options.getExpand(),
options == null ? null : options.getSelect(),
options != null && options.getWriteOnlyReferences(),
json);
json.close();
outputStream.close();
return buffer.getInputStream();
} catch (final IOException e) {
return new ByteArrayInputStream(("ERROR" + e.getMessage()).getBytes());
// } catch (SerializerException e) {
// return new ByteArrayInputStream(("ERROR" + e.getMessage()).getBytes());
}
}
}
@Override
public InputStream getContent() {
return content;
}
private StreamSerializerResult(InputStream content) {
this.content = content;
}
public static SerializerResultBuilder with(EntityStreamCollection coll, EdmEntityType entityType,
ODataJsonStreamSerializer jsonSerializer, ServiceMetadata metadata, EntitySerializerOptions options) {
return new SerializerResultBuilder(coll, entityType, jsonSerializer, metadata, options);
}
public static class SerializerResultBuilder {
private ODataJsonStreamSerializer jsonSerializer;
private EntityStreamCollection coll;
private ServiceMetadata metadata;
private EdmEntityType entityType;
private EntitySerializerOptions options;
private String head;
private String tail;
public SerializerResultBuilder(EntityStreamCollection coll, EdmEntityType entityType,
ODataJsonStreamSerializer jsonSerializer, ServiceMetadata metadata, EntitySerializerOptions options) {
this.coll = coll;
this.entityType = entityType;
this.jsonSerializer = jsonSerializer;
this.metadata = metadata;
this.options = options;
}
public SerializerResultBuilder addHead(String head) {
this.head = head;
return this;
}
public SerializerResultBuilder addTail(String tail) {
this.tail = tail;
return this;
}
public SerializerResult build() {
InputStream input = new StreamInputStream(coll, entityType, head, jsonSerializer, metadata, options, tail);
return new StreamSerializerResult(input);
}
}
}

View File

@ -203,7 +203,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
}
}
private ContextURL checkContextURL(final ContextURL contextURL) throws SerializerException {
ContextURL checkContextURL(final ContextURL contextURL) throws SerializerException {
if (isODataMetadataNone) {
return null;
} else if (contextURL == null) {
@ -767,13 +767,13 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
}
private void writeContextURL(final ContextURL contextURL, JsonGenerator json) throws IOException {
void writeContextURL(final ContextURL contextURL, JsonGenerator json) throws IOException {
if (!isODataMetadataNone && contextURL != null) {
json.writeStringField(Constants.JSON_CONTEXT, ContextURLBuilder.create(contextURL).toASCIIString());
}
}
private void writeMetadataETag(final ServiceMetadata metadata, JsonGenerator json) throws IOException {
void writeMetadataETag(final ServiceMetadata metadata, JsonGenerator json) throws IOException {
if (!isODataMetadataNone
&& metadata != null
&& metadata.getServiceMetadataETagSupport() != null
@ -783,7 +783,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
}
}
private void writeCount(final EntityCollection entityCollection, JsonGenerator json) throws IOException {
void writeCount(final EntityCollection entityCollection, JsonGenerator json) throws IOException {
if (entityCollection.getCount() != null) {
if (isIEEE754Compatible) {
json.writeStringField(Constants.JSON_COUNT, entityCollection.getCount().toString());
@ -793,7 +793,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
}
}
private void writeNextLink(final EntityCollection entitySet, JsonGenerator json) throws IOException {
void writeNextLink(final EntityCollection entitySet, JsonGenerator json) throws IOException {
if (entitySet.getNext() != null) {
json.writeStringField(Constants.JSON_NEXT_LINK, entitySet.getNext().toASCIIString());
}

View File

@ -0,0 +1,183 @@
/*
* 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.serializer.json;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import org.apache.olingo.commons.api.Constants;
import org.apache.olingo.commons.api.data.ComplexValue;
import org.apache.olingo.commons.api.data.ContextURL;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.EntityCollection;
import org.apache.olingo.commons.api.data.EntityStreamCollection;
import org.apache.olingo.commons.api.data.Link;
import org.apache.olingo.commons.api.data.Linked;
import org.apache.olingo.commons.api.data.Property;
import org.apache.olingo.commons.api.edm.EdmComplexType;
import org.apache.olingo.commons.api.edm.EdmEntitySet;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
import org.apache.olingo.commons.api.edm.EdmProperty;
import org.apache.olingo.commons.api.edm.EdmStructuredType;
import org.apache.olingo.commons.api.edm.EdmType;
import org.apache.olingo.commons.api.edm.FullQualifiedName;
import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
import org.apache.olingo.server.api.ODataServerError;
import org.apache.olingo.server.api.ServiceMetadata;
import org.apache.olingo.server.api.serializer.ComplexSerializerOptions;
import org.apache.olingo.server.api.serializer.EntityCollectionSerializerOptions;
import org.apache.olingo.server.api.serializer.EntitySerializerOptions;
import org.apache.olingo.server.api.serializer.PrimitiveSerializerOptions;
import org.apache.olingo.server.api.serializer.ReferenceCollectionSerializerOptions;
import org.apache.olingo.server.api.serializer.ReferenceSerializerOptions;
import org.apache.olingo.server.api.serializer.SerializerException;
import org.apache.olingo.server.api.serializer.SerializerResult;
import org.apache.olingo.server.api.uri.UriHelper;
import org.apache.olingo.server.api.uri.queryoption.ExpandItem;
import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
import org.apache.olingo.server.api.uri.queryoption.SelectOption;
import org.apache.olingo.server.core.serializer.AbstractODataSerializer;
import org.apache.olingo.server.core.serializer.SerializerResultImpl;
import org.apache.olingo.server.core.serializer.StreamSerializerResult;
import org.apache.olingo.server.core.serializer.utils.CircleStreamBuffer;
import org.apache.olingo.server.core.serializer.utils.ContentTypeHelper;
import org.apache.olingo.server.core.serializer.utils.ContextURLBuilder;
import org.apache.olingo.server.core.serializer.utils.ExpandSelectHelper;
import org.apache.olingo.server.core.uri.UriHelperImpl;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class ODataJsonStreamSerializer extends ODataJsonSerializer {
private final ODataJsonSerializer serializer;
public ODataJsonStreamSerializer(final ContentType contentType) {
super(contentType);
this.serializer = new ODataJsonSerializer(contentType);
}
@Override
public SerializerResult entityCollection(final ServiceMetadata metadata,
final EdmEntityType entityType, final EntityCollection entitySet,
final EntityCollectionSerializerOptions options) throws SerializerException {
EntityStreamCollection coll;
if(entitySet instanceof EntityStreamCollection) {
coll = (EntityStreamCollection) entitySet;
} else {
return serializer.entityCollection(metadata, entityType, entitySet, options);
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
SerializerException cachedException = null;
try {
JsonGenerator json = new JsonFactory().createGenerator(outputStream);
json.writeStartObject();
final ContextURL contextURL = serializer.checkContextURL(options == null ? null : options.getContextURL());
serializer.writeContextURL(contextURL, json);
serializer.writeMetadataETag(metadata, json);
if (options != null && options.getCount() != null && options.getCount().getValue()) {
serializer.writeCount(entitySet, json);
}
json.writeFieldName(Constants.VALUE);
json.writeStartArray();
json.close();
outputStream.close();
String temp = new String(outputStream.toByteArray(), Charset.forName("UTF-8"));
String head = temp.substring(0, temp.length()-2);
// if (options == null) {
// writeEntitySet(metadata, entityType, entitySet, null, null, false, json);
// } else {
// writeEntitySet(metadata, entityType, entitySet,
// options.getExpand(), options.getSelect(), options.getWriteOnlyReferences(), json);
// }
outputStream = new ByteArrayOutputStream();
outputStream.write(']');
outputStream.write('}');
outputStream.close();
String tail = new String(outputStream.toByteArray(), Charset.forName("UTF-8"));
EntitySerializerOptions.Builder opt = EntitySerializerOptions.with();
if(options != null) {
opt.expand(options.getExpand()).select(options
.getSelect()).writeOnlyReferences(options.getWriteOnlyReferences());
}
return StreamSerializerResult.with(coll, entityType, this, metadata, opt.build())
.addHead(head).addTail(tail).build();
} catch (final IOException e) {
cachedException =
new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION);
throw cachedException;
} finally {
closeCircleStreamBufferOutput(outputStream, cachedException);
}
}
@Override
public void writeEntity(final ServiceMetadata metadata, final EdmEntityType entityType,
final Entity entity, final ContextURL contextURL, final ExpandOption expand,
final SelectOption select, final boolean onlyReference, final JsonGenerator json)
throws IOException, SerializerException {
serializer.writeEntity(metadata, entityType, entity, contextURL, expand, select, onlyReference, json);
}
// @Override
// public SerializerResult entity(final ServiceMetadata metadata, final EdmEntityType entityType,
// final Entity entity, final EntitySerializerOptions options) throws SerializerException {
// return serializer.entity(metadata, entityType, entity, options);
// }
// protected void writeEntitySet(final ServiceMetadata metadata, final EdmEntityType entityType,
// final EntityCollection entitySet, final ExpandOption expand, final SelectOption select,
// final boolean onlyReference, final JsonGenerator json) throws IOException,
// SerializerException {
//
// json.writeStartArray();
// json.writeEndArray();
//
// json.writeStartArray();
// for (final Entity entity : entitySet.getEntities()) {
// if (onlyReference) {
// json.writeStartObject();
// json.writeStringField(Constants.JSON_ID, entity.getId().toASCIIString());
// json.writeEndObject();
// } else {
// serializer.writeEntity(metadata, entityType, entity, null, expand, select, false, json);
// }
// }
// json.writeEndArray();
// }
}

View File

@ -18,13 +18,20 @@
*/
package org.apache.olingo.server.tecsvc.processor;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import org.apache.olingo.commons.api.data.ContextURL;
import org.apache.olingo.commons.api.data.ContextURL.Builder;
import org.apache.olingo.commons.api.data.ContextURL.Suffix;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.EntityCollection;
import org.apache.olingo.commons.api.data.EntityStreamCollection;
import org.apache.olingo.commons.api.data.Property;
import org.apache.olingo.commons.api.data.ValueType;
import org.apache.olingo.commons.api.edm.EdmEntitySet;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.format.ContentType;
@ -520,11 +527,17 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
}
// Serialize
// final SerializerResult serializerResult = (isReference) ?
// serializeReferenceCollection(entitySetSerialization, edmEntitySet, requestedContentType, countOption) :
// serializeEntityCollection(request, entitySetSerialization, edmEntitySet, edmEntityType, requestedContentType,
// expand, select, countOption, id);
final SerializerResult serializerResult = (isReference) ?
serializeReferenceCollection(entitySetSerialization, edmEntitySet, requestedContentType, countOption) :
serializeEntityCollection(request, entitySetSerialization, edmEntitySet, edmEntityType, requestedContentType,
serializeEntityStreamCollectionFixed(request,
entitySetSerialization, edmEntitySet, edmEntityType, requestedContentType,
expand, select, countOption, id);
response.setContent(serializerResult.getContent());
response.setStatusCode(HttpStatusCode.OK.getStatusCode());
response.setHeader(HttpHeader.CONTENT_TYPE, requestedContentType.toContentTypeString());
if (pageSize != null) {
@ -533,6 +546,107 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
}
}
// just for demonstration
private SerializerResult serializeEntityStreamCollectionFixed(final ODataRequest request,
final EntityCollection entityCollection, final EdmEntitySet edmEntitySet,
final EdmEntityType edmEntityType,
final ContentType requestedFormat, final ExpandOption expand, final SelectOption select,
final CountOption countOption, final String id) throws ODataLibraryException {
EntityStreamCollection streamCollection = new EntityStreamCollection() {
Iterator<Entity> entityIterator = entityCollection.getEntities().iterator();
@Override
public boolean hasNext() {
return entityIterator.hasNext();
}
@Override
public Entity nextEntity() {
Entity next = entityIterator.next();
replacePrimitiveProperty(next, "PropertyString", generateData(28192));
// next.getProperties().remove(1);
// next.addProperty(new Property(null, "PropertyString", ValueType.PRIMITIVE, generateData(28192)));
try {
TimeUnit.MILLISECONDS.sleep(2500);
} catch (InterruptedException e) { }
return next;
}
@Override
public List<Entity> getEntities() {
return entityCollection.getEntities();
}
private void replacePrimitiveProperty(Entity entity, String name, Object data) {
List<Property> properties = entity.getProperties();
int pos = 0;
for (Property property : properties) {
if(name.equals(property.getName())) {
properties.remove(pos);
entity.addProperty(new Property(null, name, ValueType.PRIMITIVE, data));
break;
}
pos++;
}
}
private String generateData(final int len) {
Random random = new Random();
StringBuilder b = new StringBuilder(len);
for (int j = 0; j < len; j++) {
final char c = (char) ('A' + random.nextInt('Z' - 'A' + 1));
b.append(c);
}
return b.toString();
}
};
return odata.createSerializer(requestedFormat).entityCollection(
serviceMetadata,
edmEntityType,
streamCollection,
EntityCollectionSerializerOptions.with()
.contextURL(isODataMetadataNone(requestedFormat) ? null :
getContextUrl(request.getRawODataPath(), edmEntitySet, edmEntityType, false, expand, select))
.count(countOption)
.expand(expand).select(select)
.id(id)
.build());
}
private SerializerResult serializeEntityStreamCollection(final ODataRequest request,
final EntityCollection entityCollection, final EdmEntitySet edmEntitySet,
final EdmEntityType edmEntityType,
final ContentType requestedFormat, final ExpandOption expand, final SelectOption select,
final CountOption countOption, final String id) throws ODataLibraryException {
EntityStreamCollection streamCollection = new EntityStreamCollection() {
Iterator<Entity> test = entityCollection.getEntities().iterator();
@Override
public boolean hasNext() {
return test.hasNext();
}
@Override
public Entity nextEntity() {
return test.next();
}
};
return odata.createSerializer(requestedFormat).entityCollection(
serviceMetadata,
edmEntityType,
streamCollection,
EntityCollectionSerializerOptions.with()
.contextURL(isODataMetadataNone(requestedFormat) ? null :
getContextUrl(request.getRawODataPath(), edmEntitySet, edmEntityType, false, expand, select))
.count(countOption)
.expand(expand).select(select)
.id(id)
.build());
}
private SerializerResult serializeEntityCollection(final ODataRequest request, final EntityCollection
entityCollection, final EdmEntitySet edmEntitySet, final EdmEntityType edmEntityType,
final ContentType requestedFormat, final ExpandOption expand, final SelectOption select,