blob: d40962b95238a26b8a5beaf63bfcec458487d85d [file] [log] [blame]
Dave Borowitz209d0aa2012-12-28 14:28:53 -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;
16
17import static com.google.common.base.Preconditions.checkNotNull;
Dave Borowitzd0b7e182013-01-11 15:55:09 -080018import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
Dave Borowitz209d0aa2012-12-28 14:28:53 -080019
Dave Borowitz209d0aa2012-12-28 14:28:53 -080020import com.google.common.collect.ImmutableMap;
21import com.google.common.collect.Lists;
Dave Borowitze5fead02013-01-07 13:12:59 -080022import com.google.common.collect.Maps;
Dave Borowitz209d0aa2012-12-28 14:28:53 -080023import com.google.common.collect.Ordering;
Dave Borowitz1d94e652014-07-30 12:45:09 -070024import com.google.common.primitives.Ints;
Dave Borowitz209d0aa2012-12-28 14:28:53 -080025import com.google.common.util.concurrent.UncheckedExecutionException;
Kevin Graney77dbea92014-07-08 14:07:40 -040026import com.google.gson.reflect.TypeToken;
Dave Borowitz3b744b12016-08-19 16:11:10 -040027import java.io.IOException;
28import java.io.Writer;
29import java.util.Collection;
30import java.util.LinkedHashMap;
31import java.util.List;
32import java.util.Map;
33import javax.annotation.Nullable;
34import javax.servlet.http.HttpServletRequest;
35import javax.servlet.http.HttpServletResponse;
Dave Borowitz209d0aa2012-12-28 14:28:53 -080036import org.eclipse.jgit.http.server.ServletUtils;
Dave Borowitzd0b7e182013-01-11 15:55:09 -080037import org.eclipse.jgit.lib.AnyObjectId;
Dave Borowitz209d0aa2012-12-28 14:28:53 -080038import org.eclipse.jgit.lib.Constants;
39import org.eclipse.jgit.lib.Ref;
40import org.eclipse.jgit.lib.RefComparator;
41import org.eclipse.jgit.lib.RefDatabase;
42import org.eclipse.jgit.revwalk.RevWalk;
Dave Borowitzd0b7e182013-01-11 15:55:09 -080043import org.eclipse.jgit.transport.RefAdvertiser;
Dave Borowitz209d0aa2012-12-28 14:28:53 -080044
Dave Borowitz209d0aa2012-12-28 14:28:53 -080045/** Serves an HTML page with all the refs in a repository. */
46public class RefServlet extends BaseServlet {
47 private static final long serialVersionUID = 1L;
48
49 private final TimeCache timeCache;
50
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020051 protected RefServlet(
52 GitilesAccess.Factory accessFactory, Renderer renderer, TimeCache timeCache) {
Dave Borowitz8d6d6872014-03-16 15:18:14 -070053 super(renderer, accessFactory);
Dave Borowitz209d0aa2012-12-28 14:28:53 -080054 this.timeCache = checkNotNull(timeCache, "timeCache");
55 }
56
57 @Override
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020058 protected void doGetHtml(HttpServletRequest req, HttpServletResponse res) throws IOException {
Dave Borowitzdd3c3d92013-03-11 16:38:41 -070059 if (!ViewFilter.getView(req).getPathPart().isEmpty()) {
Dave Borowitzd0b7e182013-01-11 15:55:09 -080060 res.setStatus(SC_NOT_FOUND);
61 return;
62 }
Dave Borowitze5fead02013-01-07 13:12:59 -080063 List<Map<String, Object>> tags;
Shawn Pearceb5ad0a02015-05-24 20:33:17 -070064 try (RevWalk walk = new RevWalk(ServletUtils.getRepository(req))) {
Dave Borowitzd0b7e182013-01-11 15:55:09 -080065 tags = getTagsSoyData(req, timeCache, walk, 0);
Dave Borowitz209d0aa2012-12-28 14:28:53 -080066 }
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020067 renderHtml(
68 req,
69 res,
70 "gitiles.refsDetail",
Dave Borowitzd0b7e182013-01-11 15:55:09 -080071 ImmutableMap.of("branches", getBranchesSoyData(req, 0), "tags", tags));
Dave Borowitz209d0aa2012-12-28 14:28:53 -080072 }
73
Dave Borowitzd0b7e182013-01-11 15:55:09 -080074 @Override
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020075 protected void doGetText(HttpServletRequest req, HttpServletResponse res) throws IOException {
Dave Borowitzd0b7e182013-01-11 15:55:09 -080076 GitilesView view = ViewFilter.getView(req);
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020077 Map<String, Ref> refs =
78 getRefs(ServletUtils.getRepository(req).getRefDatabase(), view.getPathPart());
Dave Borowitzd0b7e182013-01-11 15:55:09 -080079 TextRefAdvertiser adv = new TextRefAdvertiser(startRenderText(req, res));
80 adv.setDerefTags(true);
81 adv.send(refs);
82 adv.end();
83 }
84
Kevin Graney77dbea92014-07-08 14:07:40 -040085 @Override
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020086 protected void doGetJson(HttpServletRequest req, HttpServletResponse res) throws IOException {
Kevin Graney77dbea92014-07-08 14:07:40 -040087 GitilesView view = ViewFilter.getView(req);
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020088 Map<String, Ref> refs =
89 getRefs(ServletUtils.getRepository(req).getRefDatabase(), view.getPathPart());
Dave Borowitz96a6f472014-11-04 16:38:20 -080090 Map<String, RefJsonData> jsonRefs = new LinkedHashMap<>();
Kevin Graney77dbea92014-07-08 14:07:40 -040091 for (Map.Entry<String, Ref> ref : refs.entrySet()) {
92 jsonRefs.put(ref.getKey(), new RefJsonData(ref.getValue()));
93 }
94 renderJson(req, res, jsonRefs, new TypeToken<Map<String, RefJsonData>>() {}.getType());
95 }
96
Dave Borowitzd0b7e182013-01-11 15:55:09 -080097 static List<Map<String, Object>> getBranchesSoyData(HttpServletRequest req, int limit)
Dave Borowitz209d0aa2012-12-28 14:28:53 -080098 throws IOException {
Dave Borowitze5fead02013-01-07 13:12:59 -080099 RefDatabase refdb = ServletUtils.getRepository(req).getRefDatabase();
Jonathan Niedercbe013f2016-05-26 15:28:12 -0700100 Ref head = refdb.exactRef(Constants.HEAD);
Dave Borowitze5fead02013-01-07 13:12:59 -0800101 Ref headLeaf = head != null && head.isSymbolic() ? head.getLeaf() : null;
Dave Borowitzd0b7e182013-01-11 15:55:09 -0800102 return getRefsSoyData(
Dave Borowitze5fead02013-01-07 13:12:59 -0800103 refdb,
104 ViewFilter.getView(req),
105 Constants.R_HEADS,
106 branchComparator(headLeaf),
107 headLeaf,
108 limit);
Dave Borowitz209d0aa2012-12-28 14:28:53 -0800109 }
110
Dave Borowitze5fead02013-01-07 13:12:59 -0800111 private static Ordering<Ref> branchComparator(Ref headLeaf) {
112 if (headLeaf == null) {
113 return Ordering.from(RefComparator.INSTANCE);
114 }
115 final String headLeafName = headLeaf.getName();
116 return new Ordering<Ref>() {
117 @Override
118 public int compare(@Nullable Ref left, @Nullable Ref right) {
119 int l = isHead(left) ? 1 : 0;
120 int r = isHead(right) ? 1 : 0;
Dave Borowitz5d11e2d2013-01-08 10:03:58 -0800121 return r - l;
Dave Borowitze5fead02013-01-07 13:12:59 -0800122 }
123
David Pursehousee3d3ec82016-06-15 22:10:48 +0900124 private boolean isHead(Ref ref) {
Dave Borowitze5fead02013-01-07 13:12:59 -0800125 return ref != null && ref.getName().equals(headLeafName);
126 }
127 }.compound(RefComparator.INSTANCE);
128 }
129
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200130 static List<Map<String, Object>> getTagsSoyData(
131 HttpServletRequest req, TimeCache timeCache, RevWalk walk, int limit) throws IOException {
Dave Borowitzd0b7e182013-01-11 15:55:09 -0800132 return getRefsSoyData(
Dave Borowitze5fead02013-01-07 13:12:59 -0800133 ServletUtils.getRepository(req).getRefDatabase(),
134 ViewFilter.getView(req),
135 Constants.R_TAGS,
136 tagComparator(timeCache, walk),
137 null,
138 limit);
Dave Borowitz209d0aa2012-12-28 14:28:53 -0800139 }
140
David Pursehousedcde0af2016-10-04 15:24:59 +0900141 private static Long getTime(RevWalk walk, TimeCache timeCache, Ref ref) {
142 try {
143 return timeCache.getTime(walk, ref.getObjectId());
144 } catch (IOException e) {
145 throw new UncheckedExecutionException(e);
146 }
147 }
148
149 private static Ordering<Ref> tagComparator(TimeCache timeCache, RevWalk walk) {
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200150 return Ordering.natural()
David Pursehousedcde0af2016-10-04 15:24:59 +0900151 .onResultOf((Ref r) -> getTime(walk, timeCache, r))
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200152 .reverse()
153 .compound(RefComparator.INSTANCE);
Dave Borowitz209d0aa2012-12-28 14:28:53 -0800154 }
155
Dave Borowitzd0b7e182013-01-11 15:55:09 -0800156 private static List<Map<String, Object>> getRefsSoyData(
Dave Borowitze5fead02013-01-07 13:12:59 -0800157 RefDatabase refdb,
158 GitilesView view,
159 String prefix,
160 Ordering<Ref> ordering,
161 @Nullable Ref headLeaf,
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200162 int limit)
163 throws IOException {
Dave Borowitz209d0aa2012-12-28 14:28:53 -0800164 Collection<Ref> refs = refdb.getRefs(prefix).values();
Dave Borowitz1d94e652014-07-30 12:45:09 -0700165 refs = ordering.leastOf(refs, limit > 0 ? Ints.saturatedCast(limit + 1L) : refs.size());
Dave Borowitze5fead02013-01-07 13:12:59 -0800166 List<Map<String, Object>> result = Lists.newArrayListWithCapacity(refs.size());
Dave Borowitz209d0aa2012-12-28 14:28:53 -0800167
168 for (Ref ref : refs) {
169 String name = ref.getName().substring(prefix.length());
Claudio Pacchiega2809c482016-03-01 00:24:07 +0100170 Ref refForName = refdb.getRef(name);
171 if (refForName != null) {
172 boolean needPrefix = !ref.getName().equals(refForName.getName());
173 Map<String, Object> value = Maps.newHashMapWithExpectedSize(3);
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200174 value.put(
175 "url",
176 GitilesView.revision()
177 .copyFrom(view)
178 .setRevision(
179 Revision.unpeeled(needPrefix ? ref.getName() : name, ref.getObjectId()))
180 .toUrl());
Claudio Pacchiega2809c482016-03-01 00:24:07 +0100181 value.put("name", name);
182 if (headLeaf != null) {
183 value.put("isHead", headLeaf.equals(ref));
184 }
185 result.add(value);
Dave Borowitze5fead02013-01-07 13:12:59 -0800186 }
Dave Borowitz209d0aa2012-12-28 14:28:53 -0800187 }
188 return result;
189 }
Dave Borowitzd0b7e182013-01-11 15:55:09 -0800190
Dave Borowitzba9c1182013-03-13 14:16:43 -0700191 static String sanitizeRefForText(String refName) {
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200192 return refName.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;");
Dave Borowitzd0b7e182013-01-11 15:55:09 -0800193 }
194
195 private static Map<String, Ref> getRefs(RefDatabase refdb, String path) throws IOException {
196 path = GitilesView.maybeTrimLeadingAndTrailingSlash(path);
197 if (path.isEmpty()) {
198 return refdb.getRefs(RefDatabase.ALL);
199 }
200 path = Constants.R_REFS + path;
Jonathan Niedercbe013f2016-05-26 15:28:12 -0700201 Ref singleRef = refdb.exactRef(path);
Dave Borowitzd0b7e182013-01-11 15:55:09 -0800202 if (singleRef != null) {
203 return ImmutableMap.of(singleRef.getName(), singleRef);
204 }
205 return refdb.getRefs(path + '/');
206 }
207
208 private static class TextRefAdvertiser extends RefAdvertiser {
Dave Borowitz673d1982014-05-02 12:30:49 -0700209 private final Writer writer;
Dave Borowitzd0b7e182013-01-11 15:55:09 -0800210
Dave Borowitz673d1982014-05-02 12:30:49 -0700211 private TextRefAdvertiser(Writer writer) {
Dave Borowitzd0b7e182013-01-11 15:55:09 -0800212 this.writer = writer;
213 }
214
215 @Override
216 public void advertiseId(AnyObjectId id, String refName) throws IOException {
217 super.advertiseId(id, sanitizeRefForText(refName));
218 }
219
220 @Override
221 protected void writeOne(CharSequence line) throws IOException {
Dave Borowitz673d1982014-05-02 12:30:49 -0700222 writer.append(line);
Dave Borowitzd0b7e182013-01-11 15:55:09 -0800223 }
224
225 @Override
226 public void end() throws IOException {
227 writer.close();
228 }
229 }
Kevin Graney77dbea92014-07-08 14:07:40 -0400230
Dave Borowitz32ec5b92014-07-30 07:43:28 -0700231 static class RefJsonData {
232 RefJsonData(Ref ref) {
Kevin Graney77dbea92014-07-08 14:07:40 -0400233 value = ref.getObjectId().getName();
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200234 if (ref.getPeeledObjectId() != null) {
Kevin Graney77dbea92014-07-08 14:07:40 -0400235 peeled = ref.getPeeledObjectId().getName();
236 }
237 if (ref.isSymbolic()) {
238 target = ref.getTarget().getName();
239 }
240 }
241
Dave Borowitz32ec5b92014-07-30 07:43:28 -0700242 String value;
243 String peeled;
244 String target;
Kevin Graney77dbea92014-07-08 14:07:40 -0400245 }
Dave Borowitz209d0aa2012-12-28 14:28:53 -0800246}