001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. The ASF licenses this file to You
004: * under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License. For additional information regarding
015: * copyright in this work, please see the NOTICE file in the top level
016: * directory of this distribution.
017: */
018:
019: package org.apache.roller.ui.rendering.servlets;
020:
021: import java.io.IOException;
022: import java.util.Date;
023: import java.util.HashMap;
024: import javax.servlet.ServletConfig;
025: import javax.servlet.ServletException;
026: import javax.servlet.http.HttpServlet;
027: import javax.servlet.http.HttpServletRequest;
028: import javax.servlet.http.HttpServletResponse;
029: import org.apache.commons.logging.Log;
030: import org.apache.commons.logging.LogFactory;
031: import org.apache.roller.RollerException;
032: import org.apache.roller.config.RollerRuntimeConfig;
033: import org.apache.roller.planet.business.PlanetManager;
034: import org.apache.roller.business.RollerFactory;
035: import org.apache.roller.pojos.StaticTemplate;
036: import org.apache.roller.pojos.Template;
037: import org.apache.roller.ui.rendering.Renderer;
038: import org.apache.roller.ui.rendering.RendererManager;
039: import org.apache.roller.ui.rendering.model.UtilitiesModel;
040: import org.apache.roller.ui.rendering.util.cache.PlanetCache;
041: import org.apache.roller.ui.rendering.util.PlanetRequest;
042: import org.apache.roller.ui.rendering.util.ModDateHeaderUtil;
043: import org.apache.roller.util.cache.CachedContent;
044:
045: /**
046: * Planet Roller (i.e. NOT for Planet Tool) RSS feed.
047: *
048: * @web.servlet name="PlanetFeedServlet" load-on-startup="7"
049: * @web.servlet-mapping url-pattern="/planetrss/*"
050: */
051: public class PlanetFeedServlet extends HttpServlet {
052:
053: private static Log log = LogFactory.getLog(PlanetFeedServlet.class);
054:
055: private PlanetCache planetCache = null;
056:
057: /**
058: * Init method for this servlet
059: */
060: public void init(ServletConfig servletConfig)
061: throws ServletException {
062:
063: super .init(servletConfig);
064:
065: log.info("Initializing PlanetRssServlet");
066:
067: this .planetCache = PlanetCache.getInstance();
068: }
069:
070: /**
071: * Handle GET requests for weblog pages.
072: */
073: public void doGet(HttpServletRequest request,
074: HttpServletResponse response) throws ServletException,
075: IOException {
076:
077: log.debug("Entering");
078:
079: PlanetManager planet = null;
080: try {
081: planet = RollerFactory.getRoller().getPlanetManager();
082: } catch (RollerException ex) {
083: // error getting planet manager :(
084: log.error("Unable to get planet manager", ex);
085: response
086: .sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
087: return;
088: }
089:
090: PlanetRequest planetRequest = null;
091: try {
092: planetRequest = new PlanetRequest(request);
093: } catch (Exception e) {
094: // some kind of error parsing the request
095: log.debug("error creating planet request", e);
096: response.sendError(HttpServletResponse.SC_NOT_FOUND);
097: return;
098: }
099:
100: // figure planet last modified date
101: Date lastModified = planetCache.getLastModified();
102:
103: // Respond with 304 Not Modified if it is not modified.
104: if (ModDateHeaderUtil.respondIfNotModified(request, response,
105: lastModified.getTime())) {
106: return;
107: }
108:
109: // set content type
110: String accepts = request.getHeader("Accept");
111: String userAgent = request.getHeader("User-Agent");
112: if (accepts != null && userAgent != null
113: && accepts.indexOf("*/*") != -1
114: && userAgent.startsWith("Mozilla")) {
115: // client is a browser and now that we offer styled feeds we want
116: // browsers to load the page rather than popping up the download
117: // dialog, so we provide a content-type that browsers will display
118: response.setContentType("text/xml");
119: } else {
120: response
121: .setContentType("application/rss+xml; charset=utf-8");
122: }
123:
124: // set last-modified date
125: ModDateHeaderUtil.setLastModifiedHeader(response, lastModified
126: .getTime());
127:
128: // cached content checking
129: String cacheKey = PlanetCache.CACHE_ID + ":"
130: + this .generateKey(planetRequest);
131: CachedContent entry = (CachedContent) planetCache.get(cacheKey);
132: if (entry != null) {
133: response.setContentLength(entry.getContent().length);
134: response.getOutputStream().write(entry.getContent());
135: return;
136:
137: }
138:
139: // looks like we need to render content
140: HashMap model = new HashMap();
141: try {
142: // populate the rendering model
143: if (request.getParameter("group") != null) {
144: model.put("group", planet.getGroup(request
145: .getParameter("group")));
146: }
147: model.put("planet", planet);
148: model.put("date", new Date());
149: model.put("utils", new UtilitiesModel());
150: model.put("absoluteSite", RollerRuntimeConfig
151: .getAbsoluteContextURL());
152: model.put("feedStyle", new Boolean(RollerRuntimeConfig
153: .getBooleanProperty("site.newsfeeds.styledFeeds")));
154:
155: int numEntries = RollerRuntimeConfig
156: .getIntProperty("site.newsfeeds.defaultEntries");
157: int entryCount = numEntries;
158: String sCount = request.getParameter("count");
159: if (sCount != null) {
160: try {
161: entryCount = Integer.parseInt(sCount);
162: } catch (NumberFormatException e) {
163: log.warn("Improperly formatted count parameter");
164: }
165: if (entryCount > numEntries)
166: entryCount = numEntries;
167: if (entryCount < 0)
168: entryCount = 0;
169: }
170: model.put("entryCount", new Integer(entryCount));
171:
172: } catch (RollerException ex) {
173: log.error("Error loading model objects for page", ex);
174:
175: if (!response.isCommitted())
176: response.reset();
177: response
178: .sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
179: return;
180: }
181:
182: // lookup Renderer we are going to use
183: Renderer renderer = null;
184: try {
185: log.debug("Looking up renderer");
186: Template template = new StaticTemplate(
187: "templates/planet/planetrss.vm", null, "velocity");
188: renderer = RendererManager.getRenderer(template);
189: } catch (Exception e) {
190: // nobody wants to render my content :(
191: log.error("Couldn't find renderer for planet rss", e);
192:
193: if (!response.isCommitted())
194: response.reset();
195: response.sendError(HttpServletResponse.SC_NOT_FOUND);
196: return;
197: }
198:
199: // render content. use default size of about 24K for a standard page
200: CachedContent rendererOutput = new CachedContent(24567);
201: try {
202: log.debug("Doing rendering");
203: renderer.render(model, rendererOutput.getCachedWriter());
204:
205: // flush rendered output and close
206: rendererOutput.flush();
207: rendererOutput.close();
208: } catch (Exception e) {
209: // bummer, error during rendering
210: log.error("Error during rendering for planet rss", e);
211:
212: if (!response.isCommitted())
213: response.reset();
214: response.sendError(HttpServletResponse.SC_NOT_FOUND);
215: return;
216: }
217:
218: // post rendering process
219:
220: // flush rendered content to response
221: log.debug("Flushing response output");
222: response.setContentLength(rendererOutput.getContent().length);
223: response.getOutputStream().write(rendererOutput.getContent());
224:
225: // cache rendered content.
226: this .planetCache.put(cacheKey, rendererOutput);
227:
228: log.debug("Exiting");
229: }
230:
231: /**
232: * Generate a cache key from a parsed planet request.
233: * This generates a key of the form ...
234: *
235: * <context>/<type>/<language>[/user]
236: * or
237: * <context>/<type>[/flavor]/<language>[/excerpts]
238: *
239: *
240: * examples ...
241: *
242: * planet/page/en
243: * planet/feed/rss/en/excerpts
244: *
245: */
246: private String generateKey(PlanetRequest planetRequest) {
247:
248: StringBuffer key = new StringBuffer();
249: key.append(planetRequest.getContext());
250: key.append("/");
251: key.append(planetRequest.getType());
252:
253: if (planetRequest.getFlavor() != null) {
254: key.append("/").append(planetRequest.getFlavor());
255: }
256:
257: // add language
258: key.append("/").append(planetRequest.getLanguage());
259:
260: if (planetRequest.getFlavor() != null) {
261: // add excerpts
262: if (planetRequest.isExcerpts()) {
263: key.append("/excerpts");
264: }
265: } else {
266: // add login state
267: if (planetRequest.getAuthenticUser() != null) {
268: key.append("/user=").append(
269: planetRequest.getAuthenticUser());
270: }
271: }
272:
273: return key.toString();
274: }
275:
276: }
|