com.questdb.BootstrapMain Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of questdb-core Show documentation
Show all versions of questdb-core Show documentation
QuestDB is High Performance Time Series Database
/*******************************************************************************
* ___ _ ____ ____
* / _ \ _ _ ___ ___| |_| _ \| __ )
* | | | | | | |/ _ \/ __| __| | | | _ \
* | |_| | |_| | __/\__ \ |_| |_| | |_) |
* \__\_\\__,_|\___||___/\__|____/|____/
*
* Copyright (C) 2014-2016 Appsicle
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
******************************************************************************/
package com.questdb;
import com.questdb.factory.ReaderFactoryImpl;
import com.questdb.factory.ReaderFactoryPool;
import com.questdb.factory.WriterFactoryImpl;
import com.questdb.log.*;
import com.questdb.misc.Misc;
import com.questdb.misc.Os;
import com.questdb.mp.RingQueue;
import com.questdb.mp.Sequence;
import com.questdb.net.http.HttpServer;
import com.questdb.net.http.ServerConfiguration;
import com.questdb.net.http.SimpleUrlMatcher;
import com.questdb.net.http.handlers.*;
import com.questdb.std.CharSequenceObjHashMap;
import sun.misc.Signal;
import sun.misc.SignalHandler;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Date;
import java.util.EnumSet;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
class BootstrapMain {
public static void main(String[] args) throws Exception {
System.err.printf("QuestDB HTTP Server %s%nCopyright (C) Appsicle 2014-2016, all rights reserved.%n%n", getVersion());
if (args.length < 1) {
System.err.println("Root directory name expected");
return;
}
if (Os.type == Os._32Bit) {
System.err.println("QuestDB requires 64-bit JVM");
return;
}
final CharSequenceObjHashMap optHash = hashArgs(args);
// expected flags:
// -d = sets root directory
// -f = forces copy of site to root directory even if site exists
// -n = disables handling of HUP signal
String dir = optHash.get("-d");
extractSite(dir, optHash.get("-f") != null);
File conf = new File(dir, "conf/questdb.conf");
if (!conf.exists()) {
System.err.println("Configuration file does not exist: " + conf);
return;
}
final ServerConfiguration configuration = new ServerConfiguration(conf);
configureLoggers(configuration);
final SimpleUrlMatcher matcher = new SimpleUrlMatcher();
ReaderFactoryImpl readerFactory = new ReaderFactoryImpl(configuration.getDbPath().getAbsolutePath());
WriterFactoryImpl writerFactory = new WriterFactoryImpl(configuration.getDbPath().getAbsolutePath());
ReaderFactoryPool pool = new ReaderFactoryPool(readerFactory.getConfiguration(), configuration.getJournalPoolSize());
matcher.put("/imp", new ImportHandler(configuration, writerFactory));
matcher.put("/exec", new QueryHandler(pool, configuration, writerFactory));
matcher.put("/exp", new CsvHandler(pool, configuration));
matcher.put("/chk", new ExistenceCheckHandler(readerFactory));
matcher.setDefaultHandler(new StaticContentHandler(configuration));
StringBuilder welcome = Misc.getThreadLocalBuilder();
HttpServer server = new HttpServer(configuration, matcher);
if (!server.start(LogFactory.INSTANCE.getJobs(), configuration.getHttpQueueDepth())) {
welcome.append("Could not bind socket ").append(configuration.getHttpIP()).append(':').append(configuration.getHttpPort());
welcome.append(". Already running?");
System.err.println(welcome);
System.out.println(new Date() + " QuestDB failed to start");
} else {
welcome.append("Listening on ").append(configuration.getHttpIP()).append(':').append(configuration.getHttpPort());
if (configuration.getSslConfig().isSecure()) {
welcome.append(" [HTTPS]");
} else {
welcome.append(" [HTTP plain]");
}
System.err.println(welcome);
System.out.println(new Date() + " QuestDB is running");
if (Os.type != Os.WINDOWS && optHash.get("-n") == null) {
// suppress HUP signal
Signal.handle(new Signal("HUP"), new SignalHandler() {
public void handle(Signal signal) {
}
});
}
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
public void run() {
System.out.println(new Date() + " QuestDB is shutting down");
}
}));
}
}
private static String getVersion() throws IOException {
Enumeration resources = BootstrapMain.class.getClassLoader()
.getResources("META-INF/MANIFEST.MF");
while (resources.hasMoreElements()) {
try (InputStream is = resources.nextElement().openStream()) {
Manifest manifest = new Manifest(is);
Attributes attributes = manifest.getMainAttributes();
if ("org.questdb".equals(attributes.getValue("Implementation-Vendor-Id"))) {
return manifest.getMainAttributes().getValue("Implementation-Version");
}
}
}
return "[DEVELOPMENT]";
}
private static CharSequenceObjHashMap hashArgs(String[] args) {
CharSequenceObjHashMap optHash = new CharSequenceObjHashMap<>();
String flag = null;
for (int i = 0, n = args.length; i < n; i++) {
String s = args[i];
if (s.startsWith("-")) {
if (flag != null) {
optHash.put(flag, "");
}
flag = s;
} else {
if (flag != null) {
optHash.put(flag, s);
flag = null;
} else {
System.err.println("Unknown arg: " + s);
System.exit(55);
}
}
}
if (flag != null) {
optHash.put(flag, "");
}
return optHash;
}
private static void configureLoggers(final ServerConfiguration configuration) {
LogFactory.INSTANCE.add(new LogWriterConfig("access", LogLevel.LOG_LEVEL_ALL, new LogWriterFactory() {
@Override
public LogWriter createLogWriter(RingQueue ring, Sequence seq, int level) {
LogFileWriter w = new LogFileWriter(ring, seq, level);
w.setLocation(configuration.getAccessLog().getAbsolutePath());
return w;
}
}));
final int level = System.getProperty(LogFactory.DEBUG_TRIGGER) != null ? LogLevel.LOG_LEVEL_ALL : LogLevel.LOG_LEVEL_ERROR | LogLevel.LOG_LEVEL_INFO;
LogFactory.INSTANCE.add(new LogWriterConfig(level,
new LogWriterFactory() {
@Override
public LogWriter createLogWriter(RingQueue ring, Sequence seq, int level) {
LogFileWriter w = new LogFileWriter(ring, seq, level);
w.setLocation(configuration.getErrorLog().getAbsolutePath());
return w;
}
}));
LogFactory.INSTANCE.bind();
}
private static void extractSite(String dir, boolean force) throws URISyntaxException, IOException {
System.out.println("Preparing content...");
URL url = HttpServer.class.getResource("/site/");
String[] components = url.toURI().toString().split("!");
FileSystem fs = null;
final Path source;
final int sourceLen;
if (components.length > 1) {
fs = FileSystems.newFileSystem(URI.create(components[0]), new HashMap());
source = fs.getPath(components[1]);
sourceLen = source.toAbsolutePath().toString().length();
} else {
source = Paths.get(url.toURI());
sourceLen = source.toAbsolutePath().toString().length() + 1;
}
try {
final Path target = Paths.get(dir);
final EnumSet walkOptions = EnumSet.of(FileVisitOption.FOLLOW_LINKS);
final CopyOption[] copyOptions = new CopyOption[]{COPY_ATTRIBUTES, REPLACE_EXISTING};
if (force) {
File pub = new File(dir, "public");
if (pub.exists()) {
com.questdb.misc.Files.delete(pub);
}
}
Files.walkFileTree(source, walkOptions, Integer.MAX_VALUE, new FileVisitor() {
private boolean skip = true;
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
if (skip) {
skip = false;
} else {
try {
Files.copy(dir, toDestination(dir), copyOptions);
System.out.println("Extracted " + dir);
} catch (FileAlreadyExistsException ignore) {
} catch (IOException x) {
return FileVisitResult.SKIP_SUBTREE;
}
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Files.copy(file, toDestination(file), copyOptions);
System.out.println("Extracted " + file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) {
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
return FileVisitResult.CONTINUE;
}
private Path toDestination(final Path path) {
final Path tmp = path.toAbsolutePath();
return target.resolve(tmp.toString().substring(sourceLen));
}
});
} finally {
if (fs != null) {
fs.close();
}
}
}
}