Skip to content

Commit

Permalink
Add interface for AppWidgetset (#19770)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
makoivis authored and Vaadin Code Review committed Apr 29, 2016
1 parent e5addd7 commit 7787eee
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 43 deletions.
1 change: 1 addition & 0 deletions all/src/main/templates/release-notes.html
Expand Up @@ -112,6 +112,7 @@ <h3 id="incompatible">Incompatible or Behavior-altering Changes in @version-mino
<ul> <ul>
<li>Vaadin artifacts no longer bring a transitive dependency to javax.servlet:servlet-api.</li> <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>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> </ul>
<h3 id="knownissues">Known Issues and Limitations</h3> <h3 id="knownissues">Known Issues and Limitations</h3>
<ul> <ul>
Expand Down
58 changes: 38 additions & 20 deletions server/src/main/java/com/vaadin/server/BootstrapHandler.java
Expand Up @@ -55,10 +55,10 @@
import elemental.json.impl.JsonUtil; import elemental.json.impl.JsonUtil;


/** /**
* *
* @author Vaadin Ltd * @author Vaadin Ltd
* @since 7.0.0 * @since 7.0.0
* *
* @deprecated As of 7.0. Will likely change or be removed in a future version * @deprecated As of 7.0. Will likely change or be removed in a future version
*/ */
@Deprecated @Deprecated
Expand All @@ -76,12 +76,12 @@ protected class BootstrapContext implements Serializable {
private final VaadinResponse response; private final VaadinResponse response;
private final BootstrapFragmentResponse bootstrapResponse; private final BootstrapFragmentResponse bootstrapResponse;


private String widgetsetName;
private String themeName; private String themeName;
private String appId; private String appId;
private PushMode pushMode; private PushMode pushMode;
private JsonObject applicationParameters; private JsonObject applicationParameters;
private VaadinUriResolver uriResolver; private VaadinUriResolver uriResolver;
private WidgetsetInfo widgetsetInfo;


public BootstrapContext(VaadinResponse response, public BootstrapContext(VaadinResponse response,
BootstrapFragmentResponse bootstrapResponse) { BootstrapFragmentResponse bootstrapResponse) {
Expand All @@ -105,11 +105,20 @@ public Class<? extends UI> getUIClass() {
return bootstrapResponse.getUiClass(); return bootstrapResponse.getUiClass();
} }


public String getWidgetsetName() { public WidgetsetInfo getWidgetsetInfo() {
if (widgetsetName == null) { if (widgetsetInfo == null) {
widgetsetName = getWidgetsetForUI(this); 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() { public String getThemeName() {
Expand Down Expand Up @@ -455,18 +464,19 @@ protected String getMainDivStyle(BootstrapContext context) {
return null; return null;
} }


public String getWidgetsetForUI(BootstrapContext context) { public WidgetsetInfo getWidgetsetForUI(BootstrapContext context) {
VaadinRequest request = context.getRequest(); VaadinRequest request = context.getRequest();


UICreateEvent event = new UICreateEvent(context.getRequest(), UICreateEvent event = new UICreateEvent(context.getRequest(),
context.getUIClass()); context.getUIClass());
String widgetset = context.getBootstrapResponse().getUIProvider() WidgetsetInfo widgetset = context.getBootstrapResponse()
.getWidgetset(event); .getUIProvider().getWidgetset(event);
if (widgetset == null) { 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; return widgetset;
} }


Expand All @@ -477,9 +487,9 @@ public String getWidgetsetForUI(BootstrapContext context) {
* Override this method if you want to add some custom html around around * Override this method if you want to add some custom html around around
* the div element into which the actual Vaadin application will be * the div element into which the actual Vaadin application will be
* rendered. * rendered.
* *
* @param context * @param context
* *
* @throws IOException * @throws IOException
*/ */
private void setupMainDiv(BootstrapContext context) throws IOException { private void setupMainDiv(BootstrapContext context) throws IOException {
Expand Down Expand Up @@ -618,7 +628,15 @@ protected JsonObject getApplicationParameters(BootstrapContext context) {
} }


appConfig.put("versionInfo", versionInfo); 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 // Use locale from session if set, else from the request
Locale locale = ServletPortletHelper.findLocale(null, Locale locale = ServletPortletHelper.findLocale(null,
Expand Down Expand Up @@ -694,13 +712,13 @@ protected JsonObject getApplicationParameters(BootstrapContext context) {


/** /**
* Get the URI for the application theme. * Get the URI for the application theme.
* *
* A portal-wide default theme is fetched from the portal shared resource * A portal-wide default theme is fetched from the portal shared resource
* directory (if any), other themes from the portlet. * directory (if any), other themes from the portlet.
* *
* @param context * @param context
* @param themeName * @param themeName
* *
* @return * @return
*/ */
public String getThemeUri(BootstrapContext context, String themeName) { public String getThemeUri(BootstrapContext context, String themeName) {
Expand All @@ -713,7 +731,7 @@ public String getThemeUri(BootstrapContext context, String themeName) {


/** /**
* Override if required * Override if required
* *
* @param context * @param context
* @return * @return
*/ */
Expand All @@ -725,7 +743,7 @@ public String getThemeName(BootstrapContext context) {


/** /**
* Do not override. * Do not override.
* *
* @param context * @param context
* @return * @return
*/ */
Expand Down
110 changes: 90 additions & 20 deletions server/src/main/java/com/vaadin/server/UIProvider.java
@@ -1,12 +1,12 @@
/* /*
* Copyright 2000-2014 Vaadin Ltd. * Copyright 2000-2014 Vaadin Ltd.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not * 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 * use this file except in compliance with the License. You may obtain a copy of
* the License at * the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
Expand All @@ -20,6 +20,8 @@
import java.io.Serializable; import java.io.Serializable;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.annotation.Inherited; import java.lang.annotation.Inherited;
import java.util.logging.Level;
import java.util.logging.Logger;


import com.vaadin.annotations.PreserveOnRefresh; import com.vaadin.annotations.PreserveOnRefresh;
import com.vaadin.annotations.Push; import com.vaadin.annotations.Push;
Expand Down Expand Up @@ -58,7 +60,7 @@ public UI createInstance(UICreateEvent event) {
* do not follow the standard semantics and are supported for backwards * do not follow the standard semantics and are supported for backwards
* compatibility. Future versions of the framework might only support the * compatibility. Future versions of the framework might only support the
* standard semantics of {@code @Inherited}. * standard semantics of {@code @Inherited}.
* *
* @param clazz * @param clazz
* the class from which the annotation should be found * the class from which the annotation should be found
* @param annotationType * @param annotationType
Expand Down Expand Up @@ -104,13 +106,13 @@ protected static <T extends Annotation> T getAnnotationFor(Class<?> clazz,
* <p> * <p>
* The default implementation checks for a @{@link Theme} annotation on the * The default implementation checks for a @{@link Theme} annotation on the
* UI class. * UI class.
* *
* @param event * @param event
* the UI create event with information about the UI and the * the UI create event with information about the UI and the
* current request. * current request.
* @return the name of the theme, or <code>null</code> if the default theme * @return the name of the theme, or <code>null</code> if the default theme
* should be used * should be used
* *
*/ */
public String getTheme(UICreateEvent event) { public String getTheme(UICreateEvent event) {
Theme uiTheme = getAnnotationFor(event.getUIClass(), Theme.class); Theme uiTheme = getAnnotationFor(event.getUIClass(), Theme.class);
Expand All @@ -125,26 +127,90 @@ public String getTheme(UICreateEvent event) {
* Finds the widgetset to use for a specific UI. If no specific widgetset is * Finds the widgetset to use for a specific UI. If no specific widgetset is
* required, <code>null</code> is returned. * required, <code>null</code> is returned.
* <p> * <p>
* The default implementation uses the @{@link Widgetset} annotation if it's * The default implementation uses the following order of priority for
* defined for the UI class. * 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 * @param event
* the UI create event with information about the UI and the * the UI create event with information about the UI and the
* current request. * current request.
* @return the name of the widgetset, or <code>null</code> if the default * @return the name of the widgetset, or <code>null</code> if the default
* widgetset should be used * widgetset should be used
* *
* @since 7.7
*/ */
public String getWidgetset(UICreateEvent event) { public WidgetsetInfo getWidgetset(UICreateEvent event) {
Widgetset uiWidgetset = getAnnotationFor(event.getUIClass(), Widgetset uiWidgetset = getAnnotationFor(event.getUIClass(),
Widgetset.class); Widgetset.class);

// First case: We have an @Widgetset annotation, use that
if (uiWidgetset != null) { if (uiWidgetset != null) {
return uiWidgetset.value(); return new WidgetsetInfoImpl(uiWidgetset.value());
} else { }

// 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( InputStream resource = event.getUIClass().getResourceAsStream(
"/" + APP_WIDGETSET_NAME + ".gwt.xml"); "/" + APP_WIDGETSET_NAME + ".gwt.xml");
if (resource != null) { 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; return null;
Expand All @@ -159,12 +225,12 @@ public String getWidgetset(UICreateEvent event) {
* Whenever a preserved UI is reused, its * Whenever a preserved UI is reused, its
* {@link UI#refresh(com.vaadin.server.VaadinRequest) refresh} method is * {@link UI#refresh(com.vaadin.server.VaadinRequest) refresh} method is
* invoked by the framework first. * invoked by the framework first.
* *
* *
* @param event * @param event
* the UI create event with information about the UI and the * the UI create event with information about the UI and the
* current request. * current request.
* *
* @return <code>true</code>if the same UI instance should be reused e.g. * @return <code>true</code>if the same UI instance should be reused e.g.
* when the browser window is refreshed. * when the browser window is refreshed.
*/ */
Expand All @@ -190,13 +256,13 @@ public String getPageTitle(UICreateEvent event) {
* <p> * <p>
* The default implementation uses the @{@link Push} annotation if it's * The default implementation uses the @{@link Push} annotation if it's
* defined for the UI class. * defined for the UI class.
* *
* @param event * @param event
* the UI create event with information about the UI and the * the UI create event with information about the UI and the
* current request. * current request.
* @return the push mode to use, or <code>null</code> if the default push * @return the push mode to use, or <code>null</code> if the default push
* mode should be used * mode should be used
* *
*/ */
public PushMode getPushMode(UICreateEvent event) { public PushMode getPushMode(UICreateEvent event) {
Push push = getAnnotationFor(event.getUIClass(), Push.class); Push push = getAnnotationFor(event.getUIClass(), Push.class);
Expand All @@ -213,7 +279,7 @@ public PushMode getPushMode(UICreateEvent event) {
* <p> * <p>
* The default implementation uses the @{@link Push} annotation if it's * The default implementation uses the @{@link Push} annotation if it's
* defined for the UI class. * defined for the UI class.
* *
* @param event * @param event
* the UI create event with information about the UI and the * the UI create event with information about the UI and the
* current request. * current request.
Expand All @@ -229,4 +295,8 @@ public Transport getPushTransport(UICreateEvent event) {
} }
} }


private static final Logger getLogger() {
return Logger.getLogger(UIProvider.class.getName());
}

} }

0 comments on commit 7787eee

Please sign in to comment.