Package org.olat.core.dispatcher

Source Code of org.olat.core.dispatcher.DispatcherAction

/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS,
* <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Copyright (c) 1999-2006 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <p>
*/

package org.olat.core.dispatcher;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.olat.core.commons.persistence.DBFactory;
import org.olat.core.defaults.dispatcher.StaticMediaDispatcher;
import org.olat.core.dispatcher.mapper.GlobalMapperRegistry;
import org.olat.core.dispatcher.mapper.MapperRegistry;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.util.UserSession;
import org.olat.core.util.WebappHelper;
import org.olat.testutils.codepoints.server.Codepoint;
/**
* Initial Date: 28.11.2003
*
* @author Felix Jost
*/
public class DispatcherAction implements Dispatcher {
 
  private static OLog log = Tracing.createLoggerFor(DispatcherAction.class);
  // important: add trailing slashes for PATH_XXX
  private static String PATH_DEFAULT;
  /** Identifies requests for the mappingregistry */
  public static final String PATH_MAPPED = "/m/";
  /** Identifies requests for the global mappingregistry */
  public static final String PATH_GLOBAL_MAPPED = "/g/";
  /* default encoding */
  private static final String UTF8_ENCODING = "utf-8";

 
  /**
   * incrementing an int is not atomar which needs to be synchronized
   * AtomicInt provides the same without the need for syncing.
   */
  private static AtomicInteger concurrentCounter = new AtomicInteger(0);

  private GlobalMapperRegistry gmr;
  /**
   * set by spring
   */
  private Map<String, Dispatcher> dispatchers;

  // brasato::remove!!
  // used by core.dispatcher.jumpin.JumpInManager.getJumpInUri
  // ............used by BusinessGroupMainRunController
  // ............used by DropboxScoringViewController
  // used by olat.basesecurity.AuthHelper.doLogin
  // used by olat.repository.RepoJumpInHandlerFactory.builRepositoryDispatchURI
  public static final String PATH_AUTHENTICATED = "/auth/";
  private static final String DOUBLE_SLASH = "//";

  // brasato::remove!!
  // olat.login.DMZController
  // olat.shibboleth13.ShibbolethDispatcher
  public static String getPathDefault(){
    return "/dmz/";
  }

 
  /**
   * The main dispatcher.
   */
  public DispatcherAction() {
    // Initialize global mapper for dynamically generated, globally named mappers
    gmr = GlobalMapperRegistry.getInstance();
  }
 

  /**
   * Main method, called by OLATServlet.
   *
   * @param request
   * @param response
   */
  public void execute(HttpServletRequest request, HttpServletResponse response, String notusedhere) {
    Codepoint.codepoint(DispatcherAction.class, "execute-start");
   
    try {
      concurrentCounter.incrementAndGet();
      String pathInfo = request.getPathInfo();
     
      if (pathInfo == null) {
        // seems to depend on the servlet container if null or "/" is returned
        pathInfo = "/";
      }

      int sl = pathInfo.indexOf('/', 1);
      String sub;
      if (sl > 1) {
        //e.g. something like /dmz/ or /auth/
        sub = pathInfo.substring(0, sl + 1);
      } else {
        //e.g. something like /maintenance.html
        sub = pathInfo;
      }
      /*
       * GLOBAL MAPPED and MAPPED PATHS
       */
      if (pathInfo.startsWith(PATH_MAPPED)) {
        // Session specic file mappers, can't be cached by browser since invalid in next user session.
        // mapped paths (per usersession) -> /m/...
        //TODO:FG: cachable mappers that are user session specific (e.g. course resources)
        MapperRegistry mreg = MapperRegistry.getInstanceFor( UserSession.getUserSession(request) );
         
         // OLAT-5368: an intermediate commit is necessary here to close open transactions which could otherwise
         // run into the 2 min timeout and cause errors. The code does a return after serving the file anyway
         // and would do a commit right there as well - so this doesn't break the transaction semantics.
         DBFactory.getInstance(false).intermediateCommit();
        
        mreg.execute(request, response, subtractContextPath(request, pathInfo));
        return;
      } else if (pathInfo.startsWith(PATH_GLOBAL_MAPPED)) {
        // Dynamic files that can be cached by browsers based on last modified
        // date, but are dynamically created by the application
        gmr.execute(request, response, subtractContextPath(request, pathInfo));
        return;
      } else {
       
        // Dispatch to defaultconfig.xml and extconfig.xml configured paths
        // and also to named mappers -> /n/name...
        Dispatcher d = dispatchers.get(sub);
        if (d != null) {
          d.execute(request, response, request.getContextPath() + sub);// e.g. /olat/ + sub
          return;
        } else if (isBadRelativeLink(request, sub, pathInfo)) {
          sendNotFound(sub, response);
          return;
        } else {
          // check if we have a root dispatcher that takes all
          // remaining requests
          d = dispatchers.get("/");
          if (d != null) {
            // the root context dispatcher takes everything else
            d.execute(request, response, request.getContextPath() + "/");
            return;
          }
        }
      }
     
      // no dispatcher found that matches - redirect to default dispatcher
      redirectToDefaultDispatcher(response);
      return;

    } catch (Throwable e) {
      log.error("Exception in DispatcherAction", e);
      handleError();
      sendBadRequest(request.getPathInfo(), response);
    } finally {
     try{
      concurrentCounter.decrementAndGet();

      DBFactory.getInstance(false).commitAndCloseSession();
     
      // reset user request for tracing - do this after db closing to provide logging to db closing
      Codepoint.codepoint(DispatcherAction.class, "execute-end");
     } catch(Error er) {
        log.error("Uncaught Error in DispatcherAction.execute.finally.", er);
        throw er;
     } catch(RuntimeException re) {
        log.error("Uncaught RuntimeException in DispatcherAction.execute.finally.", re);
        throw re;
     } catch(Throwable th) {
        log.error("Uncaught Throwable in DispatcherAction.execute.finally.", th);
     }
    }

  }

  private boolean isBadRelativeLink(HttpServletRequest request, String sub, String pathInfo) {
    //absolute links in OLAT content like "http://localhost:8080/main.gif" which does not work come round here. Very rare
    //and also bad relative links from the htmlStaticPageComponent which loads relative content over olat business uri like this:
    //GET /olat/auth/1%3A-1%3A0%3A0%3A0/olat/vam_logo.gif HTTP/1.1"
    if (pathInfo.contains("."|| sub.contains(".")) {
      String msg = "BAD RELATIVE LINK IN [["+request.getHeader("referer")+"]]";
      Tracing.logWarn(msg, this.getClass());
      return true;
    }
    return false;
  }


  /**
   * As decoding a url is an expensive operation we try to minimize it here
   * as most mapped pathes contain only ascii chars
   * @param request
   * @param pathInfo
   * @return
   * @throws UnsupportedEncodingException
   */
  private String subtractContextPath(HttpServletRequest request, String pathInfo) throws UnsupportedEncodingException {
    String requestUri = request.getRequestURI();
    //context path set - normal case
    if (WebappHelper.getServletContextPath().length() > 0) {
      String requestUriPart = requestUri.substring(WebappHelper.getServletContextPath().length());
      //remove context path and if length is same no need to decode
      if (requestUriPart.length() == pathInfo.length()) {
        return requestUriPart;
      }
    } else if (requestUri.length() == pathInfo.length()) {
      //no context path set and only ascii chars, no need to decode uri
      return requestUri;
    }
    // Fix messy URL's as it is done in pathInfo by the servlet container.
    if (requestUri.indexOf(DOUBLE_SLASH) != -1) requestUri = requestUri.replaceAll(DOUBLE_SLASH, "/");
    requestUri = URLDecoder.decode(requestUri, UTF8_ENCODING);
    if (WebappHelper.getServletContextPath().length() > 0) {
      return requestUri.substring(WebappHelper.getServletContextPath().length());
    }
    return requestUri;
  }

  /**
   * Redirect to login page.
   *
   * @param response
   */
  public static final void redirectToDefaultDispatcher(HttpServletResponse response) {
    redirectTo(response, WebappHelper.getServletContextPath() + PATH_DEFAULT);
  }

  /**
   * Generic redirect method.
   *
   * @param response
   * @param url
   */
  public static final void redirectTo(HttpServletResponse response, String url) {
    try {
      response.sendRedirect(url);
    } catch (IOException e) {
      log.error("Redirect failed: url=" + url, e);
    }
  }

  /**
   * Sends a HTTP 404 response.
   *
   * @param url
   * @param response
   */
  public static final void sendNotFound(String url, HttpServletResponse response) {
    try {
      response.sendError(HttpServletResponse.SC_NOT_FOUND, url);
    } catch (IOException e) {
      log.error("Send 404 failed: url=" + url, e);
    }
  }

  /**
   * Sends a HTTP 403 response.
   *
   * @param url
   * @param response
   */
  public static final void sendForbidden(String url, HttpServletResponse response) {
    try {
      response.sendError(HttpServletResponse.SC_FORBIDDEN, url);
    } catch (IOException e) {
      log.error("Send 403 failed: url=" + url, e);
    }
  }

  /**
   * Sends a HTTP 400 response.
   *
   * @param url
   * @param response
   */
  public static final void sendBadRequest(String url, HttpServletResponse response) {
    try {
      response.sendError(HttpServletResponse.SC_BAD_REQUEST, (url == null ? "n/a" : url));
    } catch (IOException e) {
      log.error("Send 400 failed: url=" + url, e);
    }
  }

  /**
   * @return the number of requests currently handled
   */
  public static int getConcurrentCounter() {
    return concurrentCounter.intValue();
  }

  public static void handleError() {
    if (log.isDebug()) log.debug("handleError : do rollback");
    DBFactory.getInstance().rollbackAndCloseSession();
  }


  /**
   * @return
   */
  /*public static String getPathDefault() {
    return PATH_DEFAULT;
  }*/
 

  /**
   * [key, value] pairs<br>
   * <ul>
   * <li>key is a String with the dispatcher path, e.g. /dmz/ or /auth/ or /webstat.html</li>
   * <li>value is of type <code>Dispatcher</code></li>
   * </ul>
   *
   * called only by spring
   * @param dispatchers The dispatchers to set.
   */
  public void setDispatchers(Map<String, Dispatcher> dispatchers) {
    this.dispatchers = dispatchers;
  }

  /**
   * used by spring
   * @param defaultDispatcherName The defaultDispatcherName to set.
   */
  public void setDefaultDispatcherName(String defaultDispatcherName) {
    PATH_DEFAULT = defaultDispatcherName;
  }


  public static void redirectToServiceNotAvailable(HttpServletResponse response) {
    String pathStaticDir = StaticMediaDispatcher.getStaticMapperPath();
    redirectTo(response, WebappHelper.getServletContextPath() + pathStaticDir + "msg/en/service_not_available.html");
  }
 
}
TOP

Related Classes of org.olat.core.dispatcher.DispatcherAction

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.