001/*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * Sonar is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * Sonar is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public
017 * License along with Sonar; if not, write to the Free Software
018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
019 */
020package org.sonar.application;
021
022import org.apache.commons.io.FileUtils;
023import org.mortbay.jetty.NCSARequestLog;
024import org.mortbay.jetty.Server;
025import org.mortbay.jetty.handler.RequestLogHandler;
026import org.mortbay.jetty.nio.SelectChannelConnector;
027import org.mortbay.jetty.webapp.WebAppContext;
028import org.mortbay.thread.QueuedThreadPool;
029import org.mortbay.xml.XmlConfiguration;
030
031import java.io.File;
032import java.io.IOException;
033import java.net.URISyntaxException;
034import java.net.URL;
035import java.util.ArrayList;
036import java.util.Collection;
037import java.util.List;
038
039public class JettyEmbedder {
040
041  private final Server server;
042  private final String host;
043  private final int port;
044  private final String contextPath;
045
046  public JettyEmbedder(String host, int port, String contextPath, URL configurationURL) throws Exception {
047    this.host = host.trim();
048    this.port = port;
049    this.contextPath = contextPath;
050    server = new Server();
051
052    if (configurationURL == null) {
053      configureProgrammatically();
054
055    } else {
056      System.setProperty("jetty.host", this.host);
057      System.setProperty("jetty.port", String.valueOf(port));
058      System.setProperty("jetty.context", contextPath);
059      XmlConfiguration configuration = new XmlConfiguration(configurationURL);
060      configuration.configure(server);
061    }
062  }
063
064  /**
065   * for tests
066   */
067  JettyEmbedder(String host, int port) throws Exception {
068    this(host, port, null, null);
069  }
070
071  public void start() throws Exception {
072    server.start();
073
074    Runtime.getRuntime().addShutdownHook(new Thread() {
075      @Override
076      public void run() {
077        try {
078          server.stop();
079        } catch (Exception e) {
080          System.err.println("Can not stop the Jetty server");
081          e.printStackTrace();
082        }
083      }
084    });
085  }
086
087  private Server configureProgrammatically() throws URISyntaxException {
088    configureServer();
089    WebAppContext context = new WebAppContext(getPath("/war/sonar-server"), contextPath);
090    server.addHandler(context);
091    return server;
092  }
093
094  public void configureRequestLogs(String filenamePattern) {
095    RequestLogHandler requestLogHandler = new RequestLogHandler();
096    NCSARequestLog requestLog = new NCSARequestLog(filenamePattern);
097    requestLog.setRetainDays(7);
098    requestLog.setAppend(true);
099    requestLog.setExtended(true);
100    requestLog.setLogTimeZone("GMT");
101    requestLogHandler.setRequestLog(requestLog);
102    server.addHandler(requestLogHandler);
103  }
104
105  private void configureServer() {
106    QueuedThreadPool threadPool = new QueuedThreadPool();
107    threadPool.setMinThreads(5);
108    threadPool.setMaxThreads(50);
109    threadPool.setLowThreads(10);
110    server.setThreadPool(threadPool);
111    SelectChannelConnector connector = new SelectChannelConnector();
112    connector.setHost(host);
113    connector.setPort(port);
114    connector.setStatsOn(false);
115    connector.setAcceptors(2);
116    connector.setConfidentialPort(8443);
117    server.addConnector(connector);
118    server.setStopAtShutdown(true);
119    server.setSendServerVersion(false);
120    server.setSendDateHeader(true);
121    server.setGracefulShutdown(1000);
122  }
123
124  final String getPluginsClasspath(String pluginsPathFromClassloader) throws URISyntaxException, IOException {
125    final URL resource = getClass().getResource(pluginsPathFromClassloader);
126    if (resource != null) {
127      File pluginsDir = new File(resource.toURI());
128      List<String> paths = new ArrayList<String>();
129      paths.add(pluginsDir.getCanonicalPath() + System.getProperty("file.separator"));
130
131      Collection<File> files = FileUtils.listFiles(pluginsDir, new String[] {"jar"}, false);
132      if (files != null) {
133        for (File file : files) {
134          paths.add(file.getCanonicalPath());
135        }
136      }
137      return join(paths, ",");
138    }
139    return null;
140  }
141
142  private String join(List<String> paths, String separator) {
143    StringBuilder sb = new StringBuilder();
144    boolean first = true;
145    for (String path : paths) {
146      if (!first) {
147        sb.append(separator);
148      }
149      sb.append(path);
150      first = false;
151    }
152    return sb.toString();
153  }
154
155  private String getPath(String resourcePath) throws URISyntaxException {
156    URL resource = getClass().getResource(resourcePath);
157    if (resource != null) {
158      return resource.toURI().toString();
159    }
160    return null;
161  }
162
163  Server getServer() {
164    return server;
165  }
166
167  @Override
168  public String toString() {
169    return new StringBuilder().append("http://").append(host).append(":").append(port).append(contextPath).toString();
170  }
171}