001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.main; 029 030import org.opencms.file.CmsObject; 031import org.opencms.gwt.CmsCoreService; 032import org.opencms.gwt.CmsGwtActionElement; 033import org.opencms.i18n.CmsMessages; 034import org.opencms.security.CmsRoleViolationException; 035import org.opencms.ui.Messages; 036import org.opencms.ui.login.CmsLoginUI; 037import org.opencms.ui.shared.CmsVaadinConstants; 038import org.opencms.util.CmsRequestUtil; 039import org.opencms.workplace.CmsWorkplace; 040import org.opencms.workplace.CmsWorkplaceLoginHandler; 041import org.opencms.workplace.CmsWorkplaceManager; 042 043import java.io.IOException; 044import java.net.URLEncoder; 045import java.util.HashMap; 046import java.util.Locale; 047import java.util.Map; 048 049import javax.servlet.ServletException; 050import javax.servlet.http.HttpServletRequest; 051import javax.servlet.http.HttpServletResponse; 052 053import org.apache.commons.logging.Log; 054 055import org.jsoup.nodes.Document; 056import org.jsoup.nodes.Element; 057import org.jsoup.nodes.Node; 058import org.jsoup.parser.Tag; 059import org.jsoup.select.Elements; 060import org.slf4j.bridge.SLF4JBridgeHandler; 061 062import com.vaadin.server.BootstrapFragmentResponse; 063import com.vaadin.server.BootstrapListener; 064import com.vaadin.server.BootstrapPageResponse; 065import com.vaadin.server.CustomizedSystemMessages; 066import com.vaadin.server.DeploymentConfiguration; 067import com.vaadin.server.RequestHandler; 068import com.vaadin.server.ServiceException; 069import com.vaadin.server.SessionInitEvent; 070import com.vaadin.server.SessionInitListener; 071import com.vaadin.server.SystemMessages; 072import com.vaadin.server.SystemMessagesInfo; 073import com.vaadin.server.SystemMessagesProvider; 074import com.vaadin.server.UIClassSelectionEvent; 075import com.vaadin.server.UIProvider; 076import com.vaadin.server.VaadinRequest; 077import com.vaadin.server.VaadinResponse; 078import com.vaadin.server.VaadinService; 079import com.vaadin.server.VaadinServlet; 080import com.vaadin.server.VaadinServletService; 081import com.vaadin.server.VaadinSession; 082import com.vaadin.ui.UI; 083 084/** 085 * Servlet for workplace UI requests.<p> 086 */ 087public class CmsUIServlet extends VaadinServlet implements SystemMessagesProvider, SessionInitListener { 088 089 /** The bootstrap listener. */ 090 static final BootstrapListener BOOTSTRAP_LISTENER = new BootstrapListener() { 091 092 private static final long serialVersionUID = -6249561809984101044L; 093 094 public void modifyBootstrapFragment(BootstrapFragmentResponse response) { 095 096 // nothing to do 097 } 098 099 public void modifyBootstrapPage(BootstrapPageResponse response) { 100 101 CmsCoreService svc = new CmsCoreService(); 102 HttpServletRequest request = (HttpServletRequest)VaadinService.getCurrentRequest(); 103 svc.setRequest(request); 104 CmsObject cms = ((CmsUIServlet)getCurrent()).getCmsObject(); 105 svc.setCms(cms); 106 107 Document doc = response.getDocument(); 108 Elements appLoadingElements = doc.getElementsByClass("v-app-loading"); 109 if (appLoadingElements.size() > 0) { 110 for (Node node : appLoadingElements.get(0).childNodes()) { 111 node.remove(); 112 113 } 114 115 appLoadingElements.get(0).append(CmsVaadinConstants.LOADING_INDICATOR_HTML); 116 } 117 118 if (shouldShowLogin()) { 119 try { 120 String html = CmsLoginUI.generateLoginHtmlFragment(cms, response.getRequest()); 121 Element el = new Element(Tag.valueOf("div"), "").html(html); 122 doc.body().appendChild(el); 123 } catch (IOException e) { 124 LOG.error(e.getLocalizedMessage(), e); 125 } 126 } 127 Locale currentLocale = OpenCms.getWorkplaceManager().getWorkplaceLocale(cms); 128 // Inject CmsCoreData etc. for GWT dialogs 129 try { 130 doc.head().append(CmsGwtActionElement.exportCommon(cms, svc.prefetch())); 131 doc.head().append(org.opencms.ade.publish.ClientMessages.get().export(currentLocale, true)); 132 doc.head().append(org.opencms.ade.upload.ClientMessages.get().export(currentLocale, true)); 133 doc.head().append(org.opencms.ade.galleries.ClientMessages.get().export(currentLocale, true)); 134 for (String cssURI : OpenCms.getWorkplaceAppManager().getWorkplaceCssUris()) { 135 doc.head().append("<link rel=\"stylesheet\" href=\"" + CmsWorkplace.getResourceUri(cssURI) + "\">"); 136 } 137 } catch (Exception e) { 138 LOG.error(e.getLocalizedMessage(), e); 139 } 140 } 141 }; 142 143 /** The static log object for this class. */ 144 static final Log LOG = CmsLog.getLog(CmsUIServlet.class); 145 146 /** The login UI provider, overrides the default UI to display the login dialog when required. */ 147 static final UIProvider LOGIN_UI_PROVIDER = new UIProvider() { 148 149 private static final long serialVersionUID = 9154828335594149982L; 150 151 @Override 152 public Class<? extends UI> getUIClass(UIClassSelectionEvent event) { 153 154 if (shouldShowLogin() || isLoginUIRequest(event.getRequest())) { 155 return CmsLoginUI.class; 156 } 157 return null; 158 } 159 }; 160 161 /** The login redirect handler. */ 162 static final RequestHandler REQUEST_AUTHORIZATION_HANDLER = new RequestHandler() { 163 164 private static final long serialVersionUID = 1L; 165 166 public boolean handleRequest(VaadinSession session, VaadinRequest request, VaadinResponse response) 167 throws IOException { 168 169 if (shouldShowLogin() && !isLoginUIRequest(request)) { 170 171 String link = OpenCms.getLinkManager().substituteLinkForUnknownTarget( 172 ((CmsUIServlet)getCurrent()).getCmsObject(), 173 CmsWorkplaceLoginHandler.LOGIN_FORM); 174 String requestedUri = ((HttpServletRequest)request).getRequestURI(); 175 if (!requestedUri.endsWith(OpenCms.getSystemInfo().getWorkplaceContext())) { 176 link += "?" 177 + CmsWorkplaceManager.PARAM_LOGIN_REQUESTED_RESOURCE 178 + URLEncoder.encode(requestedUri, "UTF-8"); 179 } 180 OpenCms.getAuthorizationHandler().requestAuthorization( 181 (HttpServletRequest)request, 182 (HttpServletResponse)response, 183 link); 184 return true; 185 } 186 return false; 187 } 188 }; 189 190 /** Serialization id. */ 191 private static final long serialVersionUID = 8119684308154724518L; 192 193 // install the slf4j bridge to pipe vaadin logging to log4j 194 static { 195 SLF4JBridgeHandler.install(); 196 } 197 198 /** The current CMS context. */ 199 private ThreadLocal<CmsObject> m_perThreadCmsObject; 200 201 /** Map of stored system messages objects. */ 202 private Map<Locale, SystemMessages> m_systemMessages = new HashMap<Locale, SystemMessages>(); 203 204 /** 205 * Checks whether the given request was referred from the login page.<p> 206 * 207 * @param request the request 208 * 209 * @return <code>true</code> in case of login ui requests 210 */ 211 static boolean isLoginUIRequest(VaadinRequest request) { 212 213 String referrer = request.getHeader("referer"); 214 return (referrer != null) && referrer.contains(CmsWorkplaceLoginHandler.LOGIN_HANDLER); 215 } 216 217 /** 218 * Returns whether the login dialog should be shown.<p> 219 * 220 * @return <code>true</code> if the login dialog should be shown 221 */ 222 static boolean shouldShowLogin() { 223 224 return ((CmsUIServlet)getCurrent()).getCmsObject().getRequestContext().getCurrentUser().isGuestUser(); 225 } 226 227 /** 228 * Returns the current cms context.<p> 229 * 230 * @return the current cms context 231 */ 232 public CmsObject getCmsObject() { 233 234 return m_perThreadCmsObject.get(); 235 } 236 237 /** 238 * @see com.vaadin.server.SystemMessagesProvider#getSystemMessages(com.vaadin.server.SystemMessagesInfo) 239 */ 240 public SystemMessages getSystemMessages(SystemMessagesInfo systemMessagesInfo) { 241 242 Locale locale = systemMessagesInfo.getLocale(); 243 if (!m_systemMessages.containsKey(locale)) { 244 m_systemMessages.put(locale, createSystemMessages(locale)); 245 } 246 return m_systemMessages.get(locale); 247 } 248 249 /** 250 * @see com.vaadin.server.SessionInitListener#sessionInit(com.vaadin.server.SessionInitEvent) 251 */ 252 public void sessionInit(final SessionInitEvent event) { 253 254 // set the locale to the users workplace locale 255 Locale wpLocale = OpenCms.getWorkplaceManager().getWorkplaceLocale(getCmsObject()); 256 event.getSession().setLocale(wpLocale); 257 event.getSession().addRequestHandler(REQUEST_AUTHORIZATION_HANDLER); 258 event.getSession().addUIProvider(LOGIN_UI_PROVIDER); 259 event.getSession().addBootstrapListener(BOOTSTRAP_LISTENER); 260 } 261 262 /** 263 * Sets the current cms context.<p> 264 * 265 * @param cms the current cms context to set 266 */ 267 public synchronized void setCms(CmsObject cms) { 268 269 if (m_perThreadCmsObject == null) { 270 m_perThreadCmsObject = new ThreadLocal<CmsObject>(); 271 } 272 m_perThreadCmsObject.set(cms); 273 } 274 275 /** 276 * @see com.vaadin.server.VaadinServlet#createServletService(com.vaadin.server.DeploymentConfiguration) 277 */ 278 @Override 279 protected VaadinServletService createServletService(DeploymentConfiguration deploymentConfiguration) 280 throws ServiceException { 281 282 CmsVaadinServletService service = new CmsVaadinServletService(this, deploymentConfiguration); 283 service.init(); 284 return service; 285 } 286 287 /** 288 * @see com.vaadin.server.VaadinServlet#service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) 289 */ 290 @Override 291 protected void service(HttpServletRequest request, HttpServletResponse response) 292 throws ServletException, IOException { 293 294 if (request.getRequestURI().contains("/VAADIN")) { 295 super.service(request, response); 296 return; 297 } 298 // check to OpenCms runlevel 299 int runlevel = OpenCmsCore.getInstance().getRunLevel(); 300 301 // write OpenCms server identification in the response header 302 response.setHeader(CmsRequestUtil.HEADER_SERVER, OpenCmsCore.getInstance().getSystemInfo().getVersion()); 303 304 if (runlevel != OpenCms.RUNLEVEL_4_SERVLET_ACCESS) { 305 // not the "normal" servlet runlevel 306 if (runlevel == OpenCms.RUNLEVEL_3_SHELL_ACCESS) { 307 // we have shell runlevel only, upgrade to servlet runlevel (required after setup wizard) 308 init(getServletConfig()); 309 } else { 310 // illegal runlevel, we can't process requests 311 // sending status code 403, indicating the server understood the request but refused to fulfill it 312 response.sendError(HttpServletResponse.SC_FORBIDDEN); 313 // goodbye 314 return; 315 } 316 } 317 318 // check if the given request matches the workplace site 319 if ((OpenCms.getSiteManager().getSites().size() > 1) && !OpenCms.getSiteManager().isWorkplaceRequest(request)) { 320 321 // do not send any redirects to the workplace site for security reasons 322 response.sendError(HttpServletResponse.SC_NOT_FOUND); 323 return; 324 } 325 326 try { 327 OpenCmsCore.getInstance().initCmsContextForUI(request, response, this); 328 super.service(request, response); 329 OpenCms.getSessionManager().updateSessionInfo(getCmsObject(), request); 330 331 } catch (CmsRoleViolationException rv) { 332 // don't log these into the error channel 333 LOG.debug(rv.getLocalizedMessage(), rv); 334 // error code not set - set "internal server error" (500) 335 int status = HttpServletResponse.SC_INTERNAL_SERVER_ERROR; 336 response.setStatus(status); 337 try { 338 response.sendError(status, rv.toString()); 339 } catch (IOException e) { 340 // can be ignored 341 LOG.error(e.getLocalizedMessage(), e); 342 } 343 } catch (IOException io) { 344 // probably connection aborted by client, no need to write to the ERROR channel 345 LOG.warn(io.getLocalizedMessage(), io); 346 // try so set status and send error in any case 347 int status = HttpServletResponse.SC_INTERNAL_SERVER_ERROR; 348 response.setStatus(status); 349 try { 350 response.sendError(status, io.toString()); 351 } catch (Exception e) { 352 // can be ignored 353 } 354 } catch (Throwable t) { 355 LOG.error(t.getLocalizedMessage(), t); 356 int status = HttpServletResponse.SC_INTERNAL_SERVER_ERROR; 357 response.setStatus(status); 358 try { 359 response.sendError(status, t.toString()); 360 } catch (IOException e) { 361 // can be ignored 362 LOG.error(e.getLocalizedMessage(), e); 363 } 364 } finally { 365 // remove the thread local cms context 366 setCms(null); 367 } 368 } 369 370 /** 371 * @see com.vaadin.server.VaadinServlet#servletInitialized() 372 */ 373 @Override 374 protected void servletInitialized() throws ServletException { 375 376 super.servletInitialized(); 377 getService().setSystemMessagesProvider(this); 378 getService().addSessionInitListener(this); 379 } 380 381 /** 382 * Returns a system messages instance for the given locale.<p> 383 * 384 * @param locale the locale 385 * 386 * @return the system messages 387 */ 388 private SystemMessages createSystemMessages(Locale locale) { 389 390 CmsMessages messages = Messages.get().getBundle(locale); 391 CustomizedSystemMessages systemMessages = new CustomizedSystemMessages(); 392 systemMessages.setCommunicationErrorCaption(messages.key(Messages.GUI_SYSTEM_COMMUNICATION_ERROR_CAPTION_0)); 393 systemMessages.setCommunicationErrorMessage(messages.key(Messages.GUI_SYSTEM_COMMUNICATION_ERROR_MESSAGE_0)); 394 systemMessages.setCommunicationErrorNotificationEnabled(true); 395 systemMessages.setAuthenticationErrorCaption(messages.key(Messages.GUI_SYSTEM_AUTHENTICATION_ERROR_CAPTION_0)); 396 systemMessages.setAuthenticationErrorMessage(messages.key(Messages.GUI_SYSTEM_AUTHENTICATION_ERROR_MESSAGE_0)); 397 systemMessages.setAuthenticationErrorNotificationEnabled(true); 398 systemMessages.setSessionExpiredCaption(messages.key(Messages.GUI_SYSTEM_SESSION_EXPIRED_ERROR_CAPTION_0)); 399 systemMessages.setSessionExpiredMessage(messages.key(Messages.GUI_SYSTEM_SESSION_EXPIRED_ERROR_MESSAGE_0)); 400 systemMessages.setSessionExpiredNotificationEnabled(true); 401 systemMessages.setInternalErrorCaption(messages.key(Messages.GUI_SYSTEM_INTERNAL_ERROR_CAPTION_0)); 402 systemMessages.setInternalErrorMessage(messages.key(Messages.GUI_SYSTEM_INTERNAL_ERROR_MESSAGE_0)); 403 systemMessages.setInternalErrorNotificationEnabled(true); 404 return systemMessages; 405 } 406}