summaryrefslogtreecommitdiffstats
path: root/.htaccess
Commit message (Expand)AuthorAgeFilesLines
* .htaccess update making two rules non-capturingMartin2016-06-031-2/+2
* Do not automatically try to enable index.php-less URLs (#24539)Lukas Reschke2016-05-121-17/+0
* Use raw PATH_INFOLukas Reschke2016-03-171-2/+2
* always_populate_raw_post_data has been removed with PHP 7.0Lukas Reschke2016-03-151-1/+0
* Duplicate block for PHP 7Lukas Reschke2016-03-151-0/+12
* Allow jpg files to be statically servedStephan Köninger2016-03-101-1/+1
* Add base rewrite rule only when RewriteBase is definedLukas Reschke2016-03-091-1/+0
* Exclude ocs-provider from rewrite ruleLukas Reschke2016-02-251-0/+1
* Merge pull request #18194 from RealRancor/proxy_fcgiThomas Müller2016-02-051-2/+5
|\
| * Add mod_proxy_fcgi and mod_fastcgi to .htaccessRealRancor2015-11-171-2/+5
* | Do not rewrite updater requestsVictor Dubiniuk2016-01-281-0/+1
* | Add X-Download-Options and X-Permitted-Cross-Domain-PoliciesLukas Reschke2016-01-121-0/+2
* | Remove CSP stuff from .htaccessLukas Reschke2016-01-081-7/+0
* | always check if the csp is emptyJörn Friedrich Dreyer2016-01-081-1/+1
* | Use setifempty to please incompatible httpd versionsLukas Reschke2016-01-081-3/+6
* | Merge pull request #20966 from knox/masterThomas Müller2016-01-071-0/+2
|\ \
| * \ Merge branch 'master' into mastermbi2015-12-301-4/+0
| |\ \
| * | | Do not rewrite letsencrypt .well-known URImbi2015-12-081-0/+1
| * | | Merge branch 'master' into mastermbi2015-12-081-0/+5
| |\ \ \
| * | | | Allow .well-known URI for letsencryptmbi2015-12-051-0/+1
* | | | | Allow ico files to be served staticallyMorris Jobke2016-01-061-1/+1
| |_|/ / |/| | |
* | | | Merge pull request #20878 from owncloud/proper-htaccess-support-in-code-signi...Thomas Müller2015-12-111-1/+0
|\ \ \ \
| * | | | Remove version check out of .htaccessLukas Reschke2015-12-081-1/+0
| | |/ / | |/| |
* / | | Add DirectorySlash to dynamic .htaccess writeLukas Reschke2015-12-081-3/+0
|/ / /
* | | Allow .ico filesLukas Reschke2015-12-071-0/+1
* | | Add CSP header to static resourcesLukas Reschke2015-12-071-0/+4
|/ /
* | fix indentationMorris Jobke2015-12-021-4/+4
* | Append PATH_INFO to ensure that file can be loaded on updateLukas Reschke2015-12-011-3/+2
* | Disable MultiView + DirectorySlashLukas Reschke2015-12-011-1/+5
* | Set "SetEnv" within base `.htaccess` fileLukas Reschke2015-12-011-13/+12
* | Support pretty URLsLukas Reschke2015-12-011-0/+15
* | Update .well-known redirects to the new dav endpointThomas Müller2015-11-181-2/+2
* | Revert "Update .well-known redirects to the new dav endpoint"Thomas Müller2015-11-181-2/+2
* | Update .well-known redirects to the new dav endpointThomas Müller2015-11-181-2/+2
|/
* Remove legacy non-working rewrites in .htaccessRealRancor2015-10-151-2/+0
* Master is now 9.0.0 developmentJoas Schilling2015-10-141-1/+1
* Fix .htaccess: php_value should be integerRealRancor2015-09-291-1/+1
* properly indent .htaccessMorris Jobke2015-08-161-24/+24
* This will be 8.2 in the futureFrank Karlitschek2015-07-011-1/+1
* Merge pull request #15042 from wolfgangkarall/masterLukas Reschke2015-03-301-2/+2
|\
| * use permanent redirect for .well-known/(cal|card)dav, add 'L' flagWolfgang Karall2015-03-191-2/+2
* | Add some generic default headers as well via PHPLukas Reschke2015-03-261-16/+21
|/
* Let users configure security headers in their WebserverLukas Reschke2015-03-021-0/+4
* Fix version revLukas Reschke2015-02-281-1/+1
* This is 8.0.1 nowFrank Karlitschek2015-02-281-1/+1
* Use "off" and "off" instead of true booleansLukas Reschke2015-02-231-1/+1
* Add expected values to default config as wellLukas Reschke2015-02-211-0/+1
* Setting default charset to UTF-8 in .htaccess and .user.iniFernando Rodriguez Sela2015-02-101-0/+1
* Reference module with `.c`Lukas Reschke2015-01-281-2/+2
* Add check for `HTTP_RAW_POST_DATA` setting for >= 5.6Lukas Reschke2015-01-221-0/+1
="w"> throw new RuntimeException( "criticalNotification can only be used in UIDL requests"); } if (caption != null) { caption = "\"" + JsonPaintTarget.escapeJSON(caption) + "\""; } if (details != null) { if (message == null) { message = details; } else { message += "<br/><br/>" + details; } } if (message != null) { message = "\"" + JsonPaintTarget.escapeJSON(message) + "\""; } if (url != null) { url = "\"" + JsonPaintTarget.escapeJSON(url) + "\""; } // Set the response type response.setContentType("application/json; charset=UTF-8"); final ServletOutputStream out = response.getOutputStream(); final PrintWriter outWriter = new PrintWriter(new BufferedWriter( new OutputStreamWriter(out, "UTF-8"))); outWriter.print("for(;;);[{\"changes\":[], \"meta\" : {" + "\"appError\": {" + "\"caption\":" + caption + "," + "\"message\" : " + message + "," + "\"url\" : " + url + "}}, \"resources\": {}, \"locales\":[]}]"); outWriter.flush(); outWriter.close(); out.flush(); } /** * Returns the application instance to be used for the request. If an * existing instance is not found a new one is created or null is returned * to indicate that the application is not available. * * @param request * @param requestType * @return * @throws MalformedURLException * @throws SAXException * @throws IllegalAccessException * @throws InstantiationException * @throws ServletException * @throws SessionExpiredException */ private Application findApplicationInstance(HttpServletRequest request, RequestType requestType) throws MalformedURLException, ServletException, SessionExpiredException { boolean requestCanCreateApplication = requestCanCreateApplication( request, requestType); /* Find an existing application for this request. */ Application application = getExistingApplication(request, requestCanCreateApplication); if (application != null) { /* * There is an existing application. We can use this as long as the * user not specifically requested to close or restart it. */ final boolean restartApplication = (request .getParameter(URL_PARAMETER_RESTART_APPLICATION) != null); final boolean closeApplication = (request .getParameter(URL_PARAMETER_CLOSE_APPLICATION) != null); if (restartApplication) { closeApplication(application, request.getSession(false)); return createApplication(request); } else if (closeApplication) { closeApplication(application, request.getSession(false)); return null; } else { return application; } } // No existing application was found if (requestCanCreateApplication) { /* * If the request is such that it should create a new application if * one as not found, we do that. */ return createApplication(request); } else { /* * The application was not found and a new one should not be * created. Assume the session has expired. */ throw new SessionExpiredException(); } } /** * Check if the request should create an application if an existing * application is not found. * * @param request * @param requestType * @return true if an application should be created, false otherwise */ boolean requestCanCreateApplication(HttpServletRequest request, RequestType requestType) { if (requestType == RequestType.UIDL && isRepaintAll(request)) { /* * UIDL request contains valid repaintAll=1 event, the user probably * wants to initiate a new application through a custom index.html * without using writeAjaxPage. */ return true; } else if (requestType == RequestType.OTHER) { /* * I.e URIs that are not application resources or static (theme) * files. */ return true; } return false; } /** * Gets resource path using different implementations. Required to * supporting different servlet container implementations (application * servers). * * @param servletContext * @param path * the resource path. * @return the resource path. */ protected static String getResourcePath(ServletContext servletContext, String path) { String resultPath = null; resultPath = servletContext.getRealPath(path); if (resultPath != null) { return resultPath; } else { try { final URL url = servletContext.getResource(path); resultPath = url.getFile(); } catch (final Exception e) { // FIXME: Handle exception e.printStackTrace(); } } return resultPath; } /** * Handles the requested URI. An application can add handlers to do special * processing, when a certain URI is requested. The handlers are invoked * before any windows URIs are processed and if a DownloadStream is returned * it is sent to the client. * * @param stream * the download stream. * * @param request * the HTTP request instance. * @param response * the HTTP response to write to. * @throws IOException * * @see com.vaadin.terminal.URIHandler */ @SuppressWarnings("unchecked") private void handleDownload(DownloadStream stream, HttpServletRequest request, HttpServletResponse response) throws IOException { if (stream.getParameter("Location") != null) { response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); response.addHeader("Location", stream.getParameter("Location")); return; } // Download from given stream final InputStream data = stream.getStream(); if (data != null) { // Sets content type response.setContentType(stream.getContentType()); // Sets cache headers final long cacheTime = stream.getCacheTime(); if (cacheTime <= 0) { response.setHeader("Cache-Control", "no-cache"); response.setHeader("Pragma", "no-cache"); response.setDateHeader("Expires", 0); } else { response.setHeader("Cache-Control", "max-age=" + cacheTime / 1000); response.setDateHeader("Expires", System.currentTimeMillis() + cacheTime); response.setHeader("Pragma", "cache"); // Required to apply // caching in some // Tomcats } // Copy download stream parameters directly // to HTTP headers. final Iterator i = stream.getParameterNames(); if (i != null) { while (i.hasNext()) { final String param = (String) i.next(); response.setHeader(param, stream.getParameter(param)); } } // suggest local filename from DownloadStream if Content-Disposition // not explicitly set String contentDispositionValue = stream .getParameter("Content-Disposition"); if (contentDispositionValue == null) { contentDispositionValue = "filename=\"" + stream.getFileName() + "\""; response.setHeader("Content-Disposition", contentDispositionValue); } int bufferSize = stream.getBufferSize(); if (bufferSize <= 0 || bufferSize > MAX_BUFFER_SIZE) { bufferSize = DEFAULT_BUFFER_SIZE; } final byte[] buffer = new byte[bufferSize]; int bytesRead = 0; final OutputStream out = response.getOutputStream(); while ((bytesRead = data.read(buffer)) > 0) { out.write(buffer, 0, bytesRead); out.flush(); } out.close(); data.close(); } } /** * Creates a new application and registers it into WebApplicationContext * (aka session). This is not meant to be overridden. Override * getNewApplication to create the application instance in a custom way. * * @param request * @return * @throws ServletException * @throws MalformedURLException */ private Application createApplication(HttpServletRequest request) throws ServletException, MalformedURLException { Application newApplication = getNewApplication(request); final WebApplicationContext context = WebApplicationContext .getApplicationContext(request.getSession()); context.addApplication(newApplication); return newApplication; } private void handleServiceException(HttpServletRequest request, HttpServletResponse response, Application application, Throwable e) throws IOException, ServletException { // if this was an UIDL request, response UIDL back to client if (getRequestType(request) == RequestType.UIDL) { Application.SystemMessages ci = getSystemMessages(); criticalNotification(request, response, ci .getInternalErrorCaption(), ci.getInternalErrorMessage(), null, ci.getInternalErrorURL()); if (application != null) { application.getErrorHandler() .terminalError(new RequestError(e)); } else { throw new ServletException(e); } } else { // Re-throw other exceptions throw new ServletException(e); } } /** * Returns the theme for given request/window * * @param request * @param window * @return */ private String getThemeForWindow(HttpServletRequest request, Window window) { // Finds theme name String themeName; if (request.getParameter(URL_PARAMETER_THEME) != null) { themeName = request.getParameter(URL_PARAMETER_THEME); } else { themeName = window.getTheme(); } if (themeName == null) { // no explicit theme for window defined if (request.getAttribute(REQUEST_DEFAULT_THEME) != null) { // the default theme is defined in request (by portal) themeName = (String) request .getAttribute(REQUEST_DEFAULT_THEME); } else { // using the default theme defined by Vaadin themeName = getDefaultTheme(); } } return themeName; } /** * Returns the default theme. Must never return null. * * @return */ public static String getDefaultTheme() { return DEFAULT_THEME_NAME; } /** * Calls URI handlers for the request. If an URI handler returns a * DownloadStream the stream is passed to the client for downloading. * * @param applicationManager * @param window * @param request * @param response * @return true if an DownloadStream was sent to the client, false otherwise * @throws IOException */ protected boolean handleURI(CommunicationManager applicationManager, Window window, HttpServletRequest request, HttpServletResponse response) throws IOException { // Handles the URI DownloadStream download = applicationManager.handleURI(window, request, response, this); // A download request if (download != null) { // Client downloads an resource handleDownload(download, request, response); return true; } return false; } void handleServiceSessionExpired(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { if (isOnUnloadRequest(request)) { /* * Request was an unload request (e.g. window close event) and the * client expects no response if it fails. */ return; } try { Application.SystemMessages ci = getSystemMessages(); if (getRequestType(request) != RequestType.UIDL) { // 'plain' http req - e.g. browser reload; // just go ahead redirect the browser response.sendRedirect(ci.getSessionExpiredURL()); } else { /* * Invalidate session (weird to have session if we're saying * that it's expired, and worse: portal integration will fail * since the session is not created by the portal. * * Session must be invalidated before criticalNotification as it * commits the response. */ request.getSession().invalidate(); // send uidl redirect criticalNotification(request, response, ci .getSessionExpiredCaption(), ci .getSessionExpiredMessage(), null, ci .getSessionExpiredURL()); } } catch (SystemMessageException ee) { throw new ServletException(ee); } } private void handleServiceSecurityException(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { if (isOnUnloadRequest(request)) { /* * Request was an unload request (e.g. window close event) and the * client expects no response if it fails. */ return; } try { Application.SystemMessages ci = getSystemMessages(); if (getRequestType(request) != RequestType.UIDL) { // 'plain' http req - e.g. browser reload; // just go ahead redirect the browser response.sendRedirect(ci.getCommunicationErrorURL()); } else { // send uidl redirect criticalNotification(request, response, ci .getCommunicationErrorCaption(), ci .getCommunicationErrorMessage(), INVALID_SECURITY_KEY_MSG, ci.getCommunicationErrorURL()); /* * Invalidate session. Portal integration will fail otherwise * since the session is not created by the portal. */ request.getSession().invalidate(); } } catch (SystemMessageException ee) { throw new ServletException(ee); } log("Invalid security key received from " + request.getRemoteHost()); } /** * Creates a new application for the given request. * * @param request * the HTTP request. * @return A new Application instance. * @throws ServletException */ protected abstract Application getNewApplication(HttpServletRequest request) throws ServletException; /** * Starts the application if it is not already running. * * @param request * @param application * @param webApplicationContext * @throws ServletException * @throws MalformedURLException */ private void startApplication(HttpServletRequest request, Application application, WebApplicationContext webApplicationContext) throws ServletException, MalformedURLException { if (!application.isRunning()) { // Create application final URL applicationUrl = getApplicationUrl(request); // Initial locale comes from the request Locale locale = request.getLocale(); application.setLocale(locale); application.start(applicationUrl, applicationProperties, webApplicationContext); } } /** * Check if this is a request for a static resource and, if it is, serve the * resource to the client. Returns true if a file was served and the request * has been handled, false otherwise. * * @param request * @param response * @return * @throws IOException * @throws ServletException */ private boolean serveStaticResources(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // FIXME What does 10 refer to? String pathInfo = request.getPathInfo(); if (pathInfo == null || pathInfo.length() <= 10) { return false; } if ((request.getContextPath() != null) && (request.getRequestURI().startsWith("/VAADIN/"))) { serveStaticResourcesInVAADIN(request.getRequestURI(), response); return true; } else if (request.getRequestURI().startsWith( request.getContextPath() + "/VAADIN/")) { serveStaticResourcesInVAADIN(request.getRequestURI().substring( request.getContextPath().length()), response); return true; } return false; } /** * Serve resources from VAADIN directory. * * @param request * @param response * @throws IOException * @throws ServletException */ private void serveStaticResourcesInVAADIN(String filename, HttpServletResponse response) throws IOException, ServletException { final ServletContext sc = getServletContext(); InputStream is = sc.getResourceAsStream(filename); if (is == null) { // try if requested file is found from classloader // strip leading "/" otherwise stream from JAR wont work filename = filename.substring(1); is = getClassLoader().getResourceAsStream(filename); if (is == null) { // cannot serve requested file System.err .println("Requested resource [" + filename + "] not found from filesystem or through class loader." + " Add widgetset and/or theme JAR to your classpath or add files to WebContent/VAADIN folder."); response.setStatus(HttpServletResponse.SC_NOT_FOUND); return; } } final String mimetype = sc.getMimeType(filename); if (mimetype != null) { response.setContentType(mimetype); } final OutputStream os = response.getOutputStream(); final byte buffer[] = new byte[DEFAULT_BUFFER_SIZE]; int bytes; while ((bytes = is.read(buffer)) >= 0) { os.write(buffer, 0, bytes); } } enum RequestType { FILE_UPLOAD, UIDL, OTHER, STATIC_FILE, APPLICATION_RESOURCE; } protected RequestType getRequestType(HttpServletRequest request) { if (isFileUploadRequest(request)) { return RequestType.FILE_UPLOAD; } else if (isUIDLRequest(request)) { return RequestType.UIDL; } else if (isStaticResourceRequest(request)) { return RequestType.STATIC_FILE; } else if (isApplicationRequest(request)) { return RequestType.APPLICATION_RESOURCE; } return RequestType.OTHER; } private boolean isApplicationRequest(HttpServletRequest request) { String path = getRequestPathInfo(request); if (path != null && path.startsWith("/APP/")) { return true; } return false; } private boolean isStaticResourceRequest(HttpServletRequest request) { String pathInfo = request.getPathInfo(); if (pathInfo == null || pathInfo.length() <= 10) { return false; } if ((request.getContextPath() != null) && (request.getRequestURI().startsWith("/VAADIN/"))) { return true; } else if (request.getRequestURI().startsWith( request.getContextPath() + "/VAADIN/")) { return true; } return false; } private boolean isUIDLRequest(HttpServletRequest request) { String pathInfo = getRequestPathInfo(request); if (pathInfo == null) { return false; } String compare = AJAX_UIDL_URI; if (pathInfo.startsWith(compare + "/") || pathInfo.endsWith(compare)) { return true; } return false; } private boolean isFileUploadRequest(HttpServletRequest request) { return ServletFileUpload.isMultipartContent(request); } private boolean isOnUnloadRequest(HttpServletRequest request) { return request.getParameter(ApplicationConnection.PARAM_UNLOADBURST) != null; } /** * Get system messages from the current application class * * @return */ protected SystemMessages getSystemMessages() { try { Class<? extends Application> appCls = getApplicationClass(); Method m = appCls.getMethod("getSystemMessages", (Class[]) null); return (Application.SystemMessages) m.invoke(null, (Object[]) null); } catch (ClassNotFoundException e) { // This should never happen throw new SystemMessageException(e); } catch (SecurityException e) { throw new SystemMessageException( "Application.getSystemMessage() should be static public", e); } catch (NoSuchMethodException e) { // This is completely ok and should be silently ignored } catch (IllegalArgumentException e) { // This should never happen throw new SystemMessageException(e); } catch (IllegalAccessException e) { throw new SystemMessageException( "Application.getSystemMessage() should be static public", e); } catch (InvocationTargetException e) { // This should never happen throw new SystemMessageException(e); } return Application.getSystemMessages(); } protected abstract Class<? extends Application> getApplicationClass() throws ClassNotFoundException; /** * Return the URL from where static files, e.g. the widgetset and the theme, * are served. In a standard configuration the VAADIN folder inside the * returned folder is what is used for widgetsets and themes. * * The returned folder is usually the same as the context path and * independent of the application. * * @param request * @return The location of static resources (should contain the VAADIN * directory). Never ends with a slash (/). */ protected String getStaticFilesLocation(HttpServletRequest request) { // request may have an attribute explicitly telling location (portal // case) String staticFileLocation = (String) request .getAttribute(REQUEST_VAADIN_STATIC_FILE_PATH); if (staticFileLocation != null) { // TODO remove trailing slash if any? return staticFileLocation; } return getWebApplicationsStaticFileLocation(request); } /** * The default method to fetch static files location. This method does not * check for request attribute {@value #REQUEST_VAADIN_STATIC_FILE_PATH}. * * @param request * @return */ private String getWebApplicationsStaticFileLocation( HttpServletRequest request) { String staticFileLocation; // if property is defined in configurations, use that staticFileLocation = getApplicationOrSystemProperty( PARAMETER_VAADIN_RESOURCES, null); if (staticFileLocation != null) { return staticFileLocation; } // the last (but most common) option is to generate default location // from request // if context is specified add it to widgetsetUrl String ctxPath = request.getContextPath(); // FIXME: ctxPath.length() == 0 condition is probably unnecessary and // might even be wrong. if (ctxPath.length() == 0 && request.getAttribute("javax.servlet.include.context_path") != null) { // include request (e.g portlet), get context path from // attribute ctxPath = (String) request .getAttribute("javax.servlet.include.context_path"); } // Remove heading and trailing slashes from the context path ctxPath = removeHeadingOrTrailing(ctxPath, "/"); if (ctxPath.equals("")) { return ""; } else { return "/" + ctxPath; } } /** * Remove any heading or trailing "what" from the "string". * * @param string * @param what * @return */ private static String removeHeadingOrTrailing(String string, String what) { while (string.startsWith(what)) { string = string.substring(1); } while (string.endsWith(what)) { string = string.substring(0, string.length() - 1); } return string; } /** * Write a redirect response to the main page of the application. * * @param request * @param response * @throws IOException * if sending the redirect fails due to an input/output error or * a bad application URL */ private void redirectToApplication(HttpServletRequest request, HttpServletResponse response) throws IOException { String applicationUrl = getApplicationUrl(request).toExternalForm(); response.sendRedirect(response.encodeRedirectURL(applicationUrl)); } /** * This method writes the html host page (aka kickstart page) that starts * the actual Vaadin application. * <p> * If one needs to override parts of the host page, it is suggested that one * overrides on of several submethods which are called by this method: * <ul> * <li> {@link #setAjaxPageHeaders(HttpServletResponse)} * <li> {@link #writeAjaxPageHtmlHeadStart(BufferedWriter)} * <li> {@link #writeAjaxPageHtmlHeader(BufferedWriter, String, String)} * <li> {@link #writeAjaxPageHtmlBodyStart(BufferedWriter)} * <li> * {@link #writeAjaxPageHtmlVaadinScripts(Window, String, Application, BufferedWriter, String, String, String, String, String, String)} * <li> * {@link #writeAjaxPageHtmlMainDiv(BufferedWriter, String, String, String)} * <li> {@link #writeAjaxPageHtmlBodyEnd(BufferedWriter)} * </ul> * * @param request * the HTTP request. * @param response * the HTTP response to write to. * @param out * @param unhandledParameters * @param window * @param terminalType * @param theme * @throws IOException * if the writing failed due to input/output error. * @throws MalformedURLException * if the application is denied access the persistent data store * represented by the given URL. */ protected void writeAjaxPage(HttpServletRequest request, HttpServletResponse response, Window window, Application application) throws IOException, MalformedURLException, ServletException { // e.g portlets only want a html fragment boolean fragment = (request.getAttribute(REQUEST_FRAGMENT) != null); if (fragment) { // if this is a fragment request, the actual application is put to // request so ApplicationPortlet can save it for a later use request.setAttribute(Application.class.getName(), application); } final BufferedWriter page = new BufferedWriter(new OutputStreamWriter( response.getOutputStream(), "UTF-8")); String title = ((window.getCaption() == null) ? "Vaadin 6" : window .getCaption()); /* Fetch relative url to application */ // don't use server and port in uri. It may cause problems with some // virtual server configurations which lose the server name String appUrl = getApplicationUrl(request).getPath(); if (appUrl.endsWith("/")) { appUrl = appUrl.substring(0, appUrl.length() - 1); } String themeName = getThemeForWindow(request, window); String themeUri = getThemeUri(themeName, request); if (!fragment) { setAjaxPageHeaders(response); writeAjaxPageHtmlHeadStart(page); writeAjaxPageHtmlHeader(page, title, themeUri); writeAjaxPageHtmlBodyStart(page); } String appId = appUrl; if ("".equals(appUrl)) { appId = "ROOT"; } appId = appId.replaceAll("[^a-zA-Z0-9]", ""); // Add hashCode to the end, so that it is still (sort of) predictable, // but indicates that it should not be used in CSS and such: int hashCode = appId.hashCode(); if (hashCode < 0) { hashCode = -hashCode; } appId = appId + "-" + hashCode; writeAjaxPageHtmlVaadinScripts(window, themeName, application, page, appUrl, themeUri, appId, request); /*- Add classnames; * .v-app * .v-app-loading * .v-app-<simpleName for app class> * .v-theme-<themeName, remove non-alphanum> */ String appClass = "v-app-" + getApplicationCSSClassName(); String themeClass = ""; if (themeName != null) { themeClass = "v-theme-" + themeName.replaceAll("[^a-zA-Z0-9]", ""); } else { themeClass = "v-theme-" + getDefaultTheme().replaceAll("[^a-zA-Z0-9]", ""); } String classNames = "v-app v-app-loading " + themeClass + " " + appClass; String divStyle = null; if (request.getAttribute(REQUEST_APPSTYLE) != null) { divStyle = "style=\"" + request.getAttribute(REQUEST_APPSTYLE) + "\""; } writeAjaxPageHtmlMainDiv(page, appId, classNames, divStyle); if (!fragment) { page.write("</body>\n</html>\n"); } page.close(); } /** * Returns the application class identifier for use in the application CSS * class name in the root DIV. The application CSS class name is of form * "v-app-"+getApplicationCSSClassName(). * * This method should normally not be overridden. * * @return The CSS class name to use in combination with "v-app-". */ protected String getApplicationCSSClassName() { try { return getApplicationClass().getSimpleName(); } catch (ClassNotFoundException e) { e.printStackTrace(); return "unknown"; } } /** * Get the URI for the application theme. * * A portal-wide default theme is fetched from the portal shared resource * directory (if any), other themes from the portlet. * * @param themeName * @param request * @return */ private String getThemeUri(String themeName, HttpServletRequest request) { final String staticFilePath; if (themeName.equals(request.getAttribute(REQUEST_DEFAULT_THEME))) { // our window theme is the portal wide default theme, make it load // from portals directory is defined staticFilePath = getStaticFilesLocation(request); } else { /* * theme is a custom theme, which is not necessarily located in * portals VAADIN directory. Let the default servlet conf decide * (omitting request parameter) the location. Note that theme can * still be placed to portal directory with servlet parameter. */ staticFilePath = getWebApplicationsStaticFileLocation(request); } return staticFilePath + "/" + THEME_DIRECTORY_PATH + themeName; } /** * Method to write the div element into which that actual Vaadin application * is rendered. * <p> * Override this method if you want to add some custom html around around * the div element into which the actual Vaadin application will be * rendered. * * @param page * @param appId * @param classNames * @param divStyle * @throws IOException */ protected void writeAjaxPageHtmlMainDiv(final BufferedWriter page, String appId, String classNames, String divStyle) throws IOException { page.write("<div id=\"" + appId + "\" class=\"" + classNames + "\" " + (divStyle != null ? divStyle : "") + "></div>\n"); page.write("<noscript>" + getNoScriptMessage() + "</noscript>"); } /** * Method to write the script part of the page which loads needed Vaadin * scripts and themes. * <p> * Override this method if you want to add some custom html around scripts. * * @param window * @param themeName * @param application * @param page * @param appUrl * @param themeUri * @param appId * @param request * @throws ServletException * @throws IOException */ protected void writeAjaxPageHtmlVaadinScripts(Window window, String themeName, Application application, final BufferedWriter page, String appUrl, String themeUri, String appId, HttpServletRequest request) throws ServletException, IOException { // request widgetset takes precedence (e.g portlet include) String requestWidgetset = (String) request .getAttribute(REQUEST_WIDGETSET); String sharedWidgetset = (String) request .getAttribute(REQUEST_SHARED_WIDGETSET); if (requestWidgetset == null && sharedWidgetset == null) { // Use the value from configuration or DEFAULT_WIDGETSET. // If no shared widgetset is specified, the default widgetset is // assumed to be in the servlet/portlet itself. requestWidgetset = getApplicationOrSystemProperty( PARAMETER_WIDGETSET, DEFAULT_WIDGETSET); } String widgetset; String widgetsetBasePath; if (requestWidgetset != null) { widgetset = requestWidgetset; widgetsetBasePath = getWebApplicationsStaticFileLocation(request); } else { widgetset = sharedWidgetset; widgetsetBasePath = getStaticFilesLocation(request); } final String widgetsetFilePath = widgetsetBasePath + "/" + WIDGETSET_DIRECTORY_PATH + widgetset + "/" + widgetset + ".nocache.js?" + new Date().getTime(); // Get system messages Application.SystemMessages systemMessages = null; try { systemMessages = getSystemMessages(); } catch (SystemMessageException e) { // failing to get the system messages is always a problem throw new ServletException("CommunicationError!", e); } page.write("<script type=\"text/javascript\">\n"); page.write("//<![CDATA[\n"); page.write("if(!vaadin || !vaadin.vaadinConfigurations) {\n " + "if(!vaadin) { var vaadin = {}} \n" + "vaadin.vaadinConfigurations = {};\n" + "if (!vaadin.themesLoaded) { vaadin.themesLoaded = {}; }\n"); if (!isProductionMode()) { page.write("vaadin.debug = true;\n"); } page .write("document.write('<iframe tabIndex=\"-1\" id=\"__gwt_historyFrame\" " + "style=\"position:absolute;width:0;height:0;border:0;overflow:" + "hidden;\" src=\"javascript:false\"></iframe>');\n"); page.write("document.write(\"<script language='javascript' src='" + widgetsetFilePath + "'><\\/script>\");\n}\n"); page.write("vaadin.vaadinConfigurations[\"" + appId + "\"] = {"); page.write("appUri:'" + appUrl + "', "); String pathInfo = getRequestPathInfo(request); if (pathInfo == null) { pathInfo = "/"; } page.write("pathInfo: '" + pathInfo + "', "); if (window != application.getMainWindow()) { page.write("windowName: '" + window.getName() + "', "); } page.write("themeUri:"); page.write(themeUri != null ? "'" + themeUri + "'" : "null"); page.write(", versionInfo : {vaadinVersion:\""); page.write(VERSION); page.write("\",applicationVersion:\""); page.write(application.getVersion()); page.write("\"},"); if (systemMessages != null) { // Write the CommunicationError -message to client String caption = systemMessages.getCommunicationErrorCaption(); if (caption != null) { caption = "\"" + caption + "\""; } String message = systemMessages.getCommunicationErrorMessage(); if (message != null) { message = "\"" + message + "\""; } String url = systemMessages.getCommunicationErrorURL(); if (url != null) { url = "\"" + url + "\""; } page.write("\"comErrMsg\": {" + "\"caption\":" + caption + "," + "\"message\" : " + message + "," + "\"url\" : " + url + "}"); } page.write("};\n//]]>\n</script>\n"); if (themeName != null) { // Custom theme's stylesheet, load only once, in different // script // tag to be dominate styles injected by widget // set page.write("<script type=\"text/javascript\">\n"); page.write("//<![CDATA[\n"); page.write("if(!vaadin.themesLoaded['" + themeName + "']) {\n"); page.write("var stylesheet = document.createElement('link');\n"); page.write("stylesheet.setAttribute('rel', 'stylesheet');\n"); page.write("stylesheet.setAttribute('type', 'text/css');\n"); page.write("stylesheet.setAttribute('href', '" + themeUri + "/styles.css');\n"); page .write("document.getElementsByTagName('head')[0].appendChild(stylesheet);\n"); page.write("vaadin.themesLoaded['" + themeName + "'] = true;\n}\n"); page.write("//]]>\n</script>\n"); } // Warn if the widgetset has not been loaded after 15 seconds on // inactivity page.write("<script type=\"text/javascript\">\n"); page.write("//<![CDATA[\n"); page .write("setTimeout('if (typeof " + widgetset.replace('.', '_') + " == \"undefined\") {alert(\"Failed to load the widgetset: " + widgetsetFilePath + "\")};',15000);\n" + "//]]>\n</script>\n"); } /** * * Method to open the body tag of the html kickstart page. * <p> * This method is responsible for closing the head tag and opening the body * tag. * <p> * Override this method if you want to add some custom html to the page. * * @param page * @throws IOException */ protected void writeAjaxPageHtmlBodyStart(final BufferedWriter page) throws IOException { page.write("\n</head>\n<body scroll=\"auto\" class=\"" + ApplicationConnection.GENERATED_BODY_CLASSNAME + "\">\n"); } /** * Method to write the contents of head element in html kickstart page. * <p> * Override this method if you want to add some custom html to the header of * the page. * * @param page * @param title * @param themeUri * @throws IOException */ protected void writeAjaxPageHtmlHeader(final BufferedWriter page, String title, String themeUri) throws IOException { page .write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n"); page.write("<style type=\"text/css\">" + "html, body {height:100%;margin:0;}</style>"); // Add favicon links page .write("<link rel=\"shortcut icon\" type=\"image/vnd.microsoft.icon\" href=\"" + themeUri + "/favicon.ico\" />"); page .write("<link rel=\"icon\" type=\"image/vnd.microsoft.icon\" href=\"" + themeUri + "/favicon.ico\" />"); page.write("<title>" + title + "</title>"); } /** * Method to write the beginning of the html page. * <p> * This method is responsible for writing appropriate doc type declarations * and to open html and head tags. * <p> * Override this method if you want to add some custom html to the very * beginning of the page. * * @param page * @throws IOException */ protected void writeAjaxPageHtmlHeadStart(final BufferedWriter page) throws IOException { // write html header page.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD " + "XHTML 1.0 Transitional//EN\" " + "\"http://www.w3.org/TR/xhtml1/" + "DTD/xhtml1-transitional.dtd\">\n"); page.write("<html xmlns=\"http://www.w3.org/1999/xhtml\"" + ">\n<head>\n"); } /** * Method to set http request headers for the Vaadin kickstart page. * <p> * Override this method if you need to customize http headers of the page. * * @param response */ protected void setAjaxPageHeaders(HttpServletResponse response) { // Window renders are not cacheable response.setHeader("Cache-Control", "no-cache"); response.setHeader("Pragma", "no-cache"); response.setDateHeader("Expires", 0); response.setContentType("text/html; charset=UTF-8"); } /** * Returns a message printed for browsers without scripting support or if * browsers scripting support is disabled. */ protected String getNoScriptMessage() { return "You have to enable javascript in your browser to use an application built with Vaadin."; } /** * Gets the current application URL from request. * * @param request * the HTTP request. * @throws MalformedURLException * if the application is denied access to the persistent data * store represented by the given URL. */ URL getApplicationUrl(HttpServletRequest request) throws MalformedURLException { final URL reqURL = new URL( (request.isSecure() ? "https://" : "http://") + request.getServerName() + ((request.isSecure() && request.getServerPort() == 443) || (!request.isSecure() && request .getServerPort() == 80) ? "" : ":" + request.getServerPort()) + request.getRequestURI()); String servletPath = ""; if (request.getAttribute("javax.servlet.include.servlet_path") != null) { // this is an include request servletPath = request.getAttribute( "javax.servlet.include.context_path").toString() + request .getAttribute("javax.servlet.include.servlet_path"); } else { servletPath = request.getContextPath() + request.getServletPath(); } if (servletPath.length() == 0 || servletPath.charAt(servletPath.length() - 1) != '/') { servletPath = servletPath + "/"; } URL u = new URL(reqURL, servletPath); return u; } /** * Gets the existing application for given request. Looks for application * instance for given request based on the requested URL. * * @param request * the HTTP request. * @param allowSessionCreation * true if a session should be created if no session exists, * false if no session should be created * @return Application instance, or null if the URL does not map to valid * application. * @throws MalformedURLException * if the application is denied access to the persistent data * store represented by the given URL. * @throws SAXException * @throws IllegalAccessException * @throws InstantiationException * @throws SessionExpiredException */ private Application getExistingApplication(HttpServletRequest request, boolean allowSessionCreation) throws MalformedURLException, SessionExpiredException { // Ensures that the session is still valid final HttpSession session = request.getSession(allowSessionCreation); if (session == null) { throw new SessionExpiredException(); } WebApplicationContext context = WebApplicationContext .getApplicationContext(session); // Gets application list for the session. final Collection<Application> applications = context.getApplications(); // Search for the application (using the application URI) from the list for (final Iterator<Application> i = applications.iterator(); i .hasNext();) { final Application sessionApplication = i.next(); final String sessionApplicationPath = sessionApplication.getURL() .getPath(); String requestApplicationPath = getApplicationUrl(request) .getPath(); if (requestApplicationPath.equals(sessionApplicationPath)) { // Found a running application if (sessionApplication.isRunning()) { return sessionApplication; } // Application has stopped, so remove it before creating a new // application WebApplicationContext.getApplicationContext(session) .removeApplication(sessionApplication); break; } } // Existing application not found return null; } /** * Ends the application. * * @param request * the HTTP request. * @param response * the HTTP response to write to. * @param application * the application to end. * @throws IOException * if the writing failed due to input/output error. */ private void endApplication(HttpServletRequest request, HttpServletResponse response, Application application) throws IOException { String logoutUrl = application.getLogoutURL(); if (logoutUrl == null) { logoutUrl = application.getURL().toString(); } final HttpSession session = request.getSession(); if (session != null) { WebApplicationContext.getApplicationContext(session) .removeApplication(application); } response.sendRedirect(response.encodeRedirectURL(logoutUrl)); } /** * Gets the existing application or create a new one. Get a window within an * application based on the requested URI. * * @param request * the HTTP Request. * @param application * the Application to query for window. * @return Window matching the given URI or null if not found. * @throws ServletException * if an exception has occurred that interferes with the * servlet's normal operation. */ private Window getApplicationWindow(HttpServletRequest request, CommunicationManager applicationManager, Application application) throws ServletException { // Finds the window where the request is handled Window assumedWindow = null; String path = getRequestPathInfo(request); // Main window as the URI is empty if (!(path == null || path.length() == 0 || path.equals("/"))) { if (path.startsWith("/APP/")) { // Use main window for application resources return application.getMainWindow(); } String windowName = null; if (path.charAt(0) == '/') { path = path.substring(1); } final int index = path.indexOf('/'); if (index < 0) { windowName = path; path = ""; } else { windowName = path.substring(0, index); } assumedWindow = application.getWindow(windowName); } return applicationManager.getApplicationWindow(request, this, application, assumedWindow); } /** * Returns the path info; note that this _can_ be different than * request.getPathInfo() (e.g application runner). * * @param request * @return */ String getRequestPathInfo(HttpServletRequest request) { return request.getPathInfo(); } /** * Gets relative location of a theme resource. * * @param theme * the Theme name. * @param resource * the Theme resource. * @return External URI specifying the resource */ public String getResourceLocation(String theme, ThemeResource resource) { if (resourcePath == null) { return resource.getResourceId(); } return resourcePath + theme + "/" + resource.getResourceId(); } private boolean isRepaintAll(HttpServletRequest request) { return (request.getParameter(URL_PARAMETER_REPAINT_ALL) != null) && (request.getParameter(URL_PARAMETER_REPAINT_ALL).equals("1")); } private void closeApplication(Application application, HttpSession session) { if (application == null) { return; } application.close(); if (session != null) { WebApplicationContext context = WebApplicationContext .getApplicationContext(session); context.removeApplication(application); } } /** * Implementation of ParameterHandler.ErrorEvent interface. */ public class ParameterHandlerErrorImpl implements ParameterHandler.ErrorEvent, Serializable { private ParameterHandler owner; private Throwable throwable; /** * Gets the contained throwable. * * @see com.vaadin.terminal.Terminal.ErrorEvent#getThrowable() */ public Throwable getThrowable() { return throwable; } /** * Gets the source ParameterHandler. * * @see com.vaadin.terminal.ParameterHandler.ErrorEvent#getParameterHandler() */ public ParameterHandler getParameterHandler() { return owner; } } /** * Implementation of URIHandler.ErrorEvent interface. */ public class URIHandlerErrorImpl implements URIHandler.ErrorEvent, Serializable { private final URIHandler owner; private final Throwable throwable; /** * * @param owner * @param throwable */ private URIHandlerErrorImpl(URIHandler owner, Throwable throwable) { this.owner = owner; this.throwable = throwable; } /** * Gets the contained throwable. * * @see com.vaadin.terminal.Terminal.ErrorEvent#getThrowable() */ public Throwable getThrowable() { return throwable; } /** * Gets the source URIHandler. * * @see com.vaadin.terminal.URIHandler.ErrorEvent#getURIHandler() */ public URIHandler getURIHandler() { return owner; } } public class RequestError implements Terminal.ErrorEvent, Serializable { private final Throwable throwable; public RequestError(Throwable throwable) { this.throwable = throwable; } public Throwable getThrowable() { return throwable; } } }