Java tutorial
/** * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, version 2.1, dated February 1999. * * This program is free software; you can redistribute it and/or modify * it under the terms of the latest version of the GNU Lesser General * Public License as published by the Free Software Foundation; * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program (LICENSE.txt); if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.jamwiki.servlets; import java.util.LinkedHashMap; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.StringUtils; import org.jamwiki.DataAccessException; import org.jamwiki.Environment; import org.jamwiki.WikiBase; import org.jamwiki.WikiException; import org.jamwiki.WikiMessage; import org.jamwiki.authentication.RoleImpl; import org.jamwiki.authentication.WikiUserDetails; import org.jamwiki.model.VirtualWiki; import org.jamwiki.model.WikiUser; import org.jamwiki.utils.LinkUtil; import org.jamwiki.utils.NamespaceHandler; import org.jamwiki.utils.Utilities; import org.jamwiki.utils.WikiLink; import org.jamwiki.utils.WikiLogger; import org.jamwiki.utils.WikiUtil; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.AbstractController; /** * Provides the infrastructure that is common to all JAMWiki servlets. Unless * special handling is required all JAMWiki servlets should extend this servlet. */ public abstract class JAMWikiServlet extends AbstractController { private static final WikiLogger logger = WikiLogger.getLogger(JAMWikiServlet.class.getName()); /** * Flag to indicate whether or not the servlet should load the nav bar and * other layout elements. */ protected boolean layout = true; /** The prefix of the JSP file used to display the servlet output. */ protected String displayJSP = "wiki"; /** * The name of the JSP file used to render the servlet output in case of * errors. */ private static final String JSP_ERROR = "error-display.jsp"; /** * Any page that take longer than this value (specified in milliseconds) will * print a warning to the log. */ protected static final int SLOW_PAGE_LIMIT = 1000; /** * Parameter used to indicate that a topic should be the target of a * successful login. */ protected static final String PARAM_LOGIN_SUCCESS_TARGET = "returnto"; /** * This method ensures that the left menu, logo, and other required values * have been loaded into the session object. * * @param request * The servlet request object. * @param next * A ModelAndView object corresponding to the page being constructed. */ private void buildLayout(HttpServletRequest request, ModelAndView next, WikiPageInfo pageInfo) { String virtualWikiName = pageInfo.getVirtualWikiName(); if (virtualWikiName == null) { logger.severe("No virtual wiki available for page request " + request.getRequestURI()); virtualWikiName = WikiBase.DEFAULT_VWIKI; } VirtualWiki virtualWiki = ServletUtil.retrieveVirtualWiki(virtualWikiName); // build the layout contents String leftMenu = ServletUtil.cachedContent(request.getContextPath(), request.getLocale(), virtualWikiName, WikiBase.SPECIAL_PAGE_LEFT_MENU, true); next.addObject("leftMenu", leftMenu); next.addObject("defaultTopic", virtualWiki.getDefaultTopicName()); next.addObject("virtualWiki", virtualWiki.getName()); next.addObject("logo", Environment.getValue(Environment.PROP_BASE_LOGO_IMAGE)); String bottomArea = ServletUtil.cachedContent(request.getContextPath(), request.getLocale(), virtualWiki.getName(), WikiBase.SPECIAL_PAGE_BOTTOM_AREA, true); next.addObject("bottomArea", bottomArea); next.addObject(WikiUtil.PARAMETER_VIRTUAL_WIKI, virtualWiki.getName()); // int cssRevision = 0; // try { // cssRevision = // WikiBase.getDataHandler().lookupTopic(virtualWiki.getName(), // WikiBase.SPECIAL_PAGE_STYLESHEET, false, null).getCurrentVersionId(); // } catch (DataAccessException e) {} // next.addObject("cssRevision", cssRevision); } /** * Build a map of links and the corresponding link text to be used as the tab * menu links for the WikiPageInfo object. */ private LinkedHashMap buildTabMenu(HttpServletRequest request, WikiPageInfo pageInfo) { LinkedHashMap<String, WikiMessage> links = new LinkedHashMap<String, WikiMessage>(); WikiUserDetails userDetails = ServletUtil.currentUserDetails(); String pageName = pageInfo.getTopicName(); String virtualWiki = pageInfo.getVirtualWikiName(); if (pageInfo.getAdmin()) { if (userDetails.hasRole(RoleImpl.ROLE_SYSADMIN)) { links.put("Special:Admin", new WikiMessage("tab.admin.configuration")); links.put("Special:Maintenance", new WikiMessage("tab.admin.maintenance")); links.put("Special:Roles", new WikiMessage("tab.admin.roles")); } if (userDetails.hasRole(RoleImpl.ROLE_TRANSLATE)) { links.put("Special:Translation", new WikiMessage("tab.admin.translations")); } } else if (pageInfo.getSpecial()) { // append query params for pages such as Special:Contributions that need // it String specialUrl = pageName; if (!StringUtils.isBlank(request.getQueryString())) { specialUrl = pageName + "?" + request.getQueryString(); } links.put(specialUrl, new WikiMessage("tab.common.special")); } else { try { String article = WikiUtil.extractTopicLink(pageName); String comments = WikiUtil.extractCommentsLink(pageName); links.put(article, new WikiMessage("tab.common.article")); links.put(comments, new WikiMessage("tab.common.comments")); if (ServletUtil.isEditable(virtualWiki, pageName, userDetails)) { String editLink = "Special:Edit?topic=" + Utilities.encodeAndEscapeTopicName(pageName); if (!StringUtils.isBlank(request.getParameter("topicVersionId"))) { editLink += "&topicVersionId=" + request.getParameter("topicVersionId"); } links.put(editLink, new WikiMessage("tab.common.edit")); } String historyLink = "Special:History?topic=" + Utilities.encodeAndEscapeTopicName(pageName); links.put(historyLink, new WikiMessage("tab.common.history")); // if (ServletUtil.isMoveable(virtualWiki, pageName, userDetails)) { // String moveLink = "Special:Move?topic=" // + Utilities.encodeAndEscapeTopicName(pageName); // links.put(moveLink, new WikiMessage("tab.common.move")); // } // if (!userDetails.hasRole(RoleImpl.ROLE_ANONYMOUS)) { // Watchlist watchlist = ServletUtil.currentWatchlist(request, // virtualWiki); // boolean watched = watchlist.containsTopic(pageName); // String watchlistLabel = (watched) ? "tab.common.unwatch" // : "tab.common.watch"; // String watchlistLink = "Special:Watchlist?topic=" // + Utilities.encodeAndEscapeTopicName(pageName); // links.put(watchlistLink, new WikiMessage(watchlistLabel)); // } if (pageInfo.isUserPage()) { WikiLink wikiLink = LinkUtil.parseWikiLink(pageName); String contributionsLink = "Special:Contributions?contributor=" + Utilities.encodeAndEscapeTopicName(wikiLink.getArticle()); links.put(contributionsLink, new WikiMessage("tab.common.contributions")); } String linkToLink = "Special:LinkTo?topic=" + Utilities.encodeAndEscapeTopicName(pageName); links.put(linkToLink, new WikiMessage("tab.common.links")); // if (userDetails.hasRole(RoleImpl.ROLE_ADMIN)) { // String manageLink = "Special:Manage?topic=" + // Utilities.encodeAndEscapeTopicName(pageName); // links.put(manageLink, new WikiMessage("tab.common.manage")); // } String printLink = "Special:Print?topic=" + Utilities.encodeAndEscapeTopicName(pageName); links.put(printLink, new WikiMessage("tab.common.print")); } catch (WikiException e) { logger.severe("Unable to build tabbed menu links", e); } } return links; } /** * Build a map of links and the corresponding link text to be used as the user * menu links for the WikiPageInfo object. */ private LinkedHashMap buildUserMenu(WikiPageInfo pageInfo) { LinkedHashMap<String, WikiMessage> links = new LinkedHashMap<String, WikiMessage>(); WikiUserDetails userDetails = ServletUtil.currentUserDetails(); if (userDetails.hasRole(RoleImpl.ROLE_ANONYMOUS) && !userDetails.hasRole(RoleImpl.ROLE_EMBEDDED)) { // include the current page in the login link String loginLink = "Special:Login"; if (!StringUtils.startsWith(pageInfo.getTopicName(), "Special:Login")) { loginLink += LinkUtil.appendQueryParam("", PARAM_LOGIN_SUCCESS_TARGET, pageInfo.getTopicName()); } links.put(loginLink, new WikiMessage("common.login")); links.put("Special:Account", new WikiMessage("usermenu.register")); } if (!userDetails.hasRole(RoleImpl.ROLE_ANONYMOUS)) { WikiUser user = ServletUtil.currentWikiUser(); String userPage = NamespaceHandler.NAMESPACE_USER + NamespaceHandler.NAMESPACE_SEPARATOR + user.getUsername(); String userCommentsPage = NamespaceHandler.NAMESPACE_USER_COMMENTS + NamespaceHandler.NAMESPACE_SEPARATOR + user.getUsername(); String username = user.getUsername(); if (!StringUtils.isBlank(user.getDisplayName())) { username = user.getDisplayName(); } // user name will be escaped by the jamwiki:link tag WikiMessage userMenuMessage = new WikiMessage("usermenu.user"); userMenuMessage.setParamsWithoutEscaping(new String[] { username }); links.put(userPage, userMenuMessage); links.put(userCommentsPage, new WikiMessage("usermenu.usercomments")); links.put("Special:Watchlist", new WikiMessage("usermenu.watchlist")); } if (!userDetails.hasRole(RoleImpl.ROLE_ANONYMOUS) && !userDetails.hasRole(RoleImpl.ROLE_NO_ACCOUNT)) { links.put("Special:Account", new WikiMessage("usermenu.account")); } if (!userDetails.hasRole(RoleImpl.ROLE_ANONYMOUS) && !userDetails.hasRole(RoleImpl.ROLE_EMBEDDED)) { links.put("Special:Logout", new WikiMessage("common.logout")); } if (userDetails.hasRole(RoleImpl.ROLE_SYSADMIN)) { links.put("Special:Admin", new WikiMessage("usermenu.admin")); } else if (userDetails.hasRole(RoleImpl.ROLE_TRANSLATE)) { links.put("Special:Translation", new WikiMessage("tab.admin.translations")); } return links; } /** * Abstract method that must be implemented by all sub-classes to handle * servlet requests. * * @param request * The servlet request object. * @param response * The servlet response object. * @param next * A ModelAndView object that has been initialized to the view * specified by the <code>displayJSP</code> member variable. * @param pageInfo * A WikiPageInfo object that will hold output parameters to be * passed to the output JSP. * @return A ModelAndView object corresponding to the information to be * rendered, or <code>null</code> if the method directly handles its * own output, for example by writing directly to the output response. */ protected abstract ModelAndView handleJAMWikiRequest(HttpServletRequest request, HttpServletResponse response, ModelAndView next, WikiPageInfo pageInfo) throws Exception; /** * Implement the handleRequestInternal method specified by the Spring * AbstractController class. * * @param request * The servlet request object. * @param response * The servlet response object. * @return A ModelAndView object corresponding to the information to be * rendered, or <code>null</code> if the method directly handles its * own output, for example by writing directly to the output response. * @throws Exception * Thrown if any error occurs during method execution. */ public ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) { long start = System.currentTimeMillis(); initParams(); ModelAndView next = new ModelAndView(this.displayJSP); WikiPageInfo pageInfo = new WikiPageInfo(request); try { next = this.handleJAMWikiRequest(request, response, next, pageInfo); if (next != null && this.layout) { this.loadLayout(request, next, pageInfo); } if (next != null) { next.addObject(ServletUtil.PARAMETER_PAGE_INFO, pageInfo); } } catch (Throwable t) { return this.viewError(request, t); } long execution = System.currentTimeMillis() - start; if (execution > JAMWikiServlet.SLOW_PAGE_LIMIT) { logger.warning( "Slow page loading time: " + request.getRequestURI() + " (" + (execution / 1000.000) + " s.)"); } if (logger.isInfoEnabled()) { String url = request.getRequestURI() + (!StringUtils.isEmpty(request.getQueryString()) ? "?" + request.getQueryString() : ""); logger.info("Loaded page " + url + " (" + (execution / 1000.000) + " s.)"); } return next; } /** * Determine if a topic contains a spam pattern, and if so set the appropriate * page parameters including a "hasSpam" flag in the ModelAndView object. * * @param request * The servlet request object. * @param next * The current ModelAndView object. * @param topicName * The name of the topic being examined for spam. * @param contents * The contents of the topic being examined for spam. * @return <code>true</code> if the topic in question matches any spam * pattern. */ protected boolean handleSpam(HttpServletRequest request, ModelAndView next, String topicName, String contents) throws DataAccessException { return false; // String result = ServletUtil.checkForSpam(request, topicName, contents); // if (result == null) { // return false; // } // WikiMessage spam = new WikiMessage("edit.exception.spam", result); // next.addObject("spam", spam); // next.addObject("hasSpam", "true"); // return true; } /** * If any special servlet initialization needs to be performed it can be done * by overriding this method. In particular, this method can be used to * override the defaults for the <code>layout</code> member variable, which * determines whether or not the output JSP should include the left navigation * and other layout values, and the <code>displayJSP</code> member variable, * which determine the JSP file used to render output. */ protected void initParams() { } /** * This method ensures that values required for rendering a JSP page have been * loaded into the ModelAndView object. Examples of values that may be handled * by this method include topic name, username, etc. * * @param request * The current servlet request object. * @param next * The current ModelAndView object. * @param pageInfo * The current WikiPageInfo object, containing basic page rendering * information. */ private void loadLayout(HttpServletRequest request, ModelAndView next, WikiPageInfo pageInfo) throws Exception { if (next.getViewName() != null && next.getViewName().startsWith(ServletUtil.SPRING_REDIRECT_PREFIX)) { // if this is a redirect, no need to load anything return; } // load cached top area, nav bar, etc. this.buildLayout(request, next, pageInfo); if (StringUtils.isBlank(pageInfo.getTopicName())) { pageInfo.setTopicName(WikiUtil.getTopicFromURI(request)); } pageInfo.setUserMenu(this.buildUserMenu(pageInfo)); pageInfo.setTabMenu(this.buildTabMenu(request, pageInfo)); } /** * Method used when redirecting to an error page. * * @param request * The servlet request object. * @param t * The exception that is the source of the error. * @return Returns a ModelAndView object corresponding to the error page * display. */ private ModelAndView viewError(HttpServletRequest request, Throwable t) { if (!(t instanceof WikiException)) { logger.severe("Servlet error", t); } ModelAndView next = new ModelAndView("wiki"); WikiPageInfo pageInfo = new WikiPageInfo(request); pageInfo.setPageTitle(new WikiMessage("error.title")); pageInfo.setContentJsp(JSP_ERROR); pageInfo.setSpecial(true); if (t instanceof WikiException) { WikiException we = (WikiException) t; next.addObject("messageObject", we.getWikiMessage()); } else { next.addObject("messageObject", new WikiMessage("error.unknown", t.toString())); } try { this.loadLayout(request, next, pageInfo); } catch (Exception err) { logger.severe("Unable to load default layout", err); } next.addObject(ServletUtil.PARAMETER_PAGE_INFO, pageInfo); return next; } }