/* * Copyright 2011 the original author or authors. * * Licensed 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.springframework.async.web.client.grizzly; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.concurrent.LinkedBlockingDeque; import org.glassfish.grizzly.Buffer; import org.glassfish.grizzly.http.HttpContent; import org.glassfish.grizzly.http.HttpHeader; import org.glassfish.grizzly.http.HttpResponsePacket; import org.glassfish.grizzly.http.util.DataChunk; import org.glassfish.grizzly.http.util.MimeHeaders; import org.glassfish.grizzly.memory.HeapMemoryManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.async.AbstractCompletionHandler; import org.springframework.async.CompletionHandler; import org.springframework.async.Promise; import org.springframework.async.http.client.ClientHttpResponse; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; /** * @author Jon Brisbin <jon@jbrisbin.com> */ @SuppressWarnings({"unchecked"}) public class GrizzlyClientHttpResponse implements ClientHttpResponse { private final Logger log = LoggerFactory.getLogger(getClass()); private final HeapMemoryManager heap = new HeapMemoryManager(); private final String readMutex = "read"; private Promise<HttpHeaders> headers = new GrizzlyFuturePromise<HttpHeaders>(); private HttpHeader responseHeader; private Promise<HttpStatus> status = new GrizzlyFuturePromise<HttpStatus>(); private Promise<String> statusText = new GrizzlyFuturePromise<String>(); private LinkedBlockingDeque<Buffer> buffers = new LinkedBlockingDeque<Buffer>(); private CompletionHandler<?> completionHandler; private ReadableByteChannel readChannel = new ReadableByteChannel() { @Override public int read(ByteBuffer buffer) throws IOException { Buffer b = buffers.peek(); if (null == b) { return 0; } else { synchronized (readMutex) { int start = buffer.position(); b = buffers.pop(); b.get(buffer); return buffer.position() - start; } } } @Override public boolean isOpen() { return true; } @Override public void close() throws IOException { } }; public void addContent(HttpContent httpContent) { if (!headers.isDone()) { responseHeader = httpContent.getHttpHeader(); org.glassfish.grizzly.http.util.HttpStatus responseStatus = ((HttpResponsePacket) responseHeader).getHttpStatus(); status.result(HttpStatus.valueOf(responseStatus.getStatusCode())); statusText.result(new String(responseStatus.getReasonPhraseBytes())); HttpHeaders httpHeaders = new HttpHeaders(); MimeHeaders hdrs = httpContent.getHttpHeader().getHeaders(); for (String name : hdrs.names()) { List<String> values = new ArrayList<String>(); DataChunk chunk = hdrs.getValue(name); String val = null; switch (chunk.getType()) { case String: val = chunk.toString(Charset.defaultCharset()); break; case Buffer: val = chunk.getBufferChunk().toString(Charset.defaultCharset()); break; default: log.info("type=" + chunk.getType()); log.info("chunk=" + chunk); } if (null != val) { values.add(val); } if ("Content-Type".equalsIgnoreCase(name)) { httpHeaders.setContentType(MediaType.parseMediaType(val)); } else { httpHeaders.put(name, values); } } headers.result(httpHeaders); } Buffer contentBuffer = httpContent.getContent(); if (null != contentBuffer && contentBuffer.remaining() > 0) { synchronized (readMutex) { if (null != completionHandler) { completionHandler.chunk(contentBuffer.toByteBuffer()); } else { buffers.push(contentBuffer); } } } } @Override public <V> void setCompletionHandler(CompletionHandler<V> completionHandler) { this.completionHandler = completionHandler; if (!buffers.isEmpty()) { List<Buffer> bufferList = new ArrayList<Buffer>(); buffers.drainTo(bufferList); for (Buffer b : bufferList) { completionHandler.chunk(b.toByteBuffer()); } } } @Override public Promise<HttpStatus> getStatusCode() throws IOException { return status; } @Override public Promise<String> getStatusText() throws IOException { return statusText; } @Override public void close() { } @Override public Promise<HttpHeaders> getHeaders() { return headers; } private class WriteToChannelCompletionHandler extends AbstractCompletionHandler { @Override public boolean chunk(ByteBuffer buffer) { return false; } } }