/* * 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); } }