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 }