123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323 |
- /*
- * Copyright 2000-2014 Vaadin Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
- package com.vaadin.server.communication;
-
- import java.io.IOException;
- import java.io.OutputStreamWriter;
- import java.io.StringWriter;
- import java.util.List;
- import java.util.logging.Level;
- import java.util.logging.Logger;
-
- import com.vaadin.annotations.PreserveOnRefresh;
- import com.vaadin.server.LegacyApplicationUIProvider;
- import com.vaadin.server.SynchronizedRequestHandler;
- import com.vaadin.server.UIClassSelectionEvent;
- import com.vaadin.server.UICreateEvent;
- import com.vaadin.server.UIProvider;
- import com.vaadin.server.VaadinRequest;
- import com.vaadin.server.VaadinResponse;
- import com.vaadin.server.VaadinService;
- import com.vaadin.server.VaadinSession;
- import com.vaadin.shared.ApplicationConstants;
- import com.vaadin.shared.JsonConstants;
- import com.vaadin.shared.communication.PushMode;
- import com.vaadin.shared.ui.ui.Transport;
- import com.vaadin.shared.ui.ui.UIConstants;
- import com.vaadin.ui.UI;
-
- import elemental.json.Json;
- import elemental.json.JsonException;
- import elemental.json.JsonObject;
- import elemental.json.impl.JsonUtil;
-
- /**
- * Handles an initial request from the client to initialize a {@link UI}.
- *
- * @author Vaadin Ltd
- * @since 7.1
- */
- public abstract class UIInitHandler extends SynchronizedRequestHandler {
-
- public static final String BROWSER_DETAILS_PARAMETER = "v-browserDetails";
-
- protected abstract boolean isInitRequest(VaadinRequest request);
-
- @Override
- protected boolean canHandleRequest(VaadinRequest request) {
- return isInitRequest(request);
- }
-
- @Override
- public boolean synchronizedHandleRequest(VaadinSession session,
- VaadinRequest request, VaadinResponse response) throws IOException {
- StringWriter stringWriter = new StringWriter();
-
- try {
- assert UI.getCurrent() == null;
-
- // Update browser information from the request
- session.getBrowser().updateRequestDetails(request);
-
- UI uI = getBrowserDetailsUI(request, session);
-
- session.getCommunicationManager().repaintAll(uI);
-
- JsonObject params = Json.createObject();
- params.put(UIConstants.UI_ID_PARAMETER, uI.getUIId());
- String initialUIDL = getInitialUidl(request, uI);
- params.put("uidl", initialUIDL);
-
- stringWriter.write(JsonUtil.stringify(params));
- } catch (JsonException e) {
- throw new IOException("Error producing initial UIDL", e);
- } finally {
- stringWriter.close();
- }
-
- return commitJsonResponse(request, response, stringWriter.toString());
- }
-
- /**
- * Commit the JSON response. We can't write immediately to the output stream
- * as we want to write only a critical notification if something goes wrong
- * during the response handling.
- *
- * @param request
- * The request that resulted in this response
- * @param response
- * The response to write to
- * @param json
- * The JSON to write
- * @return true if the JSON was written successfully, false otherwise
- * @throws IOException
- * If there was an exception while writing to the output
- */
- static boolean commitJsonResponse(VaadinRequest request,
- VaadinResponse response, String json) throws IOException {
- // The response was produced without errors so write it to the client
- response.setContentType(JsonConstants.JSON_CONTENT_TYPE);
-
- // Ensure that the browser does not cache UIDL responses.
- // iOS 6 Safari requires this (#9732)
- response.setHeader("Cache-Control", "no-cache");
-
- // NOTE! GateIn requires, for some weird reason, getOutputStream
- // to be used instead of getWriter() (it seems to interpret
- // application/json as a binary content type)
- OutputStreamWriter outputWriter = new OutputStreamWriter(
- response.getOutputStream(), "UTF-8");
- try {
- outputWriter.write(json);
- // NOTE GateIn requires the buffers to be flushed to work
- outputWriter.flush();
- } finally {
- outputWriter.close();
- }
-
- return true;
- }
-
- private UI getBrowserDetailsUI(VaadinRequest request, VaadinSession session) {
- VaadinService vaadinService = request.getService();
-
- List<UIProvider> uiProviders = session.getUIProviders();
-
- UIClassSelectionEvent classSelectionEvent = new UIClassSelectionEvent(
- request);
-
- UIProvider provider = null;
- Class<? extends UI> uiClass = null;
- for (UIProvider p : uiProviders) {
- // Check for existing LegacyWindow
- if (p instanceof LegacyApplicationUIProvider) {
- LegacyApplicationUIProvider legacyProvider = (LegacyApplicationUIProvider) p;
-
- UI existingUi = legacyProvider
- .getExistingUI(classSelectionEvent);
- if (existingUi != null) {
- reinitUI(existingUi, request);
- return existingUi;
- }
- }
-
- uiClass = p.getUIClass(classSelectionEvent);
- if (uiClass != null) {
- provider = p;
- break;
- }
- }
-
- if (provider == null || uiClass == null) {
- return null;
- }
-
- // Check for an existing UI based on embed id
-
- String embedId = getEmbedId(request);
-
- UI retainedUI = session.getUIByEmbedId(embedId);
- if (retainedUI != null) {
- if (vaadinService.preserveUIOnRefresh(provider, new UICreateEvent(
- request, uiClass))) {
- if (uiClass.isInstance(retainedUI)) {
- reinitUI(retainedUI, request);
- return retainedUI;
- } else {
- getLogger().info(
- "Not using the preserved UI " + embedId
- + " because it is of type "
- + retainedUI.getClass() + " but " + uiClass
- + " is expected for the request.");
- }
- }
- /*
- * Previous UI without preserve on refresh will be closed when the
- * new UI gets added to the session.
- */
- }
-
- // No existing UI found - go on by creating and initializing one
-
- Integer uiId = Integer.valueOf(session.getNextUIid());
-
- // Explicit Class.cast to detect if the UIProvider does something
- // unexpected
- UICreateEvent event = new UICreateEvent(request, uiClass, uiId);
- UI ui = uiClass.cast(provider.createInstance(event));
-
- // Initialize some fields for a newly created UI
- if (ui.getSession() != session) {
- // Session already set for LegacyWindow
- ui.setSession(session);
- }
-
- PushMode pushMode = provider.getPushMode(event);
- if (pushMode == null) {
- pushMode = session.getService().getDeploymentConfiguration()
- .getPushMode();
- }
- ui.getPushConfiguration().setPushMode(pushMode);
-
- Transport transport = provider.getPushTransport(event);
- if (transport != null) {
- ui.getPushConfiguration().setTransport(transport);
- }
-
- // Set thread local here so it is available in init
- UI.setCurrent(ui);
-
- ui.doInit(request, uiId.intValue(), embedId);
-
- session.addUI(ui);
-
- // Warn if the window can't be preserved
- if (embedId == null
- && vaadinService.preserveUIOnRefresh(provider, event)) {
- getLogger().warning(
- "There is no embed id available for UI " + uiClass
- + " that should be preserved.");
- }
-
- return ui;
- }
-
- /**
- * Constructs an embed id based on information in the request.
- *
- * @since 7.2
- *
- * @param request
- * the request to get embed information from
- * @return the embed id, or <code>null</code> if id is not available.
- *
- * @see UI#getEmbedId()
- */
- protected String getEmbedId(VaadinRequest request) {
- // Parameters sent by vaadinBootstrap.js
- String windowName = request.getParameter("v-wn");
- String appId = request.getParameter("v-appId");
-
- if (windowName != null && appId != null) {
- return windowName + '.' + appId;
- } else {
- return null;
- }
- }
-
- /**
- * Updates a UI that has already been initialized but is now loaded again,
- * e.g. because of {@link PreserveOnRefresh}.
- *
- * @param ui
- * @param request
- */
- private void reinitUI(UI ui, VaadinRequest request) {
- UI.setCurrent(ui);
- ui.doRefresh(request);
- }
-
- /**
- * Generates the initial UIDL message that can e.g. be included in a html
- * page to avoid a separate round trip just for getting the UIDL.
- *
- * @param request
- * the request that caused the initialization
- * @param uI
- * the UI for which the UIDL should be generated
- * @return a string with the initial UIDL message
- * @throws IOException
- */
- protected String getInitialUidl(VaadinRequest request, UI uI)
- throws IOException {
- StringWriter writer = new StringWriter();
- try {
- writer.write("{");
-
- VaadinSession session = uI.getSession();
- if (session.getConfiguration().isXsrfProtectionEnabled()) {
- writer.write(getSecurityKeyUIDL(session));
- }
- new UidlWriter().write(uI, writer, true, false);
- writer.write("}");
-
- String initialUIDL = writer.toString();
- getLogger().log(Level.FINE, "Initial UIDL:" + initialUIDL);
- return initialUIDL;
- } finally {
- writer.close();
- }
- }
-
- /**
- * Gets the security key (and generates one if needed) as UIDL.
- *
- * @param session
- * the vaadin session to which the security key belongs
- * @return the security key UIDL or "" if the feature is turned off
- */
- private static String getSecurityKeyUIDL(VaadinSession session) {
- String seckey = session.getCsrfToken();
-
- return "\"" + ApplicationConstants.UIDL_SECURITY_TOKEN_ID + "\":\""
- + seckey + "\",";
- }
-
- private static final Logger getLogger() {
- return Logger.getLogger(UIInitHandler.class.getName());
- }
- }
|