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 }