[OLINGO-832] Merge branch 'OLINGO-832_StreamSerializerPoC'

This commit is contained in:
Michael Bolz 2016-02-22 08:03:01 +01:00
commit 47bc730a89
24 changed files with 1073 additions and 59 deletions

View File

@ -0,0 +1,66 @@
/*
* 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.fit.tecsvc.http;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.net.HttpURLConnection;
import java.net.URL;
import org.apache.commons.io.IOUtils;
import org.apache.olingo.client.api.ODataClient;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.commons.api.http.HttpHeader;
import org.apache.olingo.commons.api.http.HttpMethod;
import org.apache.olingo.commons.api.http.HttpStatusCode;
import org.apache.olingo.fit.AbstractBaseTestITCase;
import org.apache.olingo.fit.tecsvc.TecSvcConst;
import org.junit.Test;
public class BasicStreamITCase extends AbstractBaseTestITCase {
private static final String SERVICE_URI = TecSvcConst.BASE_URI + "/";
@Test
public void streamAllPrim() throws Exception {
URL url = new URL(SERVICE_URI + "ESAllPrim?$format=json");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod(HttpMethod.GET.name());
connection.setRequestProperty("odata.streaming", "true");
connection.connect();
assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode());
assertEquals(ContentType.JSON, ContentType.create(connection.getHeaderField(HttpHeader.CONTENT_TYPE)));
final String content = IOUtils.toString(connection.getInputStream());
assertTrue(content.contains("\"PropertyString\":\"First Resource - positive values->streamed\""));
assertTrue(content.contains("\"PropertyString\":\"Second Resource - negative values->streamed\""));
assertTrue(content.contains("\"PropertyString\":\"->streamed\""));
}
@Override
protected ODataClient getClient() {
return null;
}
}

View File

@ -525,7 +525,7 @@ public class AtomSerializer extends AbstractAtomDealer implements ODataSerialize
common(writer, entitySet); common(writer, entitySet);
for (Entity entity : entitySet.getEntities()) { for (Entity entity : entitySet) {
if (entity.getType() == null && entity.getProperties().isEmpty()) { if (entity.getType() == null && entity.getProperties().isEmpty()) {
entityRef(writer, entity); entityRef(writer, entity);
} else { } else {

View File

@ -0,0 +1,30 @@
/*
* 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;
import java.net.URI;
import java.util.Iterator;
public abstract class AbstractEntityCollection extends AbstractODataObject implements Iterable<Entity> {
public abstract Integer getCount();
public abstract URI getNext();
public abstract URI getDeltaLink();
}

View File

@ -20,12 +20,13 @@ package org.apache.olingo.commons.api.data;
import java.net.URI; import java.net.URI;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import java.util.List;
/** /**
* Data representation for a collection of single entities. * Data representation for a collection of single entities.
*/ */
public class EntityCollection extends AbstractODataObject { public class EntityCollection extends AbstractEntityCollection {
private final List<Entity> entities = new ArrayList<Entity>(); private final List<Entity> entities = new ArrayList<Entity>();
private Integer count; private Integer count;
@ -46,6 +47,7 @@ public class EntityCollection extends AbstractODataObject {
* *
* @return number of entries into the entity set. * @return number of entries into the entity set.
*/ */
@Override
public Integer getCount() { public Integer getCount() {
return count; return count;
} }
@ -73,6 +75,7 @@ public class EntityCollection extends AbstractODataObject {
* *
* @return next link if exists; null otherwise. * @return next link if exists; null otherwise.
*/ */
@Override
public URI getNext() { public URI getNext() {
return next; return next;
} }
@ -82,6 +85,7 @@ public class EntityCollection extends AbstractODataObject {
* *
* @return delta link if exists; null otherwise. * @return delta link if exists; null otherwise.
*/ */
@Override
public URI getDeltaLink() { public URI getDeltaLink() {
return deltaLink; return deltaLink;
} }
@ -95,6 +99,11 @@ public class EntityCollection extends AbstractODataObject {
this.deltaLink = deltaLink; this.deltaLink = deltaLink;
} }
@Override
public Iterator<Entity> iterator() {
return this.entities.iterator();
}
@Override @Override
public boolean equals(final Object o) { public boolean equals(final Object o) {
if (!super.equals(o)) { if (!super.equals(o)) {

View File

@ -0,0 +1,58 @@
/*
* 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;
import org.apache.olingo.commons.api.ex.ODataNotSupportedException;
import org.apache.olingo.commons.api.ex.ODataRuntimeException;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;
import java.net.URI;
import java.util.Iterator;
/**
* Data representation for a collection of single entities.
*/
public abstract class EntityIterator extends AbstractEntityCollection implements Iterator<Entity> {
public abstract boolean hasNext();
public abstract Entity next();
@Override
public void remove() {
//"Remove is not supported for iteration over Entities."
throw new ODataNotSupportedException("Entity Iterator does not support remove()");
}
@Override
public Iterator<Entity> iterator() {
return this;
}
public Integer getCount() {
throw new ODataNotSupportedException("Entity Iterator does not support getCount()");
}
public URI getNext() {
throw new ODataNotSupportedException("Entity Iterator does not support getNext()");
}
public URI getDeltaLink() {
throw new ODataNotSupportedException("Entity Iterator does not support getDeltaLink()");
}
}

View File

@ -0,0 +1,56 @@
/*
* 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.ex;
/**
* Core runtime exception for OData.
*/
public class ODataNotSupportedException extends ODataRuntimeException {
private static final long serialVersionUID = 42L;
/**
* Create with <code>message</code>.
*
* @param msg message text for exception
*/
public ODataNotSupportedException(final String msg) {
super(msg);
}
/**
* Create with <code>message</code> for and <code>cause</code> of exception.
*
* @param msg message text for exception
* @param cause cause of exception
*/
public ODataNotSupportedException(final String msg, final Exception cause) {
super(msg, cause);
}
/**
* Create with <code>cause</code> of exception.
*
* @param cause cause of exception
*/
public ODataNotSupportedException(final Exception cause) {
super(cause);
}
}

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.server.api;
import java.io.OutputStream;
import java.nio.channels.WritableByteChannel;
public interface ODataContent {
void write(WritableByteChannel channel);
void write(OutputStream stream);
}

View File

@ -18,12 +18,12 @@
*/ */
package org.apache.olingo.server.api; package org.apache.olingo.server.api;
import org.apache.olingo.commons.api.http.HttpStatusCode;
import java.io.InputStream; import java.io.InputStream;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.olingo.commons.api.http.HttpStatusCode;
/** /**
* Response object to carry OData-relevant HTTP information (status code, response headers, and content). * Response object to carry OData-relevant HTTP information (status code, response headers, and content).
*/ */
@ -131,4 +131,13 @@ public class ODataResponse {
return content; return content;
} }
private ODataContent odataContent;
public void setODataContent(ODataContent result) {
odataContent = result;
}
public ODataContent getODataContent() {
return odataContent;
}
} }

View File

@ -0,0 +1,30 @@
/*
* 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.api;
import java.io.OutputStream;
import java.nio.channels.WritableByteChannel;
/**
* The WriteContentErrorCallback is called when during the {@link ODataContent#write(OutputStream)}
* or the {@link ODataContent#write(WritableByteChannel)} an error occurs.
*/
public interface WriteContentErrorCallback {
void handleError(WriteContentErrorContext context, WritableByteChannel channel);
}

View File

@ -0,0 +1,27 @@
/*
* 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.api;
/**
* The WriteContentErrorErrorContext is the parameter for the WriteContentErrorCallback.
*/
public interface WriteContentErrorContext {
Exception getException();
ODataLibraryException getODataLibraryException();
}

View File

@ -19,6 +19,7 @@
package org.apache.olingo.server.api.serializer; package org.apache.olingo.server.api.serializer;
import org.apache.olingo.commons.api.data.ContextURL; import org.apache.olingo.commons.api.data.ContextURL;
import org.apache.olingo.server.api.WriteContentErrorCallback;
import org.apache.olingo.server.api.uri.queryoption.CountOption; import org.apache.olingo.server.api.uri.queryoption.CountOption;
import org.apache.olingo.server.api.uri.queryoption.ExpandOption; import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
import org.apache.olingo.server.api.uri.queryoption.SelectOption; import org.apache.olingo.server.api.uri.queryoption.SelectOption;
@ -32,6 +33,7 @@ public class EntityCollectionSerializerOptions {
private SelectOption select; private SelectOption select;
private boolean writeOnlyReferences; private boolean writeOnlyReferences;
private String id; private String id;
private WriteContentErrorCallback writeContentErrorCallback;
private String xml10InvalidCharReplacement; private String xml10InvalidCharReplacement;
/** Gets the {@link ContextURL}. */ /** Gets the {@link ContextURL}. */
@ -64,6 +66,17 @@ public class EntityCollectionSerializerOptions {
return id; return id;
} }
/**
* Gets the callback which is used in case of an exception during
* write of the content (in case the content will be written/streamed
* in the future)
* @return callback which is used in case of an exception during
* write of the content
*
*/
public WriteContentErrorCallback getWriteContentErrorCallback() {
return writeContentErrorCallback;
}
/** Gets the replacement string for unicode characters, that is not allowed in XML 1.0 */ /** Gets the replacement string for unicode characters, that is not allowed in XML 1.0 */
public String xml10InvalidCharReplacement() { public String xml10InvalidCharReplacement() {
return xml10InvalidCharReplacement; return xml10InvalidCharReplacement;
@ -119,6 +132,18 @@ public class EntityCollectionSerializerOptions {
return this; return this;
} }
/**
* Set the callback which is used in case of an exception during
* write of the content.
*
* @param writeContentErrorCallback the callback
* @return the builder
*/
public Builder writeContentErrorCallback(WriteContentErrorCallback writeContentErrorCallback) {
options.writeContentErrorCallback = writeContentErrorCallback;
return this;
}
/** set the replacement String for xml 1.0 unicode controlled characters that are not allowed */ /** set the replacement String for xml 1.0 unicode controlled characters that are not allowed */
public Builder xml10InvalidCharReplacement(final String replacement) { public Builder xml10InvalidCharReplacement(final String replacement) {
options.xml10InvalidCharReplacement = replacement; options.xml10InvalidCharReplacement = replacement;

View File

@ -19,7 +19,8 @@
package org.apache.olingo.server.api.serializer; package org.apache.olingo.server.api.serializer;
import org.apache.olingo.commons.api.data.Entity; import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.EntityCollection; import org.apache.olingo.commons.api.data.AbstractEntityCollection;
import org.apache.olingo.commons.api.data.EntityIterator;
import org.apache.olingo.commons.api.data.Property; import org.apache.olingo.commons.api.data.Property;
import org.apache.olingo.commons.api.edm.EdmComplexType; import org.apache.olingo.commons.api.edm.EdmComplexType;
import org.apache.olingo.commons.api.edm.EdmEntitySet; import org.apache.olingo.commons.api.edm.EdmEntitySet;
@ -61,7 +62,17 @@ public interface ODataSerializer {
* @param options options for the serializer * @param options options for the serializer
*/ */
SerializerResult entityCollection(ServiceMetadata metadata, EdmEntityType entityType, SerializerResult entityCollection(ServiceMetadata metadata, EdmEntityType entityType,
EntityCollection entitySet, EntityCollectionSerializerOptions options) throws SerializerException; AbstractEntityCollection entitySet, EntityCollectionSerializerOptions options) throws SerializerException;
/**
* Writes entity-collection data into an InputStream.
* @param metadata metadata for the service
* @param entityType the {@link EdmEntityType}
* @param entities the data of the entity set
* @param options options for the serializer
*/
SerializerStreamResult entityCollectionStreamed(ServiceMetadata metadata, EdmEntityType entityType,
EntityIterator entities, EntityCollectionSerializerOptions options) throws SerializerException;
/** /**
* Writes entity data into an InputStream. * Writes entity data into an InputStream.
@ -128,8 +139,9 @@ public interface ODataSerializer {
* @param metadata metadata for the service * @param metadata metadata for the service
* @param edmEntitySet {@link EdmEntitySet} * @param edmEntitySet {@link EdmEntitySet}
* @param entityCollection data of the entity collection * @param entityCollection data of the entity collection
* @param ReferenceCollectionSerializerOptions {@link ReferenceCollectionSerializerOptions} * @param options {@link ReferenceCollectionSerializerOptions}
*/ */
SerializerResult referenceCollection(ServiceMetadata metadata, EdmEntitySet edmEntitySet, SerializerResult referenceCollection(ServiceMetadata metadata, EdmEntitySet edmEntitySet,
EntityCollection entityCollection, ReferenceCollectionSerializerOptions options) throws SerializerException; AbstractEntityCollection entityCollection, ReferenceCollectionSerializerOptions options)
throws SerializerException;
} }

View File

@ -0,0 +1,33 @@
/*
* 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.api.serializer;
import org.apache.olingo.server.api.ODataContent;
/**
* Result type for {@link ODataSerializer} methods
* which supports stream/write in the future
*/
public interface SerializerStreamResult {
/**
* Returns the content as ODataContent
* @return content
*/
ODataContent getODataContent();
}

View File

@ -19,6 +19,7 @@
package org.apache.olingo.server.core; package org.apache.olingo.server.core;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.Channel; import java.nio.channels.Channel;
import java.nio.channels.Channels; import java.nio.channels.Channels;
@ -38,6 +39,7 @@ import org.apache.olingo.commons.api.ex.ODataRuntimeException;
import org.apache.olingo.commons.api.http.HttpHeader; import org.apache.olingo.commons.api.http.HttpHeader;
import org.apache.olingo.commons.api.http.HttpMethod; import org.apache.olingo.commons.api.http.HttpMethod;
import org.apache.olingo.server.api.OData; import org.apache.olingo.server.api.OData;
import org.apache.olingo.server.api.ODataContent;
import org.apache.olingo.server.api.ODataHttpHandler; import org.apache.olingo.server.api.ODataHttpHandler;
import org.apache.olingo.server.api.ODataLibraryException; import org.apache.olingo.server.api.ODataLibraryException;
import org.apache.olingo.server.api.ODataRequest; import org.apache.olingo.server.api.ODataRequest;
@ -149,22 +151,35 @@ public class ODataHttpHandlerImpl implements ODataHttpHandler {
} }
} }
if (odResponse.getContent() != null) { if (odResponse.getContent() != null ) {
copyContent(odResponse, response); copyContent(odResponse.getContent(), response);
} else if(odResponse.getODataContent() != null) {
writeContent(odResponse, response);
} }
} }
static void copyContent(final ODataResponse odataResponse, final HttpServletResponse servletResponse) { static void writeContent(final ODataResponse odataResponse, final HttpServletResponse servletResponse) {
ReadableByteChannel input = null; try {
ODataContent res = odataResponse.getODataContent();
res.write(Channels.newChannel(servletResponse.getOutputStream()));
} catch (IOException e) {
throw new ODataRuntimeException("Error on reading request content", e);
}
}
static void copyContent(final InputStream inputStream, final HttpServletResponse servletResponse) {
copyContent(Channels.newChannel(inputStream), servletResponse);
}
static void copyContent(final ReadableByteChannel input, final HttpServletResponse servletResponse) {
WritableByteChannel output = null; WritableByteChannel output = null;
try { try {
ByteBuffer inBuffer = ByteBuffer.allocate(COPY_BUFFER_SIZE); ByteBuffer inBuffer = ByteBuffer.allocate(COPY_BUFFER_SIZE);
output = Channels.newChannel(servletResponse.getOutputStream()); output = Channels.newChannel(servletResponse.getOutputStream());
input = Channels.newChannel(odataResponse.getContent());
while (input.read(inBuffer) > 0) { while (input.read(inBuffer) > 0) {
inBuffer.flip(); inBuffer.flip();
output.write(inBuffer); output.write(inBuffer);
inBuffer.rewind(); inBuffer.clear();
} }
} catch (IOException e) { } catch (IOException e) {
throw new ODataRuntimeException("Error on reading request content", e); throw new ODataRuntimeException("Error on reading request content", e);

View File

@ -0,0 +1,188 @@
/*
* 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;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.channels.Channels;
import java.nio.channels.WritableByteChannel;
import org.apache.olingo.commons.api.data.EntityIterator;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.ex.ODataRuntimeException;
import org.apache.olingo.server.api.ODataContent;
import org.apache.olingo.server.api.ODataLibraryException;
import org.apache.olingo.server.api.ServiceMetadata;
import org.apache.olingo.server.api.WriteContentErrorCallback;
import org.apache.olingo.server.api.WriteContentErrorContext;
import org.apache.olingo.server.api.serializer.EntityCollectionSerializerOptions;
import org.apache.olingo.server.api.serializer.ODataSerializer;
import org.apache.olingo.server.api.serializer.SerializerException;
import org.apache.olingo.server.api.serializer.SerializerStreamResult;
import org.apache.olingo.server.core.serializer.SerializerStreamResultImpl;
import org.apache.olingo.server.core.serializer.json.ODataJsonSerializer;
import org.apache.olingo.server.core.serializer.xml.ODataXmlSerializer;
public class ODataWritableContent implements ODataContent {
private StreamContent streamContent;
private static abstract class StreamContent {
protected ODataSerializer serializer;
protected EntityIterator iterator;
protected ServiceMetadata metadata;
protected EdmEntityType entityType;
protected EntityCollectionSerializerOptions options;
public StreamContent(EntityIterator iterator, EdmEntityType entityType,
ODataSerializer serializer, ServiceMetadata metadata,
EntityCollectionSerializerOptions options) {
this.iterator = iterator;
this.entityType = entityType;
this.serializer = serializer;
this.metadata = metadata;
this.options = options;
}
protected abstract void writeEntity(EntityIterator entity, OutputStream outputStream) throws SerializerException;
public void write(OutputStream out) {
try {
writeEntity(iterator, out);
} catch (SerializerException e) {
final WriteContentErrorCallback errorCallback = options.getWriteContentErrorCallback();
if(errorCallback != null) {
final ErrorContext errorContext = new ErrorContext(e);
errorCallback.handleError(errorContext, Channels.newChannel(out));
}
}
}
}
private static class StreamContentForJson extends StreamContent {
private ODataJsonSerializer jsonSerializer;
public StreamContentForJson(EntityIterator iterator, EdmEntityType entityType,
ODataJsonSerializer jsonSerializer, ServiceMetadata metadata,
EntityCollectionSerializerOptions options) {
super(iterator, entityType, jsonSerializer, metadata, options);
this.jsonSerializer = jsonSerializer;
}
protected void writeEntity(EntityIterator entity, OutputStream outputStream) throws SerializerException {
try {
jsonSerializer.entityCollectionIntoStream(metadata, entityType, entity, options, outputStream);
outputStream.flush();
} catch (final IOException e) {
throw new ODataRuntimeException("Failed entity serialization");
}
}
}
private static class StreamContentForXml extends StreamContent {
private ODataXmlSerializer xmlSerializer;
public StreamContentForXml(EntityIterator iterator, EdmEntityType entityType,
ODataXmlSerializer xmlSerializer, ServiceMetadata metadata,
EntityCollectionSerializerOptions options) {
super(iterator, entityType, xmlSerializer, metadata, options);
this.xmlSerializer = xmlSerializer;
}
protected void writeEntity(EntityIterator entity, OutputStream outputStream) throws SerializerException {
try {
xmlSerializer.entityCollectionIntoStream(metadata, entityType, entity, options, outputStream);
outputStream.flush();
} catch (final IOException e) {
throw new ODataRuntimeException("Failed entity serialization");
}
}
}
@Override
public void write(WritableByteChannel writeChannel) {
this.streamContent.write(Channels.newOutputStream(writeChannel));
}
@Override
public void write(OutputStream stream) {
write(Channels.newChannel(stream));
}
private ODataWritableContent(StreamContent streamContent) {
this.streamContent = streamContent;
}
public static ODataWritableContentBuilder with(EntityIterator iterator, EdmEntityType entityType,
ODataSerializer serializer, ServiceMetadata metadata,
EntityCollectionSerializerOptions options) {
return new ODataWritableContentBuilder(iterator, entityType, serializer, metadata, options);
}
public static class ErrorContext implements WriteContentErrorContext {
private ODataLibraryException exception;
public ErrorContext(ODataLibraryException exception) {
this.exception = exception;
}
@Override
public Exception getException() {
return exception;
}
@Override
public ODataLibraryException getODataLibraryException() {
return exception;
}
}
public static class ODataWritableContentBuilder {
private ODataSerializer serializer;
private EntityIterator entities;
private ServiceMetadata metadata;
private EdmEntityType entityType;
private EntityCollectionSerializerOptions options;
public ODataWritableContentBuilder(EntityIterator entities, EdmEntityType entityType,
ODataSerializer serializer,
ServiceMetadata metadata, EntityCollectionSerializerOptions options) {
this.entities = entities;
this.entityType = entityType;
this.serializer = serializer;
this.metadata = metadata;
this.options = options;
}
public ODataContent buildContent() {
if(serializer instanceof ODataJsonSerializer) {
StreamContent input = new StreamContentForJson(entities, entityType,
(ODataJsonSerializer) serializer, metadata, options);
return new ODataWritableContent(input);
} else if(serializer instanceof ODataXmlSerializer) {
StreamContentForXml input = new StreamContentForXml(entities, entityType,
(ODataXmlSerializer) serializer, metadata, options);
return new ODataWritableContent(input);
}
throw new ODataRuntimeException("No suitable serializer found");
}
public SerializerStreamResult build() {
return SerializerStreamResultImpl.with().content(buildContent()).build();
}
}
}

View File

@ -20,6 +20,7 @@ package org.apache.olingo.server.core.serializer;
import java.io.InputStream; import java.io.InputStream;
import org.apache.olingo.server.api.ODataContent;
import org.apache.olingo.server.api.serializer.SerializerResult; import org.apache.olingo.server.api.serializer.SerializerResult;
public class SerializerResultImpl implements SerializerResult { public class SerializerResultImpl implements SerializerResult {
@ -30,23 +31,34 @@ public class SerializerResultImpl implements SerializerResult {
return content; return content;
} }
// @Override
// public ReadableByteChannel getChannel() {
// return Channels.newChannel(getContent());
// }
//
// @Override
// public void write(WritableByteChannel channel) {
// ResultHelper.copy(Channels.newChannel(content), channel);
// }
//
// @Override
// public boolean isWriteSupported() {
// return false;
// }
public static SerializerResultBuilder with() { public static SerializerResultBuilder with() {
return new SerializerResultBuilder(); return new SerializerResultBuilder();
} }
public static class SerializerResultBuilder { public static class SerializerResultBuilder {
private InputStream content; private SerializerResultImpl result = new SerializerResultImpl();
public SerializerResultBuilder content(final InputStream input) { public SerializerResultBuilder content(final InputStream input) {
content = input; result.content = input;
return this; return this;
} }
public SerializerResult build() { public SerializerResult build() {
SerializerResultImpl result = new SerializerResultImpl();
result.content = content;
return result; return result;
} }
} }

View File

@ -0,0 +1,48 @@
/*
* 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 org.apache.olingo.server.api.ODataContent;
import org.apache.olingo.server.api.serializer.SerializerStreamResult;
public class SerializerStreamResultImpl implements SerializerStreamResult {
private ODataContent oDataContent;
@Override
public ODataContent getODataContent() {
return oDataContent;
}
public static SerializerResultBuilder with() {
return new SerializerResultBuilder();
}
public static class SerializerResultBuilder {
private SerializerStreamResultImpl result = new SerializerStreamResultImpl();
public SerializerResultBuilder content(final ODataContent content) {
result.oDataContent = content;
return this;
}
public SerializerStreamResult build() {
return result;
}
}
}

View File

@ -29,7 +29,8 @@ import org.apache.olingo.commons.api.Constants;
import org.apache.olingo.commons.api.data.ComplexValue; import org.apache.olingo.commons.api.data.ComplexValue;
import org.apache.olingo.commons.api.data.ContextURL; import org.apache.olingo.commons.api.data.ContextURL;
import org.apache.olingo.commons.api.data.Entity; import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.EntityCollection; import org.apache.olingo.commons.api.data.AbstractEntityCollection;
import org.apache.olingo.commons.api.data.EntityIterator;
import org.apache.olingo.commons.api.data.Link; import org.apache.olingo.commons.api.data.Link;
import org.apache.olingo.commons.api.data.Linked; import org.apache.olingo.commons.api.data.Linked;
import org.apache.olingo.commons.api.data.Property; import org.apache.olingo.commons.api.data.Property;
@ -57,11 +58,13 @@ import org.apache.olingo.server.api.serializer.ReferenceCollectionSerializerOpti
import org.apache.olingo.server.api.serializer.ReferenceSerializerOptions; import org.apache.olingo.server.api.serializer.ReferenceSerializerOptions;
import org.apache.olingo.server.api.serializer.SerializerException; import org.apache.olingo.server.api.serializer.SerializerException;
import org.apache.olingo.server.api.serializer.SerializerResult; import org.apache.olingo.server.api.serializer.SerializerResult;
import org.apache.olingo.server.api.serializer.SerializerStreamResult;
import org.apache.olingo.server.api.uri.UriHelper; 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.ExpandItem;
import org.apache.olingo.server.api.uri.queryoption.ExpandOption; import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
import org.apache.olingo.server.api.uri.queryoption.SelectOption; import org.apache.olingo.server.api.uri.queryoption.SelectOption;
import org.apache.olingo.server.core.serializer.AbstractODataSerializer; import org.apache.olingo.server.core.serializer.AbstractODataSerializer;
import org.apache.olingo.server.core.ODataWritableContent;
import org.apache.olingo.server.core.serializer.SerializerResultImpl; import org.apache.olingo.server.core.serializer.SerializerResultImpl;
import org.apache.olingo.server.core.serializer.utils.CircleStreamBuffer; 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.ContentTypeHelper;
@ -136,7 +139,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
@Override @Override
public SerializerResult entityCollection(final ServiceMetadata metadata, public SerializerResult entityCollection(final ServiceMetadata metadata,
final EdmEntityType entityType, final EntityCollection entitySet, final EdmEntityType entityType, final AbstractEntityCollection entitySet,
final EntityCollectionSerializerOptions options) throws SerializerException { final EntityCollectionSerializerOptions options) throws SerializerException {
OutputStream outputStream = null; OutputStream outputStream = null;
SerializerException cachedException = null; SerializerException cachedException = null;
@ -175,6 +178,50 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
} }
} }
@Override
public SerializerStreamResult entityCollectionStreamed(ServiceMetadata metadata, EdmEntityType entityType,
EntityIterator entities, EntityCollectionSerializerOptions options) throws SerializerException {
return ODataWritableContent.with(entities, entityType, this, metadata, options).build();
}
public void entityCollectionIntoStream(final ServiceMetadata metadata,
final EdmEntityType entityType, final EntityIterator entitySet,
final EntityCollectionSerializerOptions options, final OutputStream outputStream)
throws SerializerException {
SerializerException cachedException;
try {
JsonGenerator json = new JsonFactory().createGenerator(outputStream);
json.writeStartObject();
final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL());
writeContextURL(contextURL, json);
writeMetadataETag(metadata, json);
if (options != null && options.getCount() != null && options.getCount().getValue()) {
writeCount(entitySet, json);
}
json.writeFieldName(Constants.VALUE);
if (options == null) {
writeEntitySet(metadata, entityType, entitySet, null, null, false, json);
} else {
writeEntitySet(metadata, entityType, entitySet,
options.getExpand(), options.getSelect(), options.getWriteOnlyReferences(), json);
}
// next link not supported by default for streaming results
// writeNextLink(entitySet, json);
json.close();
} catch (final IOException e) {
cachedException =
new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION);
throw cachedException;
}
}
@Override @Override
public SerializerResult entity(final ServiceMetadata metadata, final EdmEntityType entityType, public SerializerResult entity(final ServiceMetadata metadata, final EdmEntityType entityType,
final Entity entity, final EntitySerializerOptions options) throws SerializerException { final Entity entity, final EntitySerializerOptions options) throws SerializerException {
@ -203,7 +250,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
} }
} }
private ContextURL checkContextURL(final ContextURL contextURL) throws SerializerException { ContextURL checkContextURL(final ContextURL contextURL) throws SerializerException {
if (isODataMetadataNone) { if (isODataMetadataNone) {
return null; return null;
} else if (contextURL == null) { } else if (contextURL == null) {
@ -213,11 +260,11 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
} }
protected void writeEntitySet(final ServiceMetadata metadata, final EdmEntityType entityType, protected void writeEntitySet(final ServiceMetadata metadata, final EdmEntityType entityType,
final EntityCollection entitySet, final ExpandOption expand, final SelectOption select, final AbstractEntityCollection entitySet, final ExpandOption expand, final SelectOption select,
final boolean onlyReference, final JsonGenerator json) throws IOException, final boolean onlyReference, final JsonGenerator json) throws IOException,
SerializerException { SerializerException {
json.writeStartArray(); json.writeStartArray();
for (final Entity entity : entitySet.getEntities()) { for (final Entity entity : entitySet) {
if (onlyReference) { if (onlyReference) {
json.writeStartObject(); json.writeStartObject();
json.writeStringField(Constants.JSON_ID, entity.getId().toASCIIString()); json.writeStringField(Constants.JSON_ID, entity.getId().toASCIIString());
@ -229,9 +276,9 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
json.writeEndArray(); json.writeEndArray();
} }
protected void writeEntity(final ServiceMetadata metadata, final EdmEntityType entityType, public void writeEntity(final ServiceMetadata metadata, final EdmEntityType entityType, final Entity entity,
final Entity entity, final ContextURL contextURL, final ExpandOption expand, final ContextURL contextURL, final ExpandOption expand, final SelectOption select, final boolean onlyReference,
final SelectOption select, final boolean onlyReference, final JsonGenerator json) final JsonGenerator json)
throws IOException, SerializerException { throws IOException, SerializerException {
json.writeStartObject(); json.writeStartObject();
if (!isODataMetadataNone) { if (!isODataMetadataNone) {
@ -736,7 +783,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
@Override @Override
public SerializerResult referenceCollection(final ServiceMetadata metadata, final EdmEntitySet edmEntitySet, public SerializerResult referenceCollection(final ServiceMetadata metadata, final EdmEntitySet edmEntitySet,
final EntityCollection entityCollection, final ReferenceCollectionSerializerOptions options) final AbstractEntityCollection entityCollection, final ReferenceCollectionSerializerOptions options)
throws SerializerException { throws SerializerException {
OutputStream outputStream = null; OutputStream outputStream = null;
SerializerException cachedException = null; SerializerException cachedException = null;
@ -755,7 +802,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
} }
json.writeArrayFieldStart(Constants.VALUE); json.writeArrayFieldStart(Constants.VALUE);
for (final Entity entity : entityCollection.getEntities()) { for (final Entity entity : entityCollection) {
json.writeStartObject(); json.writeStartObject();
json.writeStringField(Constants.JSON_ID, uriHelper.buildCanonicalURL(edmEntitySet, entity)); json.writeStringField(Constants.JSON_ID, uriHelper.buildCanonicalURL(edmEntitySet, entity));
json.writeEndObject(); json.writeEndObject();
@ -779,13 +826,13 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
} }
private void writeContextURL(final ContextURL contextURL, final JsonGenerator json) throws IOException { void writeContextURL(final ContextURL contextURL, final JsonGenerator json) throws IOException {
if (!isODataMetadataNone && contextURL != null) { if (!isODataMetadataNone && contextURL != null) {
json.writeStringField(Constants.JSON_CONTEXT, ContextURLBuilder.create(contextURL).toASCIIString()); json.writeStringField(Constants.JSON_CONTEXT, ContextURLBuilder.create(contextURL).toASCIIString());
} }
} }
private void writeMetadataETag(final ServiceMetadata metadata, final JsonGenerator json) throws IOException { void writeMetadataETag(final ServiceMetadata metadata, final JsonGenerator json) throws IOException {
if (!isODataMetadataNone if (!isODataMetadataNone
&& metadata != null && metadata != null
&& metadata.getServiceMetadataETagSupport() != null && metadata.getServiceMetadataETagSupport() != null
@ -795,7 +842,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
} }
} }
private void writeCount(final EntityCollection entityCollection, final JsonGenerator json) throws IOException { void writeCount(final AbstractEntityCollection entityCollection, final JsonGenerator json) throws IOException {
if (entityCollection.getCount() != null) { if (entityCollection.getCount() != null) {
if (isIEEE754Compatible) { if (isIEEE754Compatible) {
json.writeStringField(Constants.JSON_COUNT, entityCollection.getCount().toString()); json.writeStringField(Constants.JSON_COUNT, entityCollection.getCount().toString());
@ -805,7 +852,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
} }
} }
private void writeNextLink(final EntityCollection entitySet, final JsonGenerator json) throws IOException { void writeNextLink(final AbstractEntityCollection entitySet, final JsonGenerator json) throws IOException {
if (entitySet.getNext() != null) { if (entitySet.getNext() != null) {
json.writeStringField(Constants.JSON_NEXT_LINK, entitySet.getNext().toASCIIString()); json.writeStringField(Constants.JSON_NEXT_LINK, entitySet.getNext().toASCIIString());
} }

View File

@ -193,6 +193,25 @@ public class CircleStreamBuffer {
return readBuffer.get(); return readBuffer.get();
} }
public ByteBuffer getBuffer() throws IOException {
if (readClosed) {
throw new IOException("Tried to read from closed stream.");
}
writeMode = false;
// FIXME: mibo_160108: This is not efficient and only for test/poc reasons
int reqSize = 0;
for (ByteBuffer byteBuffer : bufferQueue) {
reqSize += byteBuffer.position();
}
ByteBuffer tmp = ByteBuffer.allocateDirect(reqSize);
for (ByteBuffer byteBuffer : bufferQueue) {
byteBuffer.flip();
tmp.put(byteBuffer);
}
return tmp;
}
// ############################################# // #############################################
// # // #
// # Writing parts // # Writing parts

View File

@ -0,0 +1,65 @@
/*
* 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.utils;
import org.apache.olingo.commons.api.ex.ODataRuntimeException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
public class ResultHelper {
public static final int COPY_BUFFER_SIZE = 8192;
public static void copy(InputStream input, OutputStream output) {
copy(Channels.newChannel(input), Channels.newChannel(output));
}
public static void copy(ReadableByteChannel input, WritableByteChannel output) {
try {
ByteBuffer inBuffer = ByteBuffer.allocate(COPY_BUFFER_SIZE);
while (input.read(inBuffer) > 0) {
inBuffer.flip();
output.write(inBuffer);
inBuffer.clear();
}
} catch (IOException e) {
throw new ODataRuntimeException("Error on reading request content", e);
} finally {
closeStream(input);
closeStream(output);
}
}
private static void closeStream(final Channel closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (IOException e) {
// ignore
}
}
}
}

View File

@ -35,6 +35,8 @@ import org.apache.olingo.commons.api.data.ComplexValue;
import org.apache.olingo.commons.api.data.ContextURL; import org.apache.olingo.commons.api.data.ContextURL;
import org.apache.olingo.commons.api.data.Entity; import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.EntityCollection; import org.apache.olingo.commons.api.data.EntityCollection;
import org.apache.olingo.commons.api.data.AbstractEntityCollection;
import org.apache.olingo.commons.api.data.EntityIterator;
import org.apache.olingo.commons.api.data.Link; import org.apache.olingo.commons.api.data.Link;
import org.apache.olingo.commons.api.data.Linked; import org.apache.olingo.commons.api.data.Linked;
import org.apache.olingo.commons.api.data.Property; import org.apache.olingo.commons.api.data.Property;
@ -51,6 +53,7 @@ import org.apache.olingo.commons.api.edm.EdmType;
import org.apache.olingo.commons.api.edm.FullQualifiedName; import org.apache.olingo.commons.api.edm.FullQualifiedName;
import org.apache.olingo.commons.api.edm.constants.EdmTypeKind; import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
import org.apache.olingo.commons.api.ex.ODataErrorDetail; import org.apache.olingo.commons.api.ex.ODataErrorDetail;
import org.apache.olingo.commons.api.ex.ODataRuntimeException;
import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory; import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
import org.apache.olingo.commons.core.edm.primitivetype.EdmString; import org.apache.olingo.commons.core.edm.primitivetype.EdmString;
import org.apache.olingo.server.api.ODataServerError; import org.apache.olingo.server.api.ODataServerError;
@ -63,9 +66,11 @@ import org.apache.olingo.server.api.serializer.ReferenceCollectionSerializerOpti
import org.apache.olingo.server.api.serializer.ReferenceSerializerOptions; import org.apache.olingo.server.api.serializer.ReferenceSerializerOptions;
import org.apache.olingo.server.api.serializer.SerializerException; import org.apache.olingo.server.api.serializer.SerializerException;
import org.apache.olingo.server.api.serializer.SerializerResult; import org.apache.olingo.server.api.serializer.SerializerResult;
import org.apache.olingo.server.api.serializer.SerializerStreamResult;
import org.apache.olingo.server.api.uri.queryoption.ExpandItem; 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.ExpandOption;
import org.apache.olingo.server.api.uri.queryoption.SelectOption; import org.apache.olingo.server.api.uri.queryoption.SelectOption;
import org.apache.olingo.server.core.ODataWritableContent;
import org.apache.olingo.server.core.serializer.AbstractODataSerializer; import org.apache.olingo.server.core.serializer.AbstractODataSerializer;
import org.apache.olingo.server.core.serializer.SerializerResultImpl; import org.apache.olingo.server.core.serializer.SerializerResultImpl;
import org.apache.olingo.server.core.serializer.utils.CircleStreamBuffer; import org.apache.olingo.server.core.serializer.utils.CircleStreamBuffer;
@ -210,7 +215,7 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
@Override @Override
public SerializerResult entityCollection(final ServiceMetadata metadata, public SerializerResult entityCollection(final ServiceMetadata metadata,
final EdmEntityType entityType, final EntityCollection entitySet, final EdmEntityType entityType, final AbstractEntityCollection entitySet,
final EntityCollectionSerializerOptions options) throws SerializerException { final EntityCollectionSerializerOptions options) throws SerializerException {
final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL()); final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL());
@ -278,6 +283,69 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
} }
} }
public void entityCollectionIntoStream(ServiceMetadata metadata, EdmEntityType entityType, EntityIterator entitySet,
EntityCollectionSerializerOptions options, OutputStream outputStream) throws SerializerException {
final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL());
// if (options != null && options.getWriteOnlyReferences()) {
// ReferenceCollectionSerializerOptions rso = ReferenceCollectionSerializerOptions.with()
// .contextURL(contextURL).build();
// return entityReferenceCollection(entitySet, rso);
// }
SerializerException cachedException = null;
try {
CircleStreamBuffer buffer = new CircleStreamBuffer();
outputStream = buffer.getOutputStream();
XMLStreamWriter writer = XMLOutputFactory.newInstance().createXMLStreamWriter(outputStream, DEFAULT_CHARSET);
writer.writeStartDocument(DEFAULT_CHARSET, "1.0");
writer.writeStartElement(ATOM, Constants.ATOM_ELEM_FEED, NS_ATOM);
writer.writeNamespace(ATOM, NS_ATOM);
writer.writeNamespace(METADATA, NS_METADATA);
writer.writeNamespace(DATA, NS_DATA);
writer.writeAttribute(METADATA, NS_METADATA, Constants.CONTEXT,
ContextURLBuilder.create(contextURL).toASCIIString());
writeMetadataETag(metadata, writer);
if (options != null && options.getId() != null) {
writer.writeStartElement(ATOM, Constants.ATOM_ELEM_ID, NS_ATOM);
writer.writeCharacters(options.getId());
writer.writeEndElement();
}
if (options != null && options.getCount() != null && options.getCount().getValue()
&& entitySet.getCount() != null) {
writeCount(entitySet, writer);
}
if (entitySet.getNext() != null) {
writeNextLink(entitySet, writer);
}
if (options == null) {
writeEntitySet(metadata, entityType, entitySet, null, null, null, writer);
} else {
writeEntitySet(metadata, entityType, entitySet,
options.getExpand(), options.getSelect(), options.xml10InvalidCharReplacement(), writer);
}
writer.writeEndElement();
writer.writeEndDocument();
writer.flush();
} catch (final XMLStreamException e) {
cachedException =
new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION);
throw cachedException;
}
}
@Override
public SerializerStreamResult entityCollectionStreamed(ServiceMetadata metadata, EdmEntityType entityType,
EntityIterator entities, EntityCollectionSerializerOptions options) throws SerializerException {
return ODataWritableContent.with(entities, entityType, this, metadata, options).build();
}
@Override @Override
public SerializerResult entity(final ServiceMetadata metadata, final EdmEntityType entityType, public SerializerResult entity(final ServiceMetadata metadata, final EdmEntityType entityType,
final Entity entity, final EntitySerializerOptions options) throws SerializerException { final Entity entity, final EntitySerializerOptions options) throws SerializerException {
@ -338,10 +406,10 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
} }
protected void writeEntitySet(final ServiceMetadata metadata, final EdmEntityType entityType, protected void writeEntitySet(final ServiceMetadata metadata, final EdmEntityType entityType,
final EntityCollection entitySet, final ExpandOption expand, final SelectOption select, final AbstractEntityCollection entitySet, final ExpandOption expand, final SelectOption select,
final String xml10InvalidCharReplacement,final XMLStreamWriter writer) final String xml10InvalidCharReplacement,final XMLStreamWriter writer)
throws XMLStreamException, SerializerException { throws XMLStreamException, SerializerException {
for (final Entity entity : entitySet.getEntities()) { for (final Entity entity : entitySet) {
writeEntity(metadata, entityType, entity, null, expand, select, xml10InvalidCharReplacement, writer, false); writeEntity(metadata, entityType, entity, null, expand, select, xml10InvalidCharReplacement, writer, false);
} }
} }
@ -1063,12 +1131,12 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
@Override @Override
public SerializerResult referenceCollection(final ServiceMetadata metadata, final EdmEntitySet edmEntitySet, public SerializerResult referenceCollection(final ServiceMetadata metadata, final EdmEntitySet edmEntitySet,
final EntityCollection entityCollection, final ReferenceCollectionSerializerOptions options) final AbstractEntityCollection entityCollection, final ReferenceCollectionSerializerOptions options)
throws SerializerException { throws SerializerException {
return entityReferenceCollection(entityCollection, options); return entityReferenceCollection(entityCollection, options);
} }
protected SerializerResult entityReferenceCollection(final EntityCollection entitySet, protected SerializerResult entityReferenceCollection(final AbstractEntityCollection entitySet,
final ReferenceCollectionSerializerOptions options) throws SerializerException { final ReferenceCollectionSerializerOptions options) throws SerializerException {
OutputStream outputStream = null; OutputStream outputStream = null;
SerializerException cachedException = null; SerializerException cachedException = null;
@ -1091,7 +1159,7 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
if (entitySet.getNext() != null) { if (entitySet.getNext() != null) {
writeNextLink(entitySet, writer); writeNextLink(entitySet, writer);
} }
for (final Entity entity : entitySet.getEntities()) { for (final Entity entity : entitySet) {
writeReference(entity, options == null ? null : options.getContextURL(), writer, false); writeReference(entity, options == null ? null : options.getContextURL(), writer, false);
} }
writer.writeEndElement(); writer.writeEndElement();
@ -1114,14 +1182,14 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
} }
} }
private void writeCount(final EntityCollection entitySet, final XMLStreamWriter writer) private void writeCount(final AbstractEntityCollection entitySet, final XMLStreamWriter writer)
throws XMLStreamException { throws XMLStreamException {
writer.writeStartElement(METADATA, Constants.ATOM_ELEM_COUNT, NS_METADATA); writer.writeStartElement(METADATA, Constants.ATOM_ELEM_COUNT, NS_METADATA);
writer.writeCharacters(String.valueOf(entitySet.getCount())); writer.writeCharacters(String.valueOf(entitySet.getCount()));
writer.writeEndElement(); writer.writeEndElement();
} }
private void writeNextLink(final EntityCollection entitySet, final XMLStreamWriter writer) private void writeNextLink(final AbstractEntityCollection entitySet, final XMLStreamWriter writer)
throws XMLStreamException { throws XMLStreamException {
writer.writeStartElement(ATOM, Constants.ATOM_ELEM_LINK, NS_ATOM); writer.writeStartElement(ATOM, Constants.ATOM_ELEM_LINK, NS_ATOM);
writer.writeAttribute(Constants.ATTR_REL, Constants.NEXT_LINK_REL); writer.writeAttribute(Constants.ATTR_REL, Constants.NEXT_LINK_REL);

View File

@ -18,6 +18,13 @@
*/ */
package org.apache.olingo.server.core.uri.parser.search; package org.apache.olingo.server.core.uri.parser.search;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import static org.apache.olingo.server.core.uri.parser.search.SearchQueryToken.Token.AND; import static org.apache.olingo.server.core.uri.parser.search.SearchQueryToken.Token.AND;
import static org.apache.olingo.server.core.uri.parser.search.SearchQueryToken.Token.CLOSE; import static org.apache.olingo.server.core.uri.parser.search.SearchQueryToken.Token.CLOSE;
import static org.apache.olingo.server.core.uri.parser.search.SearchQueryToken.Token.NOT; import static org.apache.olingo.server.core.uri.parser.search.SearchQueryToken.Token.NOT;
@ -26,13 +33,6 @@ import static org.apache.olingo.server.core.uri.parser.search.SearchQueryToken.T
import static org.apache.olingo.server.core.uri.parser.search.SearchQueryToken.Token.PHRASE; import static org.apache.olingo.server.core.uri.parser.search.SearchQueryToken.Token.PHRASE;
import static org.apache.olingo.server.core.uri.parser.search.SearchQueryToken.Token.WORD; import static org.apache.olingo.server.core.uri.parser.search.SearchQueryToken.Token.WORD;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
public class SearchTokenizerTest { public class SearchTokenizerTest {
@Test @Test

View File

@ -18,6 +18,8 @@
*/ */
package org.apache.olingo.server.tecsvc.processor; package org.apache.olingo.server.tecsvc.processor;
import java.util.Iterator;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import org.apache.olingo.commons.api.data.ContextURL; import org.apache.olingo.commons.api.data.ContextURL;
@ -25,6 +27,9 @@ 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.ContextURL.Suffix;
import org.apache.olingo.commons.api.data.Entity; import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.EntityCollection; import org.apache.olingo.commons.api.data.EntityCollection;
import org.apache.olingo.commons.api.data.EntityIterator;
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.EdmEntitySet;
import org.apache.olingo.commons.api.edm.EdmEntityType; import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.format.ContentType; import org.apache.olingo.commons.api.format.ContentType;
@ -51,6 +56,7 @@ import org.apache.olingo.server.api.serializer.EntitySerializerOptions;
import org.apache.olingo.server.api.serializer.ReferenceCollectionSerializerOptions; import org.apache.olingo.server.api.serializer.ReferenceCollectionSerializerOptions;
import org.apache.olingo.server.api.serializer.ReferenceSerializerOptions; import org.apache.olingo.server.api.serializer.ReferenceSerializerOptions;
import org.apache.olingo.server.api.serializer.SerializerResult; import org.apache.olingo.server.api.serializer.SerializerResult;
import org.apache.olingo.server.api.serializer.SerializerStreamResult;
import org.apache.olingo.server.api.uri.UriInfo; import org.apache.olingo.server.api.uri.UriInfo;
import org.apache.olingo.server.api.uri.UriResourceEntitySet; import org.apache.olingo.server.api.uri.UriResourceEntitySet;
import org.apache.olingo.server.api.uri.UriResourceNavigation; import org.apache.olingo.server.api.uri.UriResourceNavigation;
@ -519,12 +525,26 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
id = request.getRawBaseUri() + edmEntitySet.getName(); id = request.getRawBaseUri() + edmEntitySet.getName();
} }
// Serialize if(isReference) {
final SerializerResult serializerResult = (isReference) ? final SerializerResult serializerResult =
serializeReferenceCollection(entitySetSerialization, edmEntitySet, requestedContentType, countOption) : serializeReferenceCollection(entitySetSerialization, edmEntitySet, requestedContentType, countOption);
serializeEntityCollection(request, entitySetSerialization, edmEntitySet, edmEntityType, requestedContentType, response.setContent(serializerResult.getContent());
expand, select, countOption, id); } else if(isOdataStreaming(request)) {
response.setContent(serializerResult.getContent()); final SerializerStreamResult serializerResult =
serializeEntityCollectionStreamed(request,
entitySetSerialization, edmEntitySet, edmEntityType, requestedContentType,
expand, select, countOption, id);
response.setODataContent(serializerResult.getODataContent());
} else {
final SerializerResult serializerResult =
serializeEntityCollection(request,
entitySetSerialization, edmEntitySet, edmEntityType, requestedContentType,
expand, select, countOption, id);
response.setContent(serializerResult.getContent());
}
//
response.setStatusCode(HttpStatusCode.OK.getStatusCode()); response.setStatusCode(HttpStatusCode.OK.getStatusCode());
response.setHeader(HttpHeader.CONTENT_TYPE, requestedContentType.toContentTypeString()); response.setHeader(HttpHeader.CONTENT_TYPE, requestedContentType.toContentTypeString());
if (pageSize != null) { if (pageSize != null) {
@ -533,6 +553,11 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
} }
} }
private boolean isOdataStreaming(ODataRequest request) {
String odataStreaming = request.getHeader("odata.streaming");
return Boolean.parseBoolean(odataStreaming);
}
private SerializerResult serializeEntityCollection(final ODataRequest request, final EntityCollection private SerializerResult serializeEntityCollection(final ODataRequest request, final EntityCollection
entityCollection, final EdmEntitySet edmEntitySet, final EdmEntityType edmEntityType, entityCollection, final EdmEntitySet edmEntitySet, final EdmEntityType edmEntityType,
final ContentType requestedFormat, final ExpandOption expand, final SelectOption select, final ContentType requestedFormat, final ExpandOption expand, final SelectOption select,
@ -551,6 +576,56 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
.build()); .build());
} }
// serialise as streamed collection
private SerializerStreamResult serializeEntityCollectionStreamed(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 {
EntityIterator streamCollection = new EntityIterator() {
Iterator<Entity> entityIterator = entityCollection.iterator();
@Override
public boolean hasNext() {
return entityIterator.hasNext();
}
@Override
public Entity next() {
return addToPrimitiveProperty(entityIterator.next(), "PropertyString", "->streamed");
}
private Entity addToPrimitiveProperty(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);
final String old = property.getValue().toString();
String newValue = (old == null ? "": old) + data.toString();
entity.addProperty(new Property(null, name, ValueType.PRIMITIVE, newValue));
break;
}
pos++;
}
return entity;
}
};
return odata.createSerializer(requestedFormat).entityCollectionStreamed(
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 serializeReferenceCollection(final EntityCollection entityCollection, private SerializerResult serializeReferenceCollection(final EntityCollection entityCollection,
final EdmEntitySet edmEntitySet, final ContentType requestedFormat, final CountOption countOption) final EdmEntitySet edmEntitySet, final ContentType requestedFormat, final CountOption countOption)
throws ODataLibraryException { throws ODataLibraryException {

View File

@ -18,18 +18,25 @@
*/ */
package org.apache.olingo.server.core.serializer.json; package org.apache.olingo.server.core.serializer.json;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URI; import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator;
import java.util.Locale;
import java.util.TimeZone; import java.util.TimeZone;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.olingo.commons.api.data.ComplexValue; import org.apache.olingo.commons.api.data.ComplexValue;
import org.apache.olingo.commons.api.data.ContextURL; import org.apache.olingo.commons.api.data.ContextURL;
import org.apache.olingo.commons.api.data.ContextURL.Suffix; import org.apache.olingo.commons.api.data.ContextURL.Suffix;
import org.apache.olingo.commons.api.data.Entity; import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.EntityCollection; import org.apache.olingo.commons.api.data.EntityCollection;
import org.apache.olingo.commons.api.data.EntityIterator;
import org.apache.olingo.commons.api.data.Property; import org.apache.olingo.commons.api.data.Property;
import org.apache.olingo.commons.api.data.ValueType; import org.apache.olingo.commons.api.data.ValueType;
import org.apache.olingo.commons.api.edm.EdmComplexType; import org.apache.olingo.commons.api.edm.EdmComplexType;
@ -40,7 +47,10 @@ import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
import org.apache.olingo.commons.api.edm.EdmProperty; import org.apache.olingo.commons.api.edm.EdmProperty;
import org.apache.olingo.commons.api.format.ContentType; import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.server.api.OData; import org.apache.olingo.server.api.OData;
import org.apache.olingo.server.api.ODataContent;
import org.apache.olingo.server.api.ServiceMetadata; import org.apache.olingo.server.api.ServiceMetadata;
import org.apache.olingo.server.api.WriteContentErrorCallback;
import org.apache.olingo.server.api.WriteContentErrorContext;
import org.apache.olingo.server.api.edmx.EdmxReference; import org.apache.olingo.server.api.edmx.EdmxReference;
import org.apache.olingo.server.api.serializer.ComplexSerializerOptions; import org.apache.olingo.server.api.serializer.ComplexSerializerOptions;
import org.apache.olingo.server.api.serializer.EntityCollectionSerializerOptions; import org.apache.olingo.server.api.serializer.EntityCollectionSerializerOptions;
@ -207,6 +217,90 @@ public class ODataJsonSerializerTest {
Assert.assertEquals(8, count); Assert.assertEquals(8, count);
} }
@Test
public void entityCollectionStreamed() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESAllPrim");
final EntityIterator entityIterator = new EntityIterator() {
Iterator<Entity> innerIterator = data.readAll(edmEntitySet).iterator();
@Override
public boolean hasNext() {
return innerIterator.hasNext();
}
@Override
public Entity next() {
return innerIterator.next();
}
};
CountOption countOption = Mockito.mock(CountOption.class);
Mockito.when(countOption.getValue()).thenReturn(true);
ODataContent result = serializer.entityCollectionStreamed(
metadata, edmEntitySet.getEntityType(), entityIterator,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).build())
.build()).getODataContent();
ByteArrayOutputStream bout = new ByteArrayOutputStream();
result.write(bout);
final String resultString = new String(bout.toByteArray(), "UTF-8");
Assert.assertThat(resultString, CoreMatchers.startsWith("{"
+ "\"@odata.context\":\"$metadata#ESAllPrim\","
+ "\"@odata.metadataEtag\":\"W/\\\"metadataETag\\\"\","
+ "\"value\":[{\"PropertyInt16\":32767,\"PropertyString\""));
Assert.assertThat(resultString, CoreMatchers.endsWith(
"\"PropertyTimeOfDay\":\"00:01:01\"}]}"));
int count = 0;
int index = -1;
while ((index = resultString.indexOf("PropertyInt16\":", ++index)) > 0) {
count++;
}
Assert.assertEquals(3, count);
}
@Test
public void entityCollectionStreamedWithError() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESAllPrim");
final EntityIterator entityIterator = new EntityIterator() {
Iterator<Entity> innerIterator = data.readAll(edmEntitySet).iterator();
@Override
public boolean hasNext() {
return innerIterator.hasNext();
}
@Override
public Entity next() {
return new Entity();
}
};
CountOption countOption = Mockito.mock(CountOption.class);
Mockito.when(countOption.getValue()).thenReturn(true);
WriteContentErrorCallback errorCallback = new WriteContentErrorCallback() {
@Override
public void handleError(WriteContentErrorContext context, WritableByteChannel channel) {
try {
String msgKey = context.getODataLibraryException().getMessageKey().getKey();
String toChannel = "ERROR: " + msgKey;
channel.write(ByteBuffer.wrap(toChannel.getBytes("UTF-8")));
} catch (IOException e) {
throw new RuntimeException("Error in error.");
}
}
};
ODataContent result = serializer.entityCollectionStreamed(
metadata, edmEntitySet.getEntityType(), entityIterator,
EntityCollectionSerializerOptions.with()
.writeContentErrorCallback(errorCallback)
.contextURL(ContextURL.with().entitySet(edmEntitySet).build())
.build()).getODataContent();
ByteArrayOutputStream bout = new ByteArrayOutputStream();
result.write(bout);
final String resultString = new String(bout.toByteArray(), "UTF-8");
Assert.assertEquals(resultString, "ERROR: MISSING_PROPERTY");
}
@Test @Test
public void entityCollAllPrim() throws Exception { public void entityCollAllPrim() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESCollAllPrim"); final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESCollAllPrim");