From b55ff51e8c254c56bb415432102b878fa053d8f3 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Leif=20=C3=85strand?= Date: Wed, 27 Jun 2012 16:52:09 +0300 Subject: [PATCH] Add @JavaScript and @StyleSheet and remove @LoadScripts (#9044) --- .../{LoadScripts.java => JavaScript.java} | 12 +- src/com/vaadin/annotations/StyleSheet.java | 16 + .../gwt/client/ApplicationConnection.java | 55 +++ .../terminal/gwt/client/ResourceLoader.java | 401 ++++++++++++++++++ .../server/AbstractCommunicationManager.java | 69 ++- .../terminal/gwt/server/BootstrapHandler.java | 53 +-- .../gwt/server/CommunicationManager.java | 3 +- .../server/PortletCommunicationManager.java | 4 +- .../BasicJavaScriptComponent.java | 4 +- .../SimpleJavaScriptExtensionTest.java | 6 +- 10 files changed, 551 insertions(+), 72 deletions(-) rename src/com/vaadin/annotations/{LoadScripts.java => JavaScript.java} (52%) create mode 100644 src/com/vaadin/annotations/StyleSheet.java create mode 100644 src/com/vaadin/terminal/gwt/client/ResourceLoader.java diff --git a/src/com/vaadin/annotations/LoadScripts.java b/src/com/vaadin/annotations/JavaScript.java similarity index 52% rename from src/com/vaadin/annotations/LoadScripts.java rename to src/com/vaadin/annotations/JavaScript.java index 84ac2d2fb7..8e0bc09958 100644 --- a/src/com/vaadin/annotations/LoadScripts.java +++ b/src/com/vaadin/annotations/JavaScript.java @@ -1,6 +1,7 @@ /* @VaadinApache2LicenseForJavaFiles@ */ + package com.vaadin.annotations; import java.lang.annotation.ElementType; @@ -8,17 +9,8 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -/** - * Temporary hack used for ensuring external javascript libraries are included. - * To add a javascript, add this annotation to your Root class. - * - * @deprecated Will be removed in favor of a more robust solution before version - * 7.0.0 - */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) -@Deprecated -public @interface LoadScripts { +public @interface JavaScript { public String[] value(); - } diff --git a/src/com/vaadin/annotations/StyleSheet.java b/src/com/vaadin/annotations/StyleSheet.java new file mode 100644 index 0000000000..40f75511dd --- /dev/null +++ b/src/com/vaadin/annotations/StyleSheet.java @@ -0,0 +1,16 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ + +package com.vaadin.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface StyleSheet { + public String[] value(); +} diff --git a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java index f0470c8ee8..3ec4233c59 100644 --- a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java +++ b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java @@ -40,6 +40,8 @@ import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.HasWidgets; import com.google.gwt.user.client.ui.Widget; import com.vaadin.terminal.gwt.client.ApplicationConfiguration.ErrorMessage; +import com.vaadin.terminal.gwt.client.ResourceLoader.ResourceLoadEvent; +import com.vaadin.terminal.gwt.client.ResourceLoader.ResourceLoadListener; import com.vaadin.terminal.gwt.client.communication.HasJavaScriptConnectorHelper; import com.vaadin.terminal.gwt.client.communication.JsonDecoder; import com.vaadin.terminal.gwt.client.communication.JsonEncoder; @@ -1067,6 +1069,14 @@ public class ApplicationConnection { json.getValueMap("typeMappings"), widgetSet); } + VConsole.log("Handling resource dependencies"); + if (json.containsKey("scriptDependencies")) { + loadScriptDependencies(json.getJSStringArray("scriptDependencies")); + } + if (json.containsKey("styleDependencies")) { + loadStyleDependencies(json.getJSStringArray("styleDependencies")); + } + handleUIDLDuration.logDuration( " * Handling type mappings from server completed", 10); /* @@ -1611,6 +1621,51 @@ public class ApplicationConnection { ApplicationConfiguration.runWhenWidgetsLoaded(c); } + private static void loadStyleDependencies(JsArrayString dependencies) { + // Assuming no reason to interpret in a defined order + ResourceLoadListener resourceLoadListener = new ResourceLoadListener() { + public void onResourceLoad(ResourceLoadEvent event) { + ApplicationConfiguration.endWidgetLoading(); + } + }; + ResourceLoader loader = ResourceLoader.get(); + for (int i = 0; i < dependencies.length(); i++) { + ApplicationConfiguration.startWidgetLoading(); + loader.loadStylesheet(dependencies.get(i), resourceLoadListener); + } + } + + private static void loadScriptDependencies(final JsArrayString dependencies) { + if (dependencies.length() == 0) { + return; + } + + // Listener that loads the next when one is completed + ResourceLoadListener resourceLoadListener = new ResourceLoadListener() { + public void onResourceLoad(ResourceLoadEvent event) { + if (dependencies.length() != 0) { + ApplicationConfiguration.startWidgetLoading(); + // Load next in chain (hopefully already preloaded) + event.getResourceLoader().loadScript(dependencies.shift(), + this); + } + // Call start for next before calling end for current + ApplicationConfiguration.endWidgetLoading(); + } + }; + + ResourceLoader loader = ResourceLoader.get(); + + // Start chain by loading first + ApplicationConfiguration.startWidgetLoading(); + loader.loadScript(dependencies.shift(), resourceLoadListener); + + // Preload all remaining + for (int i = 0; i < dependencies.length(); i++) { + loader.loadScript(dependencies.get(i), null); + } + } + // Redirect browser, null reloads current page private static native void redirect(String url) /*-{ diff --git a/src/com/vaadin/terminal/gwt/client/ResourceLoader.java b/src/com/vaadin/terminal/gwt/client/ResourceLoader.java new file mode 100644 index 0000000000..7abafbc216 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ResourceLoader.java @@ -0,0 +1,401 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.core.client.Scheduler; +import com.google.gwt.core.client.Scheduler.RepeatingCommand; +import com.google.gwt.dom.client.AnchorElement; +import com.google.gwt.dom.client.Document; +import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.LinkElement; +import com.google.gwt.dom.client.NodeList; +import com.google.gwt.dom.client.ObjectElement; +import com.google.gwt.dom.client.ScriptElement; + +/** + * ResourceLoader lets you dynamically include external scripts and styles on + * the page and lets you know when the resource has been loaded. + * + * You can also preload resources, allowing them to get cached by the browser + * without being evaluated. This enables downloading multiple resources at once + * while still controlling in which order e.g. scripts are executed. + * + * @author Vaadin Ltd + * @version @VERSION@ + * @since 7.0.0 + */ +public class ResourceLoader { + /** + * Event fired when a resource has been loaded. + */ + public static class ResourceLoadEvent { + private ResourceLoader loader; + private String resourceUrl; + private final boolean preload; + + /** + * Creates a new event. + * + * @param loader + * the resource loader that has loaded the resource + * @param resourceUrl + * the url of the loaded resource + * @param preload + * true if the resource has only been preloaded, false if + * it's fully loaded + */ + public ResourceLoadEvent(ResourceLoader loader, String resourceUrl, + boolean preload) { + this.loader = loader; + this.resourceUrl = resourceUrl; + this.preload = preload; + } + + /** + * Gets the resource loader that has fired this event + * + * @return the resource loader + */ + public ResourceLoader getResourceLoader() { + return loader; + } + + /** + * Gets the absolute url of the loaded resource. + * + * @return the absolute url of the loaded resource + */ + public String getResourceUrl() { + return resourceUrl; + } + + /** + * Returns true if the resource has been preloaded, false if it's fully + * loaded + * + * @see ResourceLoader#preloadResource(String, ResourceLoadListener) + * + * @return true if the resource has been preloaded, false if it's fully + * loaded + */ + public boolean isPreload() { + return preload; + } + } + + /** + * Event listener that gets notified when a resource has been loaded + */ + public interface ResourceLoadListener { + /** + * Notified this ResourceLoadListener that a resource has been loaded + * + * @see ResourceLoadEvent + * + * @param event + * a resource load event with information about the loaded + * resource + */ + public void onResourceLoad(ResourceLoadEvent event); + } + + private static final ResourceLoader INSTANCE = GWT + .create(ResourceLoader.class); + + private ApplicationConnection connection; + + private final Set loadedResources = new HashSet(); + private final Set preloadedResources = new HashSet(); + + private final Map> loadListeners = new HashMap>(); + private final Map> preloadListeners = new HashMap>(); + + private final Element head; + + /** + * Creates a new resource loader. You should generally not create you own + * resource loader, but instead use {@link ResourceLoader#get()} to get an + * instance. + */ + protected ResourceLoader() { + Document document = Document.get(); + head = document.getElementsByTagName("head").getItem(0); + + // detect already loaded scripts and stylesheets + NodeList scripts = document.getElementsByTagName("script"); + for (int i = 0; i < scripts.getLength(); i++) { + ScriptElement element = ScriptElement.as(scripts.getItem(i)); + String src = element.getSrc(); + if (src != null && src.length() != 0) { + loadedResources.add(src); + } + } + + NodeList links = document.getElementsByTagName("link"); + for (int i = 0; i < links.getLength(); i++) { + LinkElement linkElement = LinkElement.as(links.getItem(i)); + String rel = linkElement.getRel(); + String href = linkElement.getHref(); + if ("stylesheet".equalsIgnoreCase(rel) && href != null + && href.length() != 0) { + loadedResources.add(href); + } + } + } + + /** + * Returns the default ResourceLoader + * + * @return the default ResourceLoader + */ + public static ResourceLoader get() { + return INSTANCE; + } + + /** + * Load a script and notify a listener when the script is loaded. Calling + * this method when the script is currently loading or already loaded + * doesn't cause the script to be loaded again, but the listener will still + * be notified when appropriate. + * + * + * @param scriptUrl + * the url of the script to load + * @param resourceLoadListener + * the listener that will get notified when the script is loaded + */ + public void loadScript(final String scriptUrl, + final ResourceLoadListener resourceLoadListener) { + final String url = getAbsoluteUrl(scriptUrl); + if (loadedResources.contains(url)) { + if (resourceLoadListener != null) { + resourceLoadListener.onResourceLoad(new ResourceLoadEvent(this, + url, false)); + } + return; + } + + if (preloadListeners.containsKey(url)) { + preloadResource(url, new ResourceLoadListener() { + public void onResourceLoad(ResourceLoadEvent event) { + loadScript(url, resourceLoadListener); + } + }); + return; + } + + if (addListener(url, resourceLoadListener, loadListeners)) { + ScriptElement scriptTag = Document.get().createScriptElement(); + scriptTag.setSrc(url); + scriptTag.setType("text/javascript"); + addOnloadHandler(scriptTag, url, false); + head.appendChild(scriptTag); + } + } + + private static String getAbsoluteUrl(String url) { + AnchorElement a = Document.get().createAnchorElement(); + a.setHref(url); + return a.getHref(); + } + + /** + * Download a resource and notify a listener when the resource is loaded + * without attempting to interpret the resource. When a resource has been + * preloaded, it will be present in the browser's cache (provided the HTTP + * headers allow caching), making a subsequent load operation complete + * without having to wait for the resource to be downloaded again. + * + * Calling this method when the resource is currently loading, currently + * preloading, already preloaded or already loaded doesn't cause the + * resource to be preloaded again, but the listener will still be notified + * when appropriate. + * + * @param url + * the url of the resource to preload + * @param resourceLoadListener + * the listener that will get notified when the resource is + * preloaded + */ + public void preloadResource(String url, + ResourceLoadListener resourceLoadListener) { + url = getAbsoluteUrl(url); + if (loadedResources.contains(url) || preloadedResources.contains(url)) { + if (resourceLoadListener != null) { + resourceLoadListener.onResourceLoad(new ResourceLoadEvent(this, + url, !loadedResources.contains(url))); + } + return; + } + + if (addListener(url, resourceLoadListener, preloadListeners) + && !loadListeners.containsKey(url)) { + // Inject loader element if this is the first time this is preloaded + // AND the resources isn't already being loaded in the normal way + + Element element = getPreloadElement(url); + addOnloadHandler(element, url, true); + + // TODO Remove object when loaded (without causing spinner in FF) + Document.get().getBody().appendChild(element); + } + } + + private static Element getPreloadElement(String url) { + if (BrowserInfo.get().isIE()) { + ScriptElement element = Document.get().createScriptElement(); + element.setSrc(url); + element.setType("text/cache"); + return element; + } else { + ObjectElement element = Document.get().createObjectElement(); + element.setData(url); + element.setType("text/plain"); + element.setHeight("0px"); + element.setWidth("0px"); + return element; + } + } + + private native void addOnloadHandler(Element element, String url, + boolean preload) + /*-{ + var self = this; + var done = $entry(function() { + element.onloadDone = true; + element.onload = null; + element.onreadystatechange = null; + self.@com.vaadin.terminal.gwt.client.ResourceLoader::onResourceLoad(Ljava/lang/String;Z)(url, preload); + }); + element.onload = function() { + if (!element.onloadDone) { + done(); + } + }; + element.onreadystatechange = function() { + if (("loaded" === element.readyState || "complete" === element.readyState) && !element.onloadDone ) { + done(); + } + }; + }-*/; + + /** + * Load a stylesheet and notify a listener when the stylesheet is loaded. + * Calling this method when the stylesheet is currently loading or already + * loaded doesn't cause the stylesheet to be loaded again, but the listener + * will still be notified when appropriate. + * + * @param stylesheetUrl + * the url of the stylesheet to load + * @param resourceLoadListener + * the listener that will get notified when the stylesheet is + * loaded + */ + public void loadStylesheet(final String stylesheetUrl, + final ResourceLoadListener resourceLoadListener) { + final String url = getAbsoluteUrl(stylesheetUrl); + if (loadedResources.contains(url)) { + if (resourceLoadListener != null) { + resourceLoadListener.onResourceLoad(new ResourceLoadEvent(this, + url, false)); + } + return; + } + + if (preloadListeners.containsKey(url)) { + preloadResource(url, new ResourceLoadListener() { + public void onResourceLoad(ResourceLoadEvent event) { + loadStylesheet(url, resourceLoadListener); + } + }); + return; + } + + if (addListener(url, resourceLoadListener, loadListeners)) { + LinkElement linkElement = Document.get().createLinkElement(); + linkElement.setRel("stylesheet"); + linkElement.setType("text/css"); + linkElement.setHref(url); + + if (BrowserInfo.get().isSafari()) { + // Safari doesn't fire onload events for link elements + // See http://www.phpied.com/when-is-a-stylesheet-really-loaded/ + // TODO Stop checking after some timeout + Scheduler.get().scheduleFixedPeriod(new RepeatingCommand() { + public boolean execute() { + if (isStyleSheetPresent(url)) { + onResourceLoad(url, false); + return false; // Stop repeating + } else { + return true; // Continue repeating + } + } + }, 10); + } else { + addOnloadHandler(linkElement, url, false); + } + + head.appendChild(linkElement); + } + } + + private static native boolean isStyleSheetPresent(String url) + /*-{ + for(var i = 0; i < $doc.styleSheets.length; i++) { + if ($doc.styleSheets[i].href === url) { + return true; + } + } + return false; + }-*/; + + private static boolean addListener(String url, + ResourceLoadListener listener, + Map> listenerMap) { + Collection listeners = listenerMap.get(url); + if (listeners == null) { + listeners = new HashSet(); + listeners.add(listener); + listenerMap.put(url, listeners); + return true; + } else { + listeners.add(listener); + return false; + } + } + + private void onResourceLoad(String resource, boolean preload) { + Collection listeners; + if (preload) { + preloadedResources.add(resource); + listeners = preloadListeners.remove(resource); + } else { + if (preloadListeners.containsKey(resource)) { + // Also fire preload events for potential listeners + onResourceLoad(resource, true); + } + preloadedResources.remove(resource); + loadedResources.add(resource); + listeners = loadListeners.remove(resource); + } + if (listeners != null && !listeners.isEmpty()) { + ResourceLoadEvent event = new ResourceLoadEvent(this, resource, + preload); + for (ResourceLoadListener listener : listeners) { + if (listener != null) { + listener.onResourceLoad(event); + } + } + } + } + +} diff --git a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java index 7cad8e3a33..c65b8947d6 100644 --- a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java +++ b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java @@ -25,6 +25,7 @@ import java.text.DateFormatSymbols; import java.text.SimpleDateFormat; import java.text.StringCharacterIterator; import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Collections; @@ -46,6 +47,8 @@ import com.vaadin.Application; import com.vaadin.Application.SystemMessages; import com.vaadin.RootRequiresMoreInformationException; import com.vaadin.Version; +import com.vaadin.annotations.JavaScript; +import com.vaadin.annotations.StyleSheet; import com.vaadin.external.json.JSONArray; import com.vaadin.external.json.JSONException; import com.vaadin.external.json.JSONObject; @@ -497,10 +500,11 @@ public abstract class AbstractCommunicationManager implements Serializable { * found * @throws IOException * @throws InvalidUIDLSecurityKeyException + * @throws JSONException */ public void handleUidlRequest(WrappedRequest request, WrappedResponse response, Callback callback, Root root) - throws IOException, InvalidUIDLSecurityKeyException { + throws IOException, InvalidUIDLSecurityKeyException, JSONException { checkWidgetsetVersion(request); requestThemeName = request.getParameter("theme"); @@ -696,11 +700,12 @@ public abstract class AbstractCommunicationManager implements Serializable { * @param analyzeLayouts * @throws PaintException * @throws IOException + * @throws JSONException */ private void paintAfterVariableChanges(WrappedRequest request, WrappedResponse response, Callback callback, boolean repaintAll, final PrintWriter outWriter, Root root, boolean analyzeLayouts) - throws PaintException, IOException { + throws PaintException, IOException, JSONException { // Removes application if it has stopped during variable changes if (!application.isRunning()) { @@ -764,7 +769,7 @@ public abstract class AbstractCommunicationManager implements Serializable { @SuppressWarnings("unchecked") public void writeUidlResponse(WrappedRequest request, boolean repaintAll, final PrintWriter outWriter, Root root, boolean analyzeLayouts) - throws PaintException { + throws PaintException, JSONException { ArrayList dirtyVisibleConnectors = new ArrayList(); Application application = root.getApplication(); // Paints components @@ -1095,10 +1100,14 @@ public abstract class AbstractCommunicationManager implements Serializable { boolean typeMappingsOpen = false; ClientCache clientCache = getClientCache(root); + List> newConnectorTypes = new ArrayList>(); + for (Class class1 : usedClientConnectors) { if (clientCache.cache(class1)) { // client does not know the mapping key for this type, send // mapping to client + newConnectorTypes.add(class1); + if (!typeMappingsOpen) { typeMappingsOpen = true; outWriter.print(", \"typeMappings\" : { "); @@ -1142,6 +1151,54 @@ public abstract class AbstractCommunicationManager implements Serializable { } } + /* + * Ensure super classes come before sub classes to get script dependency + * order right. Sub class @JavaScript might assume that @JavaScript + * defined by super class is already loaded. + */ + Collections.sort(newConnectorTypes, new Comparator>() { + public int compare(Class o1, Class o2) { + // TODO optimize using Class.isAssignableFrom? + return hierarchyDepth(o1) - hierarchyDepth(o2); + } + + private int hierarchyDepth(Class type) { + if (type == Object.class) { + return 0; + } else { + return hierarchyDepth(type.getSuperclass()) + 1; + } + } + }); + + List scriptDependencies = new ArrayList(); + List styleDependencies = new ArrayList(); + + for (Class class1 : newConnectorTypes) { + JavaScript jsAnnotation = class1.getAnnotation(JavaScript.class); + if (jsAnnotation != null) { + scriptDependencies.addAll(Arrays.asList(jsAnnotation.value())); + } + + StyleSheet styleAnnotation = class1.getAnnotation(StyleSheet.class); + if (styleAnnotation != null) { + styleDependencies + .addAll(Arrays.asList(styleAnnotation.value())); + } + } + + // Include script dependencies in output if there are any + if (!scriptDependencies.isEmpty()) { + outWriter.print(", \"scriptDependencies\": " + + new JSONArray(scriptDependencies).toString()); + } + + // Include style dependencies in output if there are any + if (!styleDependencies.isEmpty()) { + outWriter.print(", \"styleDependencies\": " + + new JSONArray(styleDependencies).toString()); + } + // add any pending locale definitions requested by the client printLocaleDeclarations(outWriter); @@ -1380,7 +1437,7 @@ public abstract class AbstractCommunicationManager implements Serializable { private boolean handleVariables(WrappedRequest request, WrappedResponse response, Callback callback, Application application2, Root root) throws IOException, - InvalidUIDLSecurityKeyException { + InvalidUIDLSecurityKeyException, JSONException { boolean success = true; String changes = getRequestPayload(request); @@ -2256,9 +2313,11 @@ public abstract class AbstractCommunicationManager implements Serializable { * @return a string with the initial UIDL message * @throws PaintException * if an exception occurs while painting + * @throws JSONException + * if an exception occurs while encoding output */ protected String getInitialUIDL(WrappedRequest request, Root root) - throws PaintException { + throws PaintException, JSONException { // TODO maybe unify writeUidlResponse()? StringWriter sWriter = new StringWriter(); PrintWriter pWriter = new PrintWriter(sWriter); diff --git a/src/com/vaadin/terminal/gwt/server/BootstrapHandler.java b/src/com/vaadin/terminal/gwt/server/BootstrapHandler.java index 69f033c8cd..d32fa325f4 100644 --- a/src/com/vaadin/terminal/gwt/server/BootstrapHandler.java +++ b/src/com/vaadin/terminal/gwt/server/BootstrapHandler.java @@ -9,10 +9,6 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Serializable; import java.io.Writer; -import java.lang.annotation.Annotation; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletResponse; @@ -20,7 +16,6 @@ import javax.servlet.http.HttpServletResponse; import com.vaadin.Application; import com.vaadin.RootRequiresMoreInformationException; import com.vaadin.Version; -import com.vaadin.annotations.LoadScripts; import com.vaadin.external.json.JSONException; import com.vaadin.external.json.JSONObject; import com.vaadin.terminal.DeploymentConfiguration; @@ -490,50 +485,6 @@ public abstract class BootstrapHandler implements RequestHandler { page.write("" + AbstractApplicationServlet.safeEscapeForHtml(title) + "\n"); - - if (root != null) { - List loadScriptsAnnotations = getAnnotationsFor( - root.getClass(), LoadScripts.class); - Collections.reverse(loadScriptsAnnotations); - // Begin from the end as a class might requests scripts that depend - // on script loaded by a super class - for (int i = loadScriptsAnnotations.size() - 1; i >= 0; i--) { - LoadScripts loadScripts = loadScriptsAnnotations.get(i); - String[] value = loadScripts.value(); - if (value != null) { - for (String script : value) { - page.write("\n"); - } - } - } - - } - } - - private static List getAnnotationsFor( - Class type, Class annotationType) { - List list = new ArrayList(); - // Find from the class hierarchy - Class currentType = type; - while (currentType != Object.class) { - T annotation = currentType.getAnnotation(annotationType); - if (annotation != null) { - list.add(annotation); - } - currentType = currentType.getSuperclass(); - } - - // Find from an implemented interface - for (Class iface : type.getInterfaces()) { - T annotation = iface.getAnnotation(annotationType); - if (annotation != null) { - list.add(annotation); - } - } - - return list; } /** @@ -645,8 +596,10 @@ public abstract class BootstrapHandler implements RequestHandler { * @return a string with the initial UIDL message * @throws PaintException * if an exception occurs while painting the components + * @throws JSONException + * if an exception occurs while formatting the output */ protected abstract String getInitialUIDL(WrappedRequest request, Root root) - throws PaintException; + throws PaintException, JSONException; } diff --git a/src/com/vaadin/terminal/gwt/server/CommunicationManager.java b/src/com/vaadin/terminal/gwt/server/CommunicationManager.java index cc2981dc45..2d2888e034 100644 --- a/src/com/vaadin/terminal/gwt/server/CommunicationManager.java +++ b/src/com/vaadin/terminal/gwt/server/CommunicationManager.java @@ -15,6 +15,7 @@ import java.util.UUID; import javax.servlet.ServletContext; import com.vaadin.Application; +import com.vaadin.external.json.JSONException; import com.vaadin.terminal.PaintException; import com.vaadin.terminal.StreamVariable; import com.vaadin.terminal.WrappedRequest; @@ -245,7 +246,7 @@ public class CommunicationManager extends AbstractCommunicationManager { @Override protected String getInitialUIDL(WrappedRequest request, Root root) - throws PaintException { + throws PaintException, JSONException { return CommunicationManager.this.getInitialUIDL(request, root); } }; diff --git a/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java b/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java index d3fbf4d988..7398315ee2 100644 --- a/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java +++ b/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java @@ -104,7 +104,7 @@ public class PortletCommunicationManager extends AbstractCommunicationManager { @Override public void handleUidlRequest(WrappedRequest request, WrappedResponse response, Callback callback, Root root) - throws IOException, InvalidUIDLSecurityKeyException { + throws IOException, InvalidUIDLSecurityKeyException, JSONException { setCurrentMimeReponse(response); super.handleUidlRequest(request, response, callback, root); currentMimeResponse = null; @@ -253,7 +253,7 @@ public class PortletCommunicationManager extends AbstractCommunicationManager { @Override protected String getInitialUIDL(WrappedRequest request, Root root) - throws PaintException { + throws PaintException, JSONException { return PortletCommunicationManager.this.getInitialUIDL(request, root); } diff --git a/tests/testbench/com/vaadin/tests/components/javascriptcomponent/BasicJavaScriptComponent.java b/tests/testbench/com/vaadin/tests/components/javascriptcomponent/BasicJavaScriptComponent.java index 5f2f945c8b..15c5f6b642 100644 --- a/tests/testbench/com/vaadin/tests/components/javascriptcomponent/BasicJavaScriptComponent.java +++ b/tests/testbench/com/vaadin/tests/components/javascriptcomponent/BasicJavaScriptComponent.java @@ -6,7 +6,7 @@ package com.vaadin.tests.components.javascriptcomponent; import java.util.Arrays; import java.util.List; -import com.vaadin.annotations.LoadScripts; +import com.vaadin.annotations.JavaScript; import com.vaadin.external.json.JSONArray; import com.vaadin.external.json.JSONException; import com.vaadin.terminal.WrappedRequest; @@ -17,7 +17,6 @@ import com.vaadin.ui.AbstractJavaScriptComponent; import com.vaadin.ui.JavaScriptCallback; import com.vaadin.ui.Notification; -@LoadScripts({ "/statictestfiles/jsconnector.js" }) public class BasicJavaScriptComponent extends AbstractTestRoot { public interface ExampleClickRpc extends ServerRpc { @@ -36,6 +35,7 @@ public class BasicJavaScriptComponent extends AbstractTestRoot { } } + @JavaScript("/statictestfiles/jsconnector.js") public static class ExampleWidget extends AbstractJavaScriptComponent { public ExampleWidget() { registerRpc(new ExampleClickRpc() { diff --git a/tests/testbench/com/vaadin/tests/extensions/SimpleJavaScriptExtensionTest.java b/tests/testbench/com/vaadin/tests/extensions/SimpleJavaScriptExtensionTest.java index bbfe3f0f46..72168c08df 100644 --- a/tests/testbench/com/vaadin/tests/extensions/SimpleJavaScriptExtensionTest.java +++ b/tests/testbench/com/vaadin/tests/extensions/SimpleJavaScriptExtensionTest.java @@ -4,7 +4,8 @@ package com.vaadin.tests.extensions; -import com.vaadin.annotations.LoadScripts; +import com.vaadin.annotations.JavaScript; +import com.vaadin.annotations.StyleSheet; import com.vaadin.external.json.JSONArray; import com.vaadin.external.json.JSONException; import com.vaadin.terminal.AbstractJavaScriptExtension; @@ -18,7 +19,6 @@ import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.JavaScriptCallback; import com.vaadin.ui.Notification; -@LoadScripts({ "/statictestfiles/jsextension.js" }) public class SimpleJavaScriptExtensionTest extends AbstractTestRoot { public static class SimpleJavaScriptExtensionState extends @@ -44,6 +44,8 @@ public class SimpleJavaScriptExtensionTest extends AbstractTestRoot { public void greet(String message); } + @JavaScript("/statictestfiles/jsextension.js") + @StyleSheet("/VAADIN/external1.css") public static class SimpleJavascriptExtension extends AbstractJavaScriptExtension { -- 2.39.5