Browse Source

Add support for pluggable filters for rewriting dependencies (#9182)

* Add support for pluggable filters for rewriting dependencies

Fixes #9151
tags/8.1.0.alpha7
Artur 7 years ago
parent
commit
f8921dc387
18 changed files with 445 additions and 5 deletions
  1. 5
    3
      server/src/main/java/com/vaadin/server/BootstrapHandler.java
  2. 88
    0
      server/src/main/java/com/vaadin/server/DependencyFilter.java
  3. 51
    0
      server/src/main/java/com/vaadin/server/VaadinService.java
  4. 3
    1
      server/src/main/java/com/vaadin/server/communication/UidlWriter.java
  5. 32
    1
      server/src/main/java/com/vaadin/ui/Dependency.java
  6. 19
    0
      test/dependency-rewrite-addon/pom.xml
  7. 28
    0
      test/dependency-rewrite-addon/src/main/java/com/vaadin/test/dependencyrewriteaddon/RewriteJQueryFilter.java
  8. 1
    0
      test/dependency-rewrite-addon/src/main/resources/META-INF/services/com.vaadin.server.DependencyFilter
  9. 30
    0
      test/dependency-rewrite/pom.xml
  10. 26
    0
      test/dependency-rewrite/src/main/java/com/vaadin/test/dependencyrewrite/ApplicationDependencyFilter.java
  11. 34
    0
      test/dependency-rewrite/src/main/java/com/vaadin/test/dependencyrewrite/DependencyDynamicUI.java
  12. 21
    0
      test/dependency-rewrite/src/main/java/com/vaadin/test/dependencyrewrite/DependencyInitialUI.java
  13. 27
    0
      test/dependency-rewrite/src/main/java/com/vaadin/test/dependencyrewrite/MyService.java
  14. 22
    0
      test/dependency-rewrite/src/main/java/com/vaadin/test/dependencyrewrite/MyUIProvider.java
  15. 22
    0
      test/dependency-rewrite/src/main/java/com/vaadin/test/dependencyrewrite/MyUIServlet.java
  16. 5
    0
      test/dependency-rewrite/src/main/webapp/VAADIN/sub/jquery.js
  17. 29
    0
      test/dependency-rewrite/src/test/java/com/vaadin/test/dependencyrewrite/DependencyFilterIT.java
  18. 2
    0
      test/pom.xml

+ 5
- 3
server/src/main/java/com/vaadin/server/BootstrapHandler.java View File

@@ -44,6 +44,7 @@ import org.jsoup.parser.Tag;

import com.vaadin.annotations.Viewport;
import com.vaadin.annotations.ViewportGeneratorClass;
import com.vaadin.server.DependencyFilter.FilterContext;
import com.vaadin.server.communication.AtmospherePushConnection;
import com.vaadin.shared.ApplicationConstants;
import com.vaadin.shared.VaadinUriResolver;
@@ -572,9 +573,10 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler {
.attr("href", themeUri + "/favicon.ico");
}

Collection<? extends Dependency> deps = Dependency.findDependencies(
Collections.singletonList(uiClass),
context.getSession().getCommunicationManager());
Collection<? extends Dependency> deps = Dependency
.findAndFilterDependencies(Collections.singletonList(uiClass),
context.getSession().getCommunicationManager(),
new FilterContext(context.getSession()));
for (Dependency dependency : deps) {
Type type = dependency.getType();
String url = context.getUriResolver()

+ 88
- 0
server/src/main/java/com/vaadin/server/DependencyFilter.java View File

@@ -0,0 +1,88 @@
/*
* Copyright 2000-2016 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;
import java.util.List;

import com.vaadin.annotations.HtmlImport;
import com.vaadin.annotations.JavaScript;
import com.vaadin.annotations.StyleSheet;
import com.vaadin.ui.Dependency;

/**
* Filter for dependencies loaded using {@link StyleSheet @StyleSheet},
* {@link JavaScript @JavaScript} and {@link HtmlImport @HtmlImport}.
*
* @since
*/
public interface DependencyFilter extends Serializable {

/**
* Filters the list of dependencies and returns a (possibly) updated
* version.
* <p>
* Called whenever dependencies are about to be sent to the client side for
* loading.
*
* @param dependencies
* the collected dependencies, possibly already modified by other
* filters
* @param filterContext
* context information, e.g about the target UI
* @return a list of dependencies to load
*/
public List<Dependency> filter(List<Dependency> dependencies,
FilterContext filterContext);

/**
* Provides context information for the dependency filter operation.
*
* @since
*/
public static class FilterContext implements Serializable {

private VaadinSession session;

/**
* Creates a new context for the given session.
*
* @param session
* the session which is loading dependencies
*/
public FilterContext(VaadinSession session) {
this.session = session;
}

/**
* Gets the related Vaadin session.
*
* @return the Vaadin session
*/
public VaadinSession getSession() {
return session;
}

/**
* Gets the related Vaadin service.
*
* @return the Vaadin service
*/
public VaadinService getService() {
return session.getService();
}
}
}

+ 51
- 0
server/src/main/java/com/vaadin/server/VaadinService.java View File

@@ -135,6 +135,7 @@ public abstract class VaadinService implements Serializable {
private ClassLoader classLoader;

private Iterable<RequestHandler> requestHandlers;
private Iterable<DependencyFilter> dependencyFilters;

private boolean atmosphereAvailable = checkAtmosphereSupport();

@@ -206,6 +207,8 @@ public abstract class VaadinService implements Serializable {

requestHandlers = Collections.unmodifiableCollection(handlers);

dependencyFilters = Collections
.unmodifiableCollection(createDependencyFilters());
initialized = true;
}

@@ -1429,6 +1432,54 @@ public abstract class VaadinService implements Serializable {
return requestHandlers;
}

/**
* Constructs the list of resource dependency filters to use for the
* application.
* <p>
* The filters can freely update the dependencies in any way they see fit
* (bundle, rewrite, merge).
* <p>
* By default all filters found using the service loader are added to the
* list.
* <p>
* The filters are called in the order the service loader returns the
* filters, which is undefined. If you need a specific order, you can
* override this method and alter the order.
*
* @since
* @return the list of dependency filters to use for filtering resources,
* not null
* @throws ServiceException
* if something went wrong while determining the filters
*
*/
protected List<DependencyFilter> createDependencyFilters()
throws ServiceException {
ArrayList<DependencyFilter> filters = new ArrayList<>();
ServiceLoader<DependencyFilter> loader = ServiceLoader
.load(DependencyFilter.class, getClassLoader());
loader.iterator().forEachRemaining(filters::add);

return filters;
}

/**
* Gets the filters which all resource dependencies are passed through
* before being sent to the client for loading.
*
* @see #createDependencyFilters()
*
* @since
* @return the dependency filters to pass resources dependencies through
* before loading
*/
public Iterable<DependencyFilter> getDependencyFilters() {
if (dependencyFilters == null) {
return Collections.emptyList();
}
return dependencyFilters;
}

/**
* Handles the incoming request and writes the response into the response
* object. Uses {@link #getRequestHandlers()} for handling the request.

+ 3
- 1
server/src/main/java/com/vaadin/server/communication/UidlWriter.java View File

@@ -30,6 +30,7 @@ import java.util.logging.Level;
import java.util.logging.Logger;

import com.vaadin.server.ClientConnector;
import com.vaadin.server.DependencyFilter.FilterContext;
import com.vaadin.server.JsonPaintTarget;
import com.vaadin.server.LegacyCommunicationManager;
import com.vaadin.server.LegacyCommunicationManager.ClientCache;
@@ -283,7 +284,8 @@ public class UidlWriter implements Serializable {
});

List<Dependency> dependencies = Dependency
.findDependencies(newConnectorTypes, manager);
.findAndFilterDependencies(newConnectorTypes, manager,
new FilterContext(session));

// Include dependencies in output if there are any
if (!dependencies.isEmpty()) {

+ 32
- 1
server/src/main/java/com/vaadin/ui/Dependency.java View File

@@ -24,7 +24,10 @@ import com.vaadin.annotations.HtmlImport;
import com.vaadin.annotations.JavaScript;
import com.vaadin.annotations.StyleSheet;
import com.vaadin.server.ClientConnector;
import com.vaadin.server.DependencyFilter;
import com.vaadin.server.DependencyFilter.FilterContext;
import com.vaadin.server.LegacyCommunicationManager;
import com.vaadin.server.VaadinService;

/**
* Represents a stylesheet or JavaScript to include on the page.
@@ -152,7 +155,7 @@ public class Dependency implements Serializable {
* @param manager
* a reference to the communication manager which tracks
* dependencies
* @return
* @return the list of found dependencies
*/
@SuppressWarnings("deprecation")
public static List<Dependency> findDependencies(
@@ -172,4 +175,32 @@ public class Dependency implements Serializable {
return dependencies;
}

/**
* Finds all the URLs defined for the given classes, registers the URLs to
* the communication manager, passes the registered dependencies through any
* defined filters and returns the filtered collection of dependencies to
* load.
*
* @since
* @param connectorTypes
* the collection of connector classes to scan
* @param manager
* a reference to the communication manager which tracks
* dependencies
* @param context
* the context information for the filtering operation
* @return the list of found and filtered dependencies
*/
public static List<Dependency> findAndFilterDependencies(
List<Class<? extends ClientConnector>> connectorTypes,
LegacyCommunicationManager manager, FilterContext context) {
List<Dependency> dependencies = findDependencies(connectorTypes,
manager);
VaadinService service = context.getService();
for (DependencyFilter filter : service.getDependencyFilters()) {
dependencies = filter.filter(dependencies, context);
}
return dependencies;
}

}

+ 19
- 0
test/dependency-rewrite-addon/pom.xml View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-test</artifactId>
<version>8.1-SNAPSHOT</version>
</parent>
<artifactId>vaadin-test-dependency-rewrite-addon</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-test-widget-set-testutil</artifactId>
</dependency>
</dependencies>

</project>

+ 28
- 0
test/dependency-rewrite-addon/src/main/java/com/vaadin/test/dependencyrewriteaddon/RewriteJQueryFilter.java View File

@@ -0,0 +1,28 @@
package com.vaadin.test.dependencyrewriteaddon;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import com.vaadin.server.DependencyFilter;
import com.vaadin.ui.Dependency;
import com.vaadin.ui.Dependency.Type;

public class RewriteJQueryFilter implements DependencyFilter {

@Override
public List<Dependency> filter(List<Dependency> dependencies,
FilterContext filterContext) {
List<Dependency> filtered = new ArrayList<>();
for (Dependency dependency : dependencies) {
if (dependency.getType() == Type.JAVASCRIPT && dependency.getUrl()
.toLowerCase(Locale.ENGLISH).contains("jquery")) {
filtered.add(
new Dependency(Type.JAVASCRIPT, "vaadin://jquery.js"));
} else {
filtered.add(dependency);
}
}
return filtered;
}
}

+ 1
- 0
test/dependency-rewrite-addon/src/main/resources/META-INF/services/com.vaadin.server.DependencyFilter View File

@@ -0,0 +1 @@
com.vaadin.test.dependencyrewriteaddon.RewriteJQueryFilter

+ 30
- 0
test/dependency-rewrite/pom.xml View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-test</artifactId>
<version>8.1-SNAPSHOT</version>
</parent>
<artifactId>vaadin-test-dependency-rewrite</artifactId>
<packaging>war</packaging>

<dependencies>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-test-widget-set-testutil</artifactId>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-client-compiled</artifactId>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-test-dependency-rewrite-addon</artifactId>
<version>${project.version}</version>
</dependency>

</dependencies>

</project>

+ 26
- 0
test/dependency-rewrite/src/main/java/com/vaadin/test/dependencyrewrite/ApplicationDependencyFilter.java View File

@@ -0,0 +1,26 @@
package com.vaadin.test.dependencyrewrite;

import java.util.ArrayList;
import java.util.List;

import com.vaadin.server.DependencyFilter;
import com.vaadin.ui.Dependency;

public class ApplicationDependencyFilter implements DependencyFilter {

@Override
public List<Dependency> filter(List<Dependency> dependencies,
FilterContext filterContext) {
List<Dependency> filtered = new ArrayList<>();
for (Dependency dependency : dependencies) {
if (dependency.getUrl().startsWith("vaadin://")) {
filtered.add(new Dependency(dependency.getType(), dependency
.getUrl().replace("vaadin://", "vaadin://sub/")));
} else {
filtered.add(dependency);
}
}
return filtered;
}

}

+ 34
- 0
test/dependency-rewrite/src/main/java/com/vaadin/test/dependencyrewrite/DependencyDynamicUI.java View File

@@ -0,0 +1,34 @@
package com.vaadin.test.dependencyrewrite;

import com.vaadin.annotations.JavaScript;
import com.vaadin.server.VaadinRequest;
import com.vaadin.ui.Label;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;

public class DependencyDynamicUI extends UI {

@JavaScript("http://jquery.com/jquery-1.2.3.js")
public static class MyJqueryLabel extends Label {
public MyJqueryLabel() {
super("MyJqueryLabel");
}
}

@JavaScript("vaadin://jquery-33.3.3.js")
public static class MyJqueryLabel2 extends Label {
public MyJqueryLabel2() {
super("MyJqueryLabel2");
}
}

@Override
protected void init(VaadinRequest request) {
VerticalLayout layout = new VerticalLayout();
layout.addComponent(new MyJqueryLabel());
layout.addComponent(new MyJqueryLabel2());

setContent(layout);
}

}

+ 21
- 0
test/dependency-rewrite/src/main/java/com/vaadin/test/dependencyrewrite/DependencyInitialUI.java View File

@@ -0,0 +1,21 @@
package com.vaadin.test.dependencyrewrite;

import com.vaadin.annotations.JavaScript;
import com.vaadin.server.VaadinRequest;
import com.vaadin.test.dependencyrewrite.DependencyDynamicUI.MyJqueryLabel;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;

@JavaScript("http://jquery.com/jquery-12.2.3.js")
@JavaScript("vaadin://jquery-323.3.3.js")
public class DependencyInitialUI extends UI {

@Override
protected void init(VaadinRequest request) {
VerticalLayout layout = new VerticalLayout();
layout.addComponent(new MyJqueryLabel());

setContent(layout);
}

}

+ 27
- 0
test/dependency-rewrite/src/main/java/com/vaadin/test/dependencyrewrite/MyService.java View File

@@ -0,0 +1,27 @@
package com.vaadin.test.dependencyrewrite;

import java.util.List;

import com.vaadin.server.DependencyFilter;
import com.vaadin.server.DeploymentConfiguration;
import com.vaadin.server.ServiceException;
import com.vaadin.server.VaadinServlet;
import com.vaadin.server.VaadinServletService;

public class MyService extends VaadinServletService {

public MyService(VaadinServlet servlet,
DeploymentConfiguration deploymentConfiguration)
throws ServiceException {
super(servlet, deploymentConfiguration);
}

@Override
protected List<DependencyFilter> createDependencyFilters()
throws ServiceException {
List<DependencyFilter> list = super.createDependencyFilters();
list.add(new ApplicationDependencyFilter());
return list;
}

}

+ 22
- 0
test/dependency-rewrite/src/main/java/com/vaadin/test/dependencyrewrite/MyUIProvider.java View File

@@ -0,0 +1,22 @@
package com.vaadin.test.dependencyrewrite;

import com.vaadin.server.UIClassSelectionEvent;
import com.vaadin.server.UIProvider;
import com.vaadin.ui.UI;

public class MyUIProvider extends UIProvider {

@Override
public Class<? extends UI> getUIClass(UIClassSelectionEvent event) {
String url = event.getRequest().getPathInfo();
if (url.contains("/dynamic/")) {
return DependencyDynamicUI.class;
} else if (url.contains("/initial/")) {
return DependencyInitialUI.class;
} else {
return null;
}

}

}

+ 22
- 0
test/dependency-rewrite/src/main/java/com/vaadin/test/dependencyrewrite/MyUIServlet.java View File

@@ -0,0 +1,22 @@
package com.vaadin.test.dependencyrewrite;

import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;

import com.vaadin.server.DeploymentConfiguration;
import com.vaadin.server.ServiceException;
import com.vaadin.server.VaadinServlet;
import com.vaadin.server.VaadinServletService;;

@WebServlet(urlPatterns = {
"/*" }, name = "MyUIServlet", asyncSupported = true, initParams = @WebInitParam(name = "UIProvider", value = "com.vaadin.test.dependencyrewrite.MyUIProvider"))
public class MyUIServlet extends VaadinServlet {
@Override
protected VaadinServletService createServletService(
DeploymentConfiguration deploymentConfiguration)
throws ServiceException {
MyService service = new MyService(this, deploymentConfiguration);
service.init();
return service;
}
}

+ 5
- 0
test/dependency-rewrite/src/main/webapp/VAADIN/sub/jquery.js View File

@@ -0,0 +1,5 @@
if (!window.jqueryLoaded) {
window.jqueryLoaded = 1;
} else {
window.jqueryLoaded++;
}

+ 29
- 0
test/dependency-rewrite/src/test/java/com/vaadin/test/dependencyrewrite/DependencyFilterIT.java View File

@@ -0,0 +1,29 @@
package com.vaadin.test.dependencyrewrite;

import org.junit.Assert;
import org.junit.Test;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.phantomjs.PhantomJSDriver;

import com.vaadin.testbench.TestBenchTestCase;

public class DependencyFilterIT extends TestBenchTestCase {

@Test
public void dynamicallyAddedResources() {
setDriver(new PhantomJSDriver());
getDriver().get("http://localhost:8080/dynamic/");
Assert.assertEquals(1L, ((JavascriptExecutor) getDriver())
.executeScript("return window.jqueryLoaded"));
}

@Test
public void initiallyLoadedResources() {
setDriver(new PhantomJSDriver());
getDriver().get("http://localhost:8080/initial/");
// 2 because of https://github.com/vaadin/framework/issues/9181
Assert.assertEquals(2L, ((JavascriptExecutor) getDriver())
.executeScript("return window.jqueryLoaded"));
}

}

+ 2
- 0
test/pom.xml View File

@@ -107,6 +107,8 @@
<module>servlet-containers/jsp-integration</module>
<module>bean-api-validation</module>
<module>bean-impl-validation</module>
<module>dependency-rewrite-addon</module>
<module>dependency-rewrite</module>
</modules>

<build>

Loading…
Cancel
Save