/*
* Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Florent Guillaume
*/
package org.eclipse.ecr.core.storage.sql.net;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import org.eclipse.ecr.core.storage.sql.BinaryManager;
import org.eclipse.ecr.core.storage.sql.Mapper;
import org.eclipse.ecr.core.storage.sql.RepositoryDescriptor.ServerDescriptor;
import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Handler;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.bio.SocketConnector;
import org.mortbay.jetty.handler.ContextHandler;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.ServletHandler;
import org.mortbay.jetty.servlet.ServletHolder;
import org.mortbay.jetty.servlet.ServletMapping;
/**
* Network server for a repository. Can receive remote connections for
* {@link Mapper} and {@link BinaryManager} access (depending on the registered
* servlets).
* <p>
* Runs an embedded Jetty server.
*/
public class NetServer {
protected static NetServer instance;
protected final Server server;
public static String add(ServerDescriptor serverDescriptor,
String servletName, Servlet servlet, String path) {
return instance().addRepositoryServer(serverDescriptor, servletName,
servlet, path);
}
public static Servlet get(ServerDescriptor serverDescriptor, String servletName) {
return instance().getServlet(serverDescriptor, servletName);
}
public static void remove(ServerDescriptor serverDescriptor,
String servletName) {
instance().removeRepositoryServer(serverDescriptor, servletName);
}
protected static synchronized NetServer instance() {
if (instance == null) {
instance = new NetServer();
try {
instance.server.start();
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return instance;
}
protected static synchronized void shutDown() {
if (instance != null) {
Server server = instance.server;
instance = null;
try {
server.stop();
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
protected NetServer() {
server = new Server();
// set empty handlers otherwise getHandlers fails
server.setHandlers(new ContextHandler[0]);
}
protected String addRepositoryServer(ServerDescriptor serverDescriptor,
String servletName, Servlet servlet, String path) {
try {
addConnector(serverDescriptor);
String contextPath = getContextPath(serverDescriptor);
Context context = addContext(contextPath);
ServletHandler servletHandler = context.getServletHandler();
ServletHolder servletHolder = new ServletHolder(servlet);
servletHolder.setName(servletName);
if (!path.startsWith("/")) {
path = '/' + path;
}
servletHandler.addServletWithMapping(servletHolder, path);
return "http://" + serverDescriptor.host + ':'
+ serverDescriptor.port + contextPath + path;
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected void removeRepositoryServer(ServerDescriptor serverDescriptor,
String servletName) {
try {
boolean stop = removeConnector(serverDescriptor);
Context context = removeContext(getContextPath(serverDescriptor));
ServletHandler servletHandler = context.getServletHandler();
// remove servlet mapping
LinkedList<ServletMapping> sml = new LinkedList<ServletMapping>(
Arrays.asList(servletHandler.getServletMappings()));
for (Iterator<ServletMapping> it = sml.iterator(); it.hasNext();) {
ServletMapping sm = it.next();
if (sm.getServletName().equals(servletName)) {
it.remove();
break;
}
}
servletHandler.setServletMappings(sml.toArray(new ServletMapping[0]));
// remove servlet
List<ServletHolder> sl = new LinkedList<ServletHolder>(
Arrays.asList(servletHandler.getServlets()));
for (Iterator<ServletHolder> it = sl.iterator(); it.hasNext();) {
ServletHolder s = it.next();
if (s.getName().equals(servletName)) {
it.remove();
break;
}
}
servletHandler.setServlets(sl.toArray(new ServletHolder[0]));
if (stop) {
shutDown();
}
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected Servlet getServlet(ServerDescriptor serverDescriptor, String servletName) {
Context context = getContext(getContextPath(serverDescriptor));
ServletHandler handler = context.getServletHandler();
ServletHolder holder = handler.getServlet(servletName);
try {
return holder.getServlet();
} catch (ServletException e) {
throw new Error("No such servlet " + serverDescriptor.getUrl() + ":" + servletName, e);
}
}
protected String getContextPath(ServerDescriptor serverDescriptor) {
String contextPath = serverDescriptor.path;
if (!contextPath.startsWith("/")) {
contextPath = '/' + contextPath;
}
return contextPath;
}
protected final Map<Connector, AtomicLong> connectorRefs = new HashMap<Connector, AtomicLong>();
protected void addConnector(ServerDescriptor serverDescriptor)
throws Exception {
Connector connector = getConnector(serverDescriptor);
if (connector == null) {
connector = new SocketConnector();
connector.setHost(serverDescriptor.host);
connector.setPort(serverDescriptor.port);
server.addConnector(connector);
connector.start();
connectorRefs.put(connector, new AtomicLong());
}
connectorRefs.get(connector).incrementAndGet();
}
/** Returns {@code true} if last connector removed. */
protected boolean removeConnector(ServerDescriptor serverDescriptor)
throws Exception {
Connector connector = getConnector(serverDescriptor);
if (connector == null) {
throw new RuntimeException("Unknown connector for: "
+ serverDescriptor);
}
long refs = connectorRefs.get(connector).decrementAndGet();
if (refs == 0) {
connectorRefs.remove(connector);
connector.stop();
List<Connector> cl = new LinkedList<Connector>(
Arrays.asList(server.getConnectors()));
cl.remove(connector);
server.setConnectors(cl.toArray(new Connector[0]));
if (cl.size() == 0) {
return true;
}
}
return false;
}
protected Connector getConnector(ServerDescriptor serverDescriptor) {
Connector[] connectors = server.getConnectors();
if (connectors == null) {
return null;
}
for (Connector c : connectors) {
if (c.getHost().equals(serverDescriptor.host)
&& c.getPort() == serverDescriptor.port) {
return c;
}
}
return null;
}
protected final Map<Context, AtomicLong> contextRefs = new HashMap<Context, AtomicLong>();
protected Context addContext(String path) throws Exception {
Context context = getContext(path);
if (context == null) {
context = new Context(server, path, Context.SESSIONS);
context.start();
contextRefs.put(context, new AtomicLong());
}
contextRefs.get(context).incrementAndGet();
return context;
}
protected Context removeContext(String path) throws Exception {
Context context = getContext(path);
if (context == null) {
throw new RuntimeException("Unknown context: " + path);
}
long refs = contextRefs.get(context).decrementAndGet();
if (refs == 0) {
contextRefs.remove(context);
context.stop();
server.removeHandler(context);
}
return context;
}
protected Context getContext(String path) {
Handler[] handlers = server.getHandlers();
if (handlers == null) {
return null;
}
for (Handler h : handlers) {
if (!(h instanceof Context)) {
continue;
}
Context c = (Context) h;
if (c.getContextPath().equals(path)) {
return c;
}
}
return null;
}
}