[OLINGO-832] Added additional methods for Java NIO Channels
This commit is contained in:
parent
f4ad8892ad
commit
885ec27ef2
|
@ -19,6 +19,7 @@
|
|||
package org.apache.olingo.server.api;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -32,6 +33,7 @@ public class ODataResponse {
|
|||
private int statusCode = HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode();
|
||||
private final HttpHeaders headers = new HttpHeaders();
|
||||
private InputStream content;
|
||||
private ReadableByteChannel channel;
|
||||
|
||||
/**
|
||||
* Sets the status code.
|
||||
|
@ -132,4 +134,15 @@ public class ODataResponse {
|
|||
return content;
|
||||
}
|
||||
|
||||
public void setChannel(final ReadableByteChannel channel) {
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
public ReadableByteChannel getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public boolean isChannelAvailable() {
|
||||
return channel != null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
package org.apache.olingo.server.api.serializer;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
|
||||
/**
|
||||
* Result type for {@link ODataSerializer} methods
|
||||
|
@ -29,4 +30,8 @@ public interface SerializerResult {
|
|||
* @return serialized content
|
||||
*/
|
||||
InputStream getContent();
|
||||
|
||||
ReadableByteChannel getChannel();
|
||||
|
||||
boolean isNioSupported();
|
||||
}
|
||||
|
|
|
@ -149,7 +149,7 @@ public class ODataHttpHandlerImpl implements ODataHttpHandler {
|
|||
}
|
||||
}
|
||||
|
||||
if (odResponse.getContent() != null) {
|
||||
if (odResponse.getContent() != null || odResponse.isChannelAvailable()) {
|
||||
copyContent(odResponse, response);
|
||||
}
|
||||
}
|
||||
|
@ -160,7 +160,11 @@ public class ODataHttpHandlerImpl implements ODataHttpHandler {
|
|||
try {
|
||||
ByteBuffer inBuffer = ByteBuffer.allocate(COPY_BUFFER_SIZE);
|
||||
output = Channels.newChannel(servletResponse.getOutputStream());
|
||||
input = Channels.newChannel(odataResponse.getContent());
|
||||
if(odataResponse.isChannelAvailable()) {
|
||||
input = odataResponse.getChannel();
|
||||
} else {
|
||||
input = Channels.newChannel(odataResponse.getContent());
|
||||
}
|
||||
while (input.read(inBuffer) > 0) {
|
||||
inBuffer.flip();
|
||||
output.write(inBuffer);
|
||||
|
|
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* 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 java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.apache.olingo.commons.api.data.Entity;
|
||||
import org.apache.olingo.commons.api.data.EntityStreamCollection;
|
||||
import org.apache.olingo.commons.api.edm.EdmEntityType;
|
||||
import org.apache.olingo.server.api.ServiceMetadata;
|
||||
import org.apache.olingo.server.api.serializer.EntitySerializerOptions;
|
||||
import org.apache.olingo.server.api.serializer.SerializerException;
|
||||
import org.apache.olingo.server.api.serializer.SerializerResult;
|
||||
import org.apache.olingo.server.core.serializer.json.ODataJsonStreamSerializer;
|
||||
import org.apache.olingo.server.core.serializer.utils.CircleStreamBuffer;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
|
||||
public class ChannelSerializerResult implements SerializerResult {
|
||||
private ReadableByteChannel channel;
|
||||
|
||||
private static class StreamChannel implements ReadableByteChannel {
|
||||
private static final Charset DEFAULT = Charset.forName("UTF-8");
|
||||
private ByteBuffer head;
|
||||
private ByteBuffer tail;
|
||||
private ODataJsonStreamSerializer jsonSerializer;
|
||||
private EntityStreamCollection coll;
|
||||
private ServiceMetadata metadata;
|
||||
private EdmEntityType entityType;
|
||||
private EntitySerializerOptions options;
|
||||
|
||||
public StreamChannel(EntityStreamCollection coll, EdmEntityType entityType, String head,
|
||||
ODataJsonStreamSerializer jsonSerializer, ServiceMetadata metadata,
|
||||
EntitySerializerOptions options, String tail) {
|
||||
this.coll = coll;
|
||||
this.entityType = entityType;
|
||||
this.head = ByteBuffer.wrap(head.getBytes(DEFAULT));
|
||||
this.jsonSerializer = jsonSerializer;
|
||||
this.metadata = metadata;
|
||||
this.options = options;
|
||||
this.tail = ByteBuffer.wrap(tail.getBytes(DEFAULT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(ByteBuffer dest) throws IOException {
|
||||
ByteBuffer buffer = getCurrentBuffer();
|
||||
if (buffer != null && buffer.hasRemaining()) {
|
||||
int r = buffer.remaining();
|
||||
if(r <= dest.remaining()) {
|
||||
dest.put(buffer);
|
||||
} else {
|
||||
byte[] buf = new byte[dest.remaining()];
|
||||
buffer.get(buf);
|
||||
dest.put(buf);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
ByteBuffer currentBuffer;
|
||||
|
||||
private ByteBuffer getCurrentBuffer() {
|
||||
if(currentBuffer == null) {
|
||||
currentBuffer = head;
|
||||
} if(!currentBuffer.hasRemaining()) {
|
||||
if (coll.hasNext()) {
|
||||
try {
|
||||
// FIXME: mibo_160108: Inefficient buffer handling, replace
|
||||
currentBuffer = serEntity(coll.nextEntity());
|
||||
if(coll.hasNext()) {
|
||||
ByteBuffer b = ByteBuffer.allocate(currentBuffer.position() + 1);
|
||||
currentBuffer.flip();
|
||||
b.put(currentBuffer).put(",".getBytes(DEFAULT));
|
||||
currentBuffer = b;
|
||||
}
|
||||
currentBuffer.flip();
|
||||
} catch (SerializerException e) {
|
||||
return getCurrentBuffer();
|
||||
}
|
||||
} else if(tail.hasRemaining()) {
|
||||
currentBuffer = tail;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return currentBuffer;
|
||||
}
|
||||
|
||||
private ByteBuffer serEntity(Entity entity) throws SerializerException {
|
||||
try {
|
||||
CircleStreamBuffer buffer = new CircleStreamBuffer();
|
||||
OutputStream outputStream = buffer.getOutputStream();
|
||||
JsonGenerator json = new JsonFactory().createGenerator(outputStream);
|
||||
jsonSerializer.writeEntity(metadata, entityType, entity, null,
|
||||
options == null ? null : options.getExpand(),
|
||||
options == null ? null : options.getSelect(),
|
||||
options != null && options.getWriteOnlyReferences(),
|
||||
json);
|
||||
|
||||
json.close();
|
||||
outputStream.close();
|
||||
return buffer.getBuffer();
|
||||
} catch (final IOException e) {
|
||||
return ByteBuffer.wrap(("ERROR" + e.getMessage()).getBytes());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getContent() {
|
||||
return Channels.newInputStream(this.channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadableByteChannel getChannel() {
|
||||
return this.channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNioSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private ChannelSerializerResult(ReadableByteChannel channel) {
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
public static SerializerResultBuilder with(EntityStreamCollection coll, EdmEntityType entityType,
|
||||
ODataJsonStreamSerializer jsonSerializer, ServiceMetadata metadata, EntitySerializerOptions options) {
|
||||
return new SerializerResultBuilder(coll, entityType, jsonSerializer, metadata, options);
|
||||
}
|
||||
|
||||
public static class SerializerResultBuilder {
|
||||
private ODataJsonStreamSerializer jsonSerializer;
|
||||
private EntityStreamCollection coll;
|
||||
private ServiceMetadata metadata;
|
||||
private EdmEntityType entityType;
|
||||
private EntitySerializerOptions options;
|
||||
private String head;
|
||||
private String tail;
|
||||
|
||||
public SerializerResultBuilder(EntityStreamCollection coll, EdmEntityType entityType,
|
||||
ODataJsonStreamSerializer jsonSerializer, ServiceMetadata metadata, EntitySerializerOptions options) {
|
||||
this.coll = coll;
|
||||
this.entityType = entityType;
|
||||
this.jsonSerializer = jsonSerializer;
|
||||
this.metadata = metadata;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
public SerializerResultBuilder addHead(String head) {
|
||||
this.head = head;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SerializerResultBuilder addTail(String tail) {
|
||||
this.tail = tail;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SerializerResult build() {
|
||||
ReadableByteChannel input = new StreamChannel(coll, entityType, head, jsonSerializer, metadata, options, tail);
|
||||
return new ChannelSerializerResult(input);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,6 +19,9 @@
|
|||
package org.apache.olingo.server.core.serializer;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.channels.Channel;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
|
||||
import org.apache.olingo.server.api.serializer.SerializerResult;
|
||||
|
||||
|
@ -30,6 +33,16 @@ public class SerializerResultImpl implements SerializerResult {
|
|||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadableByteChannel getChannel() {
|
||||
return Channels.newChannel(getContent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNioSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static SerializerResultBuilder with() {
|
||||
return new SerializerResultBuilder();
|
||||
}
|
||||
|
|
|
@ -34,6 +34,10 @@ import java.io.ByteArrayInputStream;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.channels.Channel;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
|
||||
public class StreamSerializerResult implements SerializerResult {
|
||||
private InputStream content;
|
||||
|
@ -121,6 +125,16 @@ public class StreamSerializerResult implements SerializerResult {
|
|||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadableByteChannel getChannel() {
|
||||
return Channels.newChannel(getContent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNioSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private StreamSerializerResult(InputStream content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@ import org.apache.olingo.server.api.uri.queryoption.ExpandItem;
|
|||
import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.SelectOption;
|
||||
import org.apache.olingo.server.core.serializer.AbstractODataSerializer;
|
||||
import org.apache.olingo.server.core.serializer.ChannelSerializerResult;
|
||||
import org.apache.olingo.server.core.serializer.SerializerResultImpl;
|
||||
import org.apache.olingo.server.core.serializer.StreamSerializerResult;
|
||||
import org.apache.olingo.server.core.serializer.utils.CircleStreamBuffer;
|
||||
|
@ -134,7 +135,9 @@ public class ODataJsonStreamSerializer extends ODataJsonSerializer {
|
|||
opt.expand(options.getExpand()).select(options
|
||||
.getSelect()).writeOnlyReferences(options.getWriteOnlyReferences());
|
||||
}
|
||||
return StreamSerializerResult.with(coll, entityType, this, metadata, opt.build())
|
||||
// return StreamSerializerResult.with(coll, entityType, this, metadata, opt.build())
|
||||
// .addHead(head).addTail(tail).build();
|
||||
return ChannelSerializerResult.with(coll, entityType, this, metadata, opt.build())
|
||||
.addHead(head).addTail(tail).build();
|
||||
} catch (final IOException e) {
|
||||
cachedException =
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
|
@ -193,6 +194,25 @@ public class CircleStreamBuffer {
|
|||
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
|
||||
|
|
|
@ -536,8 +536,11 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
|
|||
serializeEntityStreamCollectionFixed(request,
|
||||
entitySetSerialization, edmEntitySet, edmEntityType, requestedContentType,
|
||||
expand, select, countOption, id);
|
||||
response.setContent(serializerResult.getContent());
|
||||
|
||||
if(serializerResult.isNioSupported()) {
|
||||
response.setChannel(serializerResult.getChannel());
|
||||
} else {
|
||||
response.setContent(serializerResult.getContent());
|
||||
}
|
||||
response.setStatusCode(HttpStatusCode.OK.getStatusCode());
|
||||
response.setHeader(HttpHeader.CONTENT_TYPE, requestedContentType.toContentTypeString());
|
||||
if (pageSize != null) {
|
||||
|
@ -631,6 +634,9 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
|
|||
|
||||
@Override
|
||||
public Entity nextEntity() {
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(1000);
|
||||
} catch (InterruptedException e) { }
|
||||
return test.next();
|
||||
}
|
||||
};
|
||||
|
@ -647,6 +653,7 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
|
|||
.build());
|
||||
}
|
||||
|
||||
|
||||
private SerializerResult serializeEntityCollection(final ODataRequest request, final EntityCollection
|
||||
entityCollection, final EdmEntitySet edmEntitySet, final EdmEntityType edmEntityType,
|
||||
final ContentType requestedFormat, final ExpandOption expand, final SelectOption select,
|
||||
|
|
Loading…
Reference in New Issue