/** * 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> */ package org.olat.core.gui.exception; import java.util.Date; import java.util.List; import org.olat.basesecurity.BaseSecurityModule; import org.olat.core.CoreSpringFactory; import org.olat.core.gui.UserRequest; import org.olat.core.gui.WindowSettings; import org.olat.core.gui.Windows; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.Window; import org.olat.core.gui.components.htmlheader.jscss.CustomCSS; import org.olat.core.gui.components.velocity.VelocityContainer; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.DefaultChiefController; import org.olat.core.gui.control.Event; import org.olat.core.gui.control.WindowBackOffice; import org.olat.core.gui.control.info.WindowControlInfo; import org.olat.core.gui.control.navigation.SiteInstance; import org.olat.core.gui.translator.PackageTranslator; import org.olat.core.gui.translator.Translator; import org.olat.core.helpers.Settings; import org.olat.core.id.Identity; import org.olat.core.id.context.BusinessControlFactory; import org.olat.core.id.context.ContextEntry; import org.olat.core.id.context.HistoryPoint; import org.olat.core.logging.KnownIssueException; import org.olat.core.logging.OLATRuntimeException; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.Formatter; import org.olat.core.util.StringHelper; import org.olat.core.util.UserSession; import org.olat.core.util.Util; import org.olat.core.util.WebappHelper; import org.olat.core.util.i18n.I18nManager; /** * Description: <br> * * @author Felix Jost */ public class ExceptionWindowController extends DefaultChiefController { private static final OLog log = Tracing.createLoggerFor(ExceptionWindowController.class); private static final String VELOCITY_ROOT = Util.getPackageVelocityRoot(ExceptionWindowController.class); private VelocityContainer msg; /** * @param ureq * @param th */ public ExceptionWindowController(UserRequest ureq, Throwable th, boolean allowBackButton) { //TODO remove the below warn again once OLAT-5715 has been resolved - or it turns out that below warn is too verbose // the idea is that at this stage the throwable still contains a stacktrace but passed into the OLATRuntimeException // below as the cause it somehow gets lost. If this does not turn out to be true then the line below can be removed. // in any case, it is just a log.warn log.warn("ExceptionWindowController<init>: Throwable occurred, logging the full stacktrace:", th); // Disable inline translation mode whenever an exception occurs I18nManager i18nMgr = I18nManager.getInstance(); if (i18nMgr.isCurrentThreadMarkLocalizedStringsEnabled()) { // Don't show back button when previously in inline translation mode, will not work allowBackButton = false; i18nMgr.setMarkLocalizedStringsEnabled(ureq.getUserSession(), false); } Translator trans = Util.createPackageTranslator(ExceptionWindowController.class, ureq.getLocale()); Formatter formatter = Formatter.getInstance(ureq.getLocale()); msg = new VelocityContainer("olatmain", VELOCITY_ROOT + "/exception_page.html", trans, this); BaseSecurityModule securityModule = CoreSpringFactory.getImpl(BaseSecurityModule.class); msg.contextPut("enforceTopFrame", new Boolean(securityModule.isForceTopFrame())); // Disallow wrapping of divs around the panel and the main velocity page // (since it contains the "<html><head... intro of the html page, // and thus has better to be replaced as a whole (new page load) instead of // a dom replacement) msg.setDomReplaceable(false); msg.contextPut("buildversion", Settings.getVersion()); OLATRuntimeException o3e; if (th == null){ o3e = new OLATRuntimeException("Error Screen with a Throwable == null", null); } else if (!(th instanceof OLATRuntimeException)) { o3e = new OLATRuntimeException(th.getMessage(), th); } else { o3e = (OLATRuntimeException) th; } String detailedmessage = null; // translate user message if available if (o3e.getUsrMsgKey() != null && o3e.getUsrMsgPackage() != null) { PackageTranslator usrMsgTrans = new PackageTranslator(o3e.getUsrMsgPackage(), ureq.getLocale()); if (o3e.getUsrMsgArgs() == null) { detailedmessage = usrMsgTrans.translate(o3e.getUsrMsgKey()); } else { detailedmessage = usrMsgTrans.translate(o3e.getUsrMsgKey(), o3e.getUsrMsgArgs()); } } // fix detailed message if (detailedmessage == null) { detailedmessage = "-"; } // fetch more info // get the latest window which caused this exception String componentListenerInfo = ""; Windows ws = Windows.getWindows(ureq); Window window = ws.getWindow(ureq); if (window != null) { Component target = window.getAndClearLatestDispatchedComponent(); if (target != null) { // there was a component id given, and a matching target could be found componentListenerInfo = "<dispatchinfo>\n\t<componentinfo>\n\t\t<compname>" + target.getComponentName() + "</compname>\n\t\t<compclass>" + target.getClass().getName() + "</compclass>\n\t\t<extendedinfo>" + target.getExtendedDebugInfo() + "</extendedinfo>\n\t\t<event>"; Event latestEv = target.getAndClearLatestFiredEvent(); if (latestEv != null) { componentListenerInfo += "\n\t\t\t<class>"+latestEv.getClass().getName()+"</class>\n\t\t\t<command>"+latestEv.getCommand()+"</command>\n\t\t\t<tostring>"+latestEv+"</tostring>"; } componentListenerInfo += "\n\t\t</event>\n\t</componentinfo>\n\t<controllerinfo>"; Controller c = target.getLatestDispatchedController(); if (c != null) { // can be null if the error occured in the component itself // componentListenerInfo += c.toString(); //WindowControl control = c.getWindowControl(); // sorry, getting windowcontrol on a controller which does not have one (all should have one, legacy) throws an exception try { WindowControlInfo wci = c.getWindowControlForDebug().getWindowControlInfo(); while (wci != null) { String cName = wci.getControllerClassName(); componentListenerInfo += "\n\t\t<controllername>" + cName + "</controllername>"; wci = wci.getParentWindowControlInfo(); } } catch (Exception e) { componentListenerInfo += "no info, probably no windowcontrol set: "+e.getClass().getName()+", "+e.getMessage(); } } componentListenerInfo += "\n\t</controllerinfo>\n</dispatchinfo>"; } } if(o3e instanceof KnownIssueException){ KnownIssueException kie = (KnownIssueException)o3e; msg.contextPut("knownissuelink", kie.getJiraLink()); } // TODO: DB.getInstance().hasTransaction() TODO: log db transaction id if in // transaction long refNum = Tracing.logError("**RedScreen** "+o3e.getLogMsg() + " ::_::" + componentListenerInfo + " ::_::", o3e, o3e.getThrowingClazz()); // only if debug if (Settings.isDebuging()) { msg.contextPut("debug", Boolean.TRUE); } else { msg.contextPut("debug", Boolean.FALSE); } msg.contextPut("listenerInfoRaw", componentListenerInfo); msg.contextPut("listenerInfo", Formatter.escWithBR(componentListenerInfo).toString()); msg.contextPut("stacktrace", OLATRuntimeException.throwableToHtml(th)); Identity curIdent = ureq.getIdentity(); msg.contextPut("username", curIdent == null? "n/a" : curIdent.getName()); msg.contextPut("allowBackButton", Boolean.valueOf(allowBackButton)); msg.contextPut("detailedmessage", detailedmessage); // Cluster NodeId + E-Nr msg.contextPut("errnum", Settings.getNodeInfo() + "-E"+ refNum); msg.contextPut("supportaddress", WebappHelper.getMailConfig("mailError")); msg.contextPut("time", formatter.formatDateAndTime(new Date())); WindowBackOffice wbo = ws.getWindowManager().createWindowBackOffice("errormessagewindow", this, new WindowSettings()); Window w = wbo.getWindow(); msg.put("jsCssRawHtmlHeader", w.getJsCssRawHtmlHeader()); // the current GUI theme and the global settings that contains the // font-size. both are pushed as objects so that window.dirty always reads // out the correct value msg.contextPut("theme", w.getGuiTheme()); msg.contextPut("globalSettings", ws.getWindowManager().getGlobalSettings()); UserSession session = ureq.getUserSession(); if(session != null && session.getLastHistoryPoint() != null) { HistoryPoint point = session.getLastHistoryPoint(); String businessPath = point.getBusinessPath(); if(StringHelper.containsNonWhitespace(businessPath)) { List<ContextEntry> entries = BusinessControlFactory.getInstance().createCEListFromString(businessPath); String url = BusinessControlFactory.getInstance().getAsURIString(entries, true); msg.contextPut("lastbusinesspath", url); } List<HistoryPoint> stack = session.getHistoryStack(); if(stack != null && stack.size() > 1) { HistoryPoint prevPoint = stack.get(stack.size() - 2); String prevBusinessPath = prevPoint.getBusinessPath(); if(StringHelper.containsNonWhitespace(prevBusinessPath)) { List<ContextEntry> entries = BusinessControlFactory.getInstance().createCEListFromString(prevBusinessPath); String url = BusinessControlFactory.getInstance().getAsURIString(entries, true); msg.contextPut("prevbusinesspath", url); } } } w.setContentPane(msg); setWindow(w); } @Override public String getWindowTitle() { return null; } @Override public boolean isLoginInterceptionInProgress() { return false; } @Override public void addBodyCssClass(String cssClass) { // } @Override public void removeBodyCssClass(String cssClass) { // } @Override public void addCurrentCustomCSSToView(CustomCSS customCSS) { // } @Override public void removeCurrentCustomCSSFromView() { // } @Override public boolean hasStaticSite(Class<? extends SiteInstance> type){ return false; } /** * @see org.olat.core.gui.control.DefaultController#event(org.olat.core.gui.UserRequest, * org.olat.core.gui.components.Component, org.olat.core.gui.control.Event) */ public void event(UserRequest ureq, Component source, Event event) { // } /** * @see org.olat.core.gui.control.DefaultController#doDispose(boolean) */ protected void doDispose() { // nothing to do here } }