Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. The ASF licenses this file to You * 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. For additional information regarding * copyright in this work, please see the NOTICE file in the top level * directory of this distribution. */ package org.apache.roller.weblogger.ui.rendering.servlets; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.roller.weblogger.WebloggerException; import org.apache.roller.weblogger.business.HitCountQueue; import org.apache.roller.weblogger.business.WeblogEntryManager; import org.apache.roller.weblogger.business.WebloggerFactory; import org.apache.roller.weblogger.business.referrers.IncomingReferrer; import org.apache.roller.weblogger.business.referrers.ReferrerQueueManager; import org.apache.roller.weblogger.business.themes.ThemeManager; import org.apache.roller.weblogger.config.WebloggerConfig; import org.apache.roller.weblogger.config.WebloggerRuntimeConfig; import org.apache.roller.weblogger.pojos.StaticThemeTemplate; import org.apache.roller.weblogger.pojos.ThemeTemplate; import org.apache.roller.weblogger.pojos.Weblog; import org.apache.roller.weblogger.pojos.WeblogEntry; import org.apache.roller.weblogger.pojos.WeblogTemplate; import org.apache.roller.weblogger.ui.core.RollerContext; import org.apache.roller.weblogger.ui.rendering.Renderer; import org.apache.roller.weblogger.ui.rendering.RendererManager; import org.apache.roller.weblogger.ui.rendering.model.ModelLoader; import org.apache.roller.weblogger.ui.rendering.util.InvalidRequestException; import org.apache.roller.weblogger.ui.rendering.util.ModDateHeaderUtil; import org.apache.roller.weblogger.ui.rendering.util.WeblogEntryCommentForm; import org.apache.roller.weblogger.ui.rendering.util.WeblogPageRequest; import org.apache.roller.weblogger.ui.rendering.util.cache.SiteWideCache; import org.apache.roller.weblogger.ui.rendering.util.cache.WeblogPageCache; import org.apache.roller.weblogger.util.BlacklistChecker; import org.apache.roller.weblogger.util.I18nMessages; import org.apache.roller.weblogger.util.cache.CachedContent; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.jsp.JspFactory; import javax.servlet.jsp.PageContext; import java.io.IOException; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.regex.Pattern; /** * Provides access to weblog pages. */ public class PageServlet extends HttpServlet { private static Log log = LogFactory.getLog(PageServlet.class); // for referrer processing private boolean processReferrers = true; private static Pattern robotPattern = null; // for caching private boolean excludeOwnerPages = false; private WeblogPageCache weblogPageCache = null; private SiteWideCache siteWideCache = null; // Development theme reloading Boolean themeReload = false; /** * Init method for this servlet */ public void init(ServletConfig servletConfig) throws ServletException { super.init(servletConfig); log.info("Initializing PageServlet"); this.excludeOwnerPages = WebloggerConfig.getBooleanProperty("cache.excludeOwnerEditPages"); // get a reference to the weblog page cache this.weblogPageCache = WeblogPageCache.getInstance(); // get a reference to the site wide cache this.siteWideCache = SiteWideCache.getInstance(); // see if built-in referrer processing is enabled this.processReferrers = WebloggerConfig.getBooleanProperty("referrers.processing.enabled"); log.info("Referrer processing enabled = " + this.processReferrers); // check for possible robot pattern String robotPatternStr = WebloggerConfig.getProperty("referrer.robotCheck.userAgentPattern"); if (robotPatternStr != null && robotPatternStr.length() > 0) { // Parse the pattern, and store the compiled form. try { robotPattern = Pattern.compile(robotPatternStr); } catch (Exception e) { // Most likely a PatternSyntaxException; log and continue as if // it is not set. log.error("Error parsing referrer.robotCheck.userAgentPattern value '" + robotPatternStr + "'. Robots will not be filtered. ", e); } } // Development theme reloading themeReload = WebloggerConfig.getBooleanProperty("themes.reload.mode"); } /** * Handle GET requests for weblog pages. */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { log.debug("Entering"); // do referrer processing, if it's enabled // NOTE: this *must* be done first because it triggers a hibernate flush // which will close the active session and cause lazy init exceptions // otherwise if (this.processReferrers) { boolean spam = this.processReferrer(request); if (spam) { log.debug("spammer, giving 'em a 403"); if (!response.isCommitted()) { response.reset(); } response.sendError(HttpServletResponse.SC_FORBIDDEN); return; } } Weblog weblog = null; boolean isSiteWide = false; WeblogPageRequest pageRequest = null; try { pageRequest = new WeblogPageRequest(request); weblog = pageRequest.getWeblog(); if (weblog == null) { throw new WebloggerException("unable to lookup weblog: " + pageRequest.getWeblogHandle()); } // is this the site-wide weblog? isSiteWide = WebloggerRuntimeConfig.isSiteWideWeblog(pageRequest.getWeblogHandle()); } catch (Exception e) { // some kind of error parsing the request or looking up weblog log.debug("error creating page request", e); response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } // determine the lastModified date for this content long lastModified = System.currentTimeMillis(); if (isSiteWide) { lastModified = siteWideCache.getLastModified().getTime(); } else if (weblog.getLastModified() != null) { lastModified = weblog.getLastModified().getTime(); } // 304 Not Modified handling. // We skip this for logged in users to avoid the scenerio where a user // views their weblog, logs in, then gets a 304 without the 'edit' links if (!pageRequest.isLoggedIn()) { if (ModDateHeaderUtil.respondIfNotModified(request, response, lastModified)) { return; } else { // set last-modified date ModDateHeaderUtil.setLastModifiedHeader(response, lastModified); } } // generate cache key String cacheKey = null; if (isSiteWide) { cacheKey = siteWideCache.generateKey(pageRequest); } else { cacheKey = weblogPageCache.generateKey(pageRequest); } // Development only. Reload if theme has been modified if (themeReload && !weblog.getEditorTheme().equals(WeblogTemplate.ACTION_CUSTOM) && (pageRequest.getPathInfo() == null || pageRequest.getPathInfo() != null)) { try { ThemeManager manager = WebloggerFactory.getWeblogger().getThemeManager(); boolean reloaded = manager.reLoadThemeFromDisk(weblog.getEditorTheme()); if (reloaded) { if (isSiteWide) { siteWideCache.clear(); } else { weblogPageCache.clear(); } I18nMessages.reloadBundle(weblog.getLocaleInstance()); } } catch (Exception ex) { log.error("ERROR - reloading theme " + ex); } } // cached content checking if ((!this.excludeOwnerPages || !pageRequest.isLoggedIn()) && request.getAttribute("skipCache") == null) { CachedContent cachedContent = null; if (isSiteWide) { cachedContent = (CachedContent) siteWideCache.get(cacheKey); } else { cachedContent = (CachedContent) weblogPageCache.get(cacheKey, lastModified); } if (cachedContent != null) { log.debug("HIT " + cacheKey); // allow for hit counting if (!isSiteWide) { this.processHit(weblog, request.getRequestURL().toString(), request.getHeader("referer")); } response.setContentLength(cachedContent.getContent().length); response.setContentType(cachedContent.getContentType()); response.getOutputStream().write(cachedContent.getContent()); return; } else { log.debug("MISS " + cacheKey); } } log.debug("Looking for template to use for rendering"); // figure out what template to use ThemeTemplate page = null; // If this is a popup request, then deal with it specially // TODO: do we really need to keep supporting this? if (request.getParameter("popup") != null) { try { // Does user have a popupcomments page? page = weblog.getTheme().getTemplateByName("_popupcomments"); } catch (Exception e) { // ignored ... considered page not found } // User doesn't have one so return the default if (page == null) { page = new StaticThemeTemplate("templates/weblog/popupcomments.vm", "velocity"); } // If request specified the page, then go with that } else if ("page".equals(pageRequest.getContext())) { page = pageRequest.getWeblogPage(); // if we don't have this page then 404, we don't let // this one fall through to the default template if (page == null) { if (!response.isCommitted()) { response.reset(); } response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } // If request specified tags section index, then look for custom // template } else if ("tags".equals(pageRequest.getContext()) && pageRequest.getTags() != null) { try { page = weblog.getTheme().getTemplateByAction(ThemeTemplate.ACTION_TAGSINDEX); } catch (Exception e) { log.error("Error getting weblog page for action 'tagsIndex'", e); } // if we don't have a custom tags page then 404, we don't let // this one fall through to the default template if (page == null) { if (!response.isCommitted()) { response.reset(); } response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } // If this is a permalink then look for a permalink template } else if (pageRequest.getWeblogAnchor() != null) { try { page = weblog.getTheme().getTemplateByAction(ThemeTemplate.ACTION_PERMALINK); } catch (Exception e) { log.error("Error getting weblog page for action 'permalink'", e); } } // if we haven't found a page yet then try our default page if (page == null) { try { page = weblog.getTheme().getDefaultTemplate(); } catch (Exception e) { log.error("Error getting default page for weblog = " + weblog.getHandle(), e); } } // Still no page? Then that is a 404 if (page == null) { if (!response.isCommitted()) { response.reset(); } response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } log.debug("page found, dealing with it"); // validation. make sure that request input makes sense. boolean invalid = false; if (pageRequest.getWeblogPageName() != null && page.isHidden()) { invalid = true; } if (pageRequest.getLocale() != null) { // locale view only allowed if weblog has enabled it if (!pageRequest.getWeblog().isEnableMultiLang()) { invalid = true; } } if (pageRequest.getWeblogAnchor() != null) { // permalink specified. // entry must exist, be published before current time, and locale // must match WeblogEntry entry = pageRequest.getWeblogEntry(); if (entry == null) { invalid = true; } else if (pageRequest.getLocale() != null && !entry.getLocale().startsWith(pageRequest.getLocale())) { invalid = true; } else if (!entry.isPublished()) { invalid = true; } else if (new Date().before(entry.getPubTime())) { invalid = true; } } else if (pageRequest.getWeblogCategoryName() != null) { // category specified. category must exist. if (pageRequest.getWeblogCategory() == null) { invalid = true; } } else if (pageRequest.getTags() != null && pageRequest.getTags().size() > 0) { try { // tags specified. make sure they exist. WeblogEntryManager wmgr = WebloggerFactory.getWeblogger().getWeblogEntryManager(); invalid = !wmgr.getTagComboExists(pageRequest.getTags(), (isSiteWide) ? null : weblog); } catch (WebloggerException ex) { invalid = true; } } if (invalid) { log.debug("page failed validation, bailing out"); if (!response.isCommitted()) { response.reset(); } response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } // do we need to force a specific locale for the request? if (pageRequest.getLocale() == null && !weblog.isShowAllLangs()) { pageRequest.setLocale(weblog.getLocale()); } // allow for hit counting if (!isSiteWide) { this.processHit(weblog, request.getRequestURL().toString(), request.getHeader("referer")); } // looks like we need to render content // set the content deviceType String contentType = "text/html; charset=utf-8"; if (StringUtils.isNotEmpty(page.getOutputContentType())) { contentType = page.getOutputContentType() + "; charset=utf-8"; } else { String mimeType = RollerContext.getServletContext().getMimeType(page.getLink()); if (mimeType != null) { // we found a match ... set the content deviceType contentType = mimeType + "; charset=utf-8"; } else { contentType = "text/html; charset=utf-8"; } } HashMap model = new HashMap(); try { PageContext pageContext = JspFactory.getDefaultFactory().getPageContext(this, request, response, "", false, 8192, true); // special hack for menu tag request.setAttribute("pageRequest", pageRequest); // populate the rendering model Map initData = new HashMap(); initData.put("requestParameters", request.getParameterMap()); initData.put("parsedRequest", pageRequest); initData.put("pageContext", pageContext); // define url strategy initData.put("urlStrategy", WebloggerFactory.getWeblogger().getUrlStrategy()); // if this was a comment posting, check for comment form WeblogEntryCommentForm commentForm = (WeblogEntryCommentForm) request.getAttribute("commentForm"); if (commentForm != null) { initData.put("commentForm", commentForm); } // Load models for pages String pageModels = WebloggerConfig.getProperty("rendering.pageModels"); ModelLoader.loadModels(pageModels, model, initData, true); // Load special models for site-wide blog if (WebloggerRuntimeConfig.isSiteWideWeblog(weblog.getHandle())) { String siteModels = WebloggerConfig.getProperty("rendering.siteModels"); ModelLoader.loadModels(siteModels, model, initData, true); } // Load weblog custom models ModelLoader.loadCustomModels(weblog, model, initData); // ick, gotta load pre-3.0 model stuff as well :( ModelLoader.loadOldModels(model, request, response, pageContext, pageRequest, WebloggerFactory.getWeblogger().getUrlStrategy()); } catch (WebloggerException ex) { log.error("Error loading model objects for page", ex); if (!response.isCommitted()) { response.reset(); } response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return; } // lookup Renderer we are going to use Renderer renderer = null; try { log.debug("Looking up renderer"); renderer = RendererManager.getRenderer(page, pageRequest.getDeviceType()); } catch (Exception e) { // nobody wants to render my content :( log.error("Couldn't find renderer for page " + page.getId(), e); if (!response.isCommitted()) { response.reset(); } response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } // render content. use size of about 24K for a standard page CachedContent rendererOutput = new CachedContent(24567, contentType); try { log.debug("Doing rendering"); renderer.render(model, rendererOutput.getCachedWriter()); // flush rendered output and close rendererOutput.flush(); rendererOutput.close(); } catch (Exception e) { // bummer, error during rendering log.error("Error during rendering for page " + page.getId(), e); if (!response.isCommitted()) { response.reset(); } response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } // post rendering process // flush rendered content to response log.debug("Flushing response output"); response.setContentType(contentType); response.setContentLength(rendererOutput.getContent().length); response.getOutputStream().write(rendererOutput.getContent()); // cache rendered content. only cache if user is not logged in? if ((!this.excludeOwnerPages || !pageRequest.isLoggedIn()) && request.getAttribute("skipCache") == null) { log.debug("PUT " + cacheKey); // put it in the right cache if (isSiteWide) { siteWideCache.put(cacheKey, rendererOutput); } else { weblogPageCache.put(cacheKey, rendererOutput); } } else { log.debug("SKIPPED " + cacheKey); } log.debug("Exiting"); } /** * Handle POST requests. * * We have this here because the comment servlet actually forwards some of * its requests on to us to render some pages with cusom messaging. We may * want to revisit this approach in the future and see if we can do this in * a different way, but for now this is the easy way. */ public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // make sure caching is disabled request.setAttribute("skipCache", "true"); // handle just like a GET request this.doGet(request, response); } /** * Notify the hit tracker that it has an incoming page hit. */ private void processHit(Weblog weblog, String url, String referrer) { HitCountQueue counter = HitCountQueue.getInstance(); counter.processHit(weblog, url, referrer); } /** * Process the incoming request to extract referrer info and pass it on to * the referrer processing queue for tracking. * * @return true if referrer was spam, false otherwise */ private boolean processReferrer(HttpServletRequest request) { log.debug("processing referrer for " + request.getRequestURI()); // bleh! because ref processing does a flush it will close // our hibernate session and cause lazy init exceptions on // objects we have fetched, so we need to use a separate // page request object for this WeblogPageRequest pageRequest; try { pageRequest = new WeblogPageRequest(request); } catch (InvalidRequestException ex) { return false; } // if this came from site-wide frontpage then skip it if (WebloggerRuntimeConfig.isSiteWideWeblog(pageRequest.getWeblogHandle())) { return false; } // if this came from a robot then don't process it if (robotPattern != null) { String userAgent = request.getHeader("User-Agent"); if (userAgent != null && userAgent.length() > 0 && robotPattern.matcher(userAgent).matches()) { log.debug("skipping referrer from robot"); return false; } } String referrerUrl = request.getHeader("Referer"); StringBuffer reqsb = request.getRequestURL(); if (request.getQueryString() != null) { reqsb.append("?"); reqsb.append(request.getQueryString()); } String requestUrl = reqsb.toString(); log.debug("referrer = " + referrerUrl); // if this came from persons own blog then don't process it String selfSiteFragment = "/" + pageRequest.getWeblogHandle(); if (referrerUrl != null && referrerUrl.indexOf(selfSiteFragment) != -1) { log.debug("skipping referrer from own blog"); return false; } // validate the referrer if (pageRequest != null && pageRequest.getWeblogHandle() != null) { // Base page URLs, with and without www. String basePageUrlWWW = WebloggerRuntimeConfig.getAbsoluteContextURL() + "/" + pageRequest.getWeblogHandle(); String basePageUrl = basePageUrlWWW; if (basePageUrlWWW.startsWith("http://www.")) { // chop off the http://www. basePageUrl = "http://" + basePageUrlWWW.substring(11); } // ignore referrers coming from users own blog if (referrerUrl == null || (!referrerUrl.startsWith(basePageUrl) && !referrerUrl.startsWith(basePageUrlWWW))) { // validate the referrer if (referrerUrl != null) { // treat editor referral as direct int lastSlash = requestUrl.indexOf("/", 8); if (lastSlash == -1) { lastSlash = requestUrl.length(); } String requestSite = requestUrl.substring(0, lastSlash); if (referrerUrl.matches(requestSite + ".*\\.rol.*")) { referrerUrl = null; } else if (BlacklistChecker.checkReferrer(pageRequest.getWeblog(), referrerUrl)) { return true; } } } else { log.debug("Ignoring referer = " + referrerUrl); return false; } } // referrer is valid, lets record it try { IncomingReferrer referrer = new IncomingReferrer(); referrer.setReferrerUrl(referrerUrl); referrer.setRequestUrl(requestUrl); referrer.setWeblogHandle(pageRequest.getWeblogHandle()); referrer.setWeblogAnchor(pageRequest.getWeblogAnchor()); referrer.setWeblogDateString(pageRequest.getWeblogDate()); ReferrerQueueManager refQueue = WebloggerFactory.getWeblogger().getReferrerQueueManager(); refQueue.processReferrer(referrer); } catch (Exception e) { log.error("Error processing referrer", e); } return false; } }