/*
* Copyright (c) Members of the EGEE Collaboration. 2006-2010.
* See http://www.eu-egee.org/partners/ for details on the copyright holders.
*
* Licensed 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.glite.authz.common.http;
import java.util.List;
import org.glite.authz.common.util.LazyList;
import org.glite.authz.common.util.Strings;
import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Handler;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.nio.BlockingChannelConnector;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.FilterHolder;
import org.mortbay.jetty.servlet.ServletHolder;
/**
* A Jetty instance that listens on a give port for command requests.
*
* This command starts a separate Jetty instance that binds to 127.0.0.1 on a port given during service construction.
* Incoming requests are delegated to registered command objects based on their path.
*
* This service also registers a special shutdown command for itself. When the shutdown command, registered at
* <em>/shutdown</em> is run a set of registered shutdown tasks are executed after which this service is also shutdown.
*/
public class JettyAdminService {
/** Jetty service within which admin commands run. */
private Server adminService;
/** Hostname of the service. */
private String adminHost;
/** Port of the service. */
private int adminPort;
/** Password required for admin commands. */
private String adminPassword;
/** Commands registered with the service. */
private List<AbstractAdminCommand> adminCommands;
/** Tasks performed at service shutdown time. */
private List<Runnable> shutdownTasks;
/**
* Constructor.
*
* @param hostname hostname upon which the admin service listens
* @param port port upon which the admin service listens
* @param password password required to execute admin commands, may be null if no password is required
*/
public JettyAdminService(String hostname, int port, String password) {
adminHost = Strings.safeTrimOrNullString(hostname);
if (adminHost == null) {
throw new IllegalArgumentException("Admin service hostname may not be null");
}
adminPort = port;
if (adminPort < 1) {
throw new IllegalArgumentException("Admin port must be greater than 0");
}
if (adminPort > 65535) {
throw new IllegalArgumentException("Admin port must be less than 65536");
}
adminPassword = Strings.safeTrimOrNullString(password);
adminService = buildAdminService();
adminCommands = new LazyList<AbstractAdminCommand>();
shutdownTasks = new LazyList<Runnable>();
}
/**
* Registers a new administration command. New commands may not be registered after the service has been started.
*
* @param command command to register
*/
public void registerAdminCommand(AbstractAdminCommand command) {
if (command == null) {
return;
}
if (adminService.isRunning()) {
throw new IllegalStateException("Admin service is already running");
}
for (AbstractAdminCommand adminCommand : adminCommands) {
if (adminCommand.getCommandPath().equals(command.getCommandPath())) {
throw new IllegalArgumentException("Another admin command is already registered under the path "
+ command.getCommandPath());
}
}
adminCommands.add(command);
}
/**
* Registers a task to be run at shutdown time. Tasks will be run in the order they are registered. New tasks may
* not be registered once the service has been started.
*
* @param task shutdown task to run at service shutdown time
*/
public void registerShutdownTask(Runnable task) {
if (task == null) {
return;
}
if (adminService.isRunning()) {
throw new IllegalStateException("Admin service is already running");
}
shutdownTasks.add(task);
}
/**
* Creates and starts the shutdown service.
*/
public synchronized void start() {
if (adminService.isRunning()) {
throw new IllegalStateException("Admin service is already running");
}
Context commandContext = new Context(adminService, "/", false, false);
adminCommands.add(buildShutdownCommand());
ServletHolder servletHolder;
for (AbstractAdminCommand command : adminCommands) {
servletHolder = new ServletHolder(command);
commandContext.addServlet(servletHolder, command.getCommandPath());
}
if (adminPassword != null) {
FilterHolder passwordFiler = new FilterHolder(new PasswordProtectFilter(adminPassword));
commandContext.addFilter(passwordFiler, "/*", Handler.REQUEST);
}
JettyRunThread shutdownServiceRunThread = new JettyRunThread(adminService);
shutdownServiceRunThread.start();
}
/**
* Builds the Jetty server that will receive admin requests.
*
* @return Jetty server that will receive admin requests
*/
protected Server buildAdminService() {
adminService = new Server();
adminService.setSendServerVersion(false);
adminService.setSendDateHeader(false);
adminService.setStopAtShutdown(true);
BlockingChannelConnector connector = new BlockingChannelConnector();
connector.setHost(adminHost);
connector.setPort(adminPort);
adminService.setConnectors(new Connector[] { connector });
return adminService;
}
/**
* Builds an {@link AbstractAdminCommand} which shutdowns this admin service.
*
* @return the shutdown command
*/
protected AbstractAdminCommand buildShutdownCommand() {
List<Runnable> augmentedShutdownTasks = new LazyList<Runnable>();
augmentedShutdownTasks.addAll(shutdownTasks);
augmentedShutdownTasks.add(new JettyShutdownTask(adminService));
return new ShutdownCommand(augmentedShutdownTasks);
}
}