James Moger
2015-11-22 ed552ba47c02779c270ffd62841d6d1048dade70
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  */
7bf6e1 16 package com.gitblit.servlet;
c22722 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
1b34b0 25 import javax.servlet.http.HttpServlet;
JM 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
7bf6e1 33 import com.gitblit.Constants;
JM 34 import com.gitblit.IStoredSettings;
35 import com.gitblit.Keys;
db4f6b 36 import com.gitblit.manager.IProjectManager;
JM 37 import com.gitblit.manager.IRepositoryManager;
e493cf 38 import com.gitblit.models.FeedEntryModel;
13a3f5 39 import com.gitblit.models.ProjectModel;
9bdb91 40 import com.gitblit.models.RefModel;
c22722 41 import com.gitblit.models.RepositoryModel;
13a3f5 42 import com.gitblit.models.UserModel;
7bf6e1 43 import com.gitblit.servlet.AuthenticationFilter.AuthenticatedRequest;
fef234 44 import com.gitblit.utils.BugtraqProcessor;
831469 45 import com.gitblit.utils.HttpUtils;
c22722 46 import com.gitblit.utils.JGitUtils;
JM 47 import com.gitblit.utils.StringUtils;
48 import com.gitblit.utils.SyndicationUtils;
c0d028 49 import com.google.inject.Inject;
JM 50 import com.google.inject.Singleton;
c22722 51
892570 52 /**
JM 53  * SyndicationServlet generates RSS 2.0 feeds and feed links.
699e71 54  *
892570 55  * Access to this servlet is protected by the SyndicationFilter.
699e71 56  *
892570 57  * @author James Moger
699e71 58  *
892570 59  */
1b34b0 60 @Singleton
JM 61 public class SyndicationServlet extends HttpServlet {
c22722 62
JM 63     private static final long serialVersionUID = 1L;
64
65     private transient Logger logger = LoggerFactory.getLogger(SyndicationServlet.class);
66
65d5bb 67     private IStoredSettings settings;
cacf8b 68
65d5bb 69     private IRepositoryManager repositoryManager;
cacf8b 70
65d5bb 71     private IProjectManager projectManager;
cacf8b 72
1b34b0 73     @Inject
JM 74     public SyndicationServlet(
75             IStoredSettings settings,
76             IRepositoryManager repositoryManager,
77             IProjectManager projectManager) {
78
79         this.settings = settings;
80         this.repositoryManager = repositoryManager;
81         this.projectManager = projectManager;
116422 82     }
JM 83
892570 84     /**
JM 85      * Create a feed link for the specified repository and branch/tag/commit id.
699e71 86      *
892570 87      * @param baseURL
JM 88      * @param repository
89      *            the repository name
90      * @param objectId
91      *            the branch, tag, or first commit for the feed
92      * @param length
93      *            the number of commits to include in the feed
94      * @return an RSS feed url
95      */
c22722 96     public static String asLink(String baseURL, String repository, String objectId, int length) {
8c9a20 97         if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') {
c22722 98             baseURL = baseURL.substring(0, baseURL.length() - 1);
JM 99         }
8c9a20 100         StringBuilder url = new StringBuilder();
JM 101         url.append(baseURL);
5450d0 102         url.append(Constants.SYNDICATION_PATH);
8c9a20 103         url.append(repository);
JM 104         if (!StringUtils.isEmpty(objectId) || length > 0) {
105             StringBuilder parameters = new StringBuilder("?");
106             if (StringUtils.isEmpty(objectId)) {
107                 parameters.append("l=");
108                 parameters.append(length);
109             } else {
110                 parameters.append("h=");
111                 parameters.append(objectId);
112                 if (length > 0) {
113                     parameters.append("&l=");
114                     parameters.append(length);
115                 }
116             }
117             url.append(parameters);
118         }
119         return url.toString();
120     }
85c2e6 121
892570 122     /**
JM 123      * Determines the appropriate title for a feed.
699e71 124      *
892570 125      * @param repository
JM 126      * @param objectId
127      * @return title of the feed
128      */
8c9a20 129     public static String getTitle(String repository, String objectId) {
JM 130         String id = objectId;
131         if (!StringUtils.isEmpty(id)) {
132             if (id.startsWith(org.eclipse.jgit.lib.Constants.R_HEADS)) {
133                 id = id.substring(org.eclipse.jgit.lib.Constants.R_HEADS.length());
134             } else if (id.startsWith(org.eclipse.jgit.lib.Constants.R_REMOTES)) {
135                 id = id.substring(org.eclipse.jgit.lib.Constants.R_REMOTES.length());
136             } else if (id.startsWith(org.eclipse.jgit.lib.Constants.R_TAGS)) {
137                 id = id.substring(org.eclipse.jgit.lib.Constants.R_TAGS.length());
138             }
139         }
140         return MessageFormat.format("{0} ({1})", repository, id);
c22722 141     }
JM 142
892570 143     /**
JM 144      * Generates the feed content.
699e71 145      *
892570 146      * @param request
JM 147      * @param response
148      * @throws javax.servlet.ServletException
149      * @throws java.io.IOException
150      */
c22722 151     private void processRequest(javax.servlet.http.HttpServletRequest request,
JM 152             javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
153             java.io.IOException {
db4f6b 154
2179fb 155         String servletUrl = request.getContextPath() + request.getServletPath();
JM 156         String url = request.getRequestURI().substring(servletUrl.length());
2916cf 157         if (url.length() > 1 && url.charAt(0) == '/') {
8c9a20 158             url = url.substring(1);
JM 159         }
160         String repositoryName = url;
c22722 161         String objectId = request.getParameter("h");
JM 162         String l = request.getParameter("l");
e33b91 163         String page = request.getParameter("pg");
9bdb91 164         String searchString = request.getParameter("s");
33d8d8 165         Constants.SearchType searchType = Constants.SearchType.COMMIT;
9bdb91 166         if (!StringUtils.isEmpty(request.getParameter("st"))) {
33d8d8 167             Constants.SearchType type = Constants.SearchType.forName(request.getParameter("st"));
9bdb91 168             if (type != null) {
JM 169                 searchType = type;
170             }
171         }
f76fee 172
9ff0c1 173         Constants.FeedObjectType objectType = Constants.FeedObjectType.COMMIT;
f76fee 174         if (!StringUtils.isEmpty(request.getParameter("ot"))) {
9ff0c1 175             Constants.FeedObjectType type = Constants.FeedObjectType.forName(request.getParameter("ot"));
f76fee 176             if (type != null) {
GS 177                 objectType = type;
178             }
179         }
180
db4f6b 181         int length = settings.getInteger(Keys.web.syndicationEntries, 25);
c22722 182         if (StringUtils.isEmpty(objectId)) {
JM 183             objectId = org.eclipse.jgit.lib.Constants.HEAD;
184         }
185         if (!StringUtils.isEmpty(l)) {
186             try {
187                 length = Integer.parseInt(l);
188             } catch (NumberFormatException x) {
189             }
190         }
e33b91 191         int offset = 0;
JM 192         if (!StringUtils.isEmpty(page)) {
193             try {
194                 offset = length * Integer.parseInt(page);
195             } catch (NumberFormatException x) {
196             }
197         }
c22722 198
466413 199         response.setContentType("application/rss+xml; charset=UTF-8");
699e71 200
13a3f5 201         boolean isProjectFeed = false;
2916cf 202         String feedName = "Gitblit";
13a3f5 203         String feedTitle = null;
JM 204         String feedDescription = null;
699e71 205
13a3f5 206         List<String> repositories = null;
JM 207         if (repositoryName.indexOf('/') == -1 && !repositoryName.toLowerCase().endsWith(".git")) {
208             // try to find a project
209             UserModel user = null;
210             if (request instanceof AuthenticatedRequest) {
211                 user = ((AuthenticatedRequest) request).getUser();
212             }
db4f6b 213             ProjectModel project = projectManager.getProjectModel(repositoryName, user);
13a3f5 214             if (project != null) {
JM 215                 isProjectFeed = true;
216                 repositories = new ArrayList<String>(project.repositories);
699e71 217
13a3f5 218                 // project feed
JM 219                 feedName = project.name;
220                 feedTitle = project.title;
221                 feedDescription = project.description;
222             }
9bdb91 223         }
699e71 224
13a3f5 225         if (repositories == null) {
JM 226             // could not find project, assume this is a repository
227             repositories = Arrays.asList(repositoryName);
228         }
229
38688b 230
db4f6b 231         boolean mountParameters = settings.getBoolean(Keys.web.mountParameters, true);
f76fee 232
14cbbe 233         String gitblitUrl = settings.getString(Keys.web.canonicalUrl, null);
JM 234         if (StringUtils.isEmpty(gitblitUrl)) {
235             gitblitUrl = HttpUtils.getGitblitURL(request);
236         }
db4f6b 237         char fsc = settings.getChar(Keys.web.forwardSlashCharacter, '/');
13a3f5 238
JM 239         List<FeedEntryModel> entries = new ArrayList<FeedEntryModel>();
240
241         for (String name : repositories) {
db4f6b 242             Repository repository = repositoryManager.getRepository(name);
JM 243             RepositoryModel model = repositoryManager.getRepositoryModel(name);
e92c6d 244
JM 245             if (repository == null) {
2916cf 246                 if (model != null && model.isCollectingGarbage) {
e92c6d 247                     logger.warn(MessageFormat.format("Temporarily excluding {0} from feed, busy collecting garbage", name));
699e71 248                 }
e92c6d 249                 continue;
JM 250             }
13a3f5 251             if (!isProjectFeed) {
JM 252                 // single-repository feed
253                 feedName = model.name;
254                 feedTitle = model.name;
255                 feedDescription = model.description;
4eb1d8 256             }
699e71 257
9ff0c1 258             if (objectType == Constants.FeedObjectType.TAG) {
f76fee 259
GS 260                 String urlPattern;
261                 if (mountParameters) {
262                     // mounted parameters
263                     urlPattern = "{0}/tag/{1}/{2}";
264                 } else {
265                     // parameterized parameters
266                     urlPattern = "{0}/tag/?r={1}&h={2}";
267                 }
268
269                 List<RefModel> tags = JGitUtils.getTags(repository, false, length, offset);
270
271                 for (RefModel tag : tags) {
272                     FeedEntryModel entry = new FeedEntryModel();
273                     entry.title = tag.getName();
274                     entry.author = tag.getAuthorIdent().getName();
275                     entry.link = MessageFormat.format(urlPattern, gitblitUrl,
276                             StringUtils.encodeURL(model.name.replace('/', fsc)), tag.getObjectId().getName());
277                     entry.published = tag.getDate();
278                     entry.contentType = "text/html";
279                     entry.content = tag.getFullMessage();
280                     entry.repository = model.name;
281                     entry.branch = objectId;
282
283                     entry.tags = new ArrayList<String>();
284
285                     // add tag id and referenced commit id
286                     entry.tags.add("tag:" + tag.getObjectId().getName());
287                     entry.tags.add("commit:" + tag.getReferencedObjectId().getName());
288
289                     entries.add(entry);
290                 }
13a3f5 291             } else {
JM 292
f76fee 293                 String urlPattern;
GS 294                 if (mountParameters) {
295                     // mounted parameters
296                     urlPattern = "{0}/commit/{1}/{2}";
297                 } else {
298                     // parameterized parameters
299                     urlPattern = "{0}/commit/?r={1}&h={2}";
9bdb91 300                 }
13a3f5 301
f76fee 302                 List<RevCommit> commits;
GS 303                 if (StringUtils.isEmpty(searchString)) {
304                     // standard log/history lookup
305                     commits = JGitUtils.getRevLog(repository, objectId, offset, length);
306                 } else {
307                     // repository search
308                     commits = JGitUtils.searchRevlogs(repository, objectId, searchString, searchType,
309                             offset, length);
310                 }
311                 Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(repository, model.showRemoteBranches);
312                 BugtraqProcessor processor = new BugtraqProcessor(settings);
313
314                 // convert RevCommit to SyndicatedEntryModel
315                 for (RevCommit commit : commits) {
316                     FeedEntryModel entry = new FeedEntryModel();
317                     entry.title = commit.getShortMessage();
318                     entry.author = commit.getAuthorIdent().getName();
319                     entry.link = MessageFormat.format(urlPattern, gitblitUrl,
320                             StringUtils.encodeURL(model.name.replace('/', fsc)), commit.getName());
321                     entry.published = commit.getCommitterIdent().getWhen();
322                     entry.contentType = "text/html";
323                     String message = processor.processCommitMessage(repository, model, commit.getFullMessage());
324                     entry.content = message;
325                     entry.repository = model.name;
326                     entry.branch = objectId;
327                     entry.tags = new ArrayList<String>();
328
329                     // add commit id and parent commit ids
330                     entry.tags.add("commit:" + commit.getName());
331                     for (RevCommit parent : commit.getParents()) {
332                         entry.tags.add("parent:" + parent.getName());
13a3f5 333                     }
f76fee 334
GS 335                     // add refs to tabs list
336                     List<RefModel> refs = allRefs.get(commit.getId());
337                     if (refs != null && refs.size() > 0) {
338                         for (RefModel ref : refs) {
339                             entry.tags.add("ref:" + ref.getName());
340                         }
341                     }
342                     entries.add(entry);
699e71 343                 }
13a3f5 344             }
38688b 345         }
699e71 346
13a3f5 347         // sort & truncate the feed
JM 348         Collections.sort(entries);
349         if (entries.size() > length) {
350             // clip the list
351             entries = entries.subList(0, length);
352         }
699e71 353
ec5a88 354         String feedLink;
13a3f5 355         if (isProjectFeed) {
JM 356             // project feed
357             if (mountParameters) {
358                 // mounted url
359                 feedLink = MessageFormat.format("{0}/project/{1}", gitblitUrl,
360                         StringUtils.encodeURL(feedName));
361             } else {
362                 // parameterized url
363                 feedLink = MessageFormat.format("{0}/project/?p={1}", gitblitUrl,
364                         StringUtils.encodeURL(feedName));
365             }
ec5a88 366         } else {
13a3f5 367             // repository feed
JM 368             if (mountParameters) {
369                 // mounted url
370                 feedLink = MessageFormat.format("{0}/summary/{1}", gitblitUrl,
c0d028 371                         StringUtils.encodeURL(feedName.replace('/', fsc)));
13a3f5 372             } else {
JM 373                 // parameterized url
374                 feedLink = MessageFormat.format("{0}/summary/?r={1}", gitblitUrl,
375                         StringUtils.encodeURL(feedName));
376             }
ec5a88 377         }
JM 378
c22722 379         try {
13a3f5 380             SyndicationUtils.toRSS(gitblitUrl, feedLink, getTitle(feedTitle, objectId),
JM 381                     feedDescription, entries, response.getOutputStream());
c22722 382         } catch (Exception e) {
JM 383             logger.error("An error occurred during feed generation", e);
384         }
385     }
386
387     @Override
388     protected void doPost(javax.servlet.http.HttpServletRequest request,
389             javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
390             java.io.IOException {
391         processRequest(request, response);
392     }
393
394     @Override
395     protected void doGet(javax.servlet.http.HttpServletRequest request,
396             javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
397             java.io.IOException {
398         processRequest(request, response);
399     }
400 }