Java tutorial
/********************************************************************************** * $URL$ * $Id$ ********************************************************************************** * * Copyright (c) 2011, 2012 Etudes, 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.etudes.tool.melete; import java.io.IOException; import javax.servlet.RequestDispatcher; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.sakaiproject.thread_local.cover.ThreadLocalManager; import org.sakaiproject.tool.api.Tool; import org.sakaiproject.tool.api.ToolSession; import org.sakaiproject.tool.cover.SessionManager; import org.sakaiproject.util.StringUtil; import org.sakaiproject.util.Web; public class MeleteJsfTool extends HttpServlet { /** Session attribute to hold the last view visited. */ public static final String LAST_VIEW_VISITED = "sakai.jsf.tool.last.view.visited"; /** Request attribute we set to help the return URL know what extension we (or jsf) add (does not need to be in the URL. */ public static final String URL_EXT = "sakai.jsf.tool.URL.ext"; /** Request attribute we set to help the return URL know what path we add (does not need to be in the URL. */ public static final String URL_PATH = "sakai.jsf.tool.URL.path"; // TODO: Note, these two values must match those in jsf-app's SakaiViewHandler /** Our log (commons). */ private static Log M_log = LogFactory.getLog(MeleteJsfTool.class); /** The file extension to get to JSF. */ protected static final String JSF_EXT = ".jsf"; /** The default target, as configured. */ protected String m_default = null; /** if true, we preserve the last visit per placement / user, and use it if we get a request with no path. */ protected boolean m_defaultToLastView = true; /** The folder to the jsf files, as configured. Does not end with a "/". */ protected String m_path = null; /** * Shutdown the servlet. */ public void destroy() { M_log.info("destroy"); super.destroy(); } /** * Access the Servlet's information display. * * @return servlet information. */ public String getServletInfo() { return "Sakai JSF Tool Servlet"; } /** * Initialize the servlet. * * @param config * The servlet config. * @throws ServletException */ public void init(ServletConfig config) throws ServletException { super.init(config); m_default = config.getInitParameter("default"); m_path = config.getInitParameter("path"); m_defaultToLastView = "true".equals(config.getInitParameter("default.last.view")); // make sure there is no "/" at the end of the path if (m_path != null && m_path.endsWith("/")) { m_path = m_path.substring(0, m_path.length() - 1); } M_log.info("init: default: " + m_default + " path: " + m_path); } /** * Compute a target (i.e. the servlet path info, not including folder root or jsf extension) for the case of the actual path being empty. * * @return The servlet info path target computed for the case of empty actual path. */ protected String computeDefaultTarget() { // setup for the default view as configured String target = "/" + m_default; // if we are doing lastVisit and there's a last-visited view, for this tool placement / user, use that if (m_defaultToLastView) { ToolSession session = SessionManager.getCurrentToolSession(); String last = (String) session.getAttribute(LAST_VIEW_VISITED); if (last != null) { target = last; } } return target; } /** * Respond to requests. * * @param req * The servlet request. * @param res * The servlet response. * @throws ServletException. * @throws IOException. */ protected void dispatch(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { // NOTE: this is a simple path dispatching, taking the path as the view id = jsp file name for the view, // with default used if no path and a path prefix as configured. // TODO: need to allow other sorts of dispatching, such as pulling out drill-down ids and making them // available to the JSF // build up the target that will be dispatched to String target = req.getPathInfo(); // see if we have a resource request - i.e. a path with an extension, and one that is not the JSF_EXT if (isResourceRequest(target)) { // get a dispatcher to the path RequestDispatcher resourceDispatcher = getServletContext().getRequestDispatcher(target); if (resourceDispatcher != null) { resourceDispatcher.forward(req, res); return; } } if ("Title".equals(req.getParameter("panel"))) { // This allows only one Title JSF for each tool target = "/title.jsf"; } else { ToolSession session = SessionManager.getCurrentToolSession(); if (target == null || "/".equals(target)) { target = computeDefaultTarget(); // make sure it's a valid path if (!target.startsWith("/")) { target = "/" + target; } // now that we've messed with the URL, send a redirect to make it official res.sendRedirect(Web.returnUrl(req, target)); return; } // see if we want to change the specifically requested view String newTarget = redirectRequestedTarget(target); // make sure it's a valid path if (!newTarget.startsWith("/")) { newTarget = "/" + newTarget; } if (!newTarget.equals(target)) { // now that we've messed with the URL, send a redirect to make it official res.sendRedirect(Web.returnUrl(req, newTarget)); return; } target = newTarget; // store this if (m_defaultToLastView) { String params = req.getQueryString(); if (params != null) { session.setAttribute(LAST_VIEW_VISITED, target + "?" + params); } else { session.setAttribute(LAST_VIEW_VISITED, target); } } } // these URL paths have the module id encoded in the URL - remove it and place it in threadLocal if (target.startsWith("/view_module/") || target.startsWith("/edit_module/")) { String[] parts = StringUtil.split(target, "/"); if (parts.length == 6) { String id = parts[2]; ThreadLocalManager.set("MELETE_MODULE_ID", id); String returnBackUrl = "/portal/directtool/" + parts[4] + "/" + parts[5]; ThreadLocalManager.set("MELETE_NAVIGATE_BACK", returnBackUrl); target = "/" + parts[1]; } } // Add variable to autosave module and section changes if (target.startsWith("/edit_module")) { ThreadLocalManager.set("MELETE_SAVE_FROM", "editModule"); } else if (target.startsWith("/add_module")) { ThreadLocalManager.set("MELETE_SAVE_FROM", "addModule"); } else if (target.startsWith("/editmodulesections")) { ThreadLocalManager.set("MELETE_SAVE_FROM", "Section"); } else if (target.startsWith("/list_auth_modules")) { ThreadLocalManager.set("MELETE_SAVE_FROM", "listAuth"); } else if (target.startsWith("/author_preference")) { ThreadLocalManager.set("MELETE_SAVE_FROM", "authPref"); } // add the configured folder root and extension (if missing) target = m_path + target; // add the default JSF extension (if we have no extension) int lastSlash = target.lastIndexOf("/"); int lastDot = target.lastIndexOf("."); if (lastDot < 0 || lastDot < lastSlash) { target += JSF_EXT; } // set the information that can be removed from return URLs req.setAttribute(URL_PATH, m_path); req.setAttribute(URL_EXT, ".jsp"); // set the sakai request object wrappers to provide the native, not Sakai set up, URL information // - this assures that the FacesServlet can dispatch to the proper view based on the path info req.setAttribute(Tool.NATIVE_URL, Tool.NATIVE_URL); // TODO: Should setting the HTTP headers be moved up to the portal level as well? res.setContentType("text/html; charset=UTF-8"); res.setCharacterEncoding("UTF-8"); res.addDateHeader("Expires", System.currentTimeMillis() - (1000L * 60L * 60L * 24L * 365L)); res.addDateHeader("Last-Modified", System.currentTimeMillis()); res.addHeader("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0"); res.addHeader("Pragma", "no-cache"); // dispatch to the target M_log.debug("dispatching path: " + req.getPathInfo() + " to: " + target + " context: " + getServletContext().getServletContextName()); RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(target); dispatcher.forward(req, res); // restore the request object req.removeAttribute(Tool.NATIVE_URL); req.removeAttribute(URL_PATH); req.removeAttribute(URL_EXT); } /** * Respond to requests. * * @param req * The servlet request. * @param res * The servlet response. * @throws ServletException. * @throws IOException. */ protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { dispatch(req, res); } /** * Respond to requests. * * @param req * The servlet request. * @param res * The servlet response. * @throws ServletException. * @throws IOException. */ protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { dispatch(req, res); } /** * Recognize a path that is a resource request. It must have an "extension", i.e. a dot followed by characters that do not include a slash. * * @param path * The path to check * @return true if the path is a resource request, false if not. */ protected boolean isResourceRequest(String path) { // we need some path if ((path == null) || (path.length() == 0)) return false; // we need a last dot int pos = path.lastIndexOf("."); if (pos == -1) return false; // we need that last dot to be the end of the path, not buried in the path somewhere (i.e. no more slashes after the last dot) String ext = path.substring(pos); if (ext.indexOf("/") != -1) return false; // we need the ext to not be the JSF_EXT if (ext.equals(JSF_EXT)) return false; // ok, it's a resource request return true; } /** * Compute a new target (i.e. the servlet path info, not including folder root or jsf extension) if needed based on the requested target. * * @param target * The servlet path info target requested. * @return The target we will actually respond with. */ protected String redirectRequestedTarget(String target) { return target; } }