| /* | |
| * Copyright 2011 gitblit.com. | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| package com.gitblit; | |
| import java.text.MessageFormat; | |
| import java.util.ArrayList; | |
| import java.util.Arrays; | |
| import java.util.Collections; | |
| import java.util.List; | |
| import java.util.Map; | |
| import javax.servlet.http.HttpServlet; | |
| import org.eclipse.jgit.lib.ObjectId; | |
| import org.eclipse.jgit.lib.Repository; | |
| import org.eclipse.jgit.revwalk.RevCommit; | |
| import org.slf4j.Logger; | |
| import org.slf4j.LoggerFactory; | |
| import com.gitblit.AuthenticationFilter.AuthenticatedRequest; | |
| import com.gitblit.models.FeedEntryModel; | |
| import com.gitblit.models.ProjectModel; | |
| import com.gitblit.models.RefModel; | |
| import com.gitblit.models.RepositoryModel; | |
| import com.gitblit.models.UserModel; | |
| import com.gitblit.utils.HttpUtils; | |
| import com.gitblit.utils.JGitUtils; | |
| import com.gitblit.utils.StringUtils; | |
| import com.gitblit.utils.SyndicationUtils; | |
| /** | |
| * SyndicationServlet generates RSS 2.0 feeds and feed links. | |
| * | |
| * Access to this servlet is protected by the SyndicationFilter. | |
| * | |
| * @author James Moger | |
| * | |
| */ | |
| public class SyndicationServlet extends HttpServlet { | |
| private static final long serialVersionUID = 1L; | |
| private transient Logger logger = LoggerFactory.getLogger(SyndicationServlet.class); | |
| /** | |
| * Create a feed link for the specified repository and branch/tag/commit id. | |
| * | |
| * @param baseURL | |
| * @param repository | |
| * the repository name | |
| * @param objectId | |
| * the branch, tag, or first commit for the feed | |
| * @param length | |
| * the number of commits to include in the feed | |
| * @return an RSS feed url | |
| */ | |
| public static String asLink(String baseURL, String repository, String objectId, int length) { | |
| if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') { | |
| baseURL = baseURL.substring(0, baseURL.length() - 1); | |
| } | |
| StringBuilder url = new StringBuilder(); | |
| url.append(baseURL); | |
| url.append(Constants.SYNDICATION_PATH); | |
| url.append(repository); | |
| if (!StringUtils.isEmpty(objectId) || length > 0) { | |
| StringBuilder parameters = new StringBuilder("?"); | |
| if (StringUtils.isEmpty(objectId)) { | |
| parameters.append("l="); | |
| parameters.append(length); | |
| } else { | |
| parameters.append("h="); | |
| parameters.append(objectId); | |
| if (length > 0) { | |
| parameters.append("&l="); | |
| parameters.append(length); | |
| } | |
| } | |
| url.append(parameters); | |
| } | |
| return url.toString(); | |
| } | |
| /** | |
| * Determines the appropriate title for a feed. | |
| * | |
| * @param repository | |
| * @param objectId | |
| * @return title of the feed | |
| */ | |
| public static String getTitle(String repository, String objectId) { | |
| String id = objectId; | |
| if (!StringUtils.isEmpty(id)) { | |
| if (id.startsWith(org.eclipse.jgit.lib.Constants.R_HEADS)) { | |
| id = id.substring(org.eclipse.jgit.lib.Constants.R_HEADS.length()); | |
| } else if (id.startsWith(org.eclipse.jgit.lib.Constants.R_REMOTES)) { | |
| id = id.substring(org.eclipse.jgit.lib.Constants.R_REMOTES.length()); | |
| } else if (id.startsWith(org.eclipse.jgit.lib.Constants.R_TAGS)) { | |
| id = id.substring(org.eclipse.jgit.lib.Constants.R_TAGS.length()); | |
| } | |
| } | |
| return MessageFormat.format("{0} ({1})", repository, id); | |
| } | |
| /** | |
| * Generates the feed content. | |
| * | |
| * @param request | |
| * @param response | |
| * @throws javax.servlet.ServletException | |
| * @throws java.io.IOException | |
| */ | |
| private void processRequest(javax.servlet.http.HttpServletRequest request, | |
| javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, | |
| java.io.IOException { | |
| String servletUrl = request.getContextPath() + request.getServletPath(); | |
| String url = request.getRequestURI().substring(servletUrl.length()); | |
| if (url.charAt(0) == '/' && url.length() > 1) { | |
| url = url.substring(1); | |
| } | |
| String repositoryName = url; | |
| String objectId = request.getParameter("h"); | |
| String l = request.getParameter("l"); | |
| String page = request.getParameter("pg"); | |
| String searchString = request.getParameter("s"); | |
| Constants.SearchType searchType = Constants.SearchType.COMMIT; | |
| if (!StringUtils.isEmpty(request.getParameter("st"))) { | |
| Constants.SearchType type = Constants.SearchType.forName(request.getParameter("st")); | |
| if (type != null) { | |
| searchType = type; | |
| } | |
| } | |
| int length = GitBlit.getInteger(Keys.web.syndicationEntries, 25); | |
| if (StringUtils.isEmpty(objectId)) { | |
| objectId = org.eclipse.jgit.lib.Constants.HEAD; | |
| } | |
| if (!StringUtils.isEmpty(l)) { | |
| try { | |
| length = Integer.parseInt(l); | |
| } catch (NumberFormatException x) { | |
| } | |
| } | |
| int offset = 0; | |
| if (!StringUtils.isEmpty(page)) { | |
| try { | |
| offset = length * Integer.parseInt(page); | |
| } catch (NumberFormatException x) { | |
| } | |
| } | |
| response.setContentType("application/rss+xml; charset=UTF-8"); | |
| boolean isProjectFeed = false; | |
| String feedName = null; | |
| String feedTitle = null; | |
| String feedDescription = null; | |
| List<String> repositories = null; | |
| if (repositoryName.indexOf('/') == -1 && !repositoryName.toLowerCase().endsWith(".git")) { | |
| // try to find a project | |
| UserModel user = null; | |
| if (request instanceof AuthenticatedRequest) { | |
| user = ((AuthenticatedRequest) request).getUser(); | |
| } | |
| ProjectModel project = GitBlit.self().getProjectModel(repositoryName, user); | |
| if (project != null) { | |
| isProjectFeed = true; | |
| repositories = new ArrayList<String>(project.repositories); | |
| // project feed | |
| feedName = project.name; | |
| feedTitle = project.title; | |
| feedDescription = project.description; | |
| } | |
| } | |
| if (repositories == null) { | |
| // could not find project, assume this is a repository | |
| repositories = Arrays.asList(repositoryName); | |
| } | |
| boolean mountParameters = GitBlit.getBoolean(Keys.web.mountParameters, true); | |
| String urlPattern; | |
| if (mountParameters) { | |
| // mounted parameters | |
| urlPattern = "{0}/commit/{1}/{2}"; | |
| } else { | |
| // parameterized parameters | |
| urlPattern = "{0}/commit/?r={1}&h={2}"; | |
| } | |
| String gitblitUrl = HttpUtils.getGitblitURL(request); | |
| char fsc = GitBlit.getChar(Keys.web.forwardSlashCharacter, '/'); | |
| List<FeedEntryModel> entries = new ArrayList<FeedEntryModel>(); | |
| for (String name : repositories) { | |
| Repository repository = GitBlit.self().getRepository(name); | |
| RepositoryModel model = GitBlit.self().getRepositoryModel(name); | |
| if (repository == null) { | |
| if (model.isCollectingGarbage) { | |
| logger.warn(MessageFormat.format("Temporarily excluding {0} from feed, busy collecting garbage", name)); | |
| } | |
| continue; | |
| } | |
| if (!isProjectFeed) { | |
| // single-repository feed | |
| feedName = model.name; | |
| feedTitle = model.name; | |
| feedDescription = model.description; | |
| } | |
| List<RevCommit> commits; | |
| if (StringUtils.isEmpty(searchString)) { | |
| // standard log/history lookup | |
| commits = JGitUtils.getRevLog(repository, objectId, offset, length); | |
| } else { | |
| // repository search | |
| commits = JGitUtils.searchRevlogs(repository, objectId, searchString, searchType, | |
| offset, length); | |
| } | |
| Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(repository, model.showRemoteBranches); | |
| // convert RevCommit to SyndicatedEntryModel | |
| for (RevCommit commit : commits) { | |
| FeedEntryModel entry = new FeedEntryModel(); | |
| entry.title = commit.getShortMessage(); | |
| entry.author = commit.getAuthorIdent().getName(); | |
| entry.link = MessageFormat.format(urlPattern, gitblitUrl, | |
| StringUtils.encodeURL(model.name.replace('/', fsc)), commit.getName()); | |
| entry.published = commit.getCommitterIdent().getWhen(); | |
| entry.contentType = "text/html"; | |
| String message = GitBlit.self().processCommitMessage(model.name, | |
| commit.getFullMessage()); | |
| entry.content = message; | |
| entry.repository = model.name; | |
| entry.branch = objectId; | |
| entry.tags = new ArrayList<String>(); | |
| // add commit id and parent commit ids | |
| entry.tags.add("commit:" + commit.getName()); | |
| for (RevCommit parent : commit.getParents()) { | |
| entry.tags.add("parent:" + parent.getName()); | |
| } | |
| // add refs to tabs list | |
| List<RefModel> refs = allRefs.get(commit.getId()); | |
| if (refs != null && refs.size() > 0) { | |
| for (RefModel ref : refs) { | |
| entry.tags.add("ref:" + ref.getName()); | |
| } | |
| } | |
| entries.add(entry); | |
| } | |
| } | |
| // sort & truncate the feed | |
| Collections.sort(entries); | |
| if (entries.size() > length) { | |
| // clip the list | |
| entries = entries.subList(0, length); | |
| } | |
| String feedLink; | |
| if (isProjectFeed) { | |
| // project feed | |
| if (mountParameters) { | |
| // mounted url | |
| feedLink = MessageFormat.format("{0}/project/{1}", gitblitUrl, | |
| StringUtils.encodeURL(feedName)); | |
| } else { | |
| // parameterized url | |
| feedLink = MessageFormat.format("{0}/project/?p={1}", gitblitUrl, | |
| StringUtils.encodeURL(feedName)); | |
| } | |
| } else { | |
| // repository feed | |
| if (mountParameters) { | |
| // mounted url | |
| feedLink = MessageFormat.format("{0}/summary/{1}", gitblitUrl, | |
| StringUtils.encodeURL(feedName)); | |
| } else { | |
| // parameterized url | |
| feedLink = MessageFormat.format("{0}/summary/?r={1}", gitblitUrl, | |
| StringUtils.encodeURL(feedName)); | |
| } | |
| } | |
| try { | |
| SyndicationUtils.toRSS(gitblitUrl, feedLink, getTitle(feedTitle, objectId), | |
| feedDescription, entries, response.getOutputStream()); | |
| } catch (Exception e) { | |
| logger.error("An error occurred during feed generation", e); | |
| } | |
| } | |
| @Override | |
| protected void doPost(javax.servlet.http.HttpServletRequest request, | |
| javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, | |
| java.io.IOException { | |
| processRequest(request, response); | |
| } | |
| @Override | |
| protected void doGet(javax.servlet.http.HttpServletRequest request, | |
| javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, | |
| java.io.IOException { | |
| processRequest(request, response); | |
| } | |
| } |