/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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.elasticsearch.cloud.azure.storage;
import com.microsoft.azure.storage.LocationMode;
import com.microsoft.azure.storage.StorageException;
import org.elasticsearch.common.blobstore.BlobMetaData;
import org.elasticsearch.common.blobstore.support.PlainBlobMetaData;
import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.settings.Settings;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URISyntaxException;
import java.nio.file.NoSuchFileException;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* In memory storage for unit tests
*/
public class AzureStorageServiceMock extends AbstractComponent implements AzureStorageService {
protected final Map<String, ByteArrayOutputStream> blobs = new ConcurrentHashMap<>();
public AzureStorageServiceMock() {
super(Settings.EMPTY);
}
@Override
public boolean doesContainerExist(String account, LocationMode mode, String container) {
return true;
}
@Override
public void removeContainer(String account, LocationMode mode, String container) {
}
@Override
public void createContainer(String account, LocationMode mode, String container) {
}
@Override
public void deleteFiles(String account, LocationMode mode, String container, String path) {
}
@Override
public boolean blobExists(String account, LocationMode mode, String container, String blob) {
return blobs.containsKey(blob);
}
@Override
public void deleteBlob(String account, LocationMode mode, String container, String blob) {
blobs.remove(blob);
}
@Override
public InputStream getInputStream(String account, LocationMode mode, String container, String blob) throws IOException {
if (!blobExists(account, mode, container, blob)) {
throw new NoSuchFileException("missing blob [" + blob + "]");
}
return new ByteArrayInputStream(blobs.get(blob).toByteArray());
}
@Override
public OutputStream getOutputStream(String account, LocationMode mode, String container, String blob) throws URISyntaxException, StorageException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
blobs.put(blob, outputStream);
return outputStream;
}
@Override
public Map<String, BlobMetaData> listBlobsByPrefix(String account, LocationMode mode, String container, String keyPath, String prefix) {
MapBuilder<String, BlobMetaData> blobsBuilder = MapBuilder.newMapBuilder();
blobs.forEach((String blobName, ByteArrayOutputStream bos) -> {
final String checkBlob;
if (keyPath != null && !keyPath.isEmpty()) {
// strip off key path from the beginning of the blob name
checkBlob = blobName.replace(keyPath, "");
} else {
checkBlob = blobName;
}
if (prefix == null || startsWithIgnoreCase(checkBlob, prefix)) {
blobsBuilder.put(blobName, new PlainBlobMetaData(checkBlob, bos.size()));
}
});
return blobsBuilder.immutableMap();
}
@Override
public void moveBlob(String account, LocationMode mode, String container, String sourceBlob, String targetBlob) throws URISyntaxException, StorageException {
for (String blobName : blobs.keySet()) {
if (endsWithIgnoreCase(blobName, sourceBlob)) {
ByteArrayOutputStream outputStream = blobs.get(blobName);
blobs.put(blobName.replace(sourceBlob, targetBlob), outputStream);
blobs.remove(blobName);
}
}
}
/**
* Test if the given String starts with the specified prefix,
* ignoring upper/lower case.
*
* @param str the String to check
* @param prefix the prefix to look for
* @see java.lang.String#startsWith
*/
public static boolean startsWithIgnoreCase(String str, String prefix) {
if (str == null || prefix == null) {
return false;
}
if (str.startsWith(prefix)) {
return true;
}
if (str.length() < prefix.length()) {
return false;
}
String lcStr = str.substring(0, prefix.length()).toLowerCase(Locale.ROOT);
String lcPrefix = prefix.toLowerCase(Locale.ROOT);
return lcStr.equals(lcPrefix);
}
/**
* Test if the given String ends with the specified suffix,
* ignoring upper/lower case.
*
* @param str the String to check
* @param suffix the suffix to look for
* @see java.lang.String#startsWith
*/
public static boolean endsWithIgnoreCase(String str, String suffix) {
if (str == null || suffix == null) {
return false;
}
if (str.endsWith(suffix)) {
return true;
}
if (str.length() < suffix.length()) {
return false;
}
String lcStr = str.substring(0, suffix.length()).toLowerCase(Locale.ROOT);
String lcPrefix = suffix.toLowerCase(Locale.ROOT);
return lcStr.equals(lcPrefix);
}
}