James Moger
2013-03-29 ffe9bd0ea959cf768983ff1a3d2de897390016d7
commit | author | age
c22722 1 /*
JM 2  * Copyright 2011 gitblit.com.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.gitblit;
17
8c9a20 18 import java.text.MessageFormat;
38688b 19 import java.util.ArrayList;
13a3f5 20 import java.util.Arrays;
JM 21 import java.util.Collections;
c22722 22 import java.util.List;
9bdb91 23 import java.util.Map;
c22722 24
JM 25 import javax.servlet.http.HttpServlet;
26
9bdb91 27 import org.eclipse.jgit.lib.ObjectId;
c22722 28 import org.eclipse.jgit.lib.Repository;
JM 29 import org.eclipse.jgit.revwalk.RevCommit;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
13a3f5 33 import com.gitblit.AuthenticationFilter.AuthenticatedRequest;
e493cf 34 import com.gitblit.models.FeedEntryModel;
13a3f5 35 import com.gitblit.models.ProjectModel;
9bdb91 36 import com.gitblit.models.RefModel;
c22722 37 import com.gitblit.models.RepositoryModel;
13a3f5 38 import com.gitblit.models.UserModel;
831469 39 import com.gitblit.utils.HttpUtils;
c22722 40 import com.gitblit.utils.JGitUtils;
JM 41 import com.gitblit.utils.StringUtils;
42 import com.gitblit.utils.SyndicationUtils;
43
892570 44 /**
JM 45  * SyndicationServlet generates RSS 2.0 feeds and feed links.
46  * 
47  * Access to this servlet is protected by the SyndicationFilter.
48  * 
49  * @author James Moger
50  * 
51  */
c22722 52 public class SyndicationServlet extends HttpServlet {
JM 53
54     private static final long serialVersionUID = 1L;
55
56     private transient Logger logger = LoggerFactory.getLogger(SyndicationServlet.class);
57
892570 58     /**
JM 59      * Create a feed link for the specified repository and branch/tag/commit id.
60      * 
61      * @param baseURL
62      * @param repository
63      *            the repository name
64      * @param objectId
65      *            the branch, tag, or first commit for the feed
66      * @param length
67      *            the number of commits to include in the feed
68      * @return an RSS feed url
69      */
c22722 70     public static String asLink(String baseURL, String repository, String objectId, int length) {
8c9a20 71         if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') {
c22722 72             baseURL = baseURL.substring(0, baseURL.length() - 1);
JM 73         }
8c9a20 74         StringBuilder url = new StringBuilder();
JM 75         url.append(baseURL);
5450d0 76         url.append(Constants.SYNDICATION_PATH);
8c9a20 77         url.append(repository);
JM 78         if (!StringUtils.isEmpty(objectId) || length > 0) {
79             StringBuilder parameters = new StringBuilder("?");
80             if (StringUtils.isEmpty(objectId)) {
81                 parameters.append("l=");
82                 parameters.append(length);
83             } else {
84                 parameters.append("h=");
85                 parameters.append(objectId);
86                 if (length > 0) {
87                     parameters.append("&l=");
88                     parameters.append(length);
89                 }
90             }
91             url.append(parameters);
92         }
93         return url.toString();
94     }
85c2e6 95
892570 96     /**
JM 97      * Determines the appropriate title for a feed.
98      * 
99      * @param repository
100      * @param objectId
101      * @return title of the feed
102      */
8c9a20 103     public static String getTitle(String repository, String objectId) {
JM 104         String id = objectId;
105         if (!StringUtils.isEmpty(id)) {
106             if (id.startsWith(org.eclipse.jgit.lib.Constants.R_HEADS)) {
107                 id = id.substring(org.eclipse.jgit.lib.Constants.R_HEADS.length());
108             } else if (id.startsWith(org.eclipse.jgit.lib.Constants.R_REMOTES)) {
109                 id = id.substring(org.eclipse.jgit.lib.Constants.R_REMOTES.length());
110             } else if (id.startsWith(org.eclipse.jgit.lib.Constants.R_TAGS)) {
111                 id = id.substring(org.eclipse.jgit.lib.Constants.R_TAGS.length());
112             }
113         }
114         return MessageFormat.format("{0} ({1})", repository, id);
c22722 115     }
JM 116
892570 117     /**
JM 118      * Generates the feed content.
119      * 
120      * @param request
121      * @param response
122      * @throws javax.servlet.ServletException
123      * @throws java.io.IOException
124      */
c22722 125     private void processRequest(javax.servlet.http.HttpServletRequest request,
JM 126             javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
127             java.io.IOException {
8c9a20 128
2179fb 129         String servletUrl = request.getContextPath() + request.getServletPath();
JM 130         String url = request.getRequestURI().substring(servletUrl.length());
8c9a20 131         if (url.charAt(0) == '/' && url.length() > 1) {
JM 132             url = url.substring(1);
133         }
134         String repositoryName = url;
c22722 135         String objectId = request.getParameter("h");
JM 136         String l = request.getParameter("l");
e33b91 137         String page = request.getParameter("pg");
9bdb91 138         String searchString = request.getParameter("s");
33d8d8 139         Constants.SearchType searchType = Constants.SearchType.COMMIT;
9bdb91 140         if (!StringUtils.isEmpty(request.getParameter("st"))) {
33d8d8 141             Constants.SearchType type = Constants.SearchType.forName(request.getParameter("st"));
9bdb91 142             if (type != null) {
JM 143                 searchType = type;
144             }
145         }
c22722 146         int length = GitBlit.getInteger(Keys.web.syndicationEntries, 25);
JM 147         if (StringUtils.isEmpty(objectId)) {
148             objectId = org.eclipse.jgit.lib.Constants.HEAD;
149         }
150         if (!StringUtils.isEmpty(l)) {
151             try {
152                 length = Integer.parseInt(l);
153             } catch (NumberFormatException x) {
154             }
155         }
e33b91 156         int offset = 0;
JM 157         if (!StringUtils.isEmpty(page)) {
158             try {
159                 offset = length * Integer.parseInt(page);
160             } catch (NumberFormatException x) {
161             }
162         }
c22722 163
466413 164         response.setContentType("application/rss+xml; charset=UTF-8");
13a3f5 165         
JM 166         boolean isProjectFeed = false;
167         String feedName = null;
168         String feedTitle = null;
169         String feedDescription = null;
170         
171         List<String> repositories = null;
172         if (repositoryName.indexOf('/') == -1 && !repositoryName.toLowerCase().endsWith(".git")) {
173             // try to find a project
174             UserModel user = null;
175             if (request instanceof AuthenticatedRequest) {
176                 user = ((AuthenticatedRequest) request).getUser();
177             }
178             ProjectModel project = GitBlit.self().getProjectModel(repositoryName, user);
179             if (project != null) {
180                 isProjectFeed = true;
181                 repositories = new ArrayList<String>(project.repositories);
182                 
183                 // project feed
184                 feedName = project.name;
185                 feedTitle = project.title;
186                 feedDescription = project.description;
187             }
9bdb91 188         }
13a3f5 189         
JM 190         if (repositories == null) {
191             // could not find project, assume this is a repository
192             repositories = Arrays.asList(repositoryName);
193         }
194
38688b 195
ec5a88 196         boolean mountParameters = GitBlit.getBoolean(Keys.web.mountParameters, true);
JM 197         String urlPattern;
198         if (mountParameters) {
199             // mounted parameters
200             urlPattern = "{0}/commit/{1}/{2}";
201         } else {
202             // parameterized parameters
203             urlPattern = "{0}/commit/?r={1}&h={2}";
204         }
38688b 205         String gitblitUrl = HttpUtils.getGitblitURL(request);
d5477c 206         char fsc = GitBlit.getChar(Keys.web.forwardSlashCharacter, '/');
13a3f5 207
JM 208         List<FeedEntryModel> entries = new ArrayList<FeedEntryModel>();
209
210         for (String name : repositories) {
211             Repository repository = GitBlit.self().getRepository(name);
212             RepositoryModel model = GitBlit.self().getRepositoryModel(name);
e92c6d 213
JM 214             if (repository == null) {
215                 if (model.isCollectingGarbage) {
216                     logger.warn(MessageFormat.format("Temporarily excluding {0} from feed, busy collecting garbage", name));
217                 } 
218                 continue;
219             }
13a3f5 220             if (!isProjectFeed) {
JM 221                 // single-repository feed
222                 feedName = model.name;
223                 feedTitle = model.name;
224                 feedDescription = model.description;
4eb1d8 225             }
JM 226             
13a3f5 227             List<RevCommit> commits;
JM 228             if (StringUtils.isEmpty(searchString)) {
229                 // standard log/history lookup
230                 commits = JGitUtils.getRevLog(repository, objectId, offset, length);
231             } else {
232                 // repository search
233                 commits = JGitUtils.searchRevlogs(repository, objectId, searchString, searchType,
234                         offset, length);
235             }
1e1b85 236             Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(repository, model.showRemoteBranches);
13a3f5 237
JM 238             // convert RevCommit to SyndicatedEntryModel
239             for (RevCommit commit : commits) {
240                 FeedEntryModel entry = new FeedEntryModel();
241                 entry.title = commit.getShortMessage();
242                 entry.author = commit.getAuthorIdent().getName();
243                 entry.link = MessageFormat.format(urlPattern, gitblitUrl,
244                         StringUtils.encodeURL(model.name.replace('/', fsc)), commit.getName());
245                 entry.published = commit.getCommitterIdent().getWhen();
246                 entry.contentType = "text/html";
247                 String message = GitBlit.self().processCommitMessage(model.name,
248                         commit.getFullMessage());
249                 entry.content = message;
250                 entry.repository = model.name;
251                 entry.branch = objectId;            
252                 entry.tags = new ArrayList<String>();
253
254                 // add commit id and parent commit ids
255                 entry.tags.add("commit:" + commit.getName());
256                 for (RevCommit parent : commit.getParents()) {
257                     entry.tags.add("parent:" + parent.getName());
9bdb91 258                 }
13a3f5 259
JM 260                 // add refs to tabs list
261                 List<RefModel> refs = allRefs.get(commit.getId());
262                 if (refs != null && refs.size() > 0) {
263                     for (RefModel ref : refs) {
264                         entry.tags.add("ref:" + ref.getName());
265                     }
266                 }            
267                 entries.add(entry);
268             }
38688b 269         }
13a3f5 270         
JM 271         // sort & truncate the feed
272         Collections.sort(entries);
273         if (entries.size() > length) {
274             // clip the list
275             entries = entries.subList(0, length);
276         }
277         
ec5a88 278         String feedLink;
13a3f5 279         if (isProjectFeed) {
JM 280             // project feed
281             if (mountParameters) {
282                 // mounted url
283                 feedLink = MessageFormat.format("{0}/project/{1}", gitblitUrl,
284                         StringUtils.encodeURL(feedName));
285             } else {
286                 // parameterized url
287                 feedLink = MessageFormat.format("{0}/project/?p={1}", gitblitUrl,
288                         StringUtils.encodeURL(feedName));
289             }
ec5a88 290         } else {
13a3f5 291             // repository feed
JM 292             if (mountParameters) {
293                 // mounted url
294                 feedLink = MessageFormat.format("{0}/summary/{1}", gitblitUrl,
295                         StringUtils.encodeURL(feedName));
296             } else {
297                 // parameterized url
298                 feedLink = MessageFormat.format("{0}/summary/?r={1}", gitblitUrl,
299                         StringUtils.encodeURL(feedName));
300             }
ec5a88 301         }
JM 302
c22722 303         try {
13a3f5 304             SyndicationUtils.toRSS(gitblitUrl, feedLink, getTitle(feedTitle, objectId),
JM 305                     feedDescription, entries, response.getOutputStream());
c22722 306         } catch (Exception e) {
JM 307             logger.error("An error occurred during feed generation", e);
308         }
309     }
310
311     @Override
312     protected void doPost(javax.servlet.http.HttpServletRequest request,
313             javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
314             java.io.IOException {
315         processRequest(request, response);
316     }
317
318     @Override
319     protected void doGet(javax.servlet.http.HttpServletRequest request,
320             javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
321             java.io.IOException {
322         processRequest(request, response);
323     }
324 }