summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarkus Koivisto <markus@vaadin.com>2016-04-26 11:35:02 +0300
committerVaadin Code Review <review@vaadin.com>2016-04-29 10:28:13 +0000
commit7787eee10da332201e14ca69c52b5404c3b7f7f8 (patch)
tree30cdee54f4b65cd4f415fc69c62d4b01e6f833f0
parente5addd7eb2cddaaea7d5c74e6e435ca4f854e915 (diff)
downloadvaadin-framework-7787eee10da332201e14ca69c52b5404c3b7f7f8.tar.gz
vaadin-framework-7787eee10da332201e14ca69c52b5404c3b7f7f8.zip
Add interface for AppWidgetset (#19770)
This allows selecting the widgetset name and URL simply by placing a class in the default package if nothing is explicitly give by the user. This can be used e.g. by the Maven plug-in and other build automation tools to automatically generate widgetsets or use one from CDN. Change-Id: If3a7b35e3b25371e08e6d9b504fcda6f66de5119
-rw-r--r--all/src/main/templates/release-notes.html1
-rw-r--r--server/src/main/java/com/vaadin/server/BootstrapHandler.java58
-rw-r--r--server/src/main/java/com/vaadin/server/UIProvider.java110
-rw-r--r--server/src/main/java/com/vaadin/server/WidgetsetInfo.java57
-rw-r--r--server/src/main/java/com/vaadin/server/WidgetsetInfoImpl.java65
-rw-r--r--server/src/main/resources/VAADIN/vaadinBootstrap.js16
6 files changed, 264 insertions, 43 deletions
diff --git a/all/src/main/templates/release-notes.html b/all/src/main/templates/release-notes.html
index b9dcc9774b..6c522ef4d6 100644
--- a/all/src/main/templates/release-notes.html
+++ b/all/src/main/templates/release-notes.html
@@ -112,6 +112,7 @@
<ul>
<li>Vaadin artifacts no longer bring a transitive dependency to javax.servlet:servlet-api.</li>
<li>System properties now override application parameters for settings such as production mode (see above).</li>
+ <li>The return type of UIProvider.getWidgetset() and BootstrapHandler.getWidgetsetForUI() has changed.</li>
</ul>
<h3 id="knownissues">Known Issues and Limitations</h3>
<ul>
diff --git a/server/src/main/java/com/vaadin/server/BootstrapHandler.java b/server/src/main/java/com/vaadin/server/BootstrapHandler.java
index a9343a7e03..b766dfc92c 100644
--- a/server/src/main/java/com/vaadin/server/BootstrapHandler.java
+++ b/server/src/main/java/com/vaadin/server/BootstrapHandler.java
@@ -55,10 +55,10 @@ import elemental.json.JsonObject;
import elemental.json.impl.JsonUtil;
/**
- *
+ *
* @author Vaadin Ltd
* @since 7.0.0
- *
+ *
* @deprecated As of 7.0. Will likely change or be removed in a future version
*/
@Deprecated
@@ -76,12 +76,12 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler {
private final VaadinResponse response;
private final BootstrapFragmentResponse bootstrapResponse;
- private String widgetsetName;
private String themeName;
private String appId;
private PushMode pushMode;
private JsonObject applicationParameters;
private VaadinUriResolver uriResolver;
+ private WidgetsetInfo widgetsetInfo;
public BootstrapContext(VaadinResponse response,
BootstrapFragmentResponse bootstrapResponse) {
@@ -105,11 +105,20 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler {
return bootstrapResponse.getUiClass();
}
- public String getWidgetsetName() {
- if (widgetsetName == null) {
- widgetsetName = getWidgetsetForUI(this);
+ public WidgetsetInfo getWidgetsetInfo() {
+ if (widgetsetInfo == null) {
+ widgetsetInfo = getWidgetsetForUI(this);
}
- return widgetsetName;
+ return widgetsetInfo;
+ }
+
+ /**
+ * @return returns the name of the widgetset to use
+ * @deprecated use {@link #getWidgetsetInfo()} instead
+ */
+ @Deprecated
+ public String getWidgetsetName() {
+ return getWidgetsetInfo().getWidgetsetName();
}
public String getThemeName() {
@@ -455,18 +464,19 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler {
return null;
}
- public String getWidgetsetForUI(BootstrapContext context) {
+ public WidgetsetInfo getWidgetsetForUI(BootstrapContext context) {
VaadinRequest request = context.getRequest();
UICreateEvent event = new UICreateEvent(context.getRequest(),
context.getUIClass());
- String widgetset = context.getBootstrapResponse().getUIProvider()
- .getWidgetset(event);
+ WidgetsetInfo widgetset = context.getBootstrapResponse()
+ .getUIProvider().getWidgetset(event);
if (widgetset == null) {
- widgetset = request.getService().getConfiguredWidgetset(request);
+ // TODO do we want to move WidgetsetInfoImpl elsewhere?
+ widgetset = new WidgetsetInfoImpl(request.getService()
+ .getConfiguredWidgetset(request));
}
- widgetset = VaadinServlet.stripSpecialChars(widgetset);
return widgetset;
}
@@ -477,9 +487,9 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler {
* Override this method if you want to add some custom html around around
* the div element into which the actual Vaadin application will be
* rendered.
- *
+ *
* @param context
- *
+ *
* @throws IOException
*/
private void setupMainDiv(BootstrapContext context) throws IOException {
@@ -618,7 +628,15 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler {
}
appConfig.put("versionInfo", versionInfo);
- appConfig.put("widgetset", context.getWidgetsetName());
+
+ WidgetsetInfo widgetsetInfo = context.getWidgetsetInfo();
+ appConfig.put("widgetset", VaadinServlet
+ .stripSpecialChars(widgetsetInfo.getWidgetsetName()));
+ // add widgetset url if not null
+ if (widgetsetInfo.getWidgetsetUrl() != null) {
+ appConfig.put("widgetsetUrl", widgetsetInfo.getWidgetsetUrl());
+ }
+ appConfig.put("widgetsetReady", !widgetsetInfo.isCdn());
// Use locale from session if set, else from the request
Locale locale = ServletPortletHelper.findLocale(null,
@@ -694,13 +712,13 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler {
/**
* Get the URI for the application theme.
- *
+ *
* A portal-wide default theme is fetched from the portal shared resource
* directory (if any), other themes from the portlet.
- *
+ *
* @param context
* @param themeName
- *
+ *
* @return
*/
public String getThemeUri(BootstrapContext context, String themeName) {
@@ -713,7 +731,7 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler {
/**
* Override if required
- *
+ *
* @param context
* @return
*/
@@ -725,7 +743,7 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler {
/**
* Do not override.
- *
+ *
* @param context
* @return
*/
diff --git a/server/src/main/java/com/vaadin/server/UIProvider.java b/server/src/main/java/com/vaadin/server/UIProvider.java
index 7fd880919e..9b0087185b 100644
--- a/server/src/main/java/com/vaadin/server/UIProvider.java
+++ b/server/src/main/java/com/vaadin/server/UIProvider.java
@@ -1,12 +1,12 @@
/*
* 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
@@ -20,6 +20,8 @@ import java.io.InputStream;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.annotation.Inherited;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import com.vaadin.annotations.PreserveOnRefresh;
import com.vaadin.annotations.Push;
@@ -58,7 +60,7 @@ public abstract class UIProvider implements Serializable {
* do not follow the standard semantics and are supported for backwards
* compatibility. Future versions of the framework might only support the
* standard semantics of {@code @Inherited}.
- *
+ *
* @param clazz
* the class from which the annotation should be found
* @param annotationType
@@ -104,13 +106,13 @@ public abstract class UIProvider implements Serializable {
* <p>
* The default implementation checks for a @{@link Theme} annotation on the
* UI class.
- *
+ *
* @param event
* the UI create event with information about the UI and the
* current request.
* @return the name of the theme, or <code>null</code> if the default theme
* should be used
- *
+ *
*/
public String getTheme(UICreateEvent event) {
Theme uiTheme = getAnnotationFor(event.getUIClass(), Theme.class);
@@ -125,26 +127,90 @@ public abstract class UIProvider implements Serializable {
* Finds the widgetset to use for a specific UI. If no specific widgetset is
* required, <code>null</code> is returned.
* <p>
- * The default implementation uses the @{@link Widgetset} annotation if it's
- * defined for the UI class.
- *
+ * The default implementation uses the following order of priority for
+ * finding the widgetset information:
+ * <ul>
+ * <li>@{@link Widgetset} annotation if it is defined for the UI class</li>
+ * <li>The class AppWidgetset if one exists in the default package</li>
+ * <li>A widgetset called AppWidgetset if there is an AppWidgetset.gwt.xml
+ * file in the default package</li>
+ * <li>null to use the default widgetset otherwise</li>
+ * </ul>
+ *
+ * Note that the return type of this method changed in Vaadin 7.7.
+ *
* @param event
* the UI create event with information about the UI and the
* current request.
* @return the name of the widgetset, or <code>null</code> if the default
* widgetset should be used
- *
+ *
+ * @since 7.7
*/
- public String getWidgetset(UICreateEvent event) {
+ public WidgetsetInfo getWidgetset(UICreateEvent event) {
Widgetset uiWidgetset = getAnnotationFor(event.getUIClass(),
Widgetset.class);
+
+ // First case: We have an @Widgetset annotation, use that
if (uiWidgetset != null) {
- return uiWidgetset.value();
- } else {
+ return new WidgetsetInfoImpl(uiWidgetset.value());
+ }
+
+ // Find the class AppWidgetset in the default package if one exists
+ WidgetsetInfo info = getWidgetsetClassInfo();
+
+ // Second case: we have a generated class called APP_WIDGETSET_NAME
+ if (info != null) {
+ return info;
+ }
+
+ // third case: we have an AppWidgetset.gwt.xml file
+ else {
InputStream resource = event.getUIClass().getResourceAsStream(
"/" + APP_WIDGETSET_NAME + ".gwt.xml");
if (resource != null) {
- return APP_WIDGETSET_NAME;
+ return new WidgetsetInfoImpl(false, null, APP_WIDGETSET_NAME);
+ }
+ }
+
+ // fourth case: we are using the default widgetset
+ return null;
+ }
+
+ private Class<WidgetsetInfo> findWidgetsetClass() {
+ try {
+ // We cannot naively use Class.forname without getting the correct
+ // classloader
+ // FIXME This might still fail with osgi
+ ClassLoader tccl = VaadinService.getCurrent().getClassLoader();
+ Class<?> c = Class.forName(APP_WIDGETSET_NAME, true, tccl);
+
+ // if not implementing the interface, possibly a @WebListener class
+ // from an earlier version - ignore it
+ if (WidgetsetInfo.class.isAssignableFrom(c)) {
+ return (Class<WidgetsetInfo>) c;
+ }
+ } catch (ClassNotFoundException e) {
+ // ClassNotFound is a normal case
+ }
+ return null;
+ }
+
+ private WidgetsetInfo getWidgetsetClassInfo() {
+ Class<WidgetsetInfo> cls = findWidgetsetClass();
+ if (cls != null) {
+ try {
+ return cls.newInstance();
+ } catch (InstantiationException e) {
+ getLogger().log(
+ Level.INFO,
+ "Unexpected trying to instantiate class "
+ + cls.getName(), e);
+ } catch (IllegalAccessException e) {
+ getLogger()
+ .log(Level.INFO,
+ "Unexpected trying to access class "
+ + cls.getName(), e);
}
}
return null;
@@ -159,12 +225,12 @@ public abstract class UIProvider implements Serializable {
* Whenever a preserved UI is reused, its
* {@link UI#refresh(com.vaadin.server.VaadinRequest) refresh} method is
* invoked by the framework first.
- *
- *
+ *
+ *
* @param event
* the UI create event with information about the UI and the
* current request.
- *
+ *
* @return <code>true</code>if the same UI instance should be reused e.g.
* when the browser window is refreshed.
*/
@@ -190,13 +256,13 @@ public abstract class UIProvider implements Serializable {
* <p>
* The default implementation uses the @{@link Push} annotation if it's
* defined for the UI class.
- *
+ *
* @param event
* the UI create event with information about the UI and the
* current request.
* @return the push mode to use, or <code>null</code> if the default push
* mode should be used
- *
+ *
*/
public PushMode getPushMode(UICreateEvent event) {
Push push = getAnnotationFor(event.getUIClass(), Push.class);
@@ -213,7 +279,7 @@ public abstract class UIProvider implements Serializable {
* <p>
* The default implementation uses the @{@link Push} annotation if it's
* defined for the UI class.
- *
+ *
* @param event
* the UI create event with information about the UI and the
* current request.
@@ -229,4 +295,8 @@ public abstract class UIProvider implements Serializable {
}
}
+ private static final Logger getLogger() {
+ return Logger.getLogger(UIProvider.class.getName());
+ }
+
}
diff --git a/server/src/main/java/com/vaadin/server/WidgetsetInfo.java b/server/src/main/java/com/vaadin/server/WidgetsetInfo.java
new file mode 100644
index 0000000000..fd62b0b309
--- /dev/null
+++ b/server/src/main/java/com/vaadin/server/WidgetsetInfo.java
@@ -0,0 +1,57 @@
+/*
+ * 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;
+
+import java.io.Serializable;
+
+/**
+ * An interface describing the widgetset that the client should try to load.
+ * <p>
+ * In addition to explicit use within the framework, adding a class called
+ * AppWidgetset implementing this interface in the default package will
+ * configure the widgetset to use unless the user has explicitly selected a
+ * different widgetset. See {@link BootstrapHandler} and {@link UIProvider} for
+ * more information.
+ *
+ * @since 7.7
+ */
+public interface WidgetsetInfo extends Serializable {
+
+ /**
+ * Returns the name of the widgetset to use.
+ *
+ * @return widgetset name
+ */
+ public String getWidgetsetName();
+
+ /**
+ * Returns the widgetset URL. Can be null for local widgetsets at default
+ * location.
+ *
+ * @return widgetset URL or null for client generated URL
+ */
+ public String getWidgetsetUrl();
+
+ /**
+ * If cdn is true, the client side should wait if it didn't manage to load
+ * the widgetset, as it might still be compiling.
+ *
+ * @return true to wait and retry if the widgetset could not be loaded
+ */
+ public boolean isCdn();
+
+}
diff --git a/server/src/main/java/com/vaadin/server/WidgetsetInfoImpl.java b/server/src/main/java/com/vaadin/server/WidgetsetInfoImpl.java
new file mode 100644
index 0000000000..a887b0b7ac
--- /dev/null
+++ b/server/src/main/java/com/vaadin/server/WidgetsetInfoImpl.java
@@ -0,0 +1,65 @@
+/*
+ * 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;
+
+/**
+ * Default implementation of {@link WidgetsetInfo} that is used for internal
+ * communication between the parts of the framework.
+ * <p>
+ * Class needs to be static so that it can be easily used in e.g.
+ * BootstrapHandler.
+ * <p>
+ * This class is intended primarily for internal use. It is recommended to
+ * implement WidgetsetInfo directly rather than extending or using this
+ * class outside the framework, and this class is subject to changes.
+ *
+ * @since 7.7
+ */
+class WidgetsetInfoImpl implements WidgetsetInfo {
+
+ private final boolean cdn;
+ private final String widgetsetUrl;
+ private final String widgetsetName;
+
+ public WidgetsetInfoImpl(boolean cdn, String widgetsetUrl,
+ String widgetsetName) {
+
+ this.cdn = cdn;
+ this.widgetsetUrl = widgetsetUrl;
+ this.widgetsetName = widgetsetName;
+ }
+
+ public WidgetsetInfoImpl(String widgetsetName) {
+ this(false, null, widgetsetName);
+ }
+
+ @Override
+ public boolean isCdn() {
+ return cdn;
+ }
+
+ @Override
+ public String getWidgetsetUrl() {
+ return widgetsetUrl;
+ }
+
+ @Override
+ public String getWidgetsetName() {
+ return widgetsetName;
+ }
+
+} \ No newline at end of file
diff --git a/server/src/main/resources/VAADIN/vaadinBootstrap.js b/server/src/main/resources/VAADIN/vaadinBootstrap.js
index 89514dbcc2..a33884eee3 100644
--- a/server/src/main/resources/VAADIN/vaadinBootstrap.js
+++ b/server/src/main/resources/VAADIN/vaadinBootstrap.js
@@ -41,14 +41,23 @@
return (typeof window[className]) != "undefined";
};
- var loadWidgetset = function(url, widgetset) {
+ var loadWidgetset = function(url, widgetset, ready) {
if (widgetsets[widgetset]) {
return;
}
log("load widgetset", url, widgetset);
setTimeout(function() {
if (!isWidgetsetLoaded(widgetset)) {
- alert("Failed to load the widgetset: " + url);
+ if (ready) {
+ alert("Failed to load the widgetset: " + url);
+ } else {
+ if (window.confirm("Failed to load the widgetset. If using CDN for the widgetset, it is possible that compiling it takes up to a few minutes. Would you like to try again?")) {
+ window[widgetset] = undefined;
+ window.location.reload(false);
+ } else {
+ alert("Failed to load the widgetset: " + url);
+ }
+ }
}
}, 15000);
@@ -216,7 +225,8 @@
if (!widgetsetUrl) {
widgetsetUrl = vaadinDir + 'widgetsets/' + widgetset + "/" + widgetset + ".nocache.js?" + new Date().getTime();
}
- loadWidgetset(widgetsetUrl, widgetset);
+ var widgetsetReady = getConfig('widgetsetReady');
+ loadWidgetset(widgetsetUrl, widgetset, widgetsetReady);
if (getConfig('uidl') === undefined) {
if (mayDefer) {