Repository: olingo-odata4 Updated Branches: refs/heads/OLINGO-1066_StreamSupport [created] 9de1d310b
[OLINGO-1066] Collection stream support for primitive and complex type collections Signed-off-by: mibo <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/685b19e5 Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/685b19e5 Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/685b19e5 Branch: refs/heads/OLINGO-1066_StreamSupport Commit: 685b19e57225c2ffaa400457e377bb5f7ad6f35c Parents: e41b81e Author: Kavindu Dodanduwa <[email protected]> Authored: Mon Jan 2 15:31:38 2017 +0530 Committer: mibo <[email protected]> Committed: Sat Feb 18 22:39:31 2017 +0100 ---------------------------------------------------------------------- .../commons/api/data/CollectionIterator.java | 40 ++++ .../commons/api/data/ComplexIterator.java | 47 ++++ .../commons/api/data/PrimitiveIterator.java | 44 ++++ .../serializer/ComplexSerializerOptions.java | 30 ++- .../server/api/serializer/ODataSerializer.java | 21 ++ .../serializer/PrimitiveSerializerOptions.java | 21 +- .../server/core/CollectionWritableContent.java | 39 ++++ .../server/core/ComplexStreamContent.java | 86 ++++++++ .../core/ComplexStreamContentForJson.java | 52 +++++ .../server/core/ComplexStreamContentForXml.java | 52 +++++ .../server/core/ODataWritableContent.java | 22 +- .../server/core/PrimitiveStreamContent.java | 96 ++++++++ .../core/PrimitiveStreamContentForJson.java | 56 +++++ .../core/PrimitiveStreamContentForXml.java | 56 +++++ .../olingo/server/core/WriteErrorContext.java | 41 ++++ .../serializer/json/ODataJsonSerializer.java | 149 +++++++++++++ .../core/serializer/xml/ODataXmlSerializer.java | 130 ++++++++++- .../json/ODataJsonSerializerTest.java | 179 ++++++++++++++- .../serializer/xml/ODataXmlSerializerTest.java | 219 +++++++++++++++++++ 19 files changed, 1352 insertions(+), 28 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/685b19e5/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/CollectionIterator.java ---------------------------------------------------------------------- diff --git a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/CollectionIterator.java b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/CollectionIterator.java new file mode 100644 index 0000000..d0e6715 --- /dev/null +++ b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/CollectionIterator.java @@ -0,0 +1,40 @@ +/* + * 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.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public abstract class CollectionIterator<T> implements Iterator<T>, Iterable<T> { + + private List<Operation> operations = new ArrayList<Operation>(); + + public List<Operation> getOperations() { + return operations; + } + + public abstract boolean hasNext(); + + public abstract T next(); + + public Iterator<T> iterator() { + return this; + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/685b19e5/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/ComplexIterator.java ---------------------------------------------------------------------- diff --git a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/ComplexIterator.java b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/ComplexIterator.java new file mode 100644 index 0000000..f065b9b --- /dev/null +++ b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/ComplexIterator.java @@ -0,0 +1,47 @@ +/* + * 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; + +public abstract class ComplexIterator extends CollectionIterator<ComplexValue> { + + /** + * String representation of type (can be null) + */ + private final String type; + + public ComplexIterator() { + this.type = null; + } + + public ComplexIterator(final String type) { + this.type = type; + } + + public String getType() { + return type; + } + + public void remove() { + //"Remove is not supported for iteration over Collection of Complex values." + throw new ODataNotSupportedException("Complex Iterator does not support remove()"); + } +} + http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/685b19e5/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/PrimitiveIterator.java ---------------------------------------------------------------------- diff --git a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/PrimitiveIterator.java b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/PrimitiveIterator.java new file mode 100644 index 0000000..ee50401 --- /dev/null +++ b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/PrimitiveIterator.java @@ -0,0 +1,44 @@ +/* + * 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; + +public abstract class PrimitiveIterator<T> extends CollectionIterator<T> { + + private final String name; + + protected PrimitiveIterator(String name) { + this.name = name; + } + + /** + * Get name of property. + * + * @return name of property + */ + public String getName() { + return name; + } + + public void remove() { + //"Remove is not supported for iteration over Collection of Primitives." + throw new ODataNotSupportedException("Property Iterator does not support remove()"); + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/685b19e5/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ComplexSerializerOptions.java ---------------------------------------------------------------------- diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ComplexSerializerOptions.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ComplexSerializerOptions.java index 179c4d4..3eed831 100644 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ComplexSerializerOptions.java +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ComplexSerializerOptions.java @@ -19,6 +19,7 @@ package org.apache.olingo.server.api.serializer; import org.apache.olingo.commons.api.data.ContextURL; +import org.apache.olingo.server.api.ODataContentWriteErrorCallback; import org.apache.olingo.server.api.uri.queryoption.ExpandOption; import org.apache.olingo.server.api.uri.queryoption.SelectOption; @@ -29,6 +30,7 @@ public class ComplexSerializerOptions { private ExpandOption expand; private SelectOption select; private String xml10InvalidCharReplacement; + private ODataContentWriteErrorCallback odataContentWriteErrorCallback; /** Gets the {@link ContextURL}. */ public ContextURL getContextURL() { @@ -45,6 +47,18 @@ public class ComplexSerializerOptions { return select; } + /** + * 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 ODataContentWriteErrorCallback getODataContentWriteErrorCallback() { + return odataContentWriteErrorCallback; + } + /** Gets the replacement string for unicode characters, that is not allowed in XML 1.0 */ public String xml10InvalidCharReplacement() { return xml10InvalidCharReplacement; @@ -88,8 +102,20 @@ public class ComplexSerializerOptions { public Builder xml10InvalidCharReplacement(final String replacement) { options.xml10InvalidCharReplacement = replacement; return this; - } - + } + + /** + * Set the callback which is used in case of an exception during + * write of the content. + * + * @param ODataContentWriteErrorCallback the callback + * @return the builder + */ + public Builder writeContentErrorCallback(ODataContentWriteErrorCallback ODataContentWriteErrorCallback) { + options.odataContentWriteErrorCallback = ODataContentWriteErrorCallback; + return this; + } + /** Builds the OData serializer options. */ public ComplexSerializerOptions build() { return options; http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/685b19e5/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ODataSerializer.java ---------------------------------------------------------------------- diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ODataSerializer.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ODataSerializer.java index 4f6974d..890f375 100644 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ODataSerializer.java +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ODataSerializer.java @@ -19,10 +19,12 @@ package org.apache.olingo.server.api.serializer; import org.apache.olingo.commons.api.Constants; +import org.apache.olingo.commons.api.data.ComplexIterator; import org.apache.olingo.commons.api.data.Entity; 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.PrimitiveIterator; import org.apache.olingo.commons.api.edm.EdmComplexType; import org.apache.olingo.commons.api.edm.EdmEntitySet; import org.apache.olingo.commons.api.edm.EdmEntityType; @@ -116,6 +118,16 @@ public interface ODataSerializer { PrimitiveSerializerOptions options) throws SerializerException; /** + * Writes streamed primitive-collection data into OutputStream. + * @param metadata metadata for the service + * @param type the {@link EdmEntityType} + * @param iterator the data of the complex collection + * @param options options for the serializer + */ + SerializerStreamResult primitiveCollectionStreamed(ServiceMetadata metadata, EdmPrimitiveType type, + PrimitiveIterator iterator, PrimitiveSerializerOptions options) throws SerializerException; + + /** * Writes data of a collection of complex-type instances into an InputStream. * @param metadata metadata for the service * @param type complex type @@ -124,6 +136,15 @@ public interface ODataSerializer { */ SerializerResult complexCollection(ServiceMetadata metadata, EdmComplexType type, Property property, ComplexSerializerOptions options) throws SerializerException; + /** + * Writes streamed complex-collection data into OutputStream. + * @param metadata metadata for the service + * @param type the {@link EdmEntityType} + * @param iterator the data of the complex collection + * @param options options for the serializer + */ + SerializerStreamResult complexCollectionStreamed(ServiceMetadata metadata, EdmComplexType type, + ComplexIterator iterator, ComplexSerializerOptions options) throws SerializerException; /** * Writes a single entity reference into an InputStream. http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/685b19e5/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/PrimitiveSerializerOptions.java ---------------------------------------------------------------------- diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/PrimitiveSerializerOptions.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/PrimitiveSerializerOptions.java index 61a3160..5b3ad0d 100644 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/PrimitiveSerializerOptions.java +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/PrimitiveSerializerOptions.java @@ -20,6 +20,7 @@ package org.apache.olingo.server.api.serializer; import org.apache.olingo.commons.api.data.ContextURL; import org.apache.olingo.commons.api.edm.EdmProperty; +import org.apache.olingo.server.api.ODataContentWriteErrorCallback; /** Options for the OData serializer. */ public final class PrimitiveSerializerOptions { @@ -31,6 +32,7 @@ public final class PrimitiveSerializerOptions { private Integer scale; private Boolean isUnicode; private String xml10InvalidCharReplacement; + private ODataContentWriteErrorCallback odataContentWriteErrorCallback; /** Gets the {@link ContextURL}. */ public ContextURL getContextURL() { @@ -68,6 +70,18 @@ public final class PrimitiveSerializerOptions { } + /** + * 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 ODataContentWriteErrorCallback getODataContentWriteErrorCallback() { + return odataContentWriteErrorCallback; + } + private PrimitiveSerializerOptions() {} /** Initializes the options builder. */ @@ -135,7 +149,12 @@ public final class PrimitiveSerializerOptions { options.xml10InvalidCharReplacement = replacement; return this; } - + + public Builder writeContentErrorCallback(ODataContentWriteErrorCallback ODataContentWriteErrorCallback) { + options.odataContentWriteErrorCallback = ODataContentWriteErrorCallback; + return this; + } + /** Builds the OData serializer options. */ public PrimitiveSerializerOptions build() { return options; http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/685b19e5/lib/server-core/src/main/java/org/apache/olingo/server/core/CollectionWritableContent.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/CollectionWritableContent.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/CollectionWritableContent.java new file mode 100644 index 0000000..27bb061 --- /dev/null +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/CollectionWritableContent.java @@ -0,0 +1,39 @@ +/* + * 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.OutputStream; +import java.nio.channels.Channels; +import java.nio.channels.WritableByteChannel; + +import org.apache.olingo.server.api.ODataContent; + +public abstract class CollectionWritableContent implements ODataContent { + + protected abstract void writeCollection(OutputStream out); + + public void write(OutputStream stream) { + write(Channels.newChannel(stream)); + } + + @Override + public void write(WritableByteChannel writeChannel) { + writeCollection(Channels.newOutputStream(writeChannel)); + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/685b19e5/lib/server-core/src/main/java/org/apache/olingo/server/core/ComplexStreamContent.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ComplexStreamContent.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ComplexStreamContent.java new file mode 100644 index 0000000..9496420 --- /dev/null +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ComplexStreamContent.java @@ -0,0 +1,86 @@ +/* + * 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.OutputStream; +import java.nio.channels.Channels; + +import org.apache.olingo.commons.api.data.ComplexIterator; +import org.apache.olingo.commons.api.edm.EdmComplexType; +import org.apache.olingo.server.api.ODataContentWriteErrorCallback; +import org.apache.olingo.server.api.ServiceMetadata; +import org.apache.olingo.server.api.serializer.ComplexSerializerOptions; +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 abstract class ComplexStreamContent extends CollectionWritableContent { + protected ComplexIterator iterator; + protected ServiceMetadata metadata; + protected EdmComplexType complexType; + protected ComplexSerializerOptions options; + + protected ComplexStreamContent(ComplexIterator iterator, ServiceMetadata metadata, EdmComplexType + complexType, ComplexSerializerOptions options) { + this.iterator = iterator; + this.complexType = complexType; + this.metadata = metadata; + this.options = options; + } + + protected abstract void writeComplex(OutputStream outputStream) throws SerializerException; + + @Override + protected void writeCollection(OutputStream out) { + try { + writeComplex(out); + } catch (SerializerException e) { + final ODataContentWriteErrorCallback errorCallback = options.getODataContentWriteErrorCallback(); + if (errorCallback != null) { + final WriteErrorContext errorContext = new WriteErrorContext(e); + errorCallback.handleError(errorContext, Channels.newChannel(out)); + } + } + } + + public static SerializerStreamResult ComplexWritableForJson(ComplexIterator iterator, EdmComplexType edmComplexType, + ODataJsonSerializer jsonSerializer, ServiceMetadata serviceMetadata, + ComplexSerializerOptions options) { + return SerializerStreamResultImpl.with() + .content(new ComplexStreamContentForJson(iterator, + edmComplexType, + jsonSerializer, + serviceMetadata, + options)).build(); + } + + public static SerializerStreamResult ComplexWritableForXml(ComplexIterator iterator, EdmComplexType + edmComplexType, + ODataXmlSerializer xmlSerializer, ServiceMetadata serviceMetadata, + ComplexSerializerOptions options) { + return SerializerStreamResultImpl.with() + .content(new ComplexStreamContentForXml(iterator, + edmComplexType, + xmlSerializer, + serviceMetadata, + options)).build(); + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/685b19e5/lib/server-core/src/main/java/org/apache/olingo/server/core/ComplexStreamContentForJson.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ComplexStreamContentForJson.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ComplexStreamContentForJson.java new file mode 100644 index 0000000..1a6a262 --- /dev/null +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ComplexStreamContentForJson.java @@ -0,0 +1,52 @@ +/* + * 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 org.apache.olingo.commons.api.data.ComplexIterator; +import org.apache.olingo.commons.api.edm.EdmComplexType; +import org.apache.olingo.commons.api.ex.ODataRuntimeException; +import org.apache.olingo.server.api.ServiceMetadata; +import org.apache.olingo.server.api.serializer.ComplexSerializerOptions; +import org.apache.olingo.server.api.serializer.SerializerException; +import org.apache.olingo.server.core.serializer.json.ODataJsonSerializer; + +class ComplexStreamContentForJson extends ComplexStreamContent { + private final ODataJsonSerializer jsonSerializer; + + protected ComplexStreamContentForJson(ComplexIterator iterator, EdmComplexType edmComplexType, + ODataJsonSerializer jsonSerializer, ServiceMetadata serviceMetadata, + ComplexSerializerOptions options) { + super(iterator, serviceMetadata, edmComplexType, options); + + this.jsonSerializer = jsonSerializer; + } + + @Override + protected void writeComplex(OutputStream outputStream) throws SerializerException { + try { + jsonSerializer.complexCollectionIntoStream(metadata, complexType, iterator, options, outputStream); + outputStream.flush(); + } catch (final IOException e) { + throw new ODataRuntimeException("Failed complex serialization", e); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/685b19e5/lib/server-core/src/main/java/org/apache/olingo/server/core/ComplexStreamContentForXml.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ComplexStreamContentForXml.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ComplexStreamContentForXml.java new file mode 100644 index 0000000..a6aec37 --- /dev/null +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ComplexStreamContentForXml.java @@ -0,0 +1,52 @@ +/* + * 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 org.apache.olingo.commons.api.data.ComplexIterator; +import org.apache.olingo.commons.api.edm.EdmComplexType; +import org.apache.olingo.commons.api.ex.ODataRuntimeException; +import org.apache.olingo.server.api.ServiceMetadata; +import org.apache.olingo.server.api.serializer.ComplexSerializerOptions; +import org.apache.olingo.server.api.serializer.SerializerException; +import org.apache.olingo.server.core.serializer.xml.ODataXmlSerializer; + +class ComplexStreamContentForXml extends ComplexStreamContent { + private final ODataXmlSerializer xmlSerializer; + + protected ComplexStreamContentForXml(ComplexIterator iterator, EdmComplexType edmComplexType, + ODataXmlSerializer xmlSerializer, ServiceMetadata serviceMetadata, + ComplexSerializerOptions options) { + super(iterator, serviceMetadata, edmComplexType, options); + + this.xmlSerializer = xmlSerializer; + } + + @Override + protected void writeComplex(OutputStream outputStream) throws SerializerException { + try { + xmlSerializer.complexCollectionIntoStream(metadata, complexType, iterator, options, outputStream); + outputStream.flush(); + } catch (final IOException e) { + throw new ODataRuntimeException("Failed complex serialization", e); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/685b19e5/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataWritableContent.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataWritableContent.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataWritableContent.java index 8f6b213..9986a0f 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataWritableContent.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataWritableContent.java @@ -27,10 +27,8 @@ 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.ODataContentWriteErrorCallback; -import org.apache.olingo.server.api.ODataContentWriteErrorContext; +import org.apache.olingo.server.api.ServiceMetadata; import org.apache.olingo.server.api.serializer.EntityCollectionSerializerOptions; import org.apache.olingo.server.api.serializer.ODataSerializer; import org.apache.olingo.server.api.serializer.SerializerException; @@ -141,24 +139,6 @@ public class ODataWritableContent implements ODataContent { return new ODataWritableContentBuilder(iterator, entityType, serializer, metadata, options); } - public static class WriteErrorContext implements ODataContentWriteErrorContext { - private ODataLibraryException exception; - - public WriteErrorContext(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; http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/685b19e5/lib/server-core/src/main/java/org/apache/olingo/server/core/PrimitiveStreamContent.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/PrimitiveStreamContent.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/PrimitiveStreamContent.java new file mode 100644 index 0000000..1a90b92 --- /dev/null +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/PrimitiveStreamContent.java @@ -0,0 +1,96 @@ +/* + * 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.OutputStream; +import java.nio.channels.Channels; + +import org.apache.olingo.commons.api.data.PrimitiveIterator; +import org.apache.olingo.commons.api.edm.EdmPrimitiveType; +import org.apache.olingo.server.api.ODataContentWriteErrorCallback; +import org.apache.olingo.server.api.ServiceMetadata; +import org.apache.olingo.server.api.serializer.PrimitiveSerializerOptions; +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 abstract class PrimitiveStreamContent extends CollectionWritableContent { + protected PrimitiveIterator iterator; + protected ServiceMetadata metadata; + protected EdmPrimitiveType primitiveType; + protected PrimitiveSerializerOptions options; + + protected PrimitiveStreamContent( + PrimitiveIterator iterator, + ServiceMetadata metadata, + EdmPrimitiveType primitiveType, + PrimitiveSerializerOptions options) { + this.iterator = iterator; + this.primitiveType = primitiveType; + this.metadata = metadata; + this.options = options; + } + + protected abstract void writePrimitive(OutputStream out) throws SerializerException; + + @Override + protected void writeCollection(OutputStream out) { + try { + writePrimitive(out); + } catch (SerializerException e) { + final ODataContentWriteErrorCallback errorCallback = options.getODataContentWriteErrorCallback(); + if (errorCallback != null) { + final WriteErrorContext errorContext = new WriteErrorContext(e); + errorCallback.handleError(errorContext, Channels.newChannel(out)); + } + } + } + + public static SerializerStreamResult PrimitiveStreamContentForJson( + PrimitiveIterator iterator, + EdmPrimitiveType primitiveType, + ODataJsonSerializer jsonSerializer, + ServiceMetadata serviceMetadata, + PrimitiveSerializerOptions options) { + + return SerializerStreamResultImpl.with() + .content(new PrimitiveStreamContentForJson(iterator, + primitiveType, + jsonSerializer, + serviceMetadata, + options)).build(); + } + + public static SerializerStreamResult PrimitiveStreamContentForXml( + PrimitiveIterator iterator, + EdmPrimitiveType primitiveType, + ODataXmlSerializer xmlSerializer, + ServiceMetadata serviceMetadata, + PrimitiveSerializerOptions options) { + + return SerializerStreamResultImpl.with() + .content(new PrimitiveStreamContentForXml(iterator, + primitiveType, + xmlSerializer, + serviceMetadata, + options)).build(); + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/685b19e5/lib/server-core/src/main/java/org/apache/olingo/server/core/PrimitiveStreamContentForJson.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/PrimitiveStreamContentForJson.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/PrimitiveStreamContentForJson.java new file mode 100644 index 0000000..789efaf --- /dev/null +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/PrimitiveStreamContentForJson.java @@ -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.server.core; + +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.olingo.commons.api.data.PrimitiveIterator; +import org.apache.olingo.commons.api.edm.EdmPrimitiveType; +import org.apache.olingo.commons.api.ex.ODataRuntimeException; +import org.apache.olingo.server.api.ServiceMetadata; +import org.apache.olingo.server.api.serializer.PrimitiveSerializerOptions; +import org.apache.olingo.server.api.serializer.SerializerException; +import org.apache.olingo.server.core.serializer.json.ODataJsonSerializer; + +class PrimitiveStreamContentForJson extends PrimitiveStreamContent { + private final ODataJsonSerializer jsonSerializer; + + protected PrimitiveStreamContentForJson( + PrimitiveIterator iterator, + EdmPrimitiveType primitiveType, + ODataJsonSerializer jsonSerializer, + ServiceMetadata serviceMetadata, + PrimitiveSerializerOptions options) { + + super(iterator, serviceMetadata, primitiveType, options); + + this.jsonSerializer = jsonSerializer; + } + + @Override + protected void writePrimitive(OutputStream outputStream) throws SerializerException { + try { + jsonSerializer.primitiveCollectionIntoStream(metadata, primitiveType, iterator, options, outputStream); + outputStream.flush(); + } catch (final IOException e) { + throw new ODataRuntimeException("Failed complex serialization", e); + } + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/685b19e5/lib/server-core/src/main/java/org/apache/olingo/server/core/PrimitiveStreamContentForXml.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/PrimitiveStreamContentForXml.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/PrimitiveStreamContentForXml.java new file mode 100644 index 0000000..675290b --- /dev/null +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/PrimitiveStreamContentForXml.java @@ -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.server.core; + +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.olingo.commons.api.data.PrimitiveIterator; +import org.apache.olingo.commons.api.edm.EdmPrimitiveType; +import org.apache.olingo.commons.api.ex.ODataRuntimeException; +import org.apache.olingo.server.api.ServiceMetadata; +import org.apache.olingo.server.api.serializer.PrimitiveSerializerOptions; +import org.apache.olingo.server.api.serializer.SerializerException; +import org.apache.olingo.server.core.serializer.xml.ODataXmlSerializer; + +class PrimitiveStreamContentForXml extends PrimitiveStreamContent { + private final ODataXmlSerializer xmlSerializer; + + protected PrimitiveStreamContentForXml( + PrimitiveIterator iterator, + EdmPrimitiveType primitiveType, + ODataXmlSerializer xmlSerializer, + ServiceMetadata serviceMetadata, + PrimitiveSerializerOptions options) { + + super(iterator, serviceMetadata, primitiveType, options); + + this.xmlSerializer = xmlSerializer; + } + + @Override + protected void writePrimitive(OutputStream outputStream) throws SerializerException { + try { + xmlSerializer.primitiveCollectionIntoStream(metadata, primitiveType, iterator, options, outputStream); + outputStream.flush(); + } catch (final IOException e) { + throw new ODataRuntimeException("Failed complex serialization", e); + } + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/685b19e5/lib/server-core/src/main/java/org/apache/olingo/server/core/WriteErrorContext.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/WriteErrorContext.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/WriteErrorContext.java new file mode 100644 index 0000000..5c74a48 --- /dev/null +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/WriteErrorContext.java @@ -0,0 +1,41 @@ +/* + * 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 org.apache.olingo.server.api.ODataContentWriteErrorContext; +import org.apache.olingo.server.api.ODataLibraryException; + +public class WriteErrorContext implements ODataContentWriteErrorContext { + + private final ODataLibraryException exception; + + public WriteErrorContext(ODataLibraryException exception) { + this.exception = exception; + } + + @Override + public Exception getException() { + return exception; + } + + @Override + public ODataLibraryException getODataLibraryException() { + return exception; + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/685b19e5/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java index 0cf307f..ffdc6e6 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java @@ -29,6 +29,7 @@ import java.util.Set; import org.apache.olingo.commons.api.Constants; import org.apache.olingo.commons.api.data.AbstractEntityCollection; +import org.apache.olingo.commons.api.data.ComplexIterator; import org.apache.olingo.commons.api.data.ComplexValue; import org.apache.olingo.commons.api.data.ContextURL; import org.apache.olingo.commons.api.data.Entity; @@ -36,6 +37,7 @@ import org.apache.olingo.commons.api.data.EntityIterator; import org.apache.olingo.commons.api.data.Link; import org.apache.olingo.commons.api.data.Linked; import org.apache.olingo.commons.api.data.Operation; +import org.apache.olingo.commons.api.data.PrimitiveIterator; import org.apache.olingo.commons.api.data.Property; import org.apache.olingo.commons.api.edm.EdmComplexType; import org.apache.olingo.commons.api.edm.EdmEntitySet; @@ -77,7 +79,9 @@ 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.LevelsExpandOption; import org.apache.olingo.server.api.uri.queryoption.SelectOption; +import org.apache.olingo.server.core.ComplexStreamContent; import org.apache.olingo.server.core.ODataWritableContent; +import org.apache.olingo.server.core.PrimitiveStreamContent; import org.apache.olingo.server.core.serializer.AbstractODataSerializer; import org.apache.olingo.server.core.serializer.SerializerResultImpl; import org.apache.olingo.server.core.serializer.utils.CircleStreamBuffer; @@ -743,6 +747,31 @@ public class ODataJsonSerializer extends AbstractODataSerializer { json.writeEndArray(); } + private void writePrimitiveCollectionStreamed( + final EdmPrimitiveType type, + final PrimitiveIterator property, + final Boolean isNullable, + final Integer maxLength, + final Integer precision, + final Integer scale, + final Boolean isUnicode, + final JsonGenerator json) + throws IOException, SerializerException { + + json.writeStartArray(); + for (Object value : property) { + try { + writePrimitiveValue(property.getName(), type, value, isNullable, + maxLength, precision, scale, isUnicode, json); + } catch (EdmPrimitiveTypeException e) { + throw new SerializerException("Wrong value for property!", e, + SerializerException.MessageKeys.WRONG_PROPERTY_VALUE, + property.getName()); + } + } + json.writeEndArray(); + } + private void writeComplexCollection(final ServiceMetadata metadata, final EdmComplexType type, final Property property, final Set<List<String>> selectedPaths, final JsonGenerator json) @@ -767,6 +796,27 @@ public class ODataJsonSerializer extends AbstractODataSerializer { json.writeEndArray(); } + private void writeComplexCollectionStream( + final ServiceMetadata metadata, + final EdmComplexType type, + final ComplexIterator iterator, + final Set<List<String>> selectedPaths, + final JsonGenerator json) + throws IOException, SerializerException { + + json.writeStartArray(); + for (ComplexValue property : iterator) { + json.writeStartObject(); + if (isODataMetadataFull) { + json.writeStringField(Constants.JSON_TYPE, "#" + + type.getFullQualifiedName().getFullQualifiedNameAsString()); + } + writeComplexValue(metadata, type, property.getValue(), selectedPaths, json); + json.writeEndObject(); + } + json.writeEndArray(); + } + private void writePrimitive(final EdmPrimitiveType type, final Property property, final Boolean isNullable, final Integer maxLength, final Integer precision, final Integer scale, final Boolean isUnicode, final JsonGenerator json) @@ -1068,6 +1118,57 @@ public class ODataJsonSerializer extends AbstractODataSerializer { } @Override + public SerializerStreamResult primitiveCollectionStreamed( + ServiceMetadata metadata, + EdmPrimitiveType type, + PrimitiveIterator iterator, + PrimitiveSerializerOptions options) + throws SerializerException { + + return PrimitiveStreamContent.PrimitiveStreamContentForJson(iterator, type, this, metadata, options); + } + + public void primitiveCollectionIntoStream( + final ServiceMetadata metadata, + final EdmPrimitiveType type, + final PrimitiveIterator iterator, + final PrimitiveSerializerOptions options, + final OutputStream outputStream) + throws SerializerException { + + SerializerException cachedException = null; + try { + final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL()); + + JsonGenerator json = new JsonFactory().createGenerator(outputStream); + json.writeStartObject(); + writeContextURL(contextURL, json); + writeMetadataETag(metadata, json); + if (isODataMetadataFull) { + json.writeStringField(Constants.JSON_TYPE, "#Collection(" + type.getFullQualifiedName().getName() + ")"); + } + writeOperations(iterator.getOperations(), json); + json.writeFieldName(Constants.VALUE); + writePrimitiveCollectionStreamed(type, iterator, + options == null ? null : options.isNullable(), + options == null ? null : options.getMaxLength(), + options == null ? null : options.getPrecision(), + options == null ? null : options.getScale(), + options == null ? null : options.isUnicode(), json); + json.writeEndObject(); + + json.close(); + outputStream.close(); + } catch (final IOException e) { + cachedException = + new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION); + throw cachedException; + } finally { + closeCircleStreamBufferOutput(outputStream, cachedException); + } + } + + @Override public SerializerResult complexCollection(final ServiceMetadata metadata, final EdmComplexType type, final Property property, final ComplexSerializerOptions options) throws SerializerException { OutputStream outputStream = null; @@ -1102,6 +1203,54 @@ public class ODataJsonSerializer extends AbstractODataSerializer { } @Override + public SerializerStreamResult complexCollectionStreamed( + ServiceMetadata metadata, + EdmComplexType type, + ComplexIterator iterator, + ComplexSerializerOptions options) + throws SerializerException { + + return ComplexStreamContent.ComplexWritableForJson(iterator, type, this, metadata, options); + } + + public void complexCollectionIntoStream( + final ServiceMetadata metadata, + final EdmComplexType type, + final ComplexIterator iterator, + final ComplexSerializerOptions options, + final OutputStream outputStream) + throws SerializerException { + + SerializerException cachedException = null; + try { + final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL()); + + JsonGenerator json = new JsonFactory().createGenerator(outputStream); + json.writeStartObject(); + writeContextURL(contextURL, json); + writeMetadataETag(metadata, json); + if (isODataMetadataFull) { + json.writeStringField(Constants.JSON_TYPE, + "#Collection(" + type.getFullQualifiedName().getFullQualifiedNameAsString() + ")"); + } + writeOperations(iterator.getOperations(), json); + json.writeFieldName(Constants.VALUE); + writeComplexCollectionStream(metadata, type, iterator, null, json); + json.writeEndObject(); + + json.close(); + outputStream.close(); + + } catch (final IOException e) { + cachedException = + new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION); + throw cachedException; + } finally { + closeCircleStreamBufferOutput(outputStream, cachedException); + } + } + + @Override public SerializerResult reference(final ServiceMetadata metadata, final EdmEntitySet edmEntitySet, final Entity entity, final ReferenceSerializerOptions options) throws SerializerException { OutputStream outputStream = null; http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/685b19e5/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializer.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializer.java index c7ea2e2..b52545f 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializer.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializer.java @@ -31,16 +31,18 @@ import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import org.apache.olingo.commons.api.Constants; +import org.apache.olingo.commons.api.data.AbstractEntityCollection; +import org.apache.olingo.commons.api.data.ComplexIterator; 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.AbstractEntityCollection; import org.apache.olingo.commons.api.data.EntityIterator; import org.apache.olingo.commons.api.data.Link; import org.apache.olingo.commons.api.data.Linked; import org.apache.olingo.commons.api.data.Operation; import org.apache.olingo.commons.api.data.Property; +import org.apache.olingo.commons.api.data.PrimitiveIterator; import org.apache.olingo.commons.api.edm.EdmComplexType; import org.apache.olingo.commons.api.edm.EdmEntitySet; import org.apache.olingo.commons.api.edm.EdmEntityType; @@ -72,7 +74,9 @@ 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.LevelsExpandOption; import org.apache.olingo.server.api.uri.queryoption.SelectOption; +import org.apache.olingo.server.core.ComplexStreamContent; import org.apache.olingo.server.core.ODataWritableContent; +import org.apache.olingo.server.core.PrimitiveStreamContent; import org.apache.olingo.server.core.serializer.AbstractODataSerializer; import org.apache.olingo.server.core.serializer.SerializerResultImpl; import org.apache.olingo.server.core.serializer.utils.CircleStreamBuffer; @@ -873,6 +877,7 @@ public class ODataXmlSerializer extends AbstractODataSerializer { writeComplexValue(metadata, resolvedType, property.asComplex().getValue(), selectedPaths, xml10InvalidCharReplacement, writer); } + private void writePrimitiveCollection(final EdmPrimitiveType type, final Property property, final Boolean isNullable, final Integer maxLength, final Integer precision, final Integer scale, final Boolean isUnicode, final String xml10InvalidCharReplacement, @@ -896,6 +901,34 @@ public class ODataXmlSerializer extends AbstractODataSerializer { } } + private void writePrimitiveCollectionStreamed(final EdmPrimitiveType type, final PrimitiveIterator iterator, + final Boolean isNullable, final Integer maxLength, final Integer precision, final Integer scale, + final Boolean isUnicode, final String xml10InvalidCharReplacement, + final XMLStreamWriter writer) throws XMLStreamException, EdmPrimitiveTypeException, SerializerException { + for (Object value : iterator) { + writer.writeStartElement(METADATA, Constants.ELEM_ELEMENT, NS_METADATA); + writePrimitiveValue(type, value, isNullable, maxLength, precision, + scale, isUnicode, xml10InvalidCharReplacement, writer); + + writer.writeEndElement(); + } + } + + private void writeComplexCollectionStream(final ServiceMetadata metadata, + final EdmComplexType type, final ComplexIterator iterator, final Set<List<String>> selectedPaths, + final String xml10InvalidCharReplacement, final XMLStreamWriter writer) + throws XMLStreamException, SerializerException { + for (ComplexValue value : iterator) { + writer.writeStartElement(METADATA, Constants.ELEM_ELEMENT, NS_METADATA); + if (derivedComplexType(type, iterator.getType()) != null) { + writer.writeAttribute(METADATA, NS_METADATA, Constants.ATTR_TYPE, iterator.getType()); + } + writeComplexValue(metadata, type, value.getValue(), selectedPaths, + xml10InvalidCharReplacement, writer); + writer.writeEndElement(); + } + } + private void writeComplexCollection(final ServiceMetadata metadata, final EdmComplexType type, final Property property, final Set<List<String>> selectedPaths, final String xml10InvalidCharReplacement, final XMLStreamWriter writer) @@ -1146,6 +1179,61 @@ public class ODataXmlSerializer extends AbstractODataSerializer { } @Override + public SerializerStreamResult primitiveCollectionStreamed(ServiceMetadata metadata, EdmPrimitiveType type, + PrimitiveIterator iterator, PrimitiveSerializerOptions options) throws SerializerException { + return PrimitiveStreamContent.PrimitiveStreamContentForXml(iterator, type, this, metadata, options); + } + + public void primitiveCollectionIntoStream(final ServiceMetadata metadata, final EdmPrimitiveType type, + final PrimitiveIterator iterator, final PrimitiveSerializerOptions options, final OutputStream outputStream) + throws SerializerException { + final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL()); + + SerializerException cachedException = null; + try { + XMLStreamWriter writer = XMLOutputFactory.newInstance().createXMLStreamWriter(outputStream, DEFAULT_CHARSET); + + writer.writeStartDocument(DEFAULT_CHARSET, "1.0"); + writer.writeStartElement(METADATA, Constants.VALUE, NS_METADATA); + writer.writeNamespace(METADATA, NS_METADATA); + if (contextURL != null) { + writer.writeAttribute(METADATA, NS_METADATA, Constants.CONTEXT, + ContextURLBuilder.create(contextURL).toASCIIString()); + } + writeMetadataETag(metadata, writer); + writer.writeAttribute(METADATA, NS_METADATA, Constants.ATTR_TYPE, "#Collection(" + type.getName() + ")"); + writePrimitiveCollectionStreamed(type, iterator, + options == null ? null : options.isNullable(), + options == null ? null : options.getMaxLength(), + options == null ? null : options.getPrecision(), + options == null ? null : options.getScale(), + options == null ? null : options.isUnicode(), + options == null ? null : options.xml10InvalidCharReplacement(), + writer); + writer.writeEndElement(); + writer.writeEndDocument(); + writer.flush(); + writer.close(); + outputStream.close(); + } catch (final XMLStreamException e) { + cachedException = new SerializerException(IO_EXCEPTION_TEXT, e, + SerializerException.MessageKeys.IO_EXCEPTION); + throw cachedException; + } catch (final EdmPrimitiveTypeException e) { + cachedException = new SerializerException("Wrong value for property!", e, + SerializerException.MessageKeys.WRONG_PROPERTY_VALUE, + iterator.getName()); + throw cachedException; + } catch (IOException e) { + cachedException = + new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION); + throw cachedException; + } finally { + closeCircleStreamBufferOutput(outputStream, cachedException); + } + } + + @Override public SerializerResult complexCollection(final ServiceMetadata metadata, final EdmComplexType type, final Property property, final ComplexSerializerOptions options) throws SerializerException { final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL()); @@ -1186,6 +1274,46 @@ public class ODataXmlSerializer extends AbstractODataSerializer { } @Override + public SerializerStreamResult complexCollectionStreamed(ServiceMetadata metadata, EdmComplexType type, + ComplexIterator iterator, ComplexSerializerOptions options) throws SerializerException { + return ComplexStreamContent.ComplexWritableForXml(iterator, type, this, metadata, options); + } + + public void complexCollectionIntoStream(final ServiceMetadata metadata, final EdmComplexType type, + final ComplexIterator iterator, final ComplexSerializerOptions options, final OutputStream outputStream) + throws SerializerException { + final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL()); + SerializerException cachedException = null; + try { + XMLStreamWriter writer = XMLOutputFactory.newInstance().createXMLStreamWriter(outputStream, DEFAULT_CHARSET); + writer.writeStartDocument(DEFAULT_CHARSET, "1.0"); + writer.writeStartElement(METADATA, Constants.VALUE, NS_METADATA); + writer.writeNamespace(METADATA, NS_METADATA); + writer.writeNamespace(DATA, NS_DATA); + writer.writeAttribute(METADATA, NS_METADATA, Constants.ATTR_TYPE, collectionType(type)); + writer.writeAttribute(METADATA, NS_METADATA, Constants.CONTEXT, + ContextURLBuilder.create(contextURL).toASCIIString()); + writeMetadataETag(metadata, writer); + writeComplexCollectionStream(metadata, type, iterator, null, options.xml10InvalidCharReplacement(), writer); + writer.writeEndElement(); + writer.writeEndDocument(); + writer.flush(); + writer.close(); + outputStream.close(); + } catch (final XMLStreamException e) { + cachedException = + new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION); + throw cachedException; + } catch (IOException e) { + cachedException = + new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION); + throw cachedException; + } finally { + closeCircleStreamBufferOutput(outputStream, cachedException); + } + } + + @Override public SerializerResult reference(final ServiceMetadata metadata, final EdmEntitySet edmEntitySet, final Entity entity, final ReferenceSerializerOptions options) throws SerializerException { return entityReference(entity, options); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/685b19e5/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java ---------------------------------------------------------------------- diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java index cf29d5f..98371f6 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java @@ -33,6 +33,7 @@ import java.util.List; import org.apache.commons.io.IOUtils; import org.apache.commons.io.output.ByteArrayOutputStream; +import org.apache.olingo.commons.api.data.ComplexIterator; import org.apache.olingo.commons.api.data.ComplexValue; import org.apache.olingo.commons.api.data.ContextURL; import org.apache.olingo.commons.api.data.ContextURL.Suffix; @@ -40,6 +41,7 @@ import org.apache.olingo.commons.api.data.Entity; import org.apache.olingo.commons.api.data.EntityCollection; import org.apache.olingo.commons.api.data.EntityIterator; import org.apache.olingo.commons.api.data.Operation; +import org.apache.olingo.commons.api.data.PrimitiveIterator; import org.apache.olingo.commons.api.data.Property; import org.apache.olingo.commons.api.data.ValueType; import org.apache.olingo.commons.api.edm.EdmComplexType; @@ -49,15 +51,15 @@ import org.apache.olingo.commons.api.edm.EdmEntityType; import org.apache.olingo.commons.api.edm.EdmPrimitiveType; import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind; import org.apache.olingo.commons.api.edm.EdmProperty; -import org.apache.olingo.commons.api.edm.geo.Point; -import org.apache.olingo.commons.api.edm.geo.Polygon; -import org.apache.olingo.commons.api.edm.geo.SRID; import org.apache.olingo.commons.api.edm.geo.Geospatial.Dimension; import org.apache.olingo.commons.api.edm.geo.GeospatialCollection; import org.apache.olingo.commons.api.edm.geo.LineString; import org.apache.olingo.commons.api.edm.geo.MultiLineString; import org.apache.olingo.commons.api.edm.geo.MultiPoint; import org.apache.olingo.commons.api.edm.geo.MultiPolygon; +import org.apache.olingo.commons.api.edm.geo.Point; +import org.apache.olingo.commons.api.edm.geo.Polygon; +import org.apache.olingo.commons.api.edm.geo.SRID; import org.apache.olingo.commons.api.edmx.EdmxReference; import org.apache.olingo.commons.api.format.ContentType; import org.apache.olingo.server.api.OData; @@ -74,6 +76,7 @@ import org.apache.olingo.server.api.serializer.ReferenceCollectionSerializerOpti 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.serializer.SerializerStreamResult; import org.apache.olingo.server.api.uri.UriHelper; import org.apache.olingo.server.api.uri.queryoption.CountOption; import org.apache.olingo.server.api.uri.queryoption.ExpandItem; @@ -1540,6 +1543,92 @@ public class ODataJsonSerializerTest { } @Test + public void primitiveCollectionStreamed() throws Exception { + final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESCollAllPrim"); + final EdmProperty edmProperty = (EdmProperty) edmEntitySet.getEntityType().getProperty("CollPropertyString"); + final Property property = data.readAll(edmEntitySet).getEntities().get(0).getProperty(edmProperty.getName()); + + final Iterator<?> primitiveIterator = property.asCollection().iterator(); + + final PrimitiveIterator valueProvider = new PrimitiveIterator(property.getName()) { + @Override + public boolean hasNext() { + return primitiveIterator.hasNext(); + } + + @Override + public Object next() { + return primitiveIterator.next(); + } + }; + + final SerializerStreamResult serializerStreamResult = serializerNoMetadata.primitiveCollectionStreamed( + metadata, + (EdmPrimitiveType) edmProperty.getType(), + valueProvider, + null); + + final ByteArrayOutputStream bout = new ByteArrayOutputStream(); + serializerStreamResult.getODataContent().write(bout); + + final String resultString = new String(bout.toByteArray(), "UTF-8"); + + Assert.assertEquals("{\"value\":[\"[email protected]\"," + + "\"[email protected]\",\"[email protected]\"]}", + resultString); + } + + @Test + public void primitiveCollectionStreamedWithError() throws Exception { + final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESCollAllPrim"); + final EdmProperty edmProperty = (EdmProperty) edmEntitySet.getEntityType().getProperty("CollPropertyString"); + final Property property = data.readAll(edmEntitySet).getEntities().get(0).getProperty(edmProperty.getName()); + + final Iterator<?> primitiveIterator = property.asCollection().iterator(); + + // Note - We combine returning null value with serialization option of nullable to throw the error + final PrimitiveIterator valueProvider = new PrimitiveIterator(property.getName()) { + @Override + public boolean hasNext() { + return primitiveIterator.hasNext(); + } + + @Override + public Object next() { + return null; + } + }; + + final ODataContentWriteErrorCallback errorCallback = new ODataContentWriteErrorCallback() { + @Override + public void handleError(ODataContentWriteErrorContext 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."); + } + } + }; + + final SerializerStreamResult serializerStreamResult = serializerNoMetadata.primitiveCollectionStreamed( + metadata, + (EdmPrimitiveType) edmProperty.getType(), + valueProvider, + PrimitiveSerializerOptions.with() + .writeContentErrorCallback(errorCallback) + .nullable(false) + .build()); + + final ByteArrayOutputStream bout = new ByteArrayOutputStream(); + serializerStreamResult.getODataContent().write(bout); + + final String resultString = new String(bout.toByteArray(), "UTF-8"); + Assert.assertEquals("ERROR : WRONG_PROPERTY_VALUE", resultString); + } + + @Test public void complexProperty() throws Exception { final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESMixPrimCollComp"); final EdmProperty edmProperty = (EdmProperty) edmEntitySet.getEntityType().getProperty("PropertyComp"); @@ -1610,6 +1699,90 @@ public class ODataJsonSerializerTest { } @Test + public void complexCollectionStreamed() throws Exception { + final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESMixPrimCollComp"); + final EdmProperty edmProperty = (EdmProperty) edmEntitySet.getEntityType().getProperty("CollPropertyComp"); + final Property property = data.readAll(edmEntitySet).getEntities().get(0).getProperty(edmProperty.getName()); + + final Iterator<?> complexIterator = property.asCollection().iterator(); + + final ComplexIterator valueProvider = new ComplexIterator() { + @Override + public boolean hasNext() { + return complexIterator.hasNext(); + } + + @Override + public ComplexValue next() { + return (ComplexValue) complexIterator.next(); + } + }; + + final SerializerStreamResult serializerStreamResult = serializerNoMetadata.complexCollectionStreamed( + metadata, + (EdmComplexType) edmProperty.getType(), + valueProvider, + null); + + final ByteArrayOutputStream bout = new ByteArrayOutputStream(); + serializerStreamResult.getODataContent().write(bout); + + final String resultString = new String(bout.toByteArray(), "UTF-8"); + + Assert.assertEquals("{\"value\":[{\"PropertyInt16\":123,\"PropertyString\":\"TEST 1\"}," + + "{\"PropertyInt16\":456," + "\"PropertyString\":\"TEST 2\"}," + + "{\"PropertyInt16\":789,\"PropertyString\":\"TEST 3\"}]}", + resultString); + } + + @Test + public void complexCollectionStreamedWithError() throws Exception { + final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESMixPrimCollComp"); + final EdmProperty edmProperty = (EdmProperty) edmEntitySet.getEntityType().getProperty("CollPropertyComp"); + final Property property = data.readAll(edmEntitySet).getEntities().get(0).getProperty(edmProperty.getName()); + + final Iterator<?> complexIterator = property.asCollection().iterator(); + + final ComplexIterator valueProvider = new ComplexIterator() { + @Override + public boolean hasNext() { + return complexIterator.hasNext(); + } + + @Override + public ComplexValue next() { + return new ComplexValue(); + } + }; + + final ODataContentWriteErrorCallback errorCallback = new ODataContentWriteErrorCallback() { + @Override + public void handleError(ODataContentWriteErrorContext 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."); + } + } + }; + + final SerializerStreamResult serializerStreamResult = + serializerNoMetadata.complexCollectionStreamed( + metadata, + (EdmComplexType) edmProperty.getType(), + valueProvider, + ComplexSerializerOptions.with().writeContentErrorCallback(errorCallback).build()); + + final ByteArrayOutputStream bout = new ByteArrayOutputStream(); + serializerStreamResult.getODataContent().write(bout); + + final String resultString = new String(bout.toByteArray(), "UTF-8"); + Assert.assertEquals("ERROR : MISSING_PROPERTY", resultString); + } + + @Test public void complexCollectionPropertyNoMetadata() throws Exception { final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESMixPrimCollComp"); final EdmProperty edmProperty = (EdmProperty) edmEntitySet.getEntityType().getProperty("CollPropertyComp"); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/685b19e5/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerTest.java ---------------------------------------------------------------------- diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerTest.java index 7eae959..c8ee640 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerTest.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerTest.java @@ -21,20 +21,26 @@ package org.apache.olingo.server.core.serializer.xml; import java.io.IOException; import java.io.InputStream; import java.net.URI; +import java.nio.ByteBuffer; +import java.nio.channels.WritableByteChannel; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Collections; import java.util.Date; +import java.util.Iterator; import org.apache.commons.io.IOUtils; +import org.apache.commons.io.output.ByteArrayOutputStream; import org.apache.olingo.commons.api.Constants; +import org.apache.olingo.commons.api.data.ComplexIterator; import org.apache.olingo.commons.api.data.ComplexValue; import org.apache.olingo.commons.api.data.ContextURL; 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.Property; +import org.apache.olingo.commons.api.data.PrimitiveIterator; import org.apache.olingo.commons.api.data.ValueType; import org.apache.olingo.commons.api.edm.EdmComplexType; import org.apache.olingo.commons.api.edm.EdmEntityContainer; @@ -45,6 +51,8 @@ import org.apache.olingo.commons.api.edm.EdmProperty; import org.apache.olingo.commons.api.edmx.EdmxReference; import org.apache.olingo.commons.api.format.ContentType; import org.apache.olingo.server.api.OData; +import org.apache.olingo.server.api.ODataContentWriteErrorCallback; +import org.apache.olingo.server.api.ODataContentWriteErrorContext; import org.apache.olingo.server.api.ServiceMetadata; import org.apache.olingo.server.api.serializer.ComplexSerializerOptions; import org.apache.olingo.server.api.serializer.EntityCollectionSerializerOptions; @@ -55,6 +63,7 @@ import org.apache.olingo.server.api.serializer.ReferenceCollectionSerializerOpti 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.serializer.SerializerStreamResult; import org.apache.olingo.server.api.uri.UriHelper; import org.apache.olingo.server.api.uri.queryoption.CountOption; import org.apache.olingo.server.api.uri.queryoption.ExpandItem; @@ -2189,6 +2198,106 @@ public class ODataXmlSerializerTest { } @Test + public void primitiveCollectionStreamed() throws Exception { + final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESCollAllPrim"); + final EdmProperty edmProperty = (EdmProperty) edmEntitySet.getEntityType().getProperty("CollPropertyString"); + final Property property = data.readAll(edmEntitySet).getEntities().get(0).getProperty(edmProperty.getName()); + + final Iterator<?> primitiveIterator = property.asCollection().iterator(); + + final PrimitiveIterator valueProvider = new PrimitiveIterator(property.getName()) { + @Override + public boolean hasNext() { + return primitiveIterator.hasNext(); + } + + @Override + public Object next() { + return primitiveIterator.next(); + } + }; + + final SerializerStreamResult serializerStreamResult = serializer.primitiveCollectionStreamed( + metadata, + (EdmPrimitiveType) edmProperty.getType(), + valueProvider, + PrimitiveSerializerOptions.with() + .contextURL(ContextURL.with() + .entitySet(edmEntitySet).keyPath("1").navOrPropertyPath(edmProperty.getName()) + .build()) + .build()); + + final ByteArrayOutputStream bout = new ByteArrayOutputStream(); + serializerStreamResult.getODataContent().write(bout); + + final String resultString = new String(bout.toByteArray(), "UTF-8"); + + String expected = "<?xml version='1.0' encoding='UTF-8'?>" + + "<m:value xmlns:m=\"http://docs.oasis-open.org/odata/ns/metadata\" " + + "m:context=\"$metadata#ESCollAllPrim(1)/CollPropertyString\" " + + "m:metadata-etag=\"metadataETag\" m:type=\"#Collection(String)\">" + + "<m:element>[email protected]</m:element>" + + "<m:element>[email protected]</m:element>" + + "<m:element>[email protected]</m:element>" + + "</m:value>"; + checkXMLEqual(expected, resultString); + } + + @Test + public void primitiveCollectionStreamedWithError() throws Exception { + final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESCollAllPrim"); + final EdmProperty edmProperty = (EdmProperty) edmEntitySet.getEntityType().getProperty("CollPropertyString"); + final Property property = data.readAll(edmEntitySet).getEntities().get(0).getProperty(edmProperty.getName()); + + final Iterator<?> primitiveIterator = property.asCollection().iterator(); + + // Note - We combine returning null value with serialization option of nullable to throw the error + final PrimitiveIterator valueProvider = new PrimitiveIterator(property.getName()) { + @Override + public boolean hasNext() { + return primitiveIterator.hasNext(); + } + + @Override + public Object next() { + return null; + } + }; + + final ODataContentWriteErrorCallback errorCallback = new ODataContentWriteErrorCallback() { + @Override + public void handleError(ODataContentWriteErrorContext context, WritableByteChannel channel) { + try { + final String msgKey = context.getODataLibraryException().getMessageKey().getKey(); + final String toChannel = "ERROR : " + msgKey; + channel.write(ByteBuffer.wrap(toChannel.getBytes())); + } catch (IOException e) { + throw new RuntimeException("Error in error."); + } + } + }; + + final SerializerStreamResult serializerStreamResult = + serializer.primitiveCollectionStreamed( + metadata, + (EdmPrimitiveType) edmProperty.getType(), + valueProvider, + PrimitiveSerializerOptions.with() + .contextURL(ContextURL.with() + .entitySet(edmEntitySet).keyPath("1").navOrPropertyPath(edmProperty.getName()) + .build()) + .writeContentErrorCallback(errorCallback) + .nullable(false) + .build()); + + final ByteArrayOutputStream bout = new ByteArrayOutputStream(); + serializerStreamResult.getODataContent().write(bout); + + final String resultString = new String(bout.toByteArray(), "UTF-8"); + Assert.assertEquals("ERROR : WRONG_PROPERTY_VALUE", resultString); + } + + @Test public void complexProperty() throws Exception { final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESMixPrimCollComp"); final EdmProperty edmProperty = (EdmProperty) edmEntitySet.getEntityType().getProperty("PropertyComp"); @@ -2249,6 +2358,116 @@ public class ODataXmlSerializerTest { } @Test + public void complexCollectionStreamed() throws Exception { + final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESMixPrimCollComp"); + final EdmProperty edmProperty = (EdmProperty) edmEntitySet.getEntityType().getProperty("CollPropertyComp"); + final Property property = data.readAll(edmEntitySet).getEntities().get(0).getProperty(edmProperty.getName()); + + final Iterator<?> complexIterator = property.asCollection().iterator(); + + final ComplexIterator valueProvider = new ComplexIterator() { + @Override + public boolean hasNext() { + return complexIterator.hasNext(); + } + + @Override + public ComplexValue next() { + return (ComplexValue) complexIterator.next(); + } + }; + + final SerializerStreamResult serializerStreamResult = serializer.complexCollectionStreamed( + metadata, + (EdmComplexType) edmProperty.getType(), + valueProvider, + ComplexSerializerOptions.with() + .contextURL(ContextURL.with() + .entitySet(edmEntitySet).keyPath("32767").navOrPropertyPath(edmProperty.getName()) + .build()) + .build() + ); + + final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + serializerStreamResult.getODataContent().write(byteArrayOutputStream); + + final String resultString = new String(byteArrayOutputStream.toByteArray(), "UTF-8"); + + String expected = "<?xml version='1.0' encoding='UTF-8'?>\n" + + "<m:value xmlns:m=\"http://docs.oasis-open.org/odata/ns/metadata\"\n" + + " xmlns:d=\"http://docs.oasis-open.org/odata/ns/data\" " + + "m:type=\"#Collection(olingo.odata.test1.CTTwoPrim)\"\n" + + " m:context=\"$metadata#ESMixPrimCollComp(32767)/CollPropertyComp\"\n" + + " m:metadata-etag=\"metadataETag\">\n" + + " <m:element>\n" + + " <d:PropertyInt16 m:type=\"Int16\">123</d:PropertyInt16>\n" + + " <d:PropertyString>TEST 1</d:PropertyString>\n" + + " </m:element>\n" + + " <m:element>\n" + + " <d:PropertyInt16 m:type=\"Int16\">456</d:PropertyInt16>\n" + + " <d:PropertyString>TEST 2</d:PropertyString>\n" + + " </m:element>\n" + + " <m:element>\n" + + " <d:PropertyInt16 m:type=\"Int16\">789</d:PropertyInt16>\n" + + " <d:PropertyString>TEST 3</d:PropertyString>\n" + + " </m:element>\n" + + "</m:value>"; + checkXMLEqual(expected, resultString); + } + + @Test + public void complexCollectionStreamedWithError() throws Exception { + final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESMixPrimCollComp"); + final EdmProperty edmProperty = (EdmProperty) edmEntitySet.getEntityType().getProperty("CollPropertyComp"); + final Property property = data.readAll(edmEntitySet).getEntities().get(0).getProperty(edmProperty.getName()); + + final Iterator<?> complexIterator = property.asCollection().iterator(); + + final ComplexIterator valueProvider = new ComplexIterator() { + @Override + public boolean hasNext() { + return complexIterator.hasNext(); + } + + @Override + public ComplexValue next() { + return new ComplexValue(); + } + }; + + final ODataContentWriteErrorCallback errorCallback = new ODataContentWriteErrorCallback() { + @Override + public void handleError(ODataContentWriteErrorContext 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."); + } + } + }; + + final SerializerStreamResult serializerStreamResult = + serializer.complexCollectionStreamed( + metadata, + (EdmComplexType) edmProperty.getType(), + valueProvider, + ComplexSerializerOptions.with() + .contextURL(ContextURL.with() + .entitySet(edmEntitySet).keyPath("32767").navOrPropertyPath(edmProperty.getName()) + .build()) + .writeContentErrorCallback(errorCallback) + .build()); + + final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + serializerStreamResult.getODataContent().write(byteArrayOutputStream); + + final String resultString = new String(byteArrayOutputStream.toByteArray(), "UTF-8"); + Assert.assertEquals("ERROR : MISSING_PROPERTY", resultString); + } + + @Test public void entityReference() throws Exception { final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESAllPrim"); final Entity entity = data.readAll(edmEntitySet).getEntities().get(0);
