001    /*
002     * Sonar, open source software quality management tool.
003     * Copyright (C) 2008-2011 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     */
020    package org.sonar.application;
021    
022    import org.apache.commons.io.FileUtils;
023    import org.mortbay.jetty.Connector;
024    import org.mortbay.jetty.NCSARequestLog;
025    import org.mortbay.jetty.Server;
026    import org.mortbay.jetty.ajp.Ajp13SocketConnector;
027    import org.mortbay.jetty.handler.RequestLogHandler;
028    import org.mortbay.jetty.nio.SelectChannelConnector;
029    import org.mortbay.jetty.webapp.WebAppContext;
030    import org.mortbay.thread.QueuedThreadPool;
031    import org.mortbay.xml.XmlConfiguration;
032    
033    import java.io.File;
034    import java.io.IOException;
035    import java.net.URISyntaxException;
036    import java.net.URL;
037    import java.util.ArrayList;
038    import java.util.Collection;
039    import java.util.List;
040    
041    public class JettyEmbedder {
042    
043      private Server server;
044      private String host;
045      private int port;
046      private String contextPath;
047      private int ajp13Port = -1;
048    
049      public JettyEmbedder(String host, int port, String contextPath, int ajp13Port, URL configurationURL) throws Exception {
050        this.host = host.trim();
051        this.port = port;
052        this.contextPath = contextPath;
053        this.ajp13Port = ajp13Port;
054        server = new Server();
055    
056        if (configurationURL == null) {
057          configureProgrammatically();
058    
059        } else {
060          System.setProperty("jetty.host", this.host);
061          System.setProperty("jetty.port", String.valueOf(port));
062          System.setProperty("jetty.context", contextPath);
063          if (ajp13Port > 0) {
064            System.setProperty("jetty.ajp13Port", String.valueOf(ajp13Port));
065          }
066          XmlConfiguration configuration = new XmlConfiguration(configurationURL);
067          configuration.configure(server);
068        }
069      }
070    
071      /**
072       * for tests
073       */
074      JettyEmbedder(String host, int port) throws Exception {
075        this(host, port, null, 0, null);
076      }
077    
078      public void start() throws Exception {
079        server.start();
080    
081        Runtime.getRuntime().addShutdownHook(new Thread() {
082          @Override
083          public void run() {
084            try {
085              server.stop();
086            } catch (Exception e) {
087              System.err.println("Can not stop the Jetty server");
088              e.printStackTrace();
089            }
090          }
091        });
092      }
093    
094      private Server configureProgrammatically() throws URISyntaxException, IOException {
095        configureServer();
096        WebAppContext context = new WebAppContext(getPath("/war/sonar-server"), contextPath);
097        server.addHandler(context);
098        return server;
099      }
100    
101      public void configureRequestLogs(String filenamePattern) {
102        RequestLogHandler requestLogHandler = new RequestLogHandler();
103        NCSARequestLog requestLog = new NCSARequestLog(filenamePattern);
104        requestLog.setRetainDays(7);
105        requestLog.setAppend(true);
106        requestLog.setExtended(true);
107        requestLog.setLogTimeZone("GMT");
108        requestLogHandler.setRequestLog(requestLog);
109        server.addHandler(requestLogHandler);
110      }
111    
112      private void configureServer() throws URISyntaxException {
113        QueuedThreadPool threadPool = new QueuedThreadPool();
114        threadPool.setMinThreads(5);
115        threadPool.setMaxThreads(50);
116        threadPool.setLowThreads(10);
117        server.setThreadPool(threadPool);
118        SelectChannelConnector connector = new SelectChannelConnector();
119        connector.setHost(host);
120        connector.setPort(port);
121        connector.setStatsOn(false);
122        connector.setAcceptors(2);
123        connector.setConfidentialPort(8443);
124        if (ajp13Port > 0) {
125          System.out.println("AJP13 connector is on port " + ajp13Port);
126          Connector ajpConnector = new Ajp13SocketConnector();
127          ajpConnector.setPort(ajp13Port);
128          server.addConnector(ajpConnector);
129        }
130        server.addConnector(connector);
131        server.setStopAtShutdown(true);
132        server.setSendServerVersion(false);
133        server.setSendDateHeader(true);
134        server.setGracefulShutdown(1000);
135    
136      }
137    
138      final String getPluginsClasspath(String pluginsPathFromClassloader) throws URISyntaxException, IOException {
139        final URL resource = getClass().getResource(pluginsPathFromClassloader);
140        if (resource != null) {
141          File pluginsDir = new File(resource.toURI());
142          List<String> paths = new ArrayList<String>();
143          paths.add(pluginsDir.getCanonicalPath() + System.getProperty("file.separator"));
144    
145          Collection<File> files = FileUtils.listFiles(pluginsDir, new String[]{"jar"}, false);
146          if (files != null) {
147            for (File file : files) {
148              paths.add(file.getCanonicalPath());
149            }
150          }
151          return join(paths, ",");
152        }
153        return null;
154      }
155    
156      private String join(List<String> paths, String separator) {
157        StringBuilder sb = new StringBuilder();
158        boolean first = true;
159        for (String path : paths) {
160          if (!first) {
161            sb.append(separator);
162          }
163          sb.append(path);
164          first = false;
165        }
166        return sb.toString();
167      }
168    
169      private String getPath(String resourcePath) throws URISyntaxException {
170        URL resource = getClass().getResource(resourcePath);
171        if (resource != null) {
172          return resource.toURI().toString();
173        }
174        return null;
175      }
176    
177      Server getServer() {
178        return server;
179      }
180    
181      @Override
182      public String toString() {
183        return new StringBuilder().append("http://").append(host).append(":").append(port).append(contextPath).toString();
184      }
185    }