blob: 9f001e43abc3a82f98f8b712fc352e618d0d8841 [file] [log] [blame]
/*
* 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.jackrabbit.standalone;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import org.apache.commons.chain.Context;
import org.apache.commons.chain.impl.ContextBase;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.io.IOUtils;
import org.apache.jackrabbit.commons.JcrUtils;
import org.apache.jackrabbit.core.RepositoryCopier;
import org.apache.jackrabbit.core.config.RepositoryConfig;
import org.apache.jackrabbit.servlet.jackrabbit.JackrabbitRepositoryServlet;
import org.apache.jackrabbit.standalone.cli.CommandException;
import org.apache.jackrabbit.standalone.cli.CommandHelper;
import org.apache.jackrabbit.standalone.cli.JcrClient;
import org.eclipse.jetty.server.NCSARequestLog;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.RequestLogHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.webapp.WebAppContext;
/**
*
*/
public class Main {
/**
* @param args
*/
public static void main(String[] args) throws Exception {
new Main(args).run();
}
private final Options options = new Options();
private final CommandLine command;
private final RequestLogHandler accessLog = new RequestLogHandler();
private final WebAppContext webapp = new WebAppContext();
private final Server server = new Server();
private final ServerConnector connector = new ServerConnector(server);
/**
* Construct Main application instance.
* <P>
* <EM>Note:</EM> Constructor is protected because other projects such as Commons VFS can extend this for some reasons
* (e.g, unit testing against Jackrabbit WebDAV).
*/
protected Main(String[] args) throws ParseException {
options.addOption("?", "help", false, "print this message");
options.addOption("n", "notice", false, "print copyright notices");
options.addOption("l", "license", false, "print license information");
options.addOption(
"b", "backup", false, "create a backup of the repository");
options.addOption(
"i", "cli", true, "command line access to a remote repository");
options.addOption("q", "quiet", false, "disable console output");
options.addOption("d", "debug", false, "enable debug logging");
options.addOption("h", "host", true, "IP address of the HTTP server");
options.addOption("p", "port", true, "TCP port of the HTTP server (8080)");
options.addOption("f", "file", true, "location of this jar file");
options.addOption("r", "repo", true, "repository directory (jackrabbit)");
options.addOption("c", "conf", true, "repository configuration file");
options.addOption(
"R", "backup-repo", true,
"backup repository directory (jackrabbit-backupN)");
options.addOption(
"C", "backup-conf", true,
"backup repository configuration file");
command = new DefaultParser().parse(options, args);
}
/**
* Run this Main application.
* <P>
* <EM>Note:</EM> this is public because this can be used by other projects in unit tests. e.g, Commons-VFS.
* @throws Exception if any exception occurs
*/
public void run() throws Exception {
String defaultFile = "jackrabbit-standalone.jar";
URL location =
Main.class.getProtectionDomain().getCodeSource().getLocation();
if (location != null && "file".equals(location.getProtocol())) {
File file = new File(location.getPath());
if (file.isFile()) {
defaultFile = location.getPath();
}
}
File file = new File(command.getOptionValue("file", defaultFile));
if (command.hasOption("help")) {
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("java -jar " + file.getName(), options, true);
} else if (command.hasOption("notice")) {
copyToOutput("/META-INF/NOTICE.txt");
} else if (command.hasOption("license")) {
copyToOutput("/META-INF/LICENSE.txt");
} else if (command.hasOption("cli")) {
System.setProperty("logback.configurationFile", "logback-cli.xml");
String uri = command.getOptionValue("cli");
Repository repository = JcrUtils.getRepository(uri);
Context context = new ContextBase();
CommandHelper.setRepository(context, repository, uri);
try {
Session session = repository.login();
CommandHelper.setSession(context, session);
CommandHelper.setCurrentNode(context, session.getRootNode());
} catch (RepositoryException ignore) {
// anonymous login not possible
}
new JcrClient(context).runInteractive();
try {
CommandHelper.getSession(context).logout();
} catch (CommandException ignore) {
// already logged out
}
} else {
message("Welcome to Apache Jackrabbit!");
message("-------------------------------");
File repository =
new File(command.getOptionValue("repo", "jackrabbit"));
message("Using repository directory " + repository);
repository.mkdirs();
File tmp = new File(repository, "tmp");
tmp.mkdir();
File log = new File(repository, "log");
log.mkdir();
message("Writing log messages to " + log);
prepareServerLog(log);
if (command.hasOption("backup")) {
backup(repository);
} else {
message("Starting the server...");
prepareWebapp(file, repository, tmp);
accessLog.setHandler(webapp);
prepareAccessLog(log);
server.setHandler(accessLog);
prepareConnector();
server.addConnector(connector);
prepareShutdown();
try {
server.start();
String host = connector.getHost();
if (host == null) {
host = "localhost";
}
message("Apache Jackrabbit is now running at "
+"http://" + host + ":" + connector.getPort() + "/");
} catch (Throwable t) {
System.err.println(
"Unable to start the server: " + t.getMessage());
System.exit(1);
}
}
}
}
/**
* Shutdown this Main application.
* <P>
* <EM>Note:</EM> this is public because this can be used by other projects in unit tests for graceful shutdown.
* e.g, Commons-VFS. If this is not invoked properly, some unexpected exceptions may occur on shutdown hook
* due to an unexpected, invalid state for org.apache.lucene.index.IndexFileDeleter for instance.
*/
public void shutdown() {
try {
message("Shutting down the server...");
server.stop();
server.join();
message("-------------------------------");
message("Goodbye from Apache Jackrabbit!");
} catch (Exception e) {
e.printStackTrace();
}
}
private void backup(File sourceDir) throws Exception {
RepositoryConfig source;
if (command.hasOption("conf")) {
source = RepositoryConfig.create(
new File(command.getOptionValue("conf")), sourceDir);
} else {
source = RepositoryConfig.create(sourceDir);
}
File targetDir;
if (command.hasOption("backup-repo")) {
targetDir = new File(command.getOptionValue("backup-repo"));
} else {
int i = 1;
do {
targetDir = new File("jackrabbit-backup" + i++);
} while (targetDir.exists());
}
RepositoryConfig target;
if (command.hasOption("backup-conf")) {
target = RepositoryConfig.install(
new File(command.getOptionValue("backup-conf")), targetDir);
} else {
target = RepositoryConfig.install(targetDir);
}
message("Creating a repository copy in " + targetDir);
RepositoryCopier.copy(source, target);
message("The repository has been successfully copied.");
}
private void prepareServerLog(File log)
throws IOException {
System.setProperty(
"jackrabbit.log", new File(log, "jackrabbit.log").getPath());
System.setProperty(
"jetty.log", new File(log, "jetty.log").getPath());
if (command.hasOption("debug")) {
System.setProperty("log.level", "DEBUG");
} else {
System.setProperty("log.level", "INFO");
}
System.setProperty(
"derby.stream.error.file",
new File(log, "derby.log").getPath());
}
private void prepareAccessLog(File log) {
NCSARequestLog ncsa = new NCSARequestLog(
new File(log, "access.log.yyyy_mm_dd").getPath());
ncsa.setFilenameDateFormat("yyyy-MM-dd");
accessLog.setRequestLog(ncsa);
}
private void prepareWebapp(File file, File repository, File tmp) {
webapp.setContextPath("/");
webapp.setWar(file.getPath());
webapp.setExtractWAR(true);
webapp.setTempDirectory(tmp);
ServletHolder servlet =
new ServletHolder(JackrabbitRepositoryServlet.class);
servlet.setInitOrder(1);
servlet.setInitParameter("repository.home", repository.getPath());
String conf = command.getOptionValue("conf");
if (conf != null) {
servlet.setInitParameter("repository.config", conf);
}
webapp.addServlet(servlet, "/repository.properties");
}
private void prepareConnector() {
String port = command.getOptionValue("port", "8080");
connector.setPort(Integer.parseInt(port));
String host = command.getOptionValue("host");
if (host != null) {
connector.setHost(host);
}
}
private void prepareShutdown() {
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
shutdown();
}
});
}
private void message(String message) {
if (!command.hasOption("quiet")) {
System.out.println(message);
}
}
private void copyToOutput(String resource) throws IOException {
InputStream stream = Main.class.getResourceAsStream(resource);
try {
IOUtils.copy(stream, System.out);
} finally {
stream.close();
}
}
}