Main.java
package doss;
import static java.lang.System.err;
import static java.lang.System.out;
import java.io.IOException;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.FileTime;
import java.security.NoSuchAlgorithmException;
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import org.apache.thrift.transport.TTransportException;
import doss.local.LocalBlobStore;
import doss.net.BlobStoreServer;
/**
* DOSS command-line interface
*/
@SuppressWarnings("serial")
public class Main {
enum Command {
help("<command>",
"Display a list of commands or help for a given command.") {
void listCommands() {
out.println("usage: doss <command> [<args>]");
out.println("");
out.println("Available commands:");
for (Command command : Command.values()) {
out.println(String.format(" %-10s%s", command.name(),
command.description()));
}
}
@Override
void execute(Arguments args) {
if (args.isEmpty()) {
listCommands();
} else {
Command.get(args.first()).usage();
}
}
},
version("", "Displays the version number.") {
@Override
void execute(Arguments args) throws IOException {
if (!args.isEmpty()) {
usage();
} else {
if (this.getClass().getPackage().getImplementationVersion() == null) {
out.println("DOSS 2 - unknown version\nThere is no MANIFEST.MF to look at outside of a jar.");
} else {
out.println("DOSS 2 version "
+ this.getClass().getPackage()
.getImplementationVersion());
}
try (BlobStore blobStore = openBlobStore()) {
if (blobStore instanceof LocalBlobStore) {
out.println("LocalBlobStore version: " + ((LocalBlobStore) blobStore).version());
}
}
out.println("Java version: "
+ System.getProperty("java.version") + ", vendor: "
+ System.getProperty("java.vendor"));
out.println("Java home: " + System.getProperty("java.home"));
}
}
},
init("", "Initialize the blob store") {
@Override
void execute(Arguments args) throws IOException {
LocalBlobStore.init(getDossHome());
}
},
cat("<blobId ...>", "Concatinate and print blobs (like unix cat).") {
void outputBlob(String blobId) throws IOException {
try (BlobStore bs = openBlobStore()) {
Blob blob = bs.get(Long.parseLong(blobId));
ReadableByteChannel channel = blob.openChannel();
WritableByteChannel dest = Channels.newChannel(out);
final ByteBuffer buffer = ByteBuffer
.allocateDirect(16 * 1024);
while (channel.read(buffer) != -1) {
buffer.flip();
dest.write(buffer);
buffer.compact();
}
}
}
@Override
void execute(Arguments args) throws IOException {
if (args.isEmpty()) {
usage();
} else {
for (String arg : args) {
outputBlob(arg);
}
}
}
},
digest("<algorithm> <blobId>", "Prints a digest (sha1, md5, etc) of a blob") {
void digestBlob(String algorithm, String blobId) throws IOException {
try (BlobStore bs = openBlobStore()) {
Blob blob = bs.get(Long.parseLong(blobId));
try {
out.println(blob.digest(algorithm));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
}
@Override
void execute(Arguments args) throws IOException {
digestBlob(args.first(), args.rest().first());
}
},
get("<blobId ...>", "Copy blobs to the current working directory.") {
void outputBlob(String blobId) throws IOException {
try (BlobStore bs = openBlobStore()) {
Blob blob = bs.get(Long.parseLong(blobId));
ReadableByteChannel channel = blob.openChannel();
FileChannel dest = FileChannel.open(Paths.get(blobId),
StandardOpenOption.WRITE,
StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING);
long bytesTransferred = dest.transferFrom(channel, 0,
Long.MAX_VALUE);
out.println("Got " + bytesTransferred + "B of "
+ blob.size() + "B from blob " + blobId);
}
}
@Override
void execute(Arguments args) throws IOException {
if (args.isEmpty()) {
usage();
} else {
for (String arg : args) {
outputBlob(arg);
}
}
}
},
stat("[-b] <blobId ...>", "Displays metadata about blobs.") {
String formattedTime(FileTime time) {
Date date = new Date(time.toMillis());
return date.toString();
}
@Override
void execute(Arguments args) throws IOException {
if (args.isEmpty()) {
usage();
} else {
BlobStore bs = openBlobStore();
boolean humanSizes = true;
if (args.first().equals("-b")) {
humanSizes = false;
args = args.rest();
}
for (String arg : args) {
Blob blob = bs.get(Long.parseLong(arg));
out.println(blob.id() + ": ");
out.println("\tCreated:\t"
+ formattedTime(blob.created()));
out.println("\tSize:\t\t"
+ (humanSizes ? readableFileSize(blob.size())
: blob.size() + " B"));
out.println("");
}
}
}
},
put("[-b] <file ...>", "Stores files as blobs.") {
@Override
void execute(Arguments args) throws IOException {
if (args.isEmpty()) {
usage();
} else {
BlobStore bs = openBlobStore();
try (BlobTx tx = bs.begin()) {
boolean humanSizes = true;
if (args.first().equals("-b")) {
humanSizes = false;
args = args.rest();
}
out.println("ID\tFilename\tSize");
for (String filename : args) {
Path p = Paths.get(filename);
if (!Files.exists(p)) {
throw new CommandLineException("no such file: "
+ filename);
}
if (!Files.isRegularFile(p)) {
throw new CommandLineException(
"not a regular file: " + filename);
}
Blob blob = tx.put(p);
out.println(Long.toString(blob.id())
+ '\t'
+ filename
+ '\t'
+ (humanSizes ? readableFileSize(blob
.size()) : blob.size() + " B"));
}
tx.commit();
} catch (Exception e) {
err.println("Aborting, rolling back all changes...");
throw e;
}
out.println("Created " + args.list.size() + " blobs.");
}
}
},
server("[-b bindaddr] [-p port]",
"Run a DOSS server on the given part") {
final OptionParser OPTION_PARSER = new OptionParser("c:");
@Override
void execute(Arguments args) throws IOException {
String bindaddr = "127.0.0.1";
int port = 7272;
OptionSet options = args.parse("b:p:");
if (options.has("b")) {
bindaddr = (String) options.valueOf("b");
}
if (options.has("p")) {
port = Integer.parseInt((String) options.valueOf("p"));
}
System.out.println("Opening DOSS server on " + bindaddr + ":"
+ port);
try (BlobStore blobStore = openBlobStore();
ServerSocket socket = new ServerSocket(port);
BlobStoreServer server = new BlobStoreServer(blobStore,
socket)) {
server.run();
} catch (TTransportException e) {
throw new RuntimeException(e);
}
}
},
verify("<blobId ...>", "Checks the integrity of the given blobs") {
void verifyBlob(String blobId) throws IOException {
try (BlobStore bs = openBlobStore()) {
Blob blob = bs.get(Long.parseLong(blobId));
for (String error : blob.verify()) {
out.println("blob " + blobId + ": " + error);
}
}
}
@Override
void execute(Arguments args) throws IOException {
if (args.isEmpty()) {
usage();
} else {
for (String arg : args) {
verifyBlob(arg);
}
}
}
},
;
final String descrption, parameters;
Command(String parameters, String description) {
this.parameters = parameters;
this.descrption = description;
}
abstract void execute(Arguments args) throws IOException;
Path getDossHome() {
String dir = System.getProperty("doss.home");
if (dir == null) {
throw new CommandLineException(
"The doss.home system property must be set, eg.: -Ddoss.home=/path/to/doss ");
}
;
return Paths.get(dir);
}
BlobStore openBlobStore() throws CommandLineException, IOException {
return LocalBlobStore.open(getDossHome());
}
String description() {
return this.descrption;
}
String parameters() {
return this.parameters;
}
void usage() {
out.println("usage: doss " + name() + " " + parameters());
out.println(description());
}
static Command get(String name) {
try {
return valueOf(name);
} catch (IllegalArgumentException e) {
throw new NoSuchCommandException(name);
}
}
}
static void printError(Throwable e) {
if (System.getProperty("doss.trace") != null) {
e.printStackTrace();
} else {
err.println("doss: " + e.getLocalizedMessage());
Throwable cause = e.getCause();
while (cause != null) {
err.println("caused by " + cause);
cause = cause.getCause();
}
}
}
public static void main(String... arguments) throws IOException {
try {
Arguments args = new Arguments(Arrays.asList(arguments));
if (args.isEmpty()) {
Command.help.execute(args);
} else {
Command.get(args.first()).execute(args.rest());
}
} catch (CommandLineException e) {
printError(e);
} catch (CorruptBlobStoreException e) {
printError(e);
err.println();
err.println("Maybe you need to run 'doss init'?");
}
}
public static String readableFileSize(long size) {
if (size <= 0)
return "0";
final String[] units = new String[] { "B", "KB", "MB", "GB", "TB" };
int digitGroups = (int) (Math.log10(size) / Math.log10(1024));
return new DecimalFormat("#,##0.#").format(size
/ Math.pow(1024, digitGroups))
+ " " + units[digitGroups];
}
static class CommandLineException extends RuntimeException {
CommandLineException(String message) {
super(message);
}
}
static class NoSuchCommandException extends CommandLineException {
NoSuchCommandException(String command) {
super(String.format("'%s' is not a doss command", command));
}
}
static class Arguments implements Iterable<String> {
final List<String> list;
Arguments(List<String> list) {
this.list = list;
}
@Override
public Iterator<String> iterator() {
return list.iterator();
}
boolean isEmpty() {
return list.isEmpty();
}
String first() {
return list.get(0);
}
Arguments rest() {
return new Arguments(list.subList(1, list.size()));
}
OptionSet parse(String optionSpecification) {
return new OptionParser(optionSpecification).parse(list
.toArray(new String[list.size()]));
}
}
}