| /* |
| * 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.jclouds.io.internal; |
| |
| import static com.google.common.base.Preconditions.checkArgument; |
| import static com.google.common.base.Preconditions.checkNotNull; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.Arrays; |
| import java.util.Iterator; |
| import java.util.NoSuchElementException; |
| |
| import javax.inject.Singleton; |
| |
| import org.jclouds.io.ContentMetadata; |
| import org.jclouds.io.Payload; |
| import org.jclouds.io.Payloads; |
| import org.jclouds.io.PayloadSlicer; |
| import org.jclouds.io.payloads.BaseMutableContentMetadata; |
| import org.jclouds.io.payloads.ByteSourcePayload; |
| |
| import com.google.common.base.Charsets; |
| import com.google.common.base.Throwables; |
| import com.google.common.hash.HashCode; |
| import com.google.common.io.ByteSource; |
| import com.google.common.io.ByteStreams; |
| import com.google.common.io.Files; |
| |
| @Singleton |
| public class BasePayloadSlicer implements PayloadSlicer { |
| |
| private static final class InputStreamPayloadIterable implements Iterable<Payload> { |
| private final InputStream input; |
| private final ContentMetadata metaData; |
| |
| InputStreamPayloadIterable(InputStream input, ContentMetadata metaData) { |
| this.input = checkNotNull(input, "input"); |
| this.metaData = checkNotNull(metaData, "metaData"); |
| } |
| |
| @Override |
| public Iterator<Payload> iterator() { |
| return new InputStreamPayloadIterator(input, metaData); |
| } |
| } |
| |
| private static final class InputStreamPayloadIterator implements Iterator<Payload> { |
| private final InputStream input; |
| private final ContentMetadata metaData; |
| private Payload nextPayload; |
| private final int readLen; |
| |
| InputStreamPayloadIterator(InputStream input, ContentMetadata metaData) { |
| this.input = checkNotNull(input, "input"); |
| this.metaData = checkNotNull(metaData, "metaData"); |
| this.readLen = checkNotNull(this.metaData.getContentLength(), "content-length").intValue(); |
| |
| this.nextPayload = getNextPayload(); |
| } |
| |
| @Override |
| public boolean hasNext() { |
| return nextPayload != null; |
| } |
| |
| @Override |
| public Payload next() { |
| Payload payload; |
| |
| if (!hasNext()) |
| throw new NoSuchElementException(); |
| |
| payload = nextPayload; |
| nextPayload = getNextPayload(); |
| |
| return payload; |
| } |
| |
| @Override |
| public void remove() { |
| throw new UnsupportedOperationException("Payload iterator does not support removal"); |
| } |
| |
| private Payload getNextPayload() { |
| byte[] content = new byte[readLen]; |
| int offset = 0; |
| |
| try { |
| while (true) { |
| int read = input.read(content, offset, readLen - offset); |
| if (read <= 0) { |
| if (offset == 0) { |
| return null; |
| } else { |
| break; |
| } |
| } |
| offset += read; |
| } |
| } catch (IOException e) { |
| throw Throwables.propagate(e); |
| } |
| |
| return createPayload((content.length == offset) ? content : Arrays.copyOf(content, offset)); |
| } |
| |
| private Payload createPayload(byte[] content) { |
| Payload payload = null; |
| |
| if (content.length > 0) { |
| payload = Payloads.newByteArrayPayload(content); |
| ContentMetadata cm = metaData.toBuilder().contentLength((long)content.length).contentMD5((HashCode) null).build(); |
| payload.setContentMetadata(BaseMutableContentMetadata.fromContentMetadata(cm)); |
| } |
| |
| return payload; |
| } |
| |
| } |
| |
| private static final class ByteSourcePayloadIterable implements Iterable<Payload> { |
| private final ByteSource input; |
| private final ContentMetadata metaData; |
| |
| ByteSourcePayloadIterable(ByteSource input, ContentMetadata metaData) { |
| this.input = checkNotNull(input, "input"); |
| this.metaData = checkNotNull(metaData, "metaData"); |
| } |
| |
| @Override |
| public Iterator<Payload> iterator() { |
| return new ByteSourcePayloadIterator(input, metaData); |
| } |
| } |
| |
| private static final class ByteSourcePayloadIterator implements Iterator<Payload> { |
| private final ByteSource input; |
| private final ContentMetadata metaData; |
| private Payload nextPayload; |
| private long offset = 0; |
| private final long readLen; |
| |
| ByteSourcePayloadIterator(ByteSource input, ContentMetadata metaData) { |
| this.input = checkNotNull(input, "input"); |
| this.metaData = checkNotNull(metaData, "metaData"); |
| this.readLen = checkNotNull(this.metaData.getContentLength(), "content-length").longValue(); |
| this.nextPayload = getNextPayload(); |
| } |
| |
| @Override |
| public boolean hasNext() { |
| return nextPayload != null; |
| } |
| |
| @Override |
| public Payload next() { |
| if (!hasNext()) { |
| throw new NoSuchElementException(); |
| } |
| |
| Payload payload = nextPayload; |
| nextPayload = getNextPayload(); |
| |
| return payload; |
| } |
| |
| @Override |
| public void remove() { |
| throw new UnsupportedOperationException("Payload iterator does not support removal"); |
| } |
| |
| private Payload getNextPayload() { |
| ByteSource byteSource; |
| long byteSourceSize; |
| try { |
| if (offset >= input.size()) { |
| return null; |
| } |
| byteSource = input.slice(offset, readLen); |
| byteSourceSize = byteSource.size(); |
| } catch (IOException e) { |
| throw Throwables.propagate(e); |
| } |
| |
| Payload nextPayload = new ByteSourcePayload(byteSource); |
| ContentMetadata cm = metaData.toBuilder() |
| .contentLength(byteSourceSize) |
| .contentMD5((HashCode) null) |
| .build(); |
| nextPayload.setContentMetadata(BaseMutableContentMetadata.fromContentMetadata(cm)); |
| offset += byteSourceSize; |
| return nextPayload; |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public Payload slice(Payload input, long offset, long length) { |
| checkNotNull(input); |
| checkArgument(offset >= 0, "offset is negative"); |
| checkArgument(length >= 0, "length is negative"); |
| Payload returnVal; |
| if (input.getRawContent() instanceof File) { |
| returnVal = doSlice((File) input.getRawContent(), offset, length); |
| } else if (input.getRawContent() instanceof String) { |
| returnVal = doSlice((String) input.getRawContent(), offset, length); |
| } else if (input.getRawContent() instanceof byte[]) { |
| returnVal = doSlice((byte[]) input.getRawContent(), offset, length); |
| } else if (input.getRawContent() instanceof InputStream) { |
| returnVal = doSlice((InputStream) input.getRawContent(), offset, length); |
| } else if (input.getRawContent() instanceof ByteSource) { |
| returnVal = doSlice((ByteSource) input.getRawContent(), offset, length); |
| } else { |
| returnVal = doSlice(input, offset, length); |
| } |
| return copyMetadataAndSetLength(input, returnVal, length); |
| } |
| |
| protected Payload doSlice(Payload content, long offset, long length) { |
| return doSlice(content.getInput(), offset, length); |
| } |
| |
| protected Payload doSlice(String content, long offset, long length) { |
| return doSlice(content.getBytes(), offset, length); |
| } |
| |
| protected Payload doSlice(File content, long offset, long length) { |
| return doSlice(Files.asByteSource(content), offset, length); |
| } |
| |
| protected Payload doSlice(InputStream content, long offset, long length) { |
| try { |
| ByteStreams.skipFully(content, offset); |
| } catch (IOException ioe) { |
| throw Throwables.propagate(ioe); |
| } |
| return Payloads.newInputStreamPayload(ByteStreams.limit(content, length)); |
| } |
| |
| protected Payload doSlice(ByteSource content, long offset, long length) { |
| return Payloads.newByteSourcePayload(content.slice(offset, length)); |
| } |
| |
| protected Payload doSlice(byte[] content, long offset, long length) { |
| checkArgument(offset <= Integer.MAX_VALUE, "offset is too big for an array"); |
| checkArgument(length <= Integer.MAX_VALUE, "length is too big for an array"); |
| // TODO(adriancole): Make ByteArrayPayload carry offset, length as opposed to wrapping here. |
| return Payloads.newByteSourcePayload(ByteSource.wrap(content).slice(offset, length)); |
| } |
| |
| protected Payload copyMetadataAndSetLength(Payload input, Payload returnVal, long length) { |
| returnVal.setContentMetadata(BaseMutableContentMetadata.fromContentMetadata(input.getContentMetadata() |
| .toBuilder().contentLength(length).contentMD5((HashCode) null).build())); |
| return returnVal; |
| } |
| |
| @Override |
| public Iterable<Payload> slice(Payload input, long size) { |
| checkNotNull(input, "input"); |
| checkArgument(size >= 0, "size must be non-negative but was: %s", size); |
| |
| ContentMetadata meta = BaseMutableContentMetadata.fromContentMetadata(input.getContentMetadata()) |
| .toBuilder() |
| .contentLength(size) |
| .contentMD5((HashCode) null) |
| .build(); |
| Object rawContent = input.getRawContent(); |
| if (rawContent instanceof File) { |
| return doSlice((File) rawContent, meta); |
| } else if (rawContent instanceof String) { |
| return doSlice((String) rawContent, meta); |
| } else if (rawContent instanceof byte[]) { |
| return doSlice((byte[]) rawContent, meta); |
| } else if (rawContent instanceof InputStream) { |
| return doSlice((InputStream) rawContent, meta); |
| } else if (rawContent instanceof ByteSource) { |
| return doSlice((ByteSource) rawContent, meta); |
| } else { |
| return doSlice(input, meta); |
| } |
| |
| } |
| |
| protected Iterable<Payload> doSlice(Payload input, ContentMetadata meta) { |
| return doSlice(input.getInput(), meta); |
| } |
| |
| protected Iterable<Payload> doSlice(String rawContent, ContentMetadata meta) { |
| return doSlice(ByteSource.wrap(rawContent.getBytes(Charsets.UTF_8)), meta); |
| } |
| |
| protected Iterable<Payload> doSlice(byte[] rawContent, ContentMetadata meta) { |
| return doSlice(ByteSource.wrap(rawContent), meta); |
| } |
| |
| protected Iterable<Payload> doSlice(File rawContent, ContentMetadata meta) { |
| return doSlice(Files.asByteSource(rawContent), meta); |
| } |
| |
| protected Iterable<Payload> doSlice(InputStream rawContent, ContentMetadata meta) { |
| return new InputStreamPayloadIterable(rawContent, meta); |
| } |
| |
| protected Iterable<Payload> doSlice(ByteSource rawContent, ContentMetadata meta) { |
| return new ByteSourcePayloadIterable(rawContent, meta); |
| } |
| } |