Java tutorial
/* * JSmart Framework - Java Web Development Framework * Copyright (c) 2015, Jeferson Albino da Silva, All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3.0 of the License, or (at your option) any later version. * * This library 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 library. If not, see <http://www.gnu.org/licenses/>. */ package com.jsmartframework.web.manager; import static com.jsmartframework.web.config.Constants.REQUEST_REDIRECT_PATH_AJAX_ATTR; import static com.jsmartframework.web.config.Constants.REQUEST_REDIRECT_WINDOW_PATH_AJAX_ATTR; import static com.jsmartframework.web.manager.BeanHandler.HANDLER; import static com.jsmartframework.web.config.Constants.ENCODING; import static com.jsmartframework.web.config.Constants.NEXT_URL; import com.jsmartframework.web.listener.WebAsyncListener; import com.jsmartframework.web.listener.WebAsyncListener.Reason; import com.jsmartframework.web.util.WebUtils; import org.apache.commons.lang.StringUtils; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.AsyncContext; import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; import javax.servlet.ServletConfig; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public final class ServletControl extends HttpServlet { private static final long serialVersionUID = -4462762772195421585L; private static final Logger LOGGER = Logger.getLogger(ServletControl.class.getPackage().getName()); @Override public void init(ServletConfig servletConfig) throws ServletException { super.init(servletConfig); WebContext.setServlet(this); // Call registered WebContextListeners for (ServletContextListener contextListener : HANDLER.contextListeners) { HANDLER.executeInjection(contextListener); contextListener.contextInitialized(new ServletContextEvent(servletConfig.getServletContext())); } } @Override public void destroy() { // Call registered WebContextListeners for (ServletContextListener contextListener : HANDLER.contextListeners) { contextListener.contextDestroyed(new ServletContextEvent(getServletContext())); } super.destroy(); } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String path = request.getServletPath(); // If path is secure, check if user was logged case @AuthBean annotation was provided if (checkAuthentication(path, request, response)) { return; } // Return if request is for async bean handling if (doAsync(path, request, response)) { return; } // If got here the request is for web bean handling sendForward(path, request, response); } @Override @SuppressWarnings("all") protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String path = request.getServletPath(); // If path is secure, check if user was logged case @AuthBean annotation was provided if (checkAuthentication(path, request, response)) { return; } // Check if user is authorized to access the page. Send HTTP 403 response case they did not have Integer httpStatus = HANDLER.checkAuthorization(path); if (httpStatus != null) { LOGGER.log(Level.INFO, "Access not authorized on page [" + path + "]"); response.sendError(httpStatus); return; } // Check if user is truly valid by carrying CSRF token if implemented httpStatus = HANDLER.checkWebSecurityToken(request); if (httpStatus != null) { LOGGER.log(Level.INFO, "Possibly invalid access via CSRF attack on page [" + path + "]"); response.sendError(httpStatus); return; } // Decrypt expressions if needed Map<String, String> expressions = HANDLER.getRequestExpressions(request); // Initiate beans mentioned on jsp page (Case request scope beans) try { HANDLER.instantiateBeans(path, expressions); } catch (Exception ex) { LOGGER.log(Level.INFO, "WebBeans on page [" + path + "] could not be instantiated: " + ex.getMessage()); throw new ServletException(ex); } // Case user had ordered redirect to specific path in postConstruct method String redirectPath = WebContext.getRedirectTo(); if (redirectPath != null && !redirectPath.equals(path)) { sendRedirect(redirectPath, request, response); return; } boolean redirectAjax = false; String responsePath = HANDLER.handleRequestExpressions(expressions); // Case response was written directly, just return if (WebContext.isResponseWritten()) { return; } // Decode the response path case returned from action method if (responsePath != null) { responsePath = WebUtils.decodePath(responsePath); } // Case user had ordered redirect to specific path in submitted method redirectPath = WebContext.getRedirectTo(); if (redirectPath != null && !redirectPath.equals(path)) { responsePath = redirectPath; } // Case is Ajax post action and submit method returned a path, let JavaScript redirect page if (responsePath != null && "XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) { redirectAjax = true; } if (responsePath == null) { responsePath = path; } else { // Case is Ajax post action, let JavaScript redirect page if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) { if (redirectAjax) { request.setAttribute(REQUEST_REDIRECT_PATH_AJAX_ATTR, getRedirectPath(responsePath, request, false)); request.setAttribute(REQUEST_REDIRECT_WINDOW_PATH_AJAX_ATTR, WebContext.isRedirectToWindow()); } responsePath = path; } } sendRedirect(responsePath, request, response); } private boolean checkAuthentication(String path, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { AuthPath authPath = HANDLER.checkAuthentication(path); if (authPath.shouldRedirectFromPath(path)) { sendRedirect(authPath.getPath(), request, response, !authPath.isHomePath()); return true; } return false; } private boolean doAsync(String path, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { try { // Only proceed if the AsyncContext was not started to avoid looping whe dispatch is called if (!request.isAsyncStarted()) { WebAsyncListener bean = (WebAsyncListener) HANDLER.instantiateAsyncBean(path); if (bean != null) { AsyncContext asyncContext = request.startAsync(); bean.asyncContextCreated(asyncContext); asyncContext.addListener(new WebServletAsyncListener(path, bean)); return true; } } } catch (Exception ex) { LOGGER.log(Level.SEVERE, "AsyncBean on path [" + path + "] could not be instantiated: " + ex.getMessage()); throw new ServletException(ex); } return false; } private void sendForward(String path, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // Check if user is authorized to access the page. Send HTTP 403 response case they did not have Integer httpStatus = HANDLER.checkAuthorization(path); if (httpStatus != null) { LOGGER.log(Level.INFO, "WebBean access not authorized on page [" + path + "]"); response.sendError(httpStatus); return; } // Initiate beans mentioned on jsp page try { HANDLER.instantiateBeans(path, null); } catch (Exception ex) { LOGGER.log(Level.SEVERE, "WebBeans on page [" + path + "] could not be instantiated: " + ex.getMessage()); throw new ServletException(ex); } // Case user had ordered redirect to specific path in postConstruct method String redirectPath = WebContext.getRedirectTo(); if (redirectPath != null && !redirectPath.equals(path)) { sendRedirect(redirectPath, request, response); return; } // Case response was written directly, just return if (WebContext.isResponseWritten()) { return; } // Case is Ajax post action, let JavaScript redirect page if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) { request.setAttribute(REQUEST_REDIRECT_PATH_AJAX_ATTR, getRedirectPath(path, request, false)); request.setAttribute(REQUEST_REDIRECT_WINDOW_PATH_AJAX_ATTR, WebContext.isRedirectToWindow()); } // Use Forward request internally case is the same page String url = HANDLER.getForwardPath(path); if (url == null) { LOGGER.log(Level.SEVERE, "Could not find JSP page for path [" + path + "]"); return; } // Generate web security token to prevent CSRF attack HANDLER.generateWebSecurityToken(request, response); // Use Forward request internally case is the same page request.getRequestDispatcher(url).forward(request, response); } private void sendRedirect(String path, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { sendRedirect(path, request, response, false); } private void sendRedirect(String path, HttpServletRequest request, HttpServletResponse response, boolean authNeeded) throws IOException, ServletException { if (request.getServletPath().equals(path)) { String url = HANDLER.getForwardPath(path); if (url == null) { LOGGER.log(Level.SEVERE, "Could not find JSP page for path [" + path + "]"); return; } // Generate web security token to prevent CSRF attack HANDLER.generateWebSecurityToken(request, response); // Use Forward request internally case is the same page request.getRequestDispatcher(url).forward(request, response); } else { // Use Redirect response case page had changed (Do not use status 302 once cookies are not set) response.setStatus(HttpServletResponse.SC_TEMPORARY_REDIRECT); response.setHeader("Location", getRedirectPath(path, request, authNeeded)); } } private String getRedirectPath(String path, HttpServletRequest request, boolean authNeeded) { StringBuilder nextUrl = new StringBuilder(); if (authNeeded) { nextUrl.append("?").append(NEXT_URL).append("="); if (!request.getContextPath().equals("/")) { nextUrl.append(request.getContextPath()); } nextUrl.append(request.getServletPath()); if (StringUtils.isNotBlank(request.getPathInfo())) { nextUrl.append(request.getPathInfo()); } if (StringUtils.isNotBlank(request.getQueryString())) { nextUrl.append(encodeUrlQuietly("?" + request.getQueryString())); } } else { String nextPath = request.getParameter(NEXT_URL); if (StringUtils.isNotBlank(nextPath)) { return nextPath; } } return (path.startsWith("/") ? request.getContextPath() : "") + path + nextUrl; } private String encodeUrlQuietly(String url) { try { return URLEncoder.encode(url, ENCODING); } catch (UnsupportedEncodingException e) { return ""; } } private class WebServletAsyncListener implements AsyncListener { private String path; private WebAsyncListener bean; public WebServletAsyncListener(String path, WebAsyncListener bean) { this.path = path; this.bean = bean; } @Override public void onComplete(AsyncEvent event) throws IOException { finalizeAsyncContext(event, Reason.COMPLETE); } @Override public void onTimeout(AsyncEvent event) throws IOException { finalizeAsyncContext(event, Reason.TIMEOUT); } @Override public void onError(AsyncEvent event) throws IOException { finalizeAsyncContext(event, Reason.ERROR); } @Override public void onStartAsync(AsyncEvent event) throws IOException { try { bean = (WebAsyncListener) HANDLER.instantiateAsyncBean(path); bean.asyncContextCreated(event.getAsyncContext()); } catch (Exception ex) { LOGGER.log(Level.SEVERE, "AsyncBean on path [" + path + "] could not be instantiated: " + ex.getMessage()); } } private void finalizeAsyncContext(AsyncEvent event, Reason reason) throws IOException { AsyncContext asyncContext = event.getAsyncContext(); bean.asyncContextDestroyed(asyncContext, reason); HANDLER.finalizeAsyncBean(bean, (HttpServletRequest) asyncContext.getRequest()); } } }