001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.vfs2.provider.hdfs;
018
019import java.io.FileNotFoundException;
020import java.io.IOException;
021import java.io.InputStream;
022import java.io.OutputStream;
023import java.util.HashMap;
024import java.util.Map;
025import java.util.Objects;
026import java.util.stream.Stream;
027
028import org.apache.commons.vfs2.FileNotFolderException;
029import org.apache.commons.vfs2.FileObject;
030import org.apache.commons.vfs2.FileSystemException;
031import org.apache.commons.vfs2.FileType;
032import org.apache.commons.vfs2.RandomAccessContent;
033import org.apache.commons.vfs2.provider.AbstractFileName;
034import org.apache.commons.vfs2.provider.AbstractFileObject;
035import org.apache.commons.vfs2.util.RandomAccessMode;
036import org.apache.hadoop.fs.FileStatus;
037import org.apache.hadoop.fs.FileSystem;
038import org.apache.hadoop.fs.Path;
039
040/**
041 * A VFS representation of an HDFS file.
042 *
043 * @since 2.1
044 */
045public class HdfsFileObject extends AbstractFileObject<HdfsFileSystem> {
046
047    private final HdfsFileSystem fs;
048    private final FileSystem hdfs;
049    private final Path path;
050    private FileStatus stat;
051
052    /**
053     * Constructs a new HDFS FileObject
054     *
055     * @param name FileName
056     * @param fs HdfsFileSystem instance
057     * @param hdfs Hadoop FileSystem instance
058     * @param path Path to the file in HDFS
059     */
060    protected HdfsFileObject(final AbstractFileName name, final HdfsFileSystem fs, final FileSystem hdfs,
061            final Path path) {
062        super(name, fs);
063        this.fs = fs;
064        this.hdfs = hdfs;
065        this.path = path;
066    }
067
068    /**
069     * @see org.apache.commons.vfs2.provider.AbstractFileObject#canRenameTo(org.apache.commons.vfs2.FileObject)
070     */
071    @Override
072    public boolean canRenameTo(final FileObject newfile) {
073        if (!super.canRenameTo(newfile)) {
074            return false;
075        }
076        try {
077            return hdfs.getFileStatus(new Path(newfile.getName().getPath())) == null;
078        } catch (final FileNotFoundException e) {
079            return false;
080        } catch (final IOException e) {
081            throw new IllegalStateException(e);
082        }
083    }
084
085    /**
086     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doAttach()
087     */
088    @Override
089    protected void doAttach() throws Exception {
090        try {
091            stat = hdfs.getFileStatus(path);
092        } catch (final FileNotFoundException e) {
093            stat = null;
094        }
095    }
096
097    /**
098     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doCreateFolder()
099     * @since 2.7.0
100     */
101    @Override
102    protected void doCreateFolder() throws Exception {
103        hdfs.mkdirs(path);
104    }
105
106    /**
107     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doDelete()
108     * @since 2.7.0
109     */
110    @Override
111    protected void doDelete() throws Exception {
112        hdfs.delete(path, true);
113    }
114
115    /**
116     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doGetAttributes()
117     */
118    @Override
119    protected Map<String, Object> doGetAttributes() throws Exception {
120        if (null == stat) {
121            return super.doGetAttributes();
122        }
123        final Map<String, Object> attrs = new HashMap<>();
124        attrs.put(HdfsFileAttributes.LAST_ACCESS_TIME.toString(), stat.getAccessTime());
125        attrs.put(HdfsFileAttributes.BLOCK_SIZE.toString(), stat.getBlockSize());
126        attrs.put(HdfsFileAttributes.GROUP.toString(), stat.getGroup());
127        attrs.put(HdfsFileAttributes.OWNER.toString(), stat.getOwner());
128        attrs.put(HdfsFileAttributes.PERMISSIONS.toString(), stat.getPermission().toString());
129        attrs.put(HdfsFileAttributes.LENGTH.toString(), stat.getLen());
130        attrs.put(HdfsFileAttributes.MODIFICATION_TIME.toString(), stat.getModificationTime());
131        return attrs;
132    }
133
134    /**
135     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doGetContentSize()
136     */
137    @Override
138    protected long doGetContentSize() throws Exception {
139        return stat.getLen();
140    }
141
142    /**
143     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doGetInputStream(int)
144     */
145    @Override
146    protected InputStream doGetInputStream(final int bufferSize) throws Exception {
147        return hdfs.open(path, bufferSize);
148    }
149
150    /**
151     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doGetLastModifiedTime()
152     */
153    @Override
154    protected long doGetLastModifiedTime() throws Exception {
155        doAttach();
156        if (null != stat) {
157            return stat.getModificationTime();
158        }
159        return -1;
160    }
161
162    /**
163     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doGetOutputStream(boolean)
164     * @since 2.7.0
165     */
166    @Override
167    protected OutputStream doGetOutputStream(final boolean append) throws Exception {
168        if (append) {
169            throw new FileSystemException("vfs.provider/write-append-not-supported.error", path.getName());
170        }
171        return hdfs.create(path);
172    }
173
174    /**
175     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doGetRandomAccessContent
176     *      (org.apache.commons.vfs2.util.RandomAccessMode)
177     */
178    @Override
179    protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception {
180        if (mode.equals(RandomAccessMode.READWRITE)) {
181            throw new UnsupportedOperationException();
182        }
183        return new HdfsRandomAccessContent(path, hdfs);
184    }
185
186    /**
187     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doGetType()
188     */
189    @Override
190    protected FileType doGetType() throws Exception {
191        try {
192            doAttach();
193            if (null == stat) {
194                return FileType.IMAGINARY;
195            }
196            if (stat.isDirectory()) {
197                return FileType.FOLDER;
198            }
199            return FileType.FILE;
200        } catch (final FileNotFoundException fnfe) {
201            return FileType.IMAGINARY;
202        }
203    }
204
205    /**
206     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doIsHidden()
207     */
208    @Override
209    protected boolean doIsHidden() throws Exception {
210        return false;
211    }
212
213    /**
214     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doIsReadable()
215     */
216    @Override
217    protected boolean doIsReadable() throws Exception {
218        return true;
219    }
220
221    /**
222     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doIsWriteable()
223     */
224    @Override
225    protected boolean doIsWriteable() throws Exception {
226        return true;
227    }
228
229    /**
230     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doListChildren()
231     */
232    @Override
233    protected String[] doListChildren() throws Exception {
234        if (doGetType() != FileType.FOLDER) {
235            throw new FileNotFolderException(this);
236        }
237
238        final FileStatus[] fileStatuses = hdfs.listStatus(path);
239        return Stream.of(fileStatuses).filter(Objects::nonNull).map(status -> status.getPath().getName()).toArray(String[]::new);
240    }
241
242    /**
243     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doListChildrenResolved()
244     */
245    @Override
246    protected FileObject[] doListChildrenResolved() throws Exception {
247        if (doGetType() != FileType.FOLDER) {
248            return null;
249        }
250        final String[] children = doListChildren();
251        final FileObject[] fo = new FileObject[children.length];
252        for (int i = 0; i < children.length; i++) {
253            final Path p = new Path(path, children[i]);
254            fo[i] = fs.resolveFile(p.toUri().toString());
255        }
256        return fo;
257    }
258
259    /**
260     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doRemoveAttribute(String)
261     */
262    @Override
263    protected void doRemoveAttribute(final String attrName) throws Exception {
264        throw new UnsupportedOperationException();
265    }
266
267    /**
268     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doRename(FileObject)
269     * @since 2.7.0
270     */
271    @Override
272    protected void doRename(final FileObject newfile) throws Exception {
273        hdfs.rename(path, new Path(newfile.getName().getPath()));
274    }
275
276    /**
277     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doSetAttribute(String, Object)
278     */
279    @Override
280    protected void doSetAttribute(final String attrName, final Object value) throws Exception {
281        throw new UnsupportedOperationException();
282    }
283
284    /**
285     * @see org.apache.commons.vfs2.provider.AbstractFileObject#doSetLastModifiedTime(long)
286     */
287    @Override
288    protected boolean doSetLastModifiedTime(final long modtime) throws Exception {
289        try {
290            hdfs.setTimes(path, modtime, System.currentTimeMillis());
291        } catch (final IOException ioe) {
292            throw new FileSystemException(ioe);
293        }
294        return true;
295    }
296
297    /**
298     * @see org.apache.commons.vfs2.provider.AbstractFileObject#exists()
299     * @return boolean true if file exists, false if not
300     */
301    @Override
302    public boolean exists() throws FileSystemException {
303        try {
304            doAttach();
305            return stat != null;
306        } catch (final FileNotFoundException fne) {
307            return false;
308        } catch (final Exception e) {
309            throw new FileSystemException("Unable to check existence ", e);
310        }
311    }
312
313}