/** * 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.hadoop.fs.azure; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; /** * A simple memory key-value store to help mock the Windows Azure Storage * implementation for unit testing. */ public class InMemoryBlockBlobStore { private final HashMap<String, Entry> blobs = new HashMap<String, Entry>(); private HashMap<String, String> containerMetadata; public synchronized Iterable<String> getKeys() { return new ArrayList<String>(blobs.keySet()); } public static class ListBlobEntry { private final String key; private final HashMap<String, String> metadata; private final int contentLength; private final boolean isPageBlob; ListBlobEntry(String key, HashMap<String, String> metadata, int contentLength, boolean isPageBlob) { this.key = key; this.metadata = metadata; this.contentLength = contentLength; this.isPageBlob = isPageBlob; } public String getKey() { return key; } public HashMap<String, String> getMetadata() { return metadata; } public int getContentLength() { return contentLength; } public boolean isPageBlob() { return isPageBlob; } } /** * List all the blobs whose key starts with the given prefix. * * @param prefix * The prefix to check. * @param includeMetadata * If set, the metadata in the returned listing will be populated; * otherwise it'll be null. * @return The listing. */ public synchronized Iterable<ListBlobEntry> listBlobs(String prefix, boolean includeMetadata) { ArrayList<ListBlobEntry> list = new ArrayList<ListBlobEntry>(); for (Map.Entry<String, Entry> entry : blobs.entrySet()) { if (entry.getKey().startsWith(prefix)) { list.add(new ListBlobEntry( entry.getKey(), includeMetadata ? new HashMap<String, String>(entry.getValue().metadata) : null, entry.getValue().content.length, entry.getValue().isPageBlob)); } } return list; } public synchronized byte[] getContent(String key) { return blobs.get(key).content; } @SuppressWarnings("unchecked") public synchronized void setContent(String key, byte[] value, HashMap<String, String> metadata, boolean isPageBlob, long length) { blobs.put(key, new Entry(value, (HashMap<String, String>)metadata.clone(), isPageBlob, length)); } @SuppressWarnings("unchecked") public synchronized void setMetadata(String key, HashMap<String, String> metadata) { blobs.get(key).metadata = (HashMap<String, String>) metadata.clone(); } public OutputStream uploadBlockBlob(final String key, final HashMap<String, String> metadata) { setContent(key, new byte[0], metadata, false, 0); return new ByteArrayOutputStream() { @Override public void flush() throws IOException { super.flush(); byte[] tempBytes = toByteArray(); setContent(key, tempBytes, metadata, false, tempBytes.length); } @Override public void close() throws IOException { super.close(); byte[] tempBytes = toByteArray(); setContent(key, tempBytes, metadata, false, tempBytes.length); } }; } public OutputStream uploadPageBlob(final String key, final HashMap<String, String> metadata, final long length) { setContent(key, new byte[0], metadata, true, length); return new ByteArrayOutputStream() { @Override public void flush() throws IOException { super.flush(); setContent(key, toByteArray(), metadata, true, length); } }; } public synchronized void copy(String sourceKey, String destKey) { blobs.put(destKey, blobs.get(sourceKey)); } public synchronized void delete(String key) { blobs.remove(key); } public synchronized boolean exists(String key) { return blobs.containsKey(key); } @SuppressWarnings("unchecked") public synchronized HashMap<String, String> getMetadata(String key) { return (HashMap<String, String>) blobs.get(key).metadata.clone(); } public synchronized HashMap<String, String> getContainerMetadata() { return containerMetadata; } public synchronized void setContainerMetadata(HashMap<String, String> metadata) { containerMetadata = metadata; } private static class Entry { private byte[] content; private HashMap<String, String> metadata; private boolean isPageBlob; @SuppressWarnings("unused") // TODO: use it private long length; public Entry(byte[] content, HashMap<String, String> metadata, boolean isPageBlob, long length) { this.content = content; this.metadata = metadata; this.isPageBlob = isPageBlob; this.length = length; } } }