/*
* Copyright (C) 2014 Indeed Inc.
*
* 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 com.indeed.imhotep.io.caching;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SqarAutomountingRemoteFileSystem extends RemoteFileSystem {
private static final String SUFFIX = ".sqar";
final private String mountPoint;
final private RemoteFileSystem parentFS;
final private RemoteFileSystemMounter mounter;
public SqarAutomountingRemoteFileSystem(Map<String,Object> settings,
RemoteFileSystem parent,
RemoteFileSystemMounter mounter) throws IOException {
this.parentFS = parent;
this.mounter = mounter;
String mp = (String)settings.get("mountpoint");
if (! mp.endsWith(DELIMITER)) {
/* add delimiter to the end */
mp = mp + DELIMITER;
}
mp = mounter.getRootMountPoint() + mp;
mountPoint = mp.replace("//", "/");
}
private String scanPathForSqar(String fullPath) {
final String relativePath = mounter.getMountRelativePath(fullPath, mountPoint);
String subPath = "";
RemoteFileInfo info;
/* test for mounts on the path */
for (String part : relativePath.split(DELIMITER)) {
String sqarName = part + SUFFIX + DELIMITER;
info = parentFS.stat(this.mountPoint + subPath + sqarName);
if (info != null) {
/* found a sqar */
return subPath + part + DELIMITER;
}
subPath += part + DELIMITER;
}
/* did not find anything */
return null;
}
private SqarRemoteFileSystem mountNewSqarFS(String path) {
final SqarRemoteFileSystem fs;
final Map<String,Object> settings;
settings = new HashMap<String,Object>();
settings.put("mountpoint", path);
fs = new SqarRemoteFileSystem(settings, parentFS, mounter);
mounter.addFileSystem(path, fs);
return fs;
}
@Override
public void copyFileInto(String fullPath, File localFile) throws IOException {
final String sqarPath;
final SqarRemoteFileSystem newFS;
try {
parentFS.copyFileInto(fullPath, localFile);
} catch(IOException e) {
sqarPath = scanPathForSqar(fullPath);
if (sqarPath == null) {
/* no sqar archives found */
throw e;
}
newFS = mountNewSqarFS(sqarPath);
/* now rerun query with new fs */
newFS.copyFileInto(fullPath, localFile);
}
}
@Override
public File loadFile(String fullPath) throws IOException {
final String sqarPath;
final SqarRemoteFileSystem newFS;
try {
return parentFS.loadFile(fullPath);
} catch(IOException e) {
sqarPath = scanPathForSqar(fullPath);
if (sqarPath == null) {
/* no sqar archives found */
throw e;
}
newFS = mountNewSqarFS(sqarPath);
/* now rerun query with new fs */
return newFS.loadFile(fullPath);
}
}
@Override
public RemoteFileInfo stat(String fullPath) {
final String relativePath = mounter.getMountRelativePath(fullPath, mountPoint);
final RemoteFileInfo current;
final String sqarPath;
final SqarRemoteFileSystem newFS;
current = parentFS.stat(fullPath);
if (current != null) {
return current;
}
sqarPath = scanPathForSqar(fullPath);
if (sqarPath == null) {
/* no sqar archives found */
return null;
}
/* do not open the archive unless we need to */
if (sqarPath.equals(relativePath + DELIMITER)) {
return new RemoteFileInfo(fullPath, RemoteFileInfo.TYPE_DIR);
}
newFS = mountNewSqarFS(sqarPath);
/* now rerun query with new fs */
return newFS.stat(fullPath);
}
@Override
public List<RemoteFileInfo> readDir(String fullPath) {
List<RemoteFileInfo> infos;
final String sqarPath;
final SqarRemoteFileSystem newFS;
infos = parentFS.readDir(fullPath);
if (infos == null) {
/* check the path for sqar archives */
sqarPath = scanPathForSqar(fullPath);
if (sqarPath == null) {
/* no sqar archives found */
return null;
}
newFS = mountNewSqarFS(sqarPath);
/* now rerun query with new fs */
infos = newFS.readDir(fullPath);
if (infos == null) {
/* still an error */
return null;
}
}
/* scan results for sqar archives */
for (RemoteFileInfo info : infos) {
if (info.path.endsWith(SUFFIX)) {
final String path = info.path;
/* remove suffix */
info.path = path.substring(0, path.length() - SUFFIX.length());
}
}
return infos;
}
@Override
public String getMountPoint() {
return this.mountPoint;
}
@Override
public Map<String, File> loadDirectory(String fullPath, File location) throws IOException {
// not needed right now
throw new UnsupportedOperationException();
}
@Override
public InputStream getInputStreamForFile(String fullPath,
long startOffset,
long maxReadLength) throws IOException {
return parentFS.getInputStreamForFile(fullPath, startOffset, maxReadLength);
}
}