/** * Copyright 2010 JBoss Inc * * 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 org.drools.guvnor.server.files; import org.drools.guvnor.server.security.PackageNameType; import org.drools.guvnor.server.security.RoleTypes; import org.drools.guvnor.server.security.CategoryPathType; import org.drools.guvnor.server.util.Discussion; import org.drools.guvnor.server.util.ISO8601; import org.drools.guvnor.client.rpc.DiscussionRecord; import org.drools.repository.AssetItem; import org.drools.repository.PackageItem; import org.drools.repository.AssetItemPageResult; import org.drools.core.util.StringUtils; import org.jboss.seam.contexts.Contexts; import org.jboss.seam.security.Identity; import org.jboss.seam.security.AuthorizationException; import org.mvel2.templates.TemplateRuntime; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.ServletException; import java.io.IOException; import java.io.InputStreamReader; import java.util.*; /** * @author Michael Neale */ public class FeedServlet extends RepositoryServlet { private static final String VIEW_URL = "viewUrl"; @Override protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { try { String url = request.getRequestURI(); if (url.indexOf("feed/package") > -1) { doAuthorizedAction(request, response, new A() { public void a() throws Exception { doPackageFeed(request, response); } }); } else if (url.indexOf("feed/category") > -1) { doAuthorizedAction(request, response, new A() { public void a() throws Exception { doCategoryFeed(request, response); } }); } else if (url.indexOf("feed/discussion") > -1) { doAuthorizedAction(request, response, new A() { public void a() throws Exception { doDiscussionFeed(request, response); } }); } } catch (AuthorizationException e) { response.setHeader("WWW-Authenticate", "BASIC realm=\"users\""); response.sendError(HttpServletResponse.SC_UNAUTHORIZED); } } private void doDiscussionFeed(HttpServletRequest request, HttpServletResponse response) throws IOException { String assetName = request.getParameter("assetName"); String packageName = request.getParameter("package"); AssetItem asset = getFileManager().getRepository().loadPackage(packageName).loadAsset(assetName); checkPackageReadPermission(asset.getPackageName()); List<AtomFeed.AtomEntry> entries = new ArrayList<AtomFeed.AtomEntry>(); entries.add(new AtomFeed.AtomEntry(request, asset)); List<DiscussionRecord> drs = new Discussion().fromString(asset.getStringProperty(Discussion.DISCUSSION_PROPERTY_KEY)); for (DiscussionRecord dr : drs) { entries.add(new AtomFeed.AtomEntry(request, asset, dr)); } AtomFeed feed = new AtomFeed("Discussion of: " + packageName + "/" + assetName, Calendar.getInstance(), request.getServerName() + "/" + packageName + "/" + assetName, request.getParameter(VIEW_URL), request.getRequestURL().toString(), entries, "A list of updated discussion content."); response.setContentType("application/atom+xml; charset=UTF-8"); response.getWriter().print(feed.getAtom()); } private void doCategoryFeed(HttpServletRequest request, HttpServletResponse response) throws IOException { String cat = request.getParameter("name"); String status = request.getParameter("status"); checkCategoryPermission(cat); AssetItemPageResult pg = getFileManager().getRepository().findAssetsByCategory(cat, false, 0, -1); Iterator<AssetItem> it = pg.assets.iterator(); List<AtomFeed.AtomEntry> entries = new ArrayList<AtomFeed.AtomEntry>(); buildEntries(request, entries, it, status); AtomFeed feed = new AtomFeed("Category: " + cat, Calendar.getInstance(), request.getServerName() + cat, request.getParameter(VIEW_URL), request.getRequestURL().toString(), entries, "Guvnor category of items: " + cat); response.setContentType("application/atom+xml; charset=UTF-8"); response.getWriter().print(feed.getAtom()); } void checkCategoryPermission(String cat) { if ( Contexts.isSessionContextActive() ) { Identity.instance().checkPermission( new CategoryPathType( cat ), RoleTypes.ANALYST_READ ); } } private void doPackageFeed(HttpServletRequest request, HttpServletResponse response) throws IOException { String packageName = request.getParameter("name"); checkPackageReadPermission(packageName); PackageItem pkg = getFileManager().getRepository().loadPackage(packageName); List<AtomFeed.AtomEntry> entries = new ArrayList<AtomFeed.AtomEntry>(); Iterator<AssetItem> it = pkg.getAssets(); buildEntries(request, entries, it, request.getParameter("status")); AtomFeed feed = new AtomFeed("Knowledge package: " + pkg.getName(), pkg.getLastModified(), pkg.getUUID(), request.getParameter(VIEW_URL), request.getRequestURL().toString(), entries, pkg.getDescription()); response.setContentType("application/atom+xml; charset=UTF-8"); response.getWriter().print(feed.getAtom()); } private void buildEntries(HttpServletRequest request, List<AtomFeed.AtomEntry> entries, Iterator<AssetItem> it, String status) { while(it.hasNext()) { AssetItem as = it.next(); if (!as.isArchived() && !as.getDisabled()) { if (status == null || status.equals("*") || as.getStateDescription().equals(status)) { entries.add(new AtomFeed.AtomEntry(request, as)); } } } } void checkPackageReadPermission(String packageName) { if ( Contexts.isSessionContextActive() ) { Identity.instance().checkPermission( new PackageNameType( packageName ), RoleTypes.PACKAGE_READONLY); } } /** * @author Michael Neale */ public static class AtomFeed { private static String TEMPLATE = StringUtils.readFileAsString(new InputStreamReader(AtomFeed.class.getResourceAsStream("/atom-feed-template.xml"))); private String feedTitle; private String feedUpdated; private String feedId; private String feedAlternate; private String feedSelf; private String subtitle; private Collection<AtomEntry> entries; public AtomFeed(String title, Calendar whenUpdate, String feedId, String feedAlternate, String feedSelf, Collection<AtomEntry> entries, String subtitle) { this.feedTitle = title; this.feedUpdated = ISO8601.format(whenUpdate); this.feedId = feedId; this.feedAlternate = feedAlternate; this.feedSelf = feedSelf; this.entries = entries; this.subtitle = subtitle; } public String getAtom() { Map m = new HashMap(); m.put("feed", this); return (String) TemplateRuntime.eval(TEMPLATE, m); } public String getSubtitle() { return subtitle; } public Collection getEntries() { return entries; } public String getFeedTitle() { return feedTitle; } public String getFeedUpdated() { return feedUpdated; } public String getFeedId() { return feedId; } public String getFeedAlternate() { return feedAlternate; } public String getFeedSelf() { return feedSelf; } public static class AtomEntry { private String name; private String webURL; private String id; private String updated; private String published; private String author; private String contributor; private String description; private String checkinComment; private String format; /** * We are creating an entry for each asset. */ public AtomEntry(HttpServletRequest req, AssetItem asset) { this.name = asset.getName(); this.format = asset.getFormat(); //Escape & with %26 to make generated XML safe. this.webURL = req.getParameter(VIEW_URL) + "#asset=" + asset.getUUID() + "%26nochrome"; //Each history version of asset has its unique UUID. &version is not needed here. Plus &version //is not being parsed on the server side //this.id = asset.getUUID() + "&version=" + asset.getVersionNumber(); this.id = asset.getUUID(); this.updated = ISO8601.format(asset.getLastModified()); this.published = ISO8601.format(asset.getCreatedDate()); this.author = asset.getCreator(); this.contributor = asset.getLastContributor(); this.description = asset.getDescription(); this.checkinComment = asset.getCheckinComment(); } /** We are creating entries for each discussion record */ public AtomEntry(HttpServletRequest req, AssetItem asset, DiscussionRecord discussionRec) { this.name = asset.getName(); this.format = asset.getFormat(); this.webURL = req.getParameter(VIEW_URL) + "#asset=" + asset.getUUID() + "%26nochrome"; this.id = asset.getUUID(); Calendar c = Calendar.getInstance(); c.setTime(new Date(discussionRec.timestamp)); this.updated = ISO8601.format(c); this.published = ISO8601.format(c); this.author = discussionRec.author; this.contributor = asset.getLastContributor(); this.description = "Discussion comment was added by: " + discussionRec.author; this.checkinComment = discussionRec.note; } public String getName() { return name; } public String getFormat() { return format; } public String getWebURL() { return webURL; } public String getId() { return id; } public String getUpdated() { return updated; } public String getPublished() { return published; } public String getAuthor() { return author; } public String getContributor() { return contributor; } public String getDescription() { return description; } public String getCheckinComment() { return checkinComment; } } } }