View Javadoc
1   /**
2    * This file Copyright (c) 2013-2018 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.ui.admincentral;
35  
36  import info.magnolia.cms.util.ServletUtil;
37  import info.magnolia.event.EventBus;
38  import info.magnolia.event.SystemEventBus;
39  import info.magnolia.objectfactory.Components;
40  import info.magnolia.objectfactory.guice.GuiceComponentProvider;
41  import info.magnolia.ui.framework.ioc.MagnoliaUiGuiceComponentProviderFactory;
42  import info.magnolia.ui.framework.ioc.SessionStore;
43  
44  import java.io.BufferedWriter;
45  import java.io.IOException;
46  import java.io.OutputStreamWriter;
47  import java.io.PrintWriter;
48  import java.util.List;
49  
50  import javax.inject.Inject;
51  import javax.servlet.ServletException;
52  import javax.servlet.ServletOutputStream;
53  import javax.servlet.http.HttpServletRequest;
54  import javax.servlet.http.HttpServletResponse;
55  
56  import org.apache.commons.lang3.StringEscapeUtils;
57  import org.slf4j.Logger;
58  import org.slf4j.LoggerFactory;
59  
60  import com.google.inject.name.Names;
61  import com.vaadin.server.BootstrapFragmentResponse;
62  import com.vaadin.server.BootstrapListener;
63  import com.vaadin.server.BootstrapPageResponse;
64  import com.vaadin.server.DeploymentConfiguration;
65  import com.vaadin.server.RequestHandler;
66  import com.vaadin.server.ServiceException;
67  import com.vaadin.server.UIProvider;
68  import com.vaadin.server.VaadinServlet;
69  import com.vaadin.server.VaadinServletRequest;
70  import com.vaadin.server.VaadinServletResponse;
71  import com.vaadin.server.VaadinServletService;
72  import com.vaadin.server.VaadinSession;
73  import com.vaadin.server.communication.ServletBootstrapHandler;
74  import com.vaadin.shared.ApplicationConstants;
75  
76  /**
77   * The AdmincentralVaadinServlet.
78   *
79   * @deprecated since 6.0. Use new admincentral and info.magnolia.admincentral.AdmincentralServlet instead.
80   */
81  @Deprecated
82  public class AdmincentralVaadinServlet extends VaadinServlet {
83  
84      private static final Logger log = LoggerFactory.getLogger(AdmincentralVaadinServlet.class);
85  
86      private static final String ERROR_PAGE_STYLE = "<style>a {color: inherit; text-decoration:none;}" +
87              "html, body {height:100%; margin:0;}" +
88              ".error-message {color:#fff; font-family: Verdana, sans-serif; padding:24px; line-height:1.3; overflow-x:hidden; overflow-y:auto;}" +
89              "h2 {font-size:5em; font-family:DINWebPro, sans-serif; font-weight: normal; margin:0;}" +
90              ".v-button-link {font-size: 2em;} .v-button-link .v-button-caption {text-decoration:none;}" +
91              "#stacktrace {font-family: monospace; display:none; color:#3e5900;}" +
92              ".viewerror {color:#aabf2f;} .v-button-link:hover, .v-button-link:focus {color:#93bac6;}</style>";
93  
94      /**
95       * URL param forcing restart of vaadin application.
96       */
97      public static final String RESTART_APPLICATION_PARAM = "?restartApplication";
98  
99      private UIProvider admincentralUiProvider;
100     private final MagnoliaUiGuiceComponentProviderFactory uiGuiceComponentProviderFactory;
101     private GuiceComponentProvider guiceComponentProvider;
102 
103     @Inject
104     public AdmincentralVaadinServlet(UIProvider admincentralUiProvider, MagnoliaUiGuiceComponentProviderFactory uiGuiceComponentProviderFactory) {
105         this.admincentralUiProvider = admincentralUiProvider;
106         this.uiGuiceComponentProviderFactory = uiGuiceComponentProviderFactory;
107     }
108 
109     @Override
110     protected void servletInitialized() throws ServletException {
111         super.servletInitialized();
112         this.guiceComponentProvider = uiGuiceComponentProviderFactory.create();
113 
114         final EventBus systemEventBus = Components.getComponentWithAnnotation(EventBus.class, Names.named(SystemEventBus.NAME));
115         VaadinServlet.getCurrent().getServletContext().setAttribute("componentProvider", guiceComponentProvider);
116 
117         getService().addSessionInitListener(event -> {
118             final VaadinSession session = event.getSession();
119             session.setAttribute(SessionStore.class, new SessionStore(session, systemEventBus));
120             session.addBootstrapListener(new BootstrapListener() {
121 
122                 @Override
123                 public void modifyBootstrapPage(BootstrapPageResponse response) {
124                     response.getDocument().head().append("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no\" />");
125                 }
126 
127                 @Override
128                 public void modifyBootstrapFragment(BootstrapFragmentResponse response) {}
129             });
130 
131             // Set up and configure UIProvider for the admincentral
132             if (admincentralUiProvider != null) {
133                 session.addUIProvider(admincentralUiProvider);
134             } else {
135                 log.error("Could not inject AdmincentralUIProvider.");
136             }
137         });
138     }
139 
140     @Override
141     protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
142         try {
143             Components.pushProvider(this.guiceComponentProvider);
144             super.service(request, response);
145         } catch (Exception e) {
146             log.error("An internal error has occurred in the VaadinServlet.", e);
147             writeServerErrorPage(request, response, e);
148         } finally {
149             Components.popProvider();
150         }
151     }
152 
153     @Override
154     protected void criticalNotification(VaadinServletRequest request, VaadinServletResponse response, String caption, String message, String details, String url) throws IOException {
155         // invoking critical notifications only for UIDL requests, otherwise we let it fall back to writing the error page.
156         if (isUidlRequest(request)) {
157             super.criticalNotification(request, response, caption, message, details, url);
158         }
159     }
160 
161     private void writeServerErrorPage(HttpServletRequest request, HttpServletResponse response, Exception e) throws IOException {
162         if (!isUidlRequest(request)) {
163             // Create an HTML response with the error
164 
165             // compute restart application URL at previous location
166             String url = request.getRequestURL().toString() + RESTART_APPLICATION_PARAM;
167             String fragment = request.getParameter("v-loc");
168             if (fragment != null && fragment.indexOf("#") != -1) {
169                 fragment = fragment.substring(fragment.indexOf("#"));
170                 // simply escaping the fragment doesn't do it because it gets unescaped down the line
171                 // let's therefore see if it contains anything illegal, and include it only if it doesn't
172                 if (fragment.equals(StringEscapeUtils.escapeHtml4(fragment))) {
173                     url += fragment;
174                 }
175             }
176 
177             final StringBuilder output = new StringBuilder();
178             output.append(ERROR_PAGE_STYLE)
179                     .append("<div class=\"v-magnolia-shell\" style=\"height:100%;\">")
180                     .append("<div id=\"main-launcher\"><a href=\"" + url + "\"><img id=\"logo\" src=\"./../VAADIN/themes/admincentraltheme/img/logo-magnolia.svg\" /></a></div>")
181                     .append("<div class=\"error-message v-shell-viewport-slot\">")
182 
183                     .append("<h2>Whoops!</h2>")
184                     .append("<p>The server has encountered an internal error.</p>")
185 
186                     .append("<div class=\"v-button v-widget link v-button-link\" tabindex=\"0\" role=\"button\">")
187                     .append("<a href=\"" + url + "\">[<span class=\"v-button-caption\">Click here to attempt to recover from this</span>]</a></div>")
188 
189                     .append("<p>We apologize for any inconvenience caused.</p>")
190                     .append("<p>If you keep experiencing difficulties, please contact your system administrator.</p>")
191                     .append("<p>Please check your log files for the complete stack trace.</p>");
192 
193             output.append("</div></div>");
194 
195             // prepend document head if this is the first vaadin request
196             output.insert(0, "<html><head><meta charset=\"UTF-8\"/><meta content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no\" name=\"viewport\"/><title>Magnolia 5</title><link rel=\"stylesheet\" type=\"text/css\" href=\"./../VAADIN/themes/admincentral/styles.css\"/></head><body>");
197             output.append("</body></html>");
198 
199             // make sure vaadin writes this output in the document rather than treat it as a UIDL response
200             response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
201             writeResponse(response, "text/html; charset=UTF-8", output.toString());
202         }
203     }
204 
205     /**
206      * same as {@link com.vaadin.server.ServletPortletHelper#isUIDLRequest}.
207      */
208     private boolean isUidlRequest(HttpServletRequest request) {
209         String prefix = ApplicationConstants.UIDL_PATH + '/';
210         String pathInfo = request.getPathInfo();
211 
212         if (pathInfo == null) {
213             return false;
214         }
215 
216         if (!prefix.startsWith("/")) {
217             prefix = '/' + prefix;
218         }
219 
220         return pathInfo.startsWith(prefix);
221     }
222 
223     /**
224      * same as {@link VaadinServlet#writeResponse}.
225      */
226     private void writeResponse(HttpServletResponse response,
227             String contentType, String output) throws IOException {
228         response.setContentType(contentType);
229         final ServletOutputStream out = response.getOutputStream();
230         // Set the response type
231         final PrintWriter outWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(out, "UTF-8")));
232         outWriter.print(output);
233         outWriter.flush();
234         outWriter.close();
235         out.flush();
236     }
237 
238     @Override
239     protected VaadinServletService createServletService(DeploymentConfiguration deploymentConfiguration) throws ServiceException {
240         VaadinServletService service = new VaadinServletService(this, deploymentConfiguration) {
241 
242             @Override
243             protected List<RequestHandler> createRequestHandlers() throws ServiceException {
244                 List<RequestHandler> handlers = super.createRequestHandlers();
245                 for (int i = 0; i < handlers.size(); i++) {
246                     RequestHandler handler = handlers.get(i);
247                     if (handler instanceof ServletBootstrapHandler) {
248                         handlers.set(i, new ServletBootstrapHandler() {
249 
250                             @Override
251                             protected String getServiceUrl(BootstrapContext context) {
252 
253                                 // We replace the default ServletBootstrapHandler with our own so that we can specify
254                                 // the serviceUrl explicitly. It's otherwise left empty making the client determine it
255                                 // using location.href. The client does not play well with this and appends paths after
256                                 // the JSESSIONID instead of in front of it. This results in a problem loading resources
257                                 // specified using @JavaScript and @StyleSheet.
258                                 //
259                                 // see com.vaadin.client.ApplicationConfiguration.loadFromDOM()
260                                 // see MGNLUI-2291
261                                 // see http://dev.vaadin.com/ticket/10974
262 
263                                 return ServletUtil.getOriginalRequestURI(((VaadinServletRequest)context.getRequest()).getHttpServletRequest());
264                             }
265                         });
266                         break;
267                     }
268                 }
269                 return handlers;
270             }
271         };
272         service.init();
273         return service;
274     }
275 }