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     */
020    package org.sonar.application;
021    
022    import org.apache.commons.io.FileUtils;
023    import org.mortbay.jetty.NCSARequestLog;
024    import org.mortbay.jetty.Server;
025    import org.mortbay.jetty.handler.RequestLogHandler;
026    import org.mortbay.jetty.nio.SelectChannelConnector;
027    import org.mortbay.jetty.webapp.WebAppContext;
028    import org.mortbay.thread.QueuedThreadPool;
029    import org.mortbay.xml.XmlConfiguration;
030    
031    import java.io.File;
032    import java.io.IOException;
033    import java.net.URISyntaxException;
034    import java.net.URL;
035    import java.util.ArrayList;
036    import java.util.Collection;
037    import java.util.List;
038    
039    public 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, IOException {
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() throws URISyntaxException {
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    }