/** * 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) since 2004 at Multimedia- & E-Learning Services (MELS),<br> * University of Zurich, Switzerland. * <hr> * <a href="http://www.openolat.org"> * OpenOLAT - Online Learning and Training</a><br> * This file has been modified by the OpenOLAT community. Changes are licensed * under the Apache 2.0 license as the original file. * <p> * Initial code contributed and copyrighted by<br> * JGS goodsolutions GmbH, http://www.goodsolutions.ch * <p> */ package org.olat.core.util; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.UUID; import javax.servlet.ServletContext; import org.olat.core.CoreSpringFactory; import org.olat.core.configuration.Destroyable; import org.olat.core.configuration.Initializable; import org.olat.core.helpers.Settings; import org.olat.core.logging.OLog; import org.olat.core.logging.StartupException; import org.olat.core.logging.Tracing; import org.springframework.beans.factory.BeanInitializationException; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.web.context.ServletContextAware; /** * Description:<br> * * <P> * Initial Date: 04.01.2007 <br> * * @author Felix Jost */ public class WebappHelper implements Initializable, Destroyable, ServletContextAware { private static final OLog log = Tracing.createLoggerFor(WebappHelper.class); private static int nodeId; private static final String bootId = UUID.randomUUID().toString(); private static String fullPathToSrc; private static String fullPathToWebappSrc; private static ServletContext servletContext; private static String contextRoot; private static String instanceId; private static String userDataRoot; private static String defaultCharset; private static boolean enforceUtf8Filesystem; private static Map<String, String> mailConfig = new HashMap<String, String>(6); private static long timeOfServerStartup = System.currentTimeMillis(); private static String mathJaxCdn; private static String mobileContext; /** need to set this at least once before the actual request, since we cannot extract it from the servletContext, * but many methods use it (renderers) which do not have access to userRequest and thus to to getPathInfo...**/ private static String servletContextPath; private String applicationName; private String version; private static String buildJdk; private static String changeSet; private static String changeSetDate; private static String revisionNumber; private static String implementationVersion; /** * * @see Initializable */ public void init() { //servletContext.getRealPath("/"); does not work with an unpacked war file we only use it for fallback for unit testing Resource res = new ClassPathResource(CoreSpringFactory.class.getCanonicalName().replaceAll("\\.", "\\/")+".class"); try { String fullPath = res.getURL().toString(); if (fullPath.contains(File.separator + "WEB-INF")) { fullPath = fullPath.substring(fullPath.indexOf("file:")+5, fullPath.indexOf(File.separator + "WEB-INF")); } else { fullPath = servletContext.getRealPath("/"); } log.info("Sucessfully extracted context root path as: "+fullPath); contextRoot = fullPath; } catch (Exception e) { throw new StartupException("Error getting canonical context root.", e); } servletContextPath = servletContext.getContextPath(); try { InputStream meta = servletContext.getResourceAsStream("/META-INF/MANIFEST.MF"); if(meta != null) { try { Properties props = new Properties(); props.load(meta); changeSet = props.getProperty("Build-Change-Set"); changeSetDate = props.getProperty("Build-Change-Set-Date"); revisionNumber = props.getProperty("Build-Revision-Number"); implementationVersion = props.getProperty("Implementation-Version"); buildJdk = props.getProperty("Build-Jdk"); } catch (IOException e) { log.error("", e); } } } catch (Exception e) { log.warn("MANIFEST.MF not found", e); } File fil = new File(fullPathToSrc); if(fil.exists()){ log.info("Path to source set to: " + fullPathToSrc); } else { log.info("Path to source doesn't exist (only needed for debugging purpose): " + fullPathToSrc); } testUtf8FileSystem(); logStartup(); } /** * [used by spring] * @param nodeId */ public void setNodeId(int nodeId) { WebappHelper.nodeId = nodeId; } /** * * @return */ public static int getNodeId() { return nodeId; } /** * @return Return a unique ID per node and per reboot */ public static String getBootId() { return bootId; } /** * implements service interface */ public void destroy() { log.info(""); log.info("*********************************************"); log.info("* SHUTDOWM "); log.info("*********************************************"); log.info("* Application: " + applicationName); log.info("* StopTimeStamp: " + new Date()); log.info("*********************************************"); log.info(""); } /** * [spring] * @param applicationName */ public void setApplicationName(String applicationName) { this.applicationName = applicationName; } /** * @return the context path like "olat" in localhost:8080/olat */ public static String getServletContextPath() { return servletContextPath; } /** * [used by JUnitTest] * @param contextPath */ public static void setServletContextPath(String contextPath) { servletContextPath = contextPath; } /** * @param fileName * @return */ public static String getMimeType(String fileName) { return servletContext.getMimeType(fileName.toLowerCase()); } /** * This path is depends how you deploy OpenOLAT. When deploying in eclipse you will get an different path * than deploying within tomcat and a different on JBoass AS / Wildfly. Use instead * @see getContextRealPath(java.lang.String path) to retrieve a path * @return the root folder of the webapplication, e.g. /opt/olat3/olat/target/classes (no trailing slash) */ public static String getContextRoot() { return contextRoot; } public static String getContextRealPath(String path) { if(Settings.isJUnitTest()) { //The mocked servlet context of spring doesn't have a real real path return contextRoot + path; } return servletContext.getRealPath(path); } /** * needed only for development; with debug mode enabled. * The returned path does never end with a slash * @return the absolute path to the application webapp source directory, e.g. /opt/olat3/webapp/WEB-INF/src" (no trailing slash) */ public static String getSourcePath() { //String srcPath = getContextRoot() + "/" + relPathToSrc; File fil = new File(fullPathToSrc); if(fil.exists()){ log.debug("Path to source set to: " + fullPathToSrc); } else if (Settings.isDebuging()) { log.error("Path to source wrong, debugging may not work as expected: " + fullPathToSrc, new Exception("getSourcePath")); } else { log.info("Path to source not valid: " + fullPathToSrc); } return fullPathToSrc; } public static String getWebappSourcePath() { File webapp = new File(fullPathToWebappSrc); if(webapp.exists()){ return fullPathToWebappSrc; }else{ return null; } } public static String getBuildOutputFolderRoot() { try { String resource = "/serviceconfig/olat.properties"; Resource res = new ClassPathResource(resource); String protocol = res.getURL().getProtocol(); if("file".equals(protocol)) { String path = res.getFile().getParentFile().getParentFile().getAbsolutePath(); return path; } else { return null; } } catch (IOException e) { log.error("Path to build output is not accessible", e); return null; } } /** * @return the time when the server was started up * used e.g. for stats or a Last-modified of resources which are in jars and do not have a last-modified available */ public static Long getTimeOfServerStartup() { return timeOfServerStartup; } /** * @return The context of the moible application starting with / */ public static String getMobileContext() { return mobileContext; } public void setMobileContext(String mobileContext) { if(!mobileContext.startsWith("/")) { mobileContext = "/" + mobileContext; } WebappHelper.mobileContext = mobileContext; } public static String getMathJaxCdn() { return mathJaxCdn; } public void setMathJaxCdn(String mathJaxCdn) { WebappHelper.mathJaxCdn = mathJaxCdn; } public void setFullPathToSrc(String fullPathToSrc) { File path = new File(fullPathToSrc); if (path.exists()) { WebappHelper.fullPathToSrc = fullPathToSrc; } else { log.debug("Invalid fullPathToSrc configuration, reset to empty path"); WebappHelper.fullPathToSrc = ""; } } public void setFullPathToWebappSrc(String fullPathToWebappSrc) { File path = new File(fullPathToWebappSrc); if (path.exists()) { WebappHelper.fullPathToWebappSrc = fullPathToWebappSrc; } else { log.debug("Invalid fullPathToWebappSrc configuration, reset to empty path"); WebappHelper.fullPathToWebappSrc = ""; } } /** * * @return */ public static String getInstanceId() { return instanceId; } /** * [spring] * @param instanceId */ public void setInstanceId(String instanceId) { if (instanceId == null) throw new StartupException("No instance id set for this installation (see olat.properties). Please fix!"); if (instanceId.length() > 10) throw new StartupException("InstanceID is limited to 10 characters (see olat.properties). Please fix!", null); WebappHelper.instanceId = instanceId; } public static String getUserDataRoot() { return userDataRoot; } public static String getTmpDir() { return System.getProperty("java.io.tmpdir"); } /** * [spring] * @param userDataRoot */ public void setUserDataRoot(String userDataRoot) { if (!StringHelper.containsNonWhitespace(userDataRoot)) { userDataRoot = System.getProperty("java.io.tmpdir") + "/olatdata"; log.info("using java.io.tmpdir as userdata. this is the default if userdata.dir is not set"); } File fUserData = new File(userDataRoot); if (!fUserData.exists()) { if (!fUserData.mkdirs()) throw new StartupException("Unable to create userdata dir '" + userDataRoot + "'. Please fix!"); } //fxdiff: reset tmp-dir from within application to circumvent startup-params. //do not write to system default (/var/tmp) as this leads to permission problems with multiple instances on one host! log.info("Setting userdata root to: "+userDataRoot); WebappHelper.userDataRoot = userDataRoot; } /** * [spring] * @param mailConfig */ public void setMailConfig(Map<String, String> mailConfig) { WebappHelper.mailConfig = mailConfig; String mailHost = WebappHelper.mailConfig.get("mailhost"); if (mailHost == null) log.warn("Attention! mailhost fqdn is not set. The system has restricted functionality, e.g. Self Registration will not work!"); } public static String getDefaultCharset() { if (defaultCharset == null) return "ISO-8859-1"; return defaultCharset; } /** * [spring] * @param defaultCharset */ public void setDefaultCharset(String defaultCharset) { WebappHelper.defaultCharset = defaultCharset; } /** * [spring] * @param enforceUtf8Filesystem */ public void setEnforceUtf8Filesystem(boolean enforceUtf8Filesystem) { WebappHelper.enforceUtf8Filesystem = enforceUtf8Filesystem; } /** * key="mailhost" * key="mailTimeout" * key="smtpUser" * key="smtpPwd" * key="mailSupport" * key="mailReplyTo" - default from email address (reply-to) * key="mailFrom" - real from email address * key="mailFromName" - plain text name for from address * @param string * @return */ public static String getMailConfig(String key) { return WebappHelper.mailConfig.get(key); } public static boolean isMailHostAuthenticationEnabled() { return StringHelper.containsNonWhitespace(getMailConfig("smtpUser")); } /** * Test if filesystem is capable to store UTF-8 characters * Try to read/write a file with UTF-8 chars in the filename in a temporary directory. * */ private void testUtf8FileSystem() { File tmpDir = new File(new File(WebappHelper.getUserDataRoot()), "tmp"); if (!tmpDir.exists()) tmpDir.mkdir(); File writeFile = new File(tmpDir, "UTF-8 test läsÖiç-首页|新"); if (writeFile.exists()) { // remove exising files first writeFile.delete(); } try { writeFile.createNewFile(); } catch (IOException e) { log.warn("No UTF-8 capable filesystem found! Error while writing testfile to filesystem", e); } // try to lookup file: get files from filesystem and search for file we created above File[] tmpFiles = tmpDir.listFiles(); boolean foundUtf8File = false; if(tmpFiles != null){ for (int i = 0; i < tmpFiles.length; i++) { File tmpFile = tmpFiles[i]; if (tmpFile.getName().equals("UTF-8 test läsÖiç-首页|新")) { foundUtf8File = true; break; } } } if (foundUtf8File) { // test ok log.info("UTF-8 capable filesystem detected"); } else { // test failed log.warn("No UTF-8 capable filesystem found! Could not read / write UTF-8 characters from / to filesystem! " + "You probably misconfigured your system, try setting your LANG variable to a correct value."); log.warn("Your current file encoding configuration: java.nio.charset.Charset.defaultCharset().name()::" + java.nio.charset.Charset.defaultCharset().name() + " (the one used) and your system property file.encoding::" + System.getProperty("file.encoding") + " (the one configured)"); } // try to delete file anyway writeFile.delete(); if (!foundUtf8File && WebappHelper.enforceUtf8Filesystem) { throw new BeanInitializationException( "System startup aborted to to file system missconfiguration. See previous warnings in logfile and fix your " + "Java environment. This check can be disabled by setting enforce.utf8.filesystem=false, but be aware that the " + "decision to use a certain encoding on the filesystem is a one-time decision. You can not cange to UTF-8 later!"); } } /** * Either the version of the core or the webapps provided version gets printed */ private void logStartup() { log.info(""); log.info("*********************************************"); log.info("* STARTUP "); log.info("*********************************************"); log.info("* Application: " + applicationName); log.info("* Version: " + version); log.info("* StartTimeStamp: " + new Date()); log.info("*********************************************"); log.info(""); } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public static String getBuildJdk() { return buildJdk; } public static String getChangeSet() { return changeSet; } public static String getChangeSetDate() { return changeSetDate; } public static String getRevisionNumber() { return revisionNumber; } public static String getImplementationVersion() { return implementationVersion; } @Override public void setServletContext(ServletContext servletContext) { WebappHelper.servletContext = servletContext; } }