blob: 79d89272a8b4a015c940b49bfb7ca52e48e54f11 [file] [log] [blame]
Dave Borowitz9de65952012-08-13 16:09:45 -07001// 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;
16
17import static com.google.common.base.Preconditions.checkNotNull;
Dave Borowitz9de65952012-08-13 16:09:45 -070018import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
19import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
20import static javax.servlet.http.HttpServletResponse.SC_SERVICE_UNAVAILABLE;
21import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
22
23import com.google.common.base.Strings;
24import com.google.common.collect.ImmutableMap;
25import com.google.common.collect.Sets;
Dave Borowitz9de65952012-08-13 16:09:45 -070026import com.google.gson.reflect.TypeToken;
Shawn Pearcec709c4c2015-08-28 15:30:42 -070027import com.google.template.soy.data.SoyData;
Dave Borowitz9de65952012-08-13 16:09:45 -070028import com.google.template.soy.data.SoyListData;
29import com.google.template.soy.data.SoyMapData;
30
31import org.eclipse.jgit.errors.RepositoryNotFoundException;
32import org.eclipse.jgit.transport.ServiceMayNotContinueException;
33import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
34import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
35import org.slf4j.Logger;
36import org.slf4j.LoggerFactory;
37
38import java.io.IOException;
Dave Borowitz673d1982014-05-02 12:30:49 -070039import java.io.Writer;
Dave Borowitz27058932014-12-03 15:44:46 -080040import java.util.Collections;
Shawn Pearcec709c4c2015-08-28 15:30:42 -070041import java.util.LinkedHashMap;
42import java.util.List;
Dave Borowitz9de65952012-08-13 16:09:45 -070043import java.util.Map;
44import java.util.Set;
45
Shawn Pearcec709c4c2015-08-28 15:30:42 -070046import javax.annotation.Nullable;
Dave Borowitz9de65952012-08-13 16:09:45 -070047import javax.servlet.http.HttpServletRequest;
48import javax.servlet.http.HttpServletResponse;
49
50/** Serves the top level index page for a Gitiles host. */
51public class HostIndexServlet extends BaseServlet {
Chad Horohoead23f142012-11-12 09:45:39 -080052 private static final long serialVersionUID = 1L;
Dave Borowitz9de65952012-08-13 16:09:45 -070053 private static final Logger log = LoggerFactory.getLogger(HostIndexServlet.class);
54
55 protected final GitilesUrls urls;
Dave Borowitz9de65952012-08-13 16:09:45 -070056
Dave Borowitz8d6d6872014-03-16 15:18:14 -070057 public HostIndexServlet(GitilesAccess.Factory accessFactory, Renderer renderer,
58 GitilesUrls urls) {
59 super(renderer, accessFactory);
Dave Borowitz9de65952012-08-13 16:09:45 -070060 this.urls = checkNotNull(urls, "urls");
Dave Borowitz9de65952012-08-13 16:09:45 -070061 }
62
Shawn Pearcec709c4c2015-08-28 15:30:42 -070063 private Map<String, RepositoryDescription> list(
64 HttpServletRequest req, HttpServletResponse res, String prefix,
65 Set<String> branches) throws IOException {
Dave Borowitz9de65952012-08-13 16:09:45 -070066 Map<String, RepositoryDescription> descs;
67 try {
Shawn Pearcec709c4c2015-08-28 15:30:42 -070068 descs = getAccess(req).listRepositories(prefix, branches);
Dave Borowitz9de65952012-08-13 16:09:45 -070069 } catch (RepositoryNotFoundException e) {
70 res.sendError(SC_NOT_FOUND);
Dave Borowitzb1c628f2013-01-11 11:28:20 -080071 return null;
Dave Borowitz9de65952012-08-13 16:09:45 -070072 } catch (ServiceNotEnabledException e) {
73 res.sendError(SC_FORBIDDEN);
Dave Borowitzb1c628f2013-01-11 11:28:20 -080074 return null;
Dave Borowitz9de65952012-08-13 16:09:45 -070075 } catch (ServiceNotAuthorizedException e) {
76 res.sendError(SC_UNAUTHORIZED);
Dave Borowitzb1c628f2013-01-11 11:28:20 -080077 return null;
Dave Borowitz9de65952012-08-13 16:09:45 -070078 } catch (ServiceMayNotContinueException e) {
79 // TODO(dborowitz): Show the error message to the user.
80 res.sendError(SC_FORBIDDEN);
Dave Borowitzb1c628f2013-01-11 11:28:20 -080081 return null;
Dave Borowitz9de65952012-08-13 16:09:45 -070082 } catch (IOException err) {
83 String name = urls.getHostName(req);
Jonathan Niederc0aeff12013-05-06 11:00:47 -070084 log.warn("Cannot scan repositories" + (name != null ? " for " + name : ""), err);
Dave Borowitz9de65952012-08-13 16:09:45 -070085 res.sendError(SC_SERVICE_UNAVAILABLE);
Dave Borowitzb1c628f2013-01-11 11:28:20 -080086 return null;
Dave Borowitz9de65952012-08-13 16:09:45 -070087 }
Shawn Pearcec709c4c2015-08-28 15:30:42 -070088 if (prefix != null && descs.isEmpty()) {
89 res.sendError(SC_NOT_FOUND);
90 return null;
91 }
Dave Borowitzb1c628f2013-01-11 11:28:20 -080092 return descs;
Dave Borowitz9de65952012-08-13 16:09:45 -070093 }
94
Shawn Pearcec709c4c2015-08-28 15:30:42 -070095 private SoyMapData toSoyMapData(RepositoryDescription desc,
96 @Nullable String prefix, GitilesView view) {
Dave Borowitz9de65952012-08-13 16:09:45 -070097 return new SoyMapData(
Shawn Pearcec709c4c2015-08-28 15:30:42 -070098 "name", stripPrefix(prefix, desc.name),
Dave Borowitz9de65952012-08-13 16:09:45 -070099 "description", Strings.nullToEmpty(desc.description),
100 "url", GitilesView.repositoryIndex()
101 .copyFrom(view)
102 .setRepositoryName(desc.name)
103 .toUrl());
104 }
105
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800106 @Override
107 protected void doGetHtml(HttpServletRequest req, HttpServletResponse res) throws IOException {
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700108 GitilesView view = ViewFilter.getView(req);
109 String prefix = view.getRepositoryPrefix();
110 Map<String, RepositoryDescription> descs = list(req, res, prefix, parseShowBranch(req));
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800111 if (descs == null) {
112 return;
113 }
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700114
Dave Borowitz9de65952012-08-13 16:09:45 -0700115 SoyListData repos = new SoyListData();
116 for (RepositoryDescription desc : descs.values()) {
David Pursehouse969bfc82015-12-08 15:11:17 +0900117 if (prefix == null || desc.name.startsWith(prefix)) {
118 repos.add(toSoyMapData(desc, prefix, view));
119 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700120 }
121
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700122 String hostName = urls.getHostName(req);
123 List<Map<String, String>> breadcrumbs = null;
124 if (prefix != null) {
125 hostName = hostName + '/' + prefix;
126 breadcrumbs = view.getBreadcrumbs();
127 }
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800128 renderHtml(req, res, "gitiles.hostIndex", ImmutableMap.of(
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700129 "hostName", hostName,
130 "breadcrumbs", SoyData.createFromExistingData(breadcrumbs),
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700131 "prefix", prefix != null ? prefix + '/' : "",
Dave Borowitz9de65952012-08-13 16:09:45 -0700132 "repositories", repos));
133 }
134
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800135 @Override
136 protected void doGetText(HttpServletRequest req, HttpServletResponse res) throws IOException {
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700137 String prefix = ViewFilter.getView(req).getRepositoryPrefix();
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800138 Set<String> branches = parseShowBranch(req);
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700139 Map<String, RepositoryDescription> descs = list(req, res, prefix, branches);
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800140 if (descs == null) {
141 return;
142 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700143
Dave Borowitz673d1982014-05-02 12:30:49 -0700144 Writer writer = startRenderText(req, res);
Dave Borowitz9de65952012-08-13 16:09:45 -0700145 for (RepositoryDescription repo : descs.values()) {
146 for (String name : branches) {
147 String ref = repo.branches.get(name);
148 if (ref == null) {
149 // Print stub (forty '-' symbols)
150 ref = "----------------------------------------";
151 }
Dave Borowitz673d1982014-05-02 12:30:49 -0700152 writer.write(ref);
153 writer.write(' ');
Dave Borowitz9de65952012-08-13 16:09:45 -0700154 }
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700155 writer.write(GitilesUrls.NAME_ESCAPER.apply(stripPrefix(prefix, repo.name)));
Dave Borowitz673d1982014-05-02 12:30:49 -0700156 writer.write('\n');
Dave Borowitz9de65952012-08-13 16:09:45 -0700157 }
158 writer.flush();
159 writer.close();
160 }
161
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800162 @Override
163 protected void doGetJson(HttpServletRequest req, HttpServletResponse res) throws IOException {
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700164 String prefix = ViewFilter.getView(req).getRepositoryPrefix();
165 Map<String, RepositoryDescription> descs = list(req, res, prefix, parseShowBranch(req));
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800166 if (descs == null) {
167 return;
168 }
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700169 if (prefix != null) {
170 Map<String, RepositoryDescription> r = new LinkedHashMap<>();
171 for (Map.Entry<String, RepositoryDescription> e : descs.entrySet()) {
172 r.put(stripPrefix(prefix, e.getKey()), e.getValue());
173 }
174 descs = r;
175 }
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800176 renderJson(req, res, descs, new TypeToken<Map<String, RepositoryDescription>>() {}.getType());
Dave Borowitz9de65952012-08-13 16:09:45 -0700177 }
178
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700179 private static String stripPrefix(@Nullable String prefix, String name) {
David Pursehouse969bfc82015-12-08 15:11:17 +0900180 if (prefix != null && name.startsWith(prefix)) {
181 return name.substring(prefix.length() + 1);
182 }
183 return name;
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700184 }
185
Dave Borowitz9de65952012-08-13 16:09:45 -0700186 private static Set<String> parseShowBranch(HttpServletRequest req) {
187 // Roughly match Gerrit Code Review's /projects/ API by supporting
188 // both show-branch and b as query parameters.
189 Set<String> branches = Sets.newLinkedHashSet();
190 String[] values = req.getParameterValues("show-branch");
191 if (values != null) {
Dave Borowitz27058932014-12-03 15:44:46 -0800192 Collections.addAll(branches, values);
Dave Borowitz9de65952012-08-13 16:09:45 -0700193 }
194 values = req.getParameterValues("b");
195 if (values != null) {
Dave Borowitz27058932014-12-03 15:44:46 -0800196 Collections.addAll(branches, values);
Dave Borowitz9de65952012-08-13 16:09:45 -0700197 }
198 return branches;
199 }
200}