/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF 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.apache.hadoop.hdfs.server.namenode; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.net.URI; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.hdfs.server.common.HdfsConstants.StartupOption; import org.apache.hadoop.hdfs.server.common.HdfsConstants.Transition; import org.apache.hadoop.hdfs.server.common.Storage; import org.apache.hadoop.hdfs.server.common.Util; import org.apache.hadoop.hdfs.server.common.Storage.StorageDirectory; import org.apache.hadoop.hdfs.server.common.StorageInfo; import org.apache.hadoop.hdfs.server.namenode.FSImageStorageInspector.FSImageFile; import org.apache.hadoop.hdfs.server.namenode.NNStorage.NameNodeFile; import org.apache.hadoop.hdfs.server.protocol.RemoteImageManifest; import org.apache.hadoop.hdfs.util.MD5FileUtils; import org.apache.hadoop.io.MD5Hash; /** * FileImageManager provides abstraction for image location in file storage. */ public class FileImageManager implements ImageManager { static final Log LOG = LogFactory.getLog(FileImageManager.class); private final StorageDirectory sd; private final Storage storage; private volatile boolean imageDisabled = false; public FileImageManager(StorageDirectory sd, Storage storage) { this.sd = sd; this.storage = storage; } /** * Get file output stream */ @Override public OutputStream getCheckpointOutputStream(long imageTxId) throws IOException { String fileName = NNStorage.getCheckpointImageFileName(imageTxId); return new FileOutputStream(new File(sd.getCurrentDir(), fileName)); } @Override public boolean saveDigestAndRenameCheckpointImage(long txid, MD5Hash digest) { try { renameCheckpointInDir(sd, txid); // when saving image directly, we save the digest on-the-fly File imageFile = NNStorage.getImageFile(sd, txid); MD5FileUtils.saveMD5File(imageFile, digest); return true; } catch (IOException ioe) { LOG.warn("Unable to rename checkpoint in " + sd, ioe); reportError(sd); return false; } } /** * Rolls checkpointed image. */ private static void renameCheckpointInDir(StorageDirectory sd, long txid) throws IOException { File ckpt = NNStorage.getStorageFile(sd, NameNodeFile.IMAGE_NEW, txid); File curFile = NNStorage.getStorageFile(sd, NameNodeFile.IMAGE, txid); // renameTo fails on Windows if the destination file // already exists. LOG.info("Renaming " + ckpt.getAbsolutePath() + " to " + curFile.getAbsolutePath()); if (!ckpt.renameTo(curFile)) { if (!curFile.delete() || !ckpt.renameTo(curFile)) { throw new IOException("renaming " + ckpt.getAbsolutePath() + " to " + curFile.getAbsolutePath() + " FAILED"); } } } /** * Return true if the last image */ @Override public boolean isImageDisabled() { return imageDisabled; } /** * Set image status. */ @Override public void setImageDisabled(boolean isDisabled) { this.imageDisabled = isDisabled; } /** * Reports error to underlying storage. */ private void reportError(StorageDirectory sd) { if (storage instanceof NNStorage) { // pass null, since we handle the disable here ((NNStorage)storage).reportErrorsOnDirectory(sd, null); } else { LOG.error("Failed direcory: " + sd.getCurrentDir()); } } public String toString() { return sd.getCurrentDir().toString(); } @Override public ImageInputStream getImageInputStream(long txid) throws IOException { File imageFile = NNStorage.getImageFile(sd, txid); return new ImageInputStream(txid, new FileInputStream(imageFile), MD5FileUtils.readStoredMd5ForFile(imageFile), imageFile.toString(), imageFile.length()); } @Override public boolean hasSomeJournalData() throws IOException { return sd.hasSomeJournalData(); } @Override public boolean hasSomeImageData() throws IOException { return sd.hasSomeImageData(); } @Override public RemoteImageManifest getImageManifest(long fromTxId) throws IOException { throw new UnsupportedOperationException("This operation is not supported."); } @Override public FSImageFile getLatestImage() throws IOException { throw new UnsupportedOperationException("This operation is not supported."); } @Override public void transitionImage(StorageInfo si, Transition transition, StartupOption startOpt) throws IOException { throw new UnsupportedOperationException("This operation is not supported."); } @Override public RemoteStorageState analyzeImageStorage() throws IOException { throw new UnsupportedOperationException("This operation is not supported."); } @Override public String toHTMLString() { return String.format("file:/%s", sd.getRoot()); } @Override public URI getURI() throws IOException { return Util.fileAsURI(sd.getRoot()); } public StorageDirectory getStorageDirectory() { return sd; } }