Skip to content

Commit f4a5d6d

Browse files
committed
Add filter to inject scripts into host page from script registry
1 parent 1ff756f commit f4a5d6d

File tree

5 files changed

+254
-60
lines changed

5 files changed

+254
-60
lines changed

errai-cdi/errai-cdi-server/pom.xml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@
4646
</properties>
4747

4848
<dependencies>
49+
<!-- Must come before gwt-user to override servlet api -->
50+
<dependency>
51+
<groupId>org.jboss.spec.javax.servlet</groupId>
52+
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
53+
<scope>provided</scope>
54+
</dependency>
55+
4956
<!-- Errai Core -->
5057
<dependency>
5158
<groupId>org.jboss.errai</groupId>
@@ -185,6 +192,11 @@
185192
<artifactId>errai-data-binding</artifactId>
186193
<scope>provided</scope>
187194
</dependency>
195+
196+
<dependency>
197+
<groupId>org.jsoup</groupId>
198+
<artifactId>jsoup</artifactId>
199+
</dependency>
188200
</dependencies>
189201

190202
<build>
@@ -250,6 +262,12 @@
250262
</build>
251263

252264
<dependencies>
265+
<!-- Must come before gwt-user to override servlet api -->
266+
<dependency>
267+
<groupId>org.jboss.spec.javax.servlet</groupId>
268+
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
269+
<scope>provided</scope>
270+
</dependency>
253271
<dependency>
254272
<groupId>log4j</groupId>
255273
<artifactId>log4j</artifactId>
@@ -412,6 +430,11 @@
412430
<artifactId>javaee-api</artifactId>
413431
<scope>provided</scope>
414432
</dependency>
433+
434+
<dependency>
435+
<groupId>org.jsoup</groupId>
436+
<artifactId>jsoup</artifactId>
437+
</dependency>
415438
</dependencies>
416439
</profile>
417440
</profiles>
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package org.jboss.errai.cdi.server.scripts;
2+
3+
import java.io.CharArrayWriter;
4+
import java.io.IOException;
5+
import java.io.PrintWriter;
6+
7+
import javax.servlet.ServletOutputStream;
8+
import javax.servlet.WriteListener;
9+
import javax.servlet.http.HttpServletResponse;
10+
import javax.servlet.http.HttpServletResponseWrapper;
11+
12+
/**
13+
* Shared utilities for servlet filters in Errai.
14+
*
15+
* @author Christian Sadilek <[email protected]>
16+
*/
17+
public class FilterCacheUtil {
18+
private static final String EXPIRES_HEADER = "Expires";
19+
private static final String CACHE_CONTROL_HEADER = "Cache-Control";
20+
private static final String PRAGMA_HEADER = "Pragma";
21+
22+
private FilterCacheUtil() {}
23+
24+
public static HttpServletResponse noCache(final HttpServletResponse response) {
25+
response.setHeader( CACHE_CONTROL_HEADER, "no-cache, no-store, must-revalidate" );
26+
response.setHeader( PRAGMA_HEADER, "no-cache" );
27+
response.setDateHeader( EXPIRES_HEADER, 0 );
28+
return response;
29+
}
30+
31+
public static CharResponseWrapper getCharResponseWrapper(final HttpServletResponse response) {
32+
return new CharResponseWrapper(response);
33+
}
34+
35+
public static class CharResponseWrapper extends HttpServletResponseWrapper {
36+
37+
protected CharArrayWriter charWriter = new CharArrayWriter();
38+
39+
protected ServletOutputStream outputStream = new ServletOutputStream() {
40+
41+
@Override
42+
public boolean isReady() {
43+
return true;
44+
}
45+
46+
@Override
47+
public void setWriteListener(WriteListener writeListener) {
48+
// no-op
49+
}
50+
51+
@Override
52+
public void write(int b) throws IOException {
53+
charWriter.write(b);
54+
}
55+
};
56+
57+
protected PrintWriter writer = new PrintWriter(charWriter);
58+
59+
public CharResponseWrapper(final HttpServletResponse response) {
60+
super(response);
61+
}
62+
63+
@Override
64+
public ServletOutputStream getOutputStream() throws IOException {
65+
return outputStream;
66+
}
67+
68+
@Override
69+
public PrintWriter getWriter() throws IOException {
70+
return writer;
71+
}
72+
73+
@Override
74+
public void flushBuffer() throws IOException {
75+
// Don't remove this override!
76+
// When intercepting static content, WAS 8.5.5.5 prematurely calls this
77+
// method to flush the output stream before we can calculate the content
78+
// length (see above).
79+
}
80+
81+
@Override
82+
public String toString() {
83+
return charWriter.toString();
84+
}
85+
}
86+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright 2016 Red Hat, Inc. and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.jboss.errai.cdi.server.scripts;
17+
18+
import static org.jboss.errai.cdi.server.scripts.FilterCacheUtil.getCharResponseWrapper;
19+
import static org.jboss.errai.cdi.server.scripts.FilterCacheUtil.noCache;
20+
21+
import java.io.IOException;
22+
23+
import javax.inject.Inject;
24+
import javax.servlet.Filter;
25+
import javax.servlet.FilterChain;
26+
import javax.servlet.FilterConfig;
27+
import javax.servlet.ServletException;
28+
import javax.servlet.ServletRequest;
29+
import javax.servlet.ServletResponse;
30+
import javax.servlet.annotation.WebFilter;
31+
import javax.servlet.http.HttpServletResponse;
32+
33+
import org.jboss.errai.cdi.server.scripts.FilterCacheUtil.CharResponseWrapper;
34+
import org.jsoup.Jsoup;
35+
import org.jsoup.nodes.Document;
36+
37+
/**
38+
* Adds all registered scripts from {@link ScriptRegistry} to the &lt;head&gt;
39+
* of the host page.
40+
*
41+
* @author Christian Sadilek <[email protected]>
42+
*/
43+
@WebFilter(filterName = "ErraiHostPageScriptInjectorFilter", urlPatterns = { "/index.jsp", "/index.html" })
44+
public class HostPageScriptInjectorFilter implements Filter {
45+
46+
@Inject
47+
private ScriptRegistry registry;
48+
49+
@Override
50+
public void init(FilterConfig filterConfig) throws ServletException {
51+
}
52+
53+
@Override
54+
public void destroy() {
55+
}
56+
57+
@Override
58+
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
59+
throws IOException, ServletException {
60+
61+
final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
62+
63+
if (!registry.isEmpty()) {
64+
final CharResponseWrapper wrappedResponse = getCharResponseWrapper((HttpServletResponse) response);
65+
chain.doFilter(request, noCache(wrappedResponse));
66+
67+
final Document document = Jsoup.parse(wrappedResponse.toString());
68+
registry.getAllScripts().forEach(s -> document.head().append("<script src=\"" + s + "\"/>"));
69+
final byte[] outputBytes = document.html().getBytes("UTF-8");
70+
response.setContentLength(outputBytes.length);
71+
response.getOutputStream().write(outputBytes);
72+
}
73+
else {
74+
chain.doFilter(request, noCache(httpServletResponse));
75+
}
76+
}
77+
78+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright 2016 Red Hat, Inc. and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.jboss.errai.cdi.server.scripts;
17+
18+
import java.util.HashMap;
19+
import java.util.HashSet;
20+
import java.util.Map;
21+
import java.util.Set;
22+
import java.util.stream.Collectors;
23+
24+
import javax.enterprise.context.ApplicationScoped;
25+
26+
/**
27+
* Script registry for scripts loaded at runtime (i.e. dynamic plugins).
28+
*
29+
* @author Christian Sadilek <[email protected]>
30+
*/
31+
@ApplicationScoped
32+
public class ScriptRegistry {
33+
34+
private final Map<String, Set<String>> scripts = new HashMap<String, Set<String>>();
35+
36+
public void addScript(final String ns, final String url) {
37+
scripts.computeIfAbsent(ns, k -> new HashSet<String>()).add(url);
38+
}
39+
40+
public void removeScript(final String ns, final String url) {
41+
scripts.getOrDefault(ns, new HashSet<String>()).remove(url);
42+
}
43+
44+
public void removeScripts(final String key) {
45+
scripts.remove(key);
46+
}
47+
48+
public Set<String> getAllScripts() {
49+
return scripts.values().stream().flatMap(s -> s.stream()).collect(Collectors.toSet());
50+
}
51+
52+
public boolean isEmpty() {
53+
return scripts.isEmpty();
54+
}
55+
56+
@Override
57+
public String toString() {
58+
return "ScriptRegistry [scripts=" + scripts + "]";
59+
}
60+
}

errai-security/errai-security-server/src/main/java/org/jboss/errai/security/server/servlet/UserHostPageFilter.java

Lines changed: 7 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -16,26 +16,24 @@
1616

1717
package org.jboss.errai.security.server.servlet;
1818

19+
import static org.jboss.errai.cdi.server.scripts.FilterCacheUtil.getCharResponseWrapper;
20+
import static org.jboss.errai.cdi.server.scripts.FilterCacheUtil.noCache;
1921
import static org.jboss.errai.security.Properties.USER_ON_HOSTPAGE_ENABLED;
2022

21-
import java.io.CharArrayWriter;
2223
import java.io.IOException;
23-
import java.io.PrintWriter;
2424
import java.util.Properties;
2525

2626
import javax.inject.Inject;
2727
import javax.servlet.Filter;
2828
import javax.servlet.FilterChain;
2929
import javax.servlet.FilterConfig;
3030
import javax.servlet.ServletException;
31-
import javax.servlet.ServletOutputStream;
3231
import javax.servlet.ServletRequest;
3332
import javax.servlet.ServletResponse;
34-
import javax.servlet.WriteListener;
3533
import javax.servlet.annotation.WebFilter;
3634
import javax.servlet.http.HttpServletResponse;
37-
import javax.servlet.http.HttpServletResponseWrapper;
3835

36+
import org.jboss.errai.cdi.server.scripts.FilterCacheUtil.CharResponseWrapper;
3937
import org.jboss.errai.marshalling.server.MappingContextSingleton;
4038
import org.jboss.errai.marshalling.server.ServerMarshalling;
4139
import org.jboss.errai.security.server.properties.ErraiAppProperties;
@@ -78,11 +76,11 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
7876
ServletException {
7977

8078
if (!isUserOnHostPageEnabled()) {
81-
chain.doFilter(request, response);
79+
chain.doFilter(request, noCache((HttpServletResponse) response));
8280
}
8381
else {
84-
final CharResponseWrapper wrappedResponse = new CharResponseWrapper((HttpServletResponse) response);
85-
chain.doFilter(request, wrappedResponse);
82+
final CharResponseWrapper wrappedResponse = getCharResponseWrapper((HttpServletResponse) response);
83+
chain.doFilter(request, noCache(wrappedResponse));
8684

8785
final User user = authenticationService.getUser();
8886
final String output;
@@ -101,7 +99,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
10199
output = wrappedResponse.toString();
102100
}
103101

104-
byte[] outputBytes = output.getBytes("UTF-8");
102+
final byte[] outputBytes = output.getBytes("UTF-8");
105103
response.setContentLength(outputBytes.length);
106104
response.getOutputStream().write(outputBytes);
107105
}
@@ -114,55 +112,4 @@ private boolean isUserOnHostPageEnabled() {
114112
return false;
115113
}
116114

117-
class CharResponseWrapper extends HttpServletResponseWrapper {
118-
119-
protected CharArrayWriter charWriter = new CharArrayWriter();
120-
121-
protected ServletOutputStream outputStream = new ServletOutputStream() {
122-
123-
@Override
124-
public boolean isReady() {
125-
return true;
126-
}
127-
128-
@Override
129-
public void setWriteListener(WriteListener writeListener) {
130-
// no-op
131-
}
132-
133-
@Override
134-
public void write(int b) throws IOException {
135-
charWriter.write(b);
136-
}
137-
};
138-
139-
protected PrintWriter writer = new PrintWriter(charWriter);
140-
141-
public CharResponseWrapper(final HttpServletResponse response) {
142-
super(response);
143-
}
144-
145-
@Override
146-
public ServletOutputStream getOutputStream() throws IOException {
147-
return outputStream;
148-
}
149-
150-
@Override
151-
public PrintWriter getWriter() throws IOException {
152-
return writer;
153-
}
154-
155-
@Override
156-
public void flushBuffer() throws IOException {
157-
// Don't remove this override!
158-
// When intercepting static content, WAS 8.5.5.5 prematurely calls this
159-
// method to flush the output stream before we can calculate the content
160-
// length (see above).
161-
}
162-
163-
@Override
164-
public String toString() {
165-
return charWriter.toString();
166-
}
167-
}
168115
}

0 commit comments

Comments
 (0)