/* * 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 com.indeed.util.core.io.Closeables2; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FSDataInputStream; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.log4j.Logger; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class HDFSRemoteFileSystem extends RemoteFileSystem { private static final Logger log = Logger.getLogger(HDFSRemoteFileSystem.class); private final RemoteFileSystemMounter mounter; private final String mountPoint; private final Path hdfsBasePath; private final FileSystem fs; HDFSRemoteFileSystem(Map<String,Object> settings, RemoteFileSystemMounter mounter) throws IOException { this.mounter = mounter; try { mountPoint = (String) settings.get("mountpoint"); if (mountPoint != null) { hdfsBasePath = new Path(new URI(mountPoint)); } else { hdfsBasePath = new Path("/var/imhotep/"); } String kerbPrincipal = (String) settings.get("kerberos.principal"); String kerbKeytab = (String) settings.get("kerberos.keytab"); KerberosUtils.loginFromKeytab(kerbPrincipal, kerbKeytab); fs = FileSystem.get(new Configuration()); log.info("Using path " + hdfsBasePath + " on file system: " + fs); } catch (URISyntaxException e) { throw new RuntimeException(e); } } @Override public void copyFileInto(String fullPath, File localFile) throws IOException { final String relativePath = mounter.getMountRelativePath(fullPath, mountPoint); final Path hdfsPath = new Path(hdfsBasePath, relativePath); fs.copyToLocalFile(hdfsPath, new Path(localFile.getPath())); } @Override public File loadFile(String fullPath) throws IOException { final File file = File.createTempFile("imhotep.hdfs.", ".cachedFile"); copyFileInto(fullPath, file); return file; } @Override public RemoteFileInfo stat(String fullPath) { final String relativePath = mounter.getMountRelativePath(fullPath, mountPoint); final Path hdfsPath = new Path(hdfsBasePath, relativePath); try { final FileStatus status = fs.getFileStatus(hdfsPath); if (status.isDirectory()) { return new RemoteFileInfo(relativePath, RemoteFileInfo.TYPE_DIR); } else { return new RemoteFileInfo(relativePath, RemoteFileInfo.TYPE_FILE); } } catch (final IOException e) { log.error(e); return null; } } @Override public List<RemoteFileInfo> readDir(String fullPath) { final String relativePath = mounter.getMountRelativePath(fullPath, mountPoint); final Path hdfsPath = new Path(hdfsBasePath, relativePath); try { final FileStatus[] contents = fs.listStatus(hdfsPath); final List<RemoteFileInfo> results = new ArrayList<>(); for (FileStatus status : contents) { if (status.getPath().equals(hdfsPath) && !status.isDirectory()) { throw new IOException("Not a directory: " + fullPath); } if (status.isDirectory()) { results.add(new RemoteFileInfo(status.getPath().getName(), RemoteFileInfo.TYPE_DIR)); } else { results.add(new RemoteFileInfo(status.getPath().getName(), RemoteFileInfo.TYPE_FILE)); } } return results; } catch (IOException e) { log.error(e); } return null; } @Override public String getMountPoint() { return mountPoint; } @Override public Map<String, File> loadDirectory(String fullPath, File location) throws IOException { final String relativePath = mounter.getMountRelativePath(fullPath, mountPoint); final Path hdfsPath = new Path(hdfsBasePath, relativePath); final Map<String, File> results = new HashMap<>(100); if (location == null) { location = File.createTempFile("hdfs", "remoteFile"); location.delete(); location.mkdir(); } try { final FileStatus[] contents = fs.listStatus(hdfsPath); for (FileStatus status : contents) { if (!status.isDirectory()) { File localFile = new File(location, status.getPath().getName()); localFile.getParentFile().mkdirs(); copyFileInto(status.getPath().toString(), localFile); results.put(fullPath + status.getPath().getName(), localFile); } } } catch (IOException e) { log.error(e); } return results; } @Override public InputStream getInputStreamForFile(String fullPath, long startOffset, long maxReadLength) throws IOException { final String relativePath = mounter.getMountRelativePath(fullPath, mountPoint); final Path hdfsPath = new Path(hdfsBasePath, relativePath); final FSDataInputStream stream = fs.open(hdfsPath); try { stream.seek(startOffset); } catch (final IOException e) { Closeables2.closeQuietly(stream, log); throw new IOException("Failed to open " + fullPath + " with offset " + startOffset, e); } return stream; } }