blob: 6cf73adee8b0d34a25396fa9f85e8cc2788f94bd [file] [log] [blame]
Dave Borowitz09ff62b2012-12-17 14:09:16 -08001// Copyright 2012 Google Inc. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package com.google.gitiles.dev;
16
Dave Borowitzc410f962014-09-23 10:49:26 -070017import static com.google.common.base.MoreObjects.firstNonNull;
Dave Borowitz03397692012-12-18 17:11:16 -080018import static com.google.gitiles.GitilesServlet.STATIC_PREFIX;
19
Shawn Pearce68311c72015-06-09 17:01:34 -070020import com.google.common.base.Strings;
Dave Borowitz03397692012-12-18 17:11:16 -080021import com.google.gitiles.DebugRenderer;
Shawn Pearce68311c72015-06-09 17:01:34 -070022import com.google.gitiles.GitilesAccess;
Dave Borowitz09ff62b2012-12-17 14:09:16 -080023import com.google.gitiles.GitilesServlet;
Dave Borowitz03397692012-12-18 17:11:16 -080024import com.google.gitiles.PathServlet;
Shawn Pearce68311c72015-06-09 17:01:34 -070025import com.google.gitiles.RepositoryDescription;
26import com.google.gitiles.RootedDocServlet;
Dave Borowitz09ff62b2012-12-17 14:09:16 -080027
Dave Borowitz09ff62b2012-12-17 14:09:16 -080028import org.eclipse.jetty.server.Handler;
29import org.eclipse.jetty.server.Server;
30import org.eclipse.jetty.server.handler.ContextHandler;
31import org.eclipse.jetty.server.handler.ContextHandlerCollection;
32import org.eclipse.jetty.server.handler.ResourceHandler;
Dave Borowitz09ff62b2012-12-17 14:09:16 -080033import org.eclipse.jetty.servlet.ServletContextHandler;
34import org.eclipse.jetty.servlet.ServletHolder;
35import org.eclipse.jetty.util.resource.FileResource;
Dave Borowitz03397692012-12-18 17:11:16 -080036import org.eclipse.jgit.errors.ConfigInvalidException;
Shawn Pearce68311c72015-06-09 17:01:34 -070037import org.eclipse.jgit.errors.RepositoryNotFoundException;
Dave Borowitz09ff62b2012-12-17 14:09:16 -080038import org.eclipse.jgit.lib.Config;
Shawn Pearce68311c72015-06-09 17:01:34 -070039import org.eclipse.jgit.lib.Constants;
40import org.eclipse.jgit.lib.Repository;
41import org.eclipse.jgit.lib.RepositoryCache;
42import org.eclipse.jgit.lib.RepositoryCache.FileKey;
Dave Borowitz03397692012-12-18 17:11:16 -080043import org.eclipse.jgit.storage.file.FileBasedConfig;
Shawn Pearce68311c72015-06-09 17:01:34 -070044import org.eclipse.jgit.transport.resolver.RepositoryResolver;
Dave Borowitz03397692012-12-18 17:11:16 -080045import org.eclipse.jgit.util.FS;
46import org.slf4j.Logger;
47import org.slf4j.LoggerFactory;
Dave Borowitz09ff62b2012-12-17 14:09:16 -080048
49import java.io.File;
Dave Borowitz09ff62b2012-12-17 14:09:16 -080050import java.io.IOException;
Dave Borowitz03397692012-12-18 17:11:16 -080051import java.net.InetAddress;
Dave Borowitz09ff62b2012-12-17 14:09:16 -080052import java.net.URISyntaxException;
Dave Borowitz03397692012-12-18 17:11:16 -080053import java.net.UnknownHostException;
Dave Borowitz197d3ec2015-12-07 10:02:33 -050054import java.nio.file.NoSuchFileException;
55import java.nio.file.Path;
56import java.nio.file.Paths;
Dave Borowitz76bbefd2014-03-11 16:57:45 -070057import java.util.Arrays;
Shawn Pearce68311c72015-06-09 17:01:34 -070058import java.util.Collections;
59import java.util.Map;
60import java.util.Set;
61
62import javax.servlet.Servlet;
63import javax.servlet.http.HttpServletRequest;
Dave Borowitz09ff62b2012-12-17 14:09:16 -080064
65class DevServer {
Dave Borowitz03397692012-12-18 17:11:16 -080066 private static final Logger log = LoggerFactory.getLogger(PathServlet.class);
67
Dave Borowitz823724a2013-07-30 13:18:42 -070068 private static Config defaultConfig() {
Dave Borowitz03397692012-12-18 17:11:16 -080069 Config cfg = new Config();
70 String cwd = System.getProperty("user.dir");
71 cfg.setString("gitiles", null, "basePath", cwd);
72 cfg.setBoolean("gitiles", null, "exportAll", true);
73 cfg.setString("gitiles", null, "baseGitUrl", "file://" + cwd + "/");
Dave Borowitz823724a2013-07-30 13:18:42 -070074 String networkHostName;
75 try {
76 networkHostName = InetAddress.getLocalHost().getCanonicalHostName();
77 } catch (UnknownHostException e) {
78 networkHostName = "127.0.0.1";
79 }
Dave Borowitzcf38c032016-05-02 11:06:23 -040080 cfg.setString(
81 "gitiles", null, "siteTitle", String.format("Gitiles - %s:%s", networkHostName, cwd));
Dave Borowitz03397692012-12-18 17:11:16 -080082 cfg.setString("gitiles", null, "canonicalHostName", new File(cwd).getName());
83 return cfg;
84 }
85
Dave Borowitz197d3ec2015-12-07 10:02:33 -050086 private static Path findSourceRoot() throws IOException {
Han-Wen Nienhuysad160922016-03-30 13:22:27 +020087 String prop = "com.google.gitiles.sourcePath";
88 String sourceRoot = System.getProperty(prop);
89 if (sourceRoot == null) {
90 throw new NoSuchFileException(
Dave Borowitzcf38c032016-05-02 11:06:23 -040091 String.format("Must set system property %s to top of source directory", prop));
Dave Borowitz03397692012-12-18 17:11:16 -080092 }
Han-Wen Nienhuysad160922016-03-30 13:22:27 +020093 return Paths.get(sourceRoot);
Dave Borowitz03397692012-12-18 17:11:16 -080094 }
95
Han-Wen Nienhuys5ca80022016-03-30 12:58:27 +020096 private final Path sourceRoot;
Dave Borowitz03397692012-12-18 17:11:16 -080097 private final Config cfg;
Dave Borowitz09ff62b2012-12-17 14:09:16 -080098 private final Server httpd;
99
Dave Borowitz03397692012-12-18 17:11:16 -0800100 DevServer(File cfgFile) throws IOException, ConfigInvalidException {
Han-Wen Nienhuysad160922016-03-30 13:22:27 +0200101 // Jetty doesn't doesn't allow symlinks, so canonicalize.
102 sourceRoot = findSourceRoot().toRealPath();
Dave Borowitz03397692012-12-18 17:11:16 -0800103
104 Config cfg = defaultConfig();
105 if (cfgFile.exists() && cfgFile.isFile()) {
106 FileBasedConfig fcfg = new FileBasedConfig(cfg, cfgFile, FS.DETECTED);
107 fcfg.load();
108 cfg = fcfg;
109 } else {
Dave Borowitzfd25c3a2013-01-11 14:37:11 -0800110 log.info("Config file {} not found, using defaults", cfgFile.getPath());
Dave Borowitz03397692012-12-18 17:11:16 -0800111 }
112 this.cfg = cfg;
113
David Pursehouse6e7b58f2015-12-07 19:37:21 +0900114 httpd = new Server(cfg.getInt("gitiles", null, "port", 8080));
Dave Borowitz09ff62b2012-12-17 14:09:16 -0800115 httpd.setHandler(handler());
116 }
117
118 void start() throws Exception {
119 httpd.start();
120 httpd.join();
121 }
122
Dave Borowitz09ff62b2012-12-17 14:09:16 -0800123 private Handler handler() throws IOException {
124 ContextHandlerCollection handlers = new ContextHandlerCollection();
125 handlers.addHandler(staticHandler());
126 handlers.addHandler(appHandler());
127 return handlers;
Dave Borowitz09ff62b2012-12-17 14:09:16 -0800128 }
129
130 private Handler appHandler() {
Dave Borowitzcf38c032016-05-02 11:06:23 -0400131 DebugRenderer renderer =
132 new DebugRenderer(
133 STATIC_PREFIX,
134 Arrays.asList(cfg.getStringList("gitiles", null, "customTemplates")),
135 sourceRoot
136 .resolve("gitiles-servlet/src/main/resources/com/google/gitiles/templates")
137 .toString(),
138 firstNonNull(cfg.getString("gitiles", null, "siteTitle"), "Gitiles"));
Shawn Pearce68311c72015-06-09 17:01:34 -0700139
140 String docRoot = cfg.getString("gitiles", null, "docroot");
141 Servlet servlet;
142 if (!Strings.isNullOrEmpty(docRoot)) {
143 servlet = createRootedDocServlet(renderer, docRoot);
144 } else {
Dave Borowitzcf38c032016-05-02 11:06:23 -0400145 servlet = new GitilesServlet(cfg, renderer, null, null, null, null, null, null, null);
Shawn Pearce68311c72015-06-09 17:01:34 -0700146 }
Dave Borowitz03397692012-12-18 17:11:16 -0800147
Dave Borowitz09ff62b2012-12-17 14:09:16 -0800148 ServletContextHandler handler = new ServletContextHandler();
149 handler.setContextPath("");
Dave Borowitz03397692012-12-18 17:11:16 -0800150 handler.addServlet(new ServletHolder(servlet), "/*");
Dave Borowitz09ff62b2012-12-17 14:09:16 -0800151 return handler;
152 }
153
Dave Borowitz03397692012-12-18 17:11:16 -0800154 private Handler staticHandler() throws IOException {
Dave Borowitzcf38c032016-05-02 11:06:23 -0400155 Path staticRoot =
156 sourceRoot.resolve("gitiles-servlet/src/main/resources/com/google/gitiles/static");
Dave Borowitz09ff62b2012-12-17 14:09:16 -0800157 ResourceHandler rh = new ResourceHandler();
158 try {
Han-Wen Nienhuys5ca80022016-03-30 12:58:27 +0200159 rh.setBaseResource(new FileResource(staticRoot.toUri().toURL()));
Dave Borowitz09ff62b2012-12-17 14:09:16 -0800160 } catch (URISyntaxException e) {
Dave Borowitz03397692012-12-18 17:11:16 -0800161 throw new IOException(e);
Dave Borowitz09ff62b2012-12-17 14:09:16 -0800162 }
Dave Borowitzcf38c032016-05-02 11:06:23 -0400163 rh.setWelcomeFiles(new String[] {});
Dave Borowitz09ff62b2012-12-17 14:09:16 -0800164 rh.setDirectoriesListed(false);
165 ContextHandler handler = new ContextHandler("/+static");
166 handler.setHandler(rh);
167 return handler;
168 }
Shawn Pearce68311c72015-06-09 17:01:34 -0700169
170 private Servlet createRootedDocServlet(DebugRenderer renderer, String docRoot) {
171 File docRepo = new File(docRoot);
172 final FileKey repoKey = FileKey.exact(docRepo, FS.DETECTED);
173
Dave Borowitzcf38c032016-05-02 11:06:23 -0400174 RepositoryResolver<HttpServletRequest> resolver =
175 new RepositoryResolver<HttpServletRequest>() {
176 @Override
177 public Repository open(HttpServletRequest req, String name)
178 throws RepositoryNotFoundException {
179 try {
180 return RepositoryCache.open(repoKey, true);
181 } catch (IOException e) {
182 throw new RepositoryNotFoundException(repoKey.getFile(), e);
183 }
184 }
185 };
Shawn Pearce68311c72015-06-09 17:01:34 -0700186
Dave Borowitzcf38c032016-05-02 11:06:23 -0400187 return new RootedDocServlet(resolver, new RootedDocAccess(docRepo), renderer);
Shawn Pearce68311c72015-06-09 17:01:34 -0700188 }
189
190 private class RootedDocAccess implements GitilesAccess.Factory {
191 private final String repoName;
192
193 RootedDocAccess(File docRepo) {
194 if (Constants.DOT_GIT.equals(docRepo.getName())) {
195 repoName = docRepo.getParentFile().getName();
196 } else {
197 repoName = docRepo.getName();
198 }
199 }
200
201 @Override
202 public GitilesAccess forRequest(HttpServletRequest req) {
203 return new GitilesAccess() {
204 @Override
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700205 public Map<String, RepositoryDescription> listRepositories(
206 String prefix, Set<String> branches) {
Shawn Pearce68311c72015-06-09 17:01:34 -0700207 return Collections.emptyMap();
208 }
209
210 @Override
211 public Object getUserKey() {
212 return null;
213 }
214
215 @Override
216 public String getRepositoryName() {
217 return repoName;
218 }
219
220 @Override
221 public RepositoryDescription getRepositoryDescription() {
222 RepositoryDescription d = new RepositoryDescription();
223 d.name = getRepositoryName();
224 return d;
225 }
226
227 @Override
228 public Config getConfig() {
229 return cfg;
230 }
231 };
232 }
233 }
Dave Borowitz09ff62b2012-12-17 14:09:16 -0800234}