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 // LoggerFactory.getLogger("org.sonar.INFO").info("Sonar started: " + toString());
093 }
094
095 private Server configureProgrammatically() throws URISyntaxException, IOException {
096 configureServer();
097 WebAppContext context = new WebAppContext(getPath("/war/sonar-server"), contextPath);
098 server.addHandler(context);
099 return server;
100 }
101
102 public void configureRequestLogs(String filenamePattern) {
103 RequestLogHandler requestLogHandler = new RequestLogHandler();
104 NCSARequestLog requestLog = new NCSARequestLog(filenamePattern);
105 requestLog.setRetainDays(7);
106 requestLog.setAppend(true);
107 requestLog.setExtended(true);
108 requestLog.setLogTimeZone("GMT");
109 requestLogHandler.setRequestLog(requestLog);
110 server.addHandler(requestLogHandler);
111 }
112
113 private void configureServer() throws URISyntaxException {
114 QueuedThreadPool threadPool = new QueuedThreadPool();
115 threadPool.setMinThreads(5);
116 threadPool.setMaxThreads(50);
117 threadPool.setLowThreads(10);
118 server.setThreadPool(threadPool);
119 SelectChannelConnector connector = new SelectChannelConnector();
120 connector.setHost(host);
121 connector.setPort(port);
122 connector.setStatsOn(false);
123 connector.setAcceptors(2);
124 connector.setConfidentialPort(8443);
125 if (ajp13Port > 0) {
126 System.out.println("AJP13 connector is on port " + ajp13Port);
127 Connector ajpConnector = new Ajp13SocketConnector();
128 ajpConnector.setPort(ajp13Port);
129 server.addConnector(ajpConnector);
130 }
131 server.addConnector(connector);
132 server.setStopAtShutdown(true);
133 server.setSendServerVersion(false);
134 server.setSendDateHeader(true);
135 server.setGracefulShutdown(1000);
136
137 }
138
139 final String getPluginsClasspath(String pluginsPathFromClassloader) throws URISyntaxException, IOException {
140 final URL resource = getClass().getResource(pluginsPathFromClassloader);
141 if (resource != null) {
142 File pluginsDir = new File(resource.toURI());
143 List<String> paths = new ArrayList<String>();
144 paths.add(pluginsDir.getCanonicalPath() + System.getProperty("file.separator"));
145
146 Collection<File> files = FileUtils.listFiles(pluginsDir, new String[]{"jar"}, false);
147 if (files != null) {
148 for (File file : files) {
149 paths.add(file.getCanonicalPath());
150 }
151 }
152 return join(paths, ",");
153 }
154 return null;
155 }
156
157 private String join(List<String> paths, String separator) {
158 StringBuilder sb = new StringBuilder();
159 boolean first = true;
160 for (String path : paths) {
161 if (!first) {
162 sb.append(separator);
163 }
164 sb.append(path);
165 first = false;
166 }
167 return sb.toString();
168 }
169
170 private String getPath(String resourcePath) throws URISyntaxException {
171 URL resource = getClass().getResource(resourcePath);
172 if (resource != null) {
173 return resource.toURI().toString();
174 }
175 return null;
176 }
177
178 Server getServer() {
179 return server;
180 }
181
182 @Override
183 public String toString() {
184 return new StringBuilder().append("http://").append(host).append(":").append(port).append(contextPath).toString();
185 }
186 }