diff options
author | Teemu Suo-Anttila <teemusa@vaadin.com> | 2015-01-12 11:28:55 +0200 |
---|---|---|
committer | Teemu Suo-Anttila <teemusa@vaadin.com> | 2015-01-12 11:28:55 +0200 |
commit | 2286f9871f7f77dea6139d0cd1dfc6754b1946d5 (patch) | |
tree | c8a978d77dff74788b5a290f6c7928e39932b8a5 /server/src | |
parent | 35d91245de3218283c8f4c733a3aa72ea395fb1c (diff) | |
parent | 3f27e02f121c0a39b217532afcf9530bfd2caba7 (diff) | |
download | vaadin-framework-2286f9871f7f77dea6139d0cd1dfc6754b1946d5.tar.gz vaadin-framework-2286f9871f7f77dea6139d0cd1dfc6754b1946d5.zip |
Merge remote-tracking branch 'origin/master' into grid
Change-Id: Ic6b667ea6ceff43a609ce2037f656c6274871fb7
Diffstat (limited to 'server/src')
-rw-r--r-- | server/src/com/vaadin/server/BootstrapHandler.java | 58 | ||||
-rw-r--r-- | server/src/com/vaadin/server/LegacyCommunicationManager.java | 33 | ||||
-rw-r--r-- | server/src/com/vaadin/server/VaadinPortletResponse.java | 8 | ||||
-rw-r--r-- | server/src/com/vaadin/server/VaadinResponse.java | 13 | ||||
-rw-r--r-- | server/src/com/vaadin/server/VaadinServlet.java | 124 | ||||
-rw-r--r-- | server/src/com/vaadin/server/communication/UIInitHandler.java | 2 | ||||
-rw-r--r-- | server/src/com/vaadin/ui/AbsoluteLayout.java | 2 | ||||
-rw-r--r-- | server/src/com/vaadin/ui/Calendar.java | 37 |
8 files changed, 232 insertions, 45 deletions
diff --git a/server/src/com/vaadin/server/BootstrapHandler.java b/server/src/com/vaadin/server/BootstrapHandler.java index bfe195ccf9..c45e2b70e0 100644 --- a/server/src/com/vaadin/server/BootstrapHandler.java +++ b/server/src/com/vaadin/server/BootstrapHandler.java @@ -414,6 +414,9 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler { String vaadinLocation = vaadinService.getStaticFileLocation(request) + "/VAADIN/"; + // Parameter appended to JS to bypass caches after version upgrade. + String versionQueryParam = "?v=" + Version.getFullVersion(); + if (context.getPushMode().isEnabled()) { // Load client-side dependencies for push support String pushJS = vaadinLocation; @@ -424,12 +427,14 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler { pushJS += ApplicationConstants.VAADIN_PUSH_DEBUG_JS; } + pushJS += versionQueryParam; + fragmentNodes.add(new Element(Tag.valueOf("script"), "").attr( "type", "text/javascript").attr("src", pushJS)); } String bootstrapLocation = vaadinLocation - + ApplicationConstants.VAADIN_BOOTSTRAP_JS; + + ApplicationConstants.VAADIN_BOOTSTRAP_JS + versionQueryParam; fragmentNodes.add(new Element(Tag.valueOf("script"), "").attr("type", "text/javascript").attr("src", bootstrapLocation)); Element mainScriptTag = new Element(Tag.valueOf("script"), "").attr( @@ -513,7 +518,6 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler { } appConfig.put("versionInfo", versionInfo); - appConfig.put("widgetset", context.getWidgetsetName()); // Use locale from session if set, else from the request @@ -525,42 +529,32 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler { if (systemMessages != null) { // Write the CommunicationError -message to client JsonObject comErrMsg = Json.createObject(); - comErrMsg.put("caption", + putValueOrNull(comErrMsg, "caption", systemMessages.getCommunicationErrorCaption()); - comErrMsg.put("message", + putValueOrNull(comErrMsg, "message", systemMessages.getCommunicationErrorMessage()); - if (systemMessages.getCommunicationErrorURL() == null) { - comErrMsg.put("url", Json.createNull()); - } else { - comErrMsg.put("url", systemMessages.getCommunicationErrorURL()); - } + putValueOrNull(comErrMsg, "url", + systemMessages.getCommunicationErrorURL()); appConfig.put("comErrMsg", comErrMsg); JsonObject authErrMsg = Json.createObject(); - authErrMsg.put("caption", + putValueOrNull(authErrMsg, "caption", systemMessages.getAuthenticationErrorCaption()); - authErrMsg.put("message", + putValueOrNull(authErrMsg, "message", systemMessages.getAuthenticationErrorMessage()); - if (systemMessages.getAuthenticationErrorURL() == null) { - authErrMsg.put("url", Json.createNull()); - } else { - authErrMsg.put("url", - systemMessages.getAuthenticationErrorURL()); - } + putValueOrNull(authErrMsg, "url", + systemMessages.getAuthenticationErrorURL()); appConfig.put("authErrMsg", authErrMsg); JsonObject sessExpMsg = Json.createObject(); - sessExpMsg - .put("caption", systemMessages.getSessionExpiredCaption()); - sessExpMsg - .put("message", systemMessages.getSessionExpiredMessage()); - if (systemMessages.getSessionExpiredURL() == null) { - sessExpMsg.put("url", Json.createNull()); - } else { - sessExpMsg.put("url", systemMessages.getSessionExpiredURL()); - } + putValueOrNull(sessExpMsg, "caption", + systemMessages.getSessionExpiredCaption()); + putValueOrNull(sessExpMsg, "message", + systemMessages.getSessionExpiredMessage()); + putValueOrNull(sessExpMsg, "url", + systemMessages.getSessionExpiredURL()); appConfig.put("sessExpMsg", sessExpMsg); } @@ -624,7 +618,7 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler { } /** - * Don not override. + * Do not override. * * @param context * @return @@ -648,4 +642,14 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler { response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getLocalizedMessage()); } + + private void putValueOrNull(JsonObject object, String key, String value) { + assert object != null; + assert key != null; + if (value == null) { + object.put(key, Json.createNull()); + } else { + object.put(key, value); + } + } } diff --git a/server/src/com/vaadin/server/LegacyCommunicationManager.java b/server/src/com/vaadin/server/LegacyCommunicationManager.java index 485084b515..fda5ad444f 100644 --- a/server/src/com/vaadin/server/LegacyCommunicationManager.java +++ b/server/src/com/vaadin/server/LegacyCommunicationManager.java @@ -24,6 +24,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; @@ -81,6 +82,8 @@ public class LegacyCommunicationManager implements Serializable { return session; } + private static final ConcurrentHashMap<Class<? extends SharedState>, JsonValue> referenceDiffStates = new ConcurrentHashMap<Class<? extends SharedState>, JsonValue>(); + /** * @deprecated As of 7.1. See #11411. */ @@ -96,17 +99,10 @@ public class LegacyCommunicationManager implements Serializable { if (diffState == null && supportsDiffState) { // Use an empty state object as reference for full // repaints - - try { - SharedState referenceState = stateType.newInstance(); - EncodeResult encodeResult = JsonCodec.encode(referenceState, - null, stateType, uI.getConnectorTracker()); - diffState = encodeResult.getEncodedValue(); - } catch (Exception e) { - getLogger() - .log(Level.WARNING, - "Error creating reference object for state of type {0}", - stateType.getName()); + diffState = referenceDiffStates.get(stateType); + if (diffState == null) { + diffState = createReferenceDiffStateState(stateType); + referenceDiffStates.put(stateType, diffState); } } EncodeResult encodeResult = JsonCodec.encode(state, diffState, @@ -118,6 +114,21 @@ public class LegacyCommunicationManager implements Serializable { return (JsonObject) encodeResult.getDiff(); } + private static JsonValue createReferenceDiffStateState( + Class<? extends SharedState> stateType) { + try { + SharedState referenceState = stateType.newInstance(); + EncodeResult encodeResult = JsonCodec.encode(referenceState, null, + stateType, null); + return encodeResult.getEncodedValue(); + } catch (Exception e) { + getLogger().log(Level.WARNING, + "Error creating reference object for state of type {0}", + stateType.getName()); + return null; + } + } + /** * Resolves a dependency URI, registering the URI with this * {@code LegacyCommunicationManager} if needed and returns a fully diff --git a/server/src/com/vaadin/server/VaadinPortletResponse.java b/server/src/com/vaadin/server/VaadinPortletResponse.java index d9f133ac8a..2b6e0c75fb 100644 --- a/server/src/com/vaadin/server/VaadinPortletResponse.java +++ b/server/src/com/vaadin/server/VaadinPortletResponse.java @@ -97,6 +97,14 @@ public class VaadinPortletResponse implements VaadinResponse { } @Override + public void setContentLength(int len) { + if (response instanceof ResourceResponse) { + ((ResourceResponse) response).setContentLength(len); + } + + } + + @Override public PrintWriter getWriter() throws IOException { if (response instanceof MimeResponse) { return ((MimeResponse) response).getWriter(); diff --git a/server/src/com/vaadin/server/VaadinResponse.java b/server/src/com/vaadin/server/VaadinResponse.java index 1d5fcf141f..c31c6c05d8 100644 --- a/server/src/com/vaadin/server/VaadinResponse.java +++ b/server/src/com/vaadin/server/VaadinResponse.java @@ -169,4 +169,17 @@ public interface VaadinResponse extends Serializable { * @see PortletResponse#addProperty(Cookie) */ public void addCookie(Cookie cookie); + + /** + * Sets the length of the content body in the response In HTTP servlets, + * this method sets the HTTP Content-Length header. For some portlet + * responses, this method sets the content-length header, for others this + * method does nothing. + * + * @param len + * an integer specifying the length of the content being returned + * to the client + * @since 7.3.8 + */ + public void setContentLength(int len); } diff --git a/server/src/com/vaadin/server/VaadinServlet.java b/server/src/com/vaadin/server/VaadinServlet.java index d1242676da..aa76dc8e08 100644 --- a/server/src/com/vaadin/server/VaadinServlet.java +++ b/server/src/com/vaadin/server/VaadinServlet.java @@ -28,6 +28,7 @@ import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Enumeration; @@ -45,14 +46,21 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import com.google.gwt.thirdparty.guava.common.base.Charsets; +import com.google.gwt.thirdparty.guava.common.io.Files; import com.vaadin.annotations.VaadinServletConfiguration; import com.vaadin.annotations.VaadinServletConfiguration.InitParameterName; import com.vaadin.sass.internal.ScssStylesheet; import com.vaadin.server.communication.ServletUIInitHandler; import com.vaadin.shared.JsonConstants; +import com.vaadin.shared.Version; import com.vaadin.ui.UI; import com.vaadin.util.CurrentInstance; +import elemental.json.Json; +import elemental.json.JsonArray; +import elemental.json.JsonObject; + @SuppressWarnings("serial") public class VaadinServlet extends HttpServlet implements Constants { @@ -61,14 +69,47 @@ public class VaadinServlet extends HttpServlet implements Constants { private final String css; private final List<String> sourceUris; private final long timestamp; + private final String scssFileName; - public ScssCacheEntry(String css, List<String> sourceUris) { + public ScssCacheEntry(String scssFileName, String css, + List<String> sourceUris) { + this.scssFileName = scssFileName; this.css = css; this.sourceUris = sourceUris; timestamp = getLastModified(); } + public ScssCacheEntry(JsonObject json) { + css = json.getString("css"); + timestamp = Long.parseLong(json.getString("timestamp")); + + sourceUris = new ArrayList<String>(); + + JsonArray uris = json.getArray("uris"); + for (int i = 0; i < uris.length(); i++) { + sourceUris.add(uris.getString(i)); + } + + // Not set for cache entries read from disk + scssFileName = null; + } + + public String asJson() { + JsonArray uris = Json.createArray(); + for (String uri : sourceUris) { + uris.set(uris.length(), uri); + } + + JsonObject object = Json.createObject(); + object.put("version", Version.getFullVersion()); + object.put("timestamp", Long.toString(timestamp)); + object.put("uris", uris); + object.put("css", css); + + return object.toJson(); + } + public String getCss() { return css; } @@ -117,6 +158,10 @@ public class VaadinServlet extends HttpServlet implements Constants { } } + public String getScssFileName() { + return scssFileName; + } + } private VaadinServletService servletService; @@ -612,7 +657,14 @@ public class VaadinServlet extends HttpServlet implements Constants { * Global cache of scss compilation results. This map is protected from * concurrent access by {@link #SCSS_MUTEX}. */ - private static final Map<String, ScssCacheEntry> scssCache = new HashMap<String, ScssCacheEntry>(); + private final Map<String, ScssCacheEntry> scssCache = new HashMap<String, ScssCacheEntry>(); + + /** + * Keeps track of whether a warning about not being able to persist cache + * files has already been printed. The flag is protected from concurrent + * access by {@link #SCSS_MUTEX}. + */ + private static boolean scssCompileWarWarningEmitted = false; /** * Returns the default theme. Must never return null. @@ -900,10 +952,20 @@ public class VaadinServlet extends HttpServlet implements Constants { synchronized (SCSS_MUTEX) { ScssCacheEntry cacheEntry = scssCache.get(scssFilename); + if (cacheEntry == null) { + try { + cacheEntry = loadPersistedScssCache(scssFilename, sc); + } catch (Exception e) { + getLogger().log(Level.WARNING, + "Could not read persisted scss cache", e); + } + } + if (cacheEntry == null || !cacheEntry.isStillValid()) { cacheEntry = compileScssOnTheFly(filename, scssFilename, sc); - scssCache.put(scssFilename, cacheEntry); + persistCacheEntry(cacheEntry); } + scssCache.put(scssFilename, cacheEntry); if (cacheEntry == null) { // compilation did not produce any result, but logged a message @@ -920,6 +982,29 @@ public class VaadinServlet extends HttpServlet implements Constants { } } + private ScssCacheEntry loadPersistedScssCache(String scssFilename, + ServletContext sc) throws IOException { + String realFilename = sc.getRealPath(scssFilename); + + File scssCacheFile = getScssCacheFile(new File(realFilename)); + if (!scssCacheFile.exists()) { + return null; + } + + String jsonString = Files.toString(scssCacheFile, Charsets.UTF_8); + + JsonObject entryJson = Json.parse(jsonString); + + String cacheVersion = entryJson.getString("version"); + if (!Version.getFullVersion().equals(cacheVersion)) { + // Compiled for some other Vaadin version, discard cache + scssCacheFile.delete(); + return null; + } + + return new ScssCacheEntry(entryJson); + } + private ScssCacheEntry compileScssOnTheFly(String filename, String scssFilename, ServletContext sc) throws IOException { String realFilename = sc.getRealPath(scssFilename); @@ -951,7 +1036,8 @@ public class VaadinServlet extends HttpServlet implements Constants { return null; } - return new ScssCacheEntry(scss.printState(), scss.getSourceUris()); + return new ScssCacheEntry(realFilename, scss.printState(), + scss.getSourceUris()); } /** @@ -1196,6 +1282,36 @@ public class VaadinServlet extends HttpServlet implements Constants { getService().destroy(); } + private static void persistCacheEntry(ScssCacheEntry cacheEntry) { + String scssFileName = cacheEntry.getScssFileName(); + if (scssFileName == null) { + if (!scssCompileWarWarningEmitted) { + getLogger() + .warning( + "Could not persist scss cache because no real file was found for the compiled scss file. " + + "This might happen e.g. if serving the scss file directly from a .war file."); + scssCompileWarWarningEmitted = true; + } + return; + } + + File scssFile = new File(scssFileName); + File cacheFile = getScssCacheFile(scssFile); + + String cacheEntryJsonString = cacheEntry.asJson(); + + try { + Files.write(cacheEntryJsonString, cacheFile, Charsets.UTF_8); + } catch (IOException e) { + getLogger().log(Level.WARNING, + "Error persisting scss cache " + cacheFile, e); + } + } + + private static File getScssCacheFile(File scssFile) { + return new File(scssFile.getParentFile(), scssFile.getName() + ".cache"); + } + /** * Escapes characters to html entities. An exception is made for some * "safe characters" to keep the text somewhat readable. diff --git a/server/src/com/vaadin/server/communication/UIInitHandler.java b/server/src/com/vaadin/server/communication/UIInitHandler.java index 018274330f..3a6dc1e55f 100644 --- a/server/src/com/vaadin/server/communication/UIInitHandler.java +++ b/server/src/com/vaadin/server/communication/UIInitHandler.java @@ -112,7 +112,7 @@ public abstract class UIInitHandler extends SynchronizedRequestHandler { response.setHeader("Cache-Control", "no-cache"); byte[] b = json.getBytes("UTF-8"); - response.setHeader("Content-Length", String.valueOf(b.length)); + response.setContentLength(b.length); OutputStream outputStream = response.getOutputStream(); outputStream.write(b); diff --git a/server/src/com/vaadin/ui/AbsoluteLayout.java b/server/src/com/vaadin/ui/AbsoluteLayout.java index 12aa8ea9a6..6353a4b25d 100644 --- a/server/src/com/vaadin/ui/AbsoluteLayout.java +++ b/server/src/com/vaadin/ui/AbsoluteLayout.java @@ -746,7 +746,7 @@ public class AbsoluteLayout extends AbstractLayout implements /** * Private method for writing position attributes * - * @since + * @since 7.4 * @param node * target node * @param key diff --git a/server/src/com/vaadin/ui/Calendar.java b/server/src/com/vaadin/ui/Calendar.java index 5b5c390fa1..206cc01d1a 100644 --- a/server/src/com/vaadin/ui/Calendar.java +++ b/server/src/com/vaadin/ui/Calendar.java @@ -297,6 +297,11 @@ public class Calendar extends AbstractComponent implements } @Override + protected CalendarState getState(boolean markAsDirty) { + return (CalendarState) super.getState(markAsDirty); + } + + @Override public void beforeClientResponse(boolean initial) { super.beforeClientResponse(initial); @@ -1667,7 +1672,7 @@ public class Calendar extends AbstractComponent implements * weekly mode */ public boolean isMonthlyMode() { - CalendarState state = (CalendarState) getState(false); + CalendarState state = getState(false); if (state.days != null) { return state.days.size() > 7; } else { @@ -1895,4 +1900,34 @@ public class Calendar extends AbstractComponent implements dropHandler.getAcceptCriterion().paint(target); } } + + /** + * Sets whether the event captions are rendered as HTML. + * <p> + * If set to true, the captions are rendered in the browser as HTML and the + * developer is responsible for ensuring no harmful HTML is used. If set to + * false, the caption is rendered in the browser as plain text. + * <p> + * The default is false, i.e. to render that caption as plain text. + * + * @param captionAsHtml + * true if the captions are rendered as HTML, false if rendered + * as plain text + */ + public void setEventCaptionAsHtml(boolean eventCaptionAsHtml) { + getState().eventCaptionAsHtml = eventCaptionAsHtml; + } + + /** + * Checks whether event captions are rendered as HTML + * <p> + * The default is false, i.e. to render that caption as plain text. + * + * @return true if the captions are rendered as HTML, false if rendered as + * plain text + */ + public boolean isEventCaptionAsHtml() { + return getState(false).eventCaptionAsHtml; + } + } |