Skip to content
This repository was archived by the owner on May 28, 2018. It is now read-only.

Commit 79d7767

Browse files
Adam LindenthalPetr Bouda
authored andcommitted
J-523 Application WADL not found when Jersey filter not mapped to /*
Change-Id: I0bb19c787a9c4d27c28ad2d3e87c8b8a40f75fae
1 parent a7b449e commit 79d7767

File tree

19 files changed

+818
-29
lines changed

19 files changed

+818
-29
lines changed

containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/ServletContainer.java

Lines changed: 69 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141

4242
import java.io.IOException;
4343
import java.net.URI;
44+
import java.util.Iterator;
45+
import java.util.List;
4446
import java.util.logging.Level;
4547
import java.util.logging.Logger;
4648
import java.util.regex.Pattern;
@@ -62,6 +64,7 @@
6264
import javax.servlet.http.HttpServletRequest;
6365
import javax.servlet.http.HttpServletResponse;
6466

67+
import org.glassfish.jersey.internal.inject.Providers;
6568
import org.glassfish.jersey.internal.util.ExtendedLogger;
6669
import org.glassfish.jersey.internal.util.collection.Value;
6770
import org.glassfish.jersey.server.ApplicationHandler;
@@ -74,6 +77,7 @@
7477
import org.glassfish.jersey.server.spi.ContainerLifecycleListener;
7578
import org.glassfish.jersey.servlet.internal.LocalizationMessages;
7679
import org.glassfish.jersey.servlet.internal.ResponseWriter;
80+
import org.glassfish.jersey.servlet.spi.FilterUrlMappingsProvider;
7781
import org.glassfish.jersey.uri.UriComponent;
7882

7983
/**
@@ -159,6 +163,7 @@ public class ServletContainer extends HttpServlet implements Filter, Container {
159163
private transient ResourceConfig resourceConfig;
160164
private transient Pattern staticContentPattern;
161165
private transient String filterContextPath;
166+
private transient List<String> filterUrlMappings;
162167

163168
private transient volatile ContainerLifecycleListener containerListener;
164169

@@ -432,6 +437,19 @@ public void init(final FilterConfig filterConfig) throws ServletException {
432437
}
433438
}
434439
}
440+
441+
// get the url-pattern defined (e.g.) in the filter-mapping section of web.xml
442+
final FilterUrlMappingsProvider filterUrlMappingsProvider = getFilterUrlMappingsProvider();
443+
if (filterUrlMappingsProvider != null) {
444+
filterUrlMappings = filterUrlMappingsProvider.getFilterUrlMappings(filterConfig);
445+
}
446+
447+
// we need either the url-pattern from the filter mapping (in case of Servlet 3) or specific init-param to
448+
// determine the baseUri and request relative URI. If we do not have either one, the app will most likely
449+
// not work (won't be accessible)
450+
if (filterUrlMappings == null && filterContextPath == null) {
451+
LOGGER.warning(LocalizationMessages.FILTER_CONTEXT_PATH_MISSING());
452+
}
435453
}
436454

437455
@Override
@@ -545,14 +563,14 @@ private void doFilter(final HttpServletRequest request, final HttpServletRespons
545563
try {
546564
final UriBuilder absoluteUriBuilder = UriBuilder.fromUri(request.getRequestURL().toString());
547565

548-
baseUri = (filterContextPath == null)
549-
? absoluteUriBuilder.replacePath(request.getContextPath())
550-
.path("/")
551-
.build()
552-
: absoluteUriBuilder.replacePath(request.getContextPath())
553-
.path(filterContextPath)
554-
.path("/")
555-
.build();
566+
// depending on circumstances, use the correct path to replace in the absolute request URI
567+
final String pickedUrlMapping = pickUrlMapping(request.getRequestURL().toString(), filterUrlMappings);
568+
final String replacingPath = pickedUrlMapping != null
569+
? pickedUrlMapping
570+
: (filterContextPath != null ? filterContextPath : "");
571+
572+
baseUri = absoluteUriBuilder.replacePath(request.getContextPath()).path(replacingPath).path("/").build();
573+
556574

557575
requestUri = absoluteUriBuilder.replacePath(requestURI)
558576
.replaceQuery(ContainerUtils.encodeUnsafeCharacters(queryString))
@@ -590,6 +608,49 @@ private void doFilter(final HttpServletRequest request, final HttpServletRespons
590608
}
591609
}
592610

611+
/**
612+
* Picks the most suitable url mapping (in case more than one is defined) based on the request URI.
613+
*
614+
* @param requestUri String representation of the request URI
615+
* @param filterUrlMappings set of configured filter url-patterns
616+
* @return the most suitable context path, or {@code null} if empty
617+
*/
618+
private String pickUrlMapping(final String requestUri, final List<String> filterUrlMappings) {
619+
if (filterUrlMappings == null || filterUrlMappings.isEmpty()) {
620+
return null;
621+
}
622+
623+
if (filterUrlMappings.size() == 1) {
624+
return filterUrlMappings.get(0);
625+
}
626+
627+
for (final String pattern : filterUrlMappings) {
628+
if (requestUri.contains(pattern)) {
629+
return pattern;
630+
}
631+
}
632+
633+
return null;
634+
}
635+
636+
/**
637+
* Resolve the {@link FilterUrlMappingsProvider} service via hk2.
638+
*
639+
* Will only work in Servlet 3 container, as the older API version
640+
* does not provide access to the filter mapping structure.
641+
*
642+
* @return {@code FilterContextPath} instance, if available, {@code null} otherwise.
643+
*/
644+
private FilterUrlMappingsProvider getFilterUrlMappingsProvider() {
645+
FilterUrlMappingsProvider filterUrlMappingsProvider = null;
646+
final Iterator<FilterUrlMappingsProvider> providers = Providers.getAllProviders(
647+
getApplicationHandler().getServiceLocator(), FilterUrlMappingsProvider.class).iterator();
648+
if (providers.hasNext()) {
649+
filterUrlMappingsProvider = providers.next();
650+
}
651+
return filterUrlMappingsProvider;
652+
}
653+
593654
/**
594655
* Get the static content path pattern.
595656
*

containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebComponent.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@
103103
import org.glassfish.jersey.servlet.internal.spi.ServletContainerProvider;
104104
import org.glassfish.jersey.servlet.spi.AsyncContextDelegate;
105105
import org.glassfish.jersey.servlet.spi.AsyncContextDelegateProvider;
106+
import org.glassfish.jersey.servlet.spi.FilterUrlMappingsProvider;
106107
import org.glassfish.jersey.uri.UriComponent;
107108

108109
import org.glassfish.hk2.api.Factory;
@@ -297,6 +298,7 @@ public void dispose(final WebConfig instance) {
297298
}).to(WebConfig.class).in(Singleton.class);
298299

299300
install(new ServiceFinderBinder<>(AsyncContextDelegateProvider.class, applicationProperties, RuntimeType.SERVER));
301+
install(new ServiceFinderBinder<>(FilterUrlMappingsProvider.class, applicationProperties, RuntimeType.SERVER));
300302
}
301303
}
302304

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3+
*
4+
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved.
5+
*
6+
* The contents of this file are subject to the terms of either the GNU
7+
* General Public License Version 2 only ("GPL") or the Common Development
8+
* and Distribution License("CDDL") (collectively, the "License"). You
9+
* may not use this file except in compliance with the License. You can
10+
* obtain a copy of the License at
11+
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
12+
* or packager/legal/LICENSE.txt. See the License for the specific
13+
* language governing permissions and limitations under the License.
14+
*
15+
* When distributing the software, include this License Header Notice in each
16+
* file and include the License file at packager/legal/LICENSE.txt.
17+
*
18+
* GPL Classpath Exception:
19+
* Oracle designates this particular file as subject to the "Classpath"
20+
* exception as provided by Oracle in the GPL Version 2 section of the License
21+
* file that accompanied this code.
22+
*
23+
* Modifications:
24+
* If applicable, add the following below the License Header, with the fields
25+
* enclosed by brackets [] replaced by your own identifying information:
26+
* "Portions Copyright [year] [name of copyright owner]"
27+
*
28+
* Contributor(s):
29+
* If you wish your version of this file to be governed by only the CDDL or
30+
* only the GPL Version 2, indicate your decision by adding "[Contributor]
31+
* elects to include this software in this distribution under the [CDDL or GPL
32+
* Version 2] license." If you don't indicate a single choice of license, a
33+
* recipient has the option to distribute your version of this file under
34+
* either the CDDL, the GPL Version 2 or to extend the choice of license to
35+
* its licensees as provided above. However, if you add GPL Version 2 code
36+
* and therefore, elected the GPL Version 2 license, then the option applies
37+
* only if the new code is made subject to such option by the copyright
38+
* holder.
39+
*/
40+
package org.glassfish.jersey.servlet.spi;
41+
42+
import javax.servlet.FilterConfig;
43+
import java.util.List;
44+
45+
/**
46+
* Provides an access to context path from the filter configuration.
47+
*
48+
* @author Adam Lindenthal (adam.lindenthal at oracle.com)
49+
*/
50+
public interface FilterUrlMappingsProvider {
51+
52+
/**
53+
* Return configured context path from the filter configuration.
54+
*
55+
* @param filterConfig the {@link FilterConfig} object
56+
* @returns the {@code List} of url-patterns
57+
*/
58+
List<String> getFilterUrlMappings(final FilterConfig filterConfig);
59+
}

containers/jersey-servlet-core/src/main/resources/org/glassfish/jersey/servlet/internal/localization.properties

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,7 @@ resource.config.unable.to.load=Resource configuration class {0} could not be loa
5252
servlet.path.mismatch=The servlet path {0} does not start with the filter context path {1}.
5353
servlet.request.suspend.failed=Attempt to put servlet request into asynchronous mode has failed. Please check your servlet configuration \
5454
- all Servlet instances and Servlet filters involved in the request processing must explicitly declare support for asynchronous request processing.
55-
header.value.read.failed=Attempt to read the header value failed.
55+
header.value.read.failed=Attempt to read the header value failed.
56+
filter.context.path.missing=The root of the app was not properly defined. Either use a Servlet 3.x container or add \
57+
an init-param 'jersey.config.servlet.filter.contextPath' to the filter configuration. Due to Servlet 2.x API, Jersey cannot \
58+
determine the request base URI solely from the ServletContext. The application will most likely not work.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3+
*
4+
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved.
5+
*
6+
* The contents of this file are subject to the terms of either the GNU
7+
* General Public License Version 2 only ("GPL") or the Common Development
8+
* and Distribution License("CDDL") (collectively, the "License"). You
9+
* may not use this file except in compliance with the License. You can
10+
* obtain a copy of the License at
11+
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
12+
* or packager/legal/LICENSE.txt. See the License for the specific
13+
* language governing permissions and limitations under the License.
14+
*
15+
* When distributing the software, include this License Header Notice in each
16+
* file and include the License file at packager/legal/LICENSE.txt.
17+
*
18+
* GPL Classpath Exception:
19+
* Oracle designates this particular file as subject to the "Classpath"
20+
* exception as provided by Oracle in the GPL Version 2 section of the License
21+
* file that accompanied this code.
22+
*
23+
* Modifications:
24+
* If applicable, add the following below the License Header, with the fields
25+
* enclosed by brackets [] replaced by your own identifying information:
26+
* "Portions Copyright [year] [name of copyright owner]"
27+
*
28+
* Contributor(s):
29+
* If you wish your version of this file to be governed by only the CDDL or
30+
* only the GPL Version 2, indicate your decision by adding "[Contributor]
31+
* elects to include this software in this distribution under the [CDDL or GPL
32+
* Version 2] license." If you don't indicate a single choice of license, a
33+
* recipient has the option to distribute your version of this file under
34+
* either the CDDL, the GPL Version 2 or to extend the choice of license to
35+
* its licensees as provided above. However, if you add GPL Version 2 code
36+
* and therefore, elected the GPL Version 2 license, then the option applies
37+
* only if the new code is made subject to such option by the copyright
38+
* holder.
39+
*/
40+
package org.glassfish.jersey.servlet.init;
41+
42+
import org.glassfish.jersey.servlet.spi.FilterUrlMappingsProvider;
43+
44+
import javax.servlet.FilterConfig;
45+
import javax.servlet.FilterRegistration;
46+
import java.util.ArrayList;
47+
import java.util.Collection;
48+
import java.util.List;
49+
50+
/**
51+
* Provide all configured context paths (url mappings) of the application deployed using filter.
52+
* <p>
53+
* The url patterns are returned without the eventual trailing asterisk.
54+
* <p>
55+
* The functionality is available in Servlet 3.x environment only, so this
56+
* implementation of {@link FilterUrlMappingsProvider} interface is Servlet 3 specific.
57+
*
58+
* @author Adam Lindenthal (adam.lindenthal at oracle.com)
59+
*/
60+
public class FilterUrlMappingsProviderImpl implements FilterUrlMappingsProvider {
61+
@Override
62+
public List<String> getFilterUrlMappings(FilterConfig filterConfig) {
63+
FilterRegistration filterRegistration =
64+
filterConfig.getServletContext().getFilterRegistration(filterConfig.getFilterName());
65+
66+
Collection<String> urlPatternMappings = filterRegistration.getUrlPatternMappings();
67+
List<String> result = new ArrayList<>();
68+
69+
for (String pattern : urlPatternMappings) {
70+
result.add(pattern.endsWith("*") ? pattern.substring(0, pattern.length() - 1) : pattern);
71+
}
72+
73+
return result;
74+
}
75+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
org.glassfish.jersey.servlet.init.FilterUrlMappingsProviderImpl

docs/src/main/docbook/deployment.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,16 @@ Server server = JettyHttpContainerFactory.createServer(baseUri, config);</progra
555555
</example>
556556
</para>
557557

558+
<important>
559+
<para>
560+
Servlet 2.x API does not provide a way how to programmatically read the filter mappings. To make application
561+
deployed using filter work correctly, either Servlet 3.x container must be used
562+
(<literal>jersey-container-servlet</literal> instead of <literal>jersey-container-servlet-core</literal>), or
563+
the context path of the app needs to be defined using init parameter
564+
<literal>jersey.config.servlet.filter.contextPath</literal>.
565+
</para>
566+
</important>
567+
558568
<para>
559569
The content of the <literal>&lt;init-param&gt;</literal> element will vary depending on the way you decide to
560570
configure Jersey resources.

tests/integration/async-jersey-filter/src/main/webapp/WEB-INF/web.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,16 +68,16 @@
6868

6969
<filter-mapping>
7070
<filter-name>jersey2812</filter-name>
71-
<url-pattern>/async/wait/*</url-pattern>
71+
<url-pattern>/asyncTest/async/wait/*</url-pattern>
7272
</filter-mapping>
7373

7474
<filter-mapping>
7575
<filter-name>jersey</filter-name>
76-
<url-pattern>/exception/*</url-pattern>
76+
<url-pattern>/exceptionTest/*</url-pattern>
7777
</filter-mapping>
7878
<filter-mapping>
7979
<filter-name>jersey</filter-name>
80-
<url-pattern>/async/*</url-pattern>
80+
<url-pattern>/asyncTest/*</url-pattern>
8181
</filter-mapping>
8282

8383
<absolute-ordering />

tests/integration/async-jersey-filter/src/test/java/org/glassfish/jersey/tests/integration/jersey2730/Jersey2730ITCase.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,48 +58,48 @@
5858
public class Jersey2730ITCase extends AbstractAsyncJerseyTest {
5959

6060
private void assertLastThreadNotStuck() {
61-
final boolean lastThreadGotStuck = target("exception/rpc/lastthreadstuck").request().get(boolean.class);
61+
final boolean lastThreadGotStuck = target("/exceptionTest/exception/rpc/lastthreadstuck").request().get(boolean.class);
6262

6363
assertFalse("Thread processing last request got stuck while processing the request for "
6464
+ TestExceptionResource.class.getCanonicalName(),
6565
lastThreadGotStuck);
6666
}
6767

6868
@Test
69-
public void asyncResourceNullThrowableReturns500AndDoesNotStuck() throws Exception {
70-
final Response response = target("/exception/null").request().get();
69+
public void asyncResourceNullThrowableReturns500AndDoesNotStuck() throws Exception {
70+
final Response response = target("/exceptionTest/exception/null").request().get();
7171

7272
assertEquals(500, response.getStatus());
7373
assertLastThreadNotStuck();
7474
}
7575

7676
@Test
77-
public void asyncResourceUnmappedExceptionReturns500AndDoesNotStuck() throws Exception {
78-
final Response response = target("/exception/unmapped").request().get();
77+
public void asyncResourceUnmappedExceptionReturns500AndDoesNotStuck() throws Exception {
78+
final Response response = target("/exceptionTest/exception/unmapped").request().get();
7979

8080
assertEquals(500, response.getStatus());
8181
assertLastThreadNotStuck();
8282
}
8383

8484
@Test
85-
public void asyncResourceUnmappedRuntimeExceptionReturns500AndDoesNotStuck() throws Exception {
86-
final Response response = target("/exception/runtime").request().get();
85+
public void asyncResourceUnmappedRuntimeExceptionReturns500AndDoesNotStuck() throws Exception {
86+
final Response response = target("/exceptionTest/exception/runtime").request().get();
8787

8888
assertEquals(500, response.getStatus());
8989
assertLastThreadNotStuck();
9090
}
9191

9292
@Test
93-
public void asyncResourceMappedExceptionReturns432() throws Exception {
94-
final Response response = target("/exception/mapped").request().get();
93+
public void asyncResourceMappedExceptionReturns432() throws Exception {
94+
final Response response = target("/exceptionTest/exception/mapped").request().get();
9595

9696
assertEquals(432, response.getStatus());
9797
assertLastThreadNotStuck();
9898
}
9999

100100
@Test
101101
public void asyncResourceNonExistentReturns404() throws Exception {
102-
final Response response = target("/exception/notfound").request().get();
102+
final Response response = target("/exceptionTest/exception/notfound").request().get();
103103

104104
assertEquals(404, response.getStatus());
105105
}

0 commit comments

Comments
 (0)