/**
* 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.blur.command;
import java.io.IOException;
import java.security.PrivilegedExceptionAction;
import org.apache.blur.command.annotation.Description;
import org.apache.blur.command.annotation.OptionalArgument;
import org.apache.blur.command.annotation.RequiredArgument;
import org.apache.blur.command.commandtype.IndexReadCommandSingleTable;
import org.apache.blur.log.Log;
import org.apache.blur.log.LogFactory;
import org.apache.blur.store.hdfs.DirectoryDecorator;
import org.apache.blur.store.hdfs.HdfsDirectory;
import org.apache.blur.store.hdfs_v2.JoinDirectory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.IOContext;
import org.apache.lucene.store.IndexInput;
import org.apache.lucene.store.IndexOutput;
import org.apache.lucene.util.IOUtils;
@Description("Copies the given table to another location.")
public class TableCopyCommand extends IndexReadCommandSingleTable<Long> {
private static final Log LOG = LogFactory.getLog(TableCopyCommand.class);
@RequiredArgument("The hdfs destination uri. (e.g. hdfs://namenode/path)")
private String destUri;
@RequiredArgument("The hdfs user to run copy command.")
private String user;
@OptionalArgument("If enabled, the copy will resume by removing files that did not complete and recopying.")
private boolean resume = false;
@Override
public String getName() {
return "table-copy";
}
@Override
public Long execute(IndexContext context) throws IOException {
final Configuration configuration = context.getTableContext().getConfiguration();
final IndexReader indexReader = context.getIndexReader();
final Shard shard = context.getShard();
UserGroupInformation remoteUser = UserGroupInformation.createRemoteUser(user);
try {
return remoteUser.doAs(new PrivilegedExceptionAction<Long>() {
@Override
public Long run() throws Exception {
Path path = new Path(destUri);
Directory srcDirectory = getDiretory(indexReader);
HdfsDirectory destDirectory = new HdfsDirectory(configuration, new Path(path, shard.getShard()));
long total = 0;
for (String srcFile : srcDirectory.listAll()) {
if (destDirectory.fileExists(srcFile)) {
LOG.info("File [{0}] already exists in dest directory.");
long srcFileLength = srcDirectory.fileLength(srcFile);
long destFileLength = destDirectory.fileLength(srcFile);
if (srcFileLength != destFileLength) {
LOG.info("Deleting file [{0}] length of [{1}] is not same as source [{2}].", srcFile, srcFileLength,
destFileLength);
destDirectory.deleteFile(srcFile);
} else {
continue;
}
}
LOG.info("Copying file [{0}] to dest directory.", srcFile);
total += copy(srcFile, srcDirectory, destDirectory);
}
return total;
}
});
} catch (InterruptedException e) {
throw new IOException(e);
}
}
private long copy(String file, Directory srcDirectory, HdfsDirectory destDirectory) throws IOException {
long fileLength = srcDirectory.fileLength(file);
IOContext context = IOContext.DEFAULT;
IndexOutput os = null;
IndexInput is = null;
IOException priorException = null;
try {
os = destDirectory.createOutput(file, context);
is = srcDirectory.openInput(file, context);
os.copyBytes(is, is.length());
} catch (IOException ioe) {
priorException = ioe;
} finally {
boolean success = false;
try {
IOUtils.closeWhileHandlingException(priorException, os, is);
success = true;
} finally {
if (!success) {
try {
destDirectory.deleteFile(file);
} catch (Throwable t) {
}
}
}
}
return fileLength;
}
private Directory getStorageDir(Directory directory) throws IOException {
if (directory instanceof DirectoryDecorator) {
DirectoryDecorator directoryDecorator = (DirectoryDecorator) directory;
return getStorageDir(directoryDecorator.getOriginalDirectory());
}
if (directory instanceof JoinDirectory) {
return directory;
}
if (directory instanceof HdfsDirectory) {
return directory;
}
if (directory instanceof FSDirectory) {
return directory;
}
throw new IOException("Directory [" + directory + "] not supported.");
}
private Directory getDiretory(IndexReader indexReader) throws IOException {
if (indexReader instanceof DirectoryReader) {
DirectoryReader reader = (DirectoryReader) indexReader;
return getStorageDir(reader.directory());
}
throw new IOException("Reader Not DirectoryReader.");
}
public String getDestUri() {
return destUri;
}
public void setDestUri(String destUri) {
this.destUri = destUri;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public boolean isResume() {
return resume;
}
public void setResume(boolean resume) {
this.resume = resume;
}
}