/*
* Copyright 2011 Future Systems
*
* 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.krakenapps.httpd.impl;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executors;
import org.apache.felix.ipojo.annotations.Component;
import org.apache.felix.ipojo.annotations.Provides;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.krakenapps.api.KeyStoreManager;
import org.krakenapps.confdb.Config;
import org.krakenapps.confdb.ConfigDatabase;
import org.krakenapps.confdb.ConfigService;
import org.krakenapps.confdb.Predicates;
import org.krakenapps.httpd.HttpConfigurationListener;
import org.krakenapps.httpd.HttpContextRegistry;
import org.krakenapps.httpd.HttpServer;
import org.krakenapps.httpd.HttpConfiguration;
import org.krakenapps.httpd.VirtualHost;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component(name = "http-server")
@Provides
public class HttpServerImpl implements HttpServer, HttpConfigurationListener {
private final Logger logger = LoggerFactory.getLogger(HttpServerImpl.class.getName());
private BundleContext bc;
private HttpConfiguration config;
private HttpContextRegistry contextRegistry;
private KeyStoreManager keyStoreManager;
private Channel listener;
private ConfigService conf;
public HttpServerImpl(BundleContext bc, HttpConfiguration config, HttpContextRegistry contextRegistry,
KeyStoreManager keyStoreManager, ConfigService conf) {
this.bc = bc;
this.config = config;
this.contextRegistry = contextRegistry;
this.keyStoreManager = keyStoreManager;
this.conf = conf;
// set configuration set listener
config.getListeners().add(this);
}
@Override
public void open() {
// Configure the server.
ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(), Executors.newCachedThreadPool()));
// Set up the event pipeline factory.
bootstrap.setPipelineFactory(new HttpPipelineFactory(bc, config, contextRegistry, keyStoreManager));
// Bind and start to accept incoming connections.
InetSocketAddress addr = config.getListenAddress();
listener = bootstrap.bind(addr);
logger.info("kraken httpd: {} ({}) opened", addr, config.isSsl() ? "https" : "http");
}
@Override
public HttpConfiguration getConfiguration() {
return config;
}
@Override
public void addVirtualHost(VirtualHost vhost) {
if (vhost.getHttpContextName() == null)
throw new IllegalArgumentException("kraken httpd: http context name shoud not null");
VirtualHost target = findVirtualHost(vhost.getHttpContextName());
if (target != null)
throw new IllegalStateException("duplicated http context exists: " + vhost.getHttpContextName());
config.getVirtualHosts().add(vhost);
saveConfig();
}
@Override
public void removeVirtualHost(String httpContextName) {
VirtualHost target = findVirtualHost(httpContextName);
if (target != null) {
config.getVirtualHosts().remove(target);
saveConfig();
}
}
private void saveConfig() {
Map<String, Object> filter = getFilter();
ConfigDatabase db = conf.ensureDatabase("kraken-httpd");
Config c = db.findOne(HttpConfiguration.class, Predicates.field(filter));
if (c != null) {
db.update(c, this.config);
} else {
logger.error("kraken httpd: cannot find configuration for " + config.getListenAddress());
}
}
private Map<String, Object> getFilter() {
Map<String, Object> filter = new HashMap<String, Object>();
InetSocketAddress listen = config.getListenAddress();
filter.put("listen_address", listen.getAddress().getHostAddress());
filter.put("listen_port", listen.getPort());
return filter;
}
private VirtualHost findVirtualHost(String httpContextName) {
VirtualHost target = null;
for (VirtualHost h : config.getVirtualHosts())
if (h.getHttpContextName().equals(httpContextName))
target = h;
return target;
}
@Override
public void onSet(String field, Object value) {
saveConfig();
}
@Override
public void close() {
// remove reference
config.getListeners().remove(this);
try {
if (listener != null) {
logger.info("kraken httpd: {} closed", listener.getLocalAddress());
listener.unbind();
}
} catch (Throwable t) {
logger.error("kraken httpd: cannot close " + listener.getLocalAddress(), t);
}
}
@Override
public boolean isOpened() {
// TODO Auto-generated method stub
return listener == null ? false : listener.isOpen();
}
}