blob: 6cb41a4392549b2c87d86f0a656dd2a18fd876c8 [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.asyncweb.server;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.apache.asyncweb.server.context.KeepAliveStrategy;
import org.apache.asyncweb.server.errorReporting.ErrorResponseFormatter;
import org.apache.asyncweb.server.errorReporting.StandardResponseFormatter;
import org.apache.asyncweb.server.session.DefaultSessionAccessor;
import org.apache.asyncweb.server.session.HttpSessionAccessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.asyncweb.server.context.BasicKeepAliveStrategy;
import org.apache.asyncweb.server.context.CounterKeepAliveStrategy;
/**
* Basic <code>ServiceContainer</code> implementation.
*
* @author irvingd
* @author trustin
*
* @version $Rev$, $Date$
*/
public class BasicServiceContainer implements ServiceContainer {
private static final Logger LOG = LoggerFactory
.getLogger(BasicServiceContainer.class);
/**
* The default number of keep-alive requests
*/
private static final int DEFAULT_KEEP_ALIVE_REQUESTS = 75;
/**
* Represents no limit on the number of keep-alive requests
*/
private static final int INFINITE_KEEP_ALIVES = -1;
private boolean isStarted;
private int maxKeepAlives = DEFAULT_KEEP_ALIVE_REQUESTS;
private KeepAliveStrategy keepAliveStrategy = new CounterKeepAliveStrategy(
maxKeepAlives);
private HttpSessionAccessor sessionAccessor;
private ErrorResponseFormatter errorResponseFormatter = new StandardResponseFormatter();
private boolean sendServerHeader = true;
private List<HttpServiceFilter> filters = new LinkedList<HttpServiceFilter>();
private List<Transport> transports = new LinkedList<Transport>();
public boolean isSendServerHeader() {
return sendServerHeader;
}
public void setSendServerHeader(boolean sendServerHeader) {
this.sendServerHeader = sendServerHeader;
}
public ErrorResponseFormatter getErrorResponseFormatter() {
return errorResponseFormatter;
}
public void setErrorResponseFormatter(
ErrorResponseFormatter errorResponseFormatter) {
if (errorResponseFormatter == null) {
throw new NullPointerException("errorResponseFormatter");
}
this.errorResponseFormatter = errorResponseFormatter;
}
public int getMaxKeepAlives() {
return maxKeepAlives;
}
/**
* Returns the employed {@link KeepAliveStrategy} of this container.
*/
public KeepAliveStrategy getKeepAliveStrategy() {
return keepAliveStrategy;
}
/**
* Sets the maximum number of keep-alive requests
*
* @param maxKeepAlives THe maximum number of keep alive requests
*/
public void setMaxKeepAlives(int maxKeepAlives) {
if (maxKeepAlives < INFINITE_KEEP_ALIVES) {
throw new IllegalArgumentException("Invalid keep alives: "
+ maxKeepAlives);
}
this.maxKeepAlives = maxKeepAlives;
if (maxKeepAlives == INFINITE_KEEP_ALIVES) {
LOG.info("Infinite keep-alives configured");
} else {
LOG.info("Max keep-alives configured: " + maxKeepAlives);
}
keepAliveStrategy = maxKeepAlives == -1 ? new BasicKeepAliveStrategy()
: new CounterKeepAliveStrategy(maxKeepAlives);
}
/**
* Adds a <code>ServiceHandler</code> to this container
*
* @param handler The handler to add
* @throws IllegalStateException If this container has been started
*/
public void addServiceFilter(HttpServiceFilter handler) {
if (isStarted) {
throw new IllegalStateException(
"Attempt to add filter to running container");
}
LOG.info("Adding service handler '" + handler + "'");
synchronized (filters) {
filters.add(handler);
}
}
/**
* Adds a <code>Transport</code> to this container
*
* @param transport The transport to add
* @throws IllegalStateException If this container has been started
*/
public void addTransport(Transport transport) {
if (isStarted) {
throw new IllegalStateException(
"Attempt to add transport to running container");
}
LOG.info("Adding transport '" + transport + "'");
transport.setServiceContainer(this);
synchronized (transports) {
transports.add(transport);
}
}
public List<HttpServiceFilter> getServiceFilters() {
return Collections.unmodifiableList(this.filters);
}
/**
* Sets all <code>ServiceHandler</code>s employed by this container.
* Any existing handlers are removed
*
* @param filters A list of <code>ServiceHandler</code>s
* @throws IllegalStateException If this container has been started
*/
public void setServiceFilters(List<HttpServiceFilter> filters) {
if (isStarted) {
throw new IllegalStateException(
"Attempt to add filter to running container");
}
synchronized (this.filters) {
this.filters.clear();
for (HttpServiceFilter filter : filters) {
addServiceFilter(filter);
}
}
}
/**
* Sets all <code>Transport</code>s employed by this container.
* Any existing transport are removed
*
* @param transports A list of <code>Transport</code>s
* @throws IllegalStateException If this container has been started
*/
public void setTransports(List<Transport> transports) {
if (isStarted) {
throw new IllegalStateException(
"Attempt to add transport to running container");
}
synchronized (this.transports) {
this.transports.clear();
for (Transport transport : transports) {
addTransport(transport);
}
}
}
public HttpSessionAccessor getSessionAccessor() {
return this.sessionAccessor;
}
public void setSessionAccessor(HttpSessionAccessor sessionAccessor) {
if (sessionAccessor == null) {
throw new NullPointerException("sessionAccessor");
}
this.sessionAccessor = sessionAccessor;
}
public void start() throws ContainerLifecycleException {
if (!isStarted) {
if (LOG.isDebugEnabled())
LOG.debug("BasicServiceContainer starting");
startSessionAccessor();
startHandlers();
startTransports();
if (LOG.isDebugEnabled())
LOG.debug("BasicServiceContainer started");
isStarted = true;
}
}
public void stop() {
if (isStarted) {
isStarted = false;
if (LOG.isDebugEnabled())
LOG.debug("BasicServiceContainer stopping");
stopHandlers();
stopTransports();
stopSessionAccessor();
if (LOG.isDebugEnabled())
LOG.debug("BasicServiceContainer stopped");
}
}
/**
* Starts our session accessor.
* If no session accessor has been configured, a default accessor is employed
*/
private void startSessionAccessor() {
if (sessionAccessor == null) {
LOG.info("No SessionAccessor configured. Using default");
sessionAccessor = new DefaultSessionAccessor();
}
sessionAccessor.init();
}
/**
* Starts all added handlers
*/
private void startHandlers() {
if (LOG.isDebugEnabled())
LOG.debug("Starting handlers");
synchronized (filters) {
for (HttpServiceFilter handler : filters) {
handler.start();
}
}
if (LOG.isDebugEnabled())
LOG.debug("Handlers started");
}
private void stopHandlers() {
if (LOG.isDebugEnabled())
LOG.debug("Stopping handlers");
synchronized (filters) {
for (HttpServiceFilter handler : filters) {
LOG.info("Stopping handler '" + handler + "'");
handler.stop();
LOG.info("Handler '" + handler + "' stopped");
}
}
if (LOG.isDebugEnabled())
LOG.debug("Handlers stopped");
}
private void stopSessionAccessor() {
if (LOG.isDebugEnabled())
LOG.debug("Disposing session accessor");
sessionAccessor.dispose();
if (LOG.isDebugEnabled())
LOG.debug("Session accessor disposed");
}
/**
* Starts all added transports
*
* @throws ContainerLifecycleException If we fail to start a transport
*/
private void startTransports() throws ContainerLifecycleException {
if (LOG.isDebugEnabled())
LOG.debug("Starting transports");
synchronized (transports) {
for (Transport transport : transports) {
LOG.info("Starting transport '" + transport + "'");
try {
transport.start();
} catch (TransportException e) {
LOG.info("Transport '" + transport + "' failed to start");
throw new ContainerLifecycleException(
"Failed to start transport ' " + transport + "'", e);
}
}
}
if (LOG.isDebugEnabled())
LOG.debug("Transports started");
}
private void stopTransports() {
if (LOG.isDebugEnabled())
LOG.debug("Stopping transports");
boolean isError = false;
synchronized (transports) {
for (Transport transport : transports) {
LOG.info("Stopping transport '" + transport + "'");
try {
transport.stop();
LOG.info("Transport '" + transport + "' stopped");
} catch (TransportException e) {
LOG.warn("Failed to stop transport '" + transport + "'", e);
isError = true;
}
}
}
String errorString = isError ? " (One or more errors encountered)" : "";
if (LOG.isDebugEnabled())
LOG.debug("Transports stopped" + errorString);
}
}