You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
901 lines
36 KiB
901 lines
36 KiB
/* |
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
|
* |
|
* Copyright (c) 2010-2013 Oracle and/or its affiliates. All rights reserved. |
|
* |
|
* The contents of this file are subject to the terms of either the GNU |
|
* General Public License Version 2 only ("GPL") or the Common Development |
|
* and Distribution License("CDDL") (collectively, the "License"). You |
|
* may not use this file except in compliance with the License. You can |
|
* obtain a copy of the License at |
|
* http://glassfish.java.net/public/CDDL+GPL_1_1.html |
|
* or packager/legal/LICENSE.txt. See the License for the specific |
|
* language governing permissions and limitations under the License. |
|
* |
|
* When distributing the software, include this License Header Notice in each |
|
* file and include the License file at packager/legal/LICENSE.txt. |
|
* |
|
* GPL Classpath Exception: |
|
* Oracle designates this particular file as subject to the "Classpath" |
|
* exception as provided by Oracle in the GPL Version 2 section of the License |
|
* file that accompanied this code. |
|
* |
|
* Modifications: |
|
* If applicable, add the following below the License Header, with the fields |
|
* enclosed by brackets [] replaced by your own identifying information: |
|
* "Portions Copyright [year] [name of copyright owner]" |
|
* |
|
* Contributor(s): |
|
* If you wish your version of this file to be governed by only the CDDL or |
|
* only the GPL Version 2, indicate your decision by adding "[Contributor] |
|
* elects to include this software in this distribution under the [CDDL or GPL |
|
* Version 2] license." If you don't indicate a single choice of license, a |
|
* recipient has the option to distribute your version of this file under |
|
* either the CDDL, the GPL Version 2 or to extend the choice of license to |
|
* its licensees as provided above. However, if you add GPL Version 2 code |
|
* and therefore, elected the GPL Version 2 license, then the option applies |
|
* only if the new code is made subject to such option by the copyright |
|
* holder. |
|
*/ |
|
|
|
/* gepatchtes class-File: |
|
* im Rahmen von #331613 muss mit dem Update auf Tomcat 10 auch die Anwendung von Serlvet API 4.0 auf 6.0 umgestellt werden. |
|
* Dies leistet ein Migrationstool, welches in erster Linie den Packagenamen von 'javax' auf 'jakarta' umstellt. |
|
* Allerdings bestand danach immer noch in der 'jersey-bunde-1.19.4.jar' ein Zugriff auf die entfernte Methode |
|
* 'HttpServletResponse.setStatus(int, String)', welcher beim Laden von Saiku zu einer Exception geführt hat, |
|
* wodurch das Frontend in der Ladeanzeige geblieben ist. |
|
* Der Patch ändert die Klasse derart, dass sie die noch vorhandene Methode 'HttpServletResponse.setStatus(int)' aufruft, |
|
* was zu funktionieren scheint. |
|
* Ursprung entspricht https://github.com/javaee/jersey-1.x/blob/864a01d7be490ab93d2424da3e446ad8eb84b1e8/jersey-servlet/src/main/java/com/sun/jersey/spi/container/servlet/WebComponent.java |
|
*/ |
|
|
|
package com.sun.jersey.spi.container.servlet; |
|
|
|
import java.io.BufferedInputStream; |
|
import java.io.IOException; |
|
import java.io.InputStream; |
|
import java.io.OutputStream; |
|
import java.lang.reflect.Constructor; |
|
import java.lang.reflect.Proxy; |
|
import java.lang.reflect.Type; |
|
import java.net.URI; |
|
import java.security.Principal; |
|
import java.util.ArrayList; |
|
import java.util.Arrays; |
|
import java.util.Enumeration; |
|
import java.util.HashMap; |
|
import java.util.Iterator; |
|
import java.util.LinkedList; |
|
import java.util.List; |
|
import java.util.Map; |
|
import java.util.logging.Level; |
|
import java.util.logging.Logger; |
|
|
|
import javax.ws.rs.WebApplicationException; |
|
import javax.ws.rs.core.Application; |
|
import javax.ws.rs.core.Context; |
|
import javax.ws.rs.core.GenericEntity; |
|
import javax.ws.rs.core.MediaType; |
|
import javax.ws.rs.core.MultivaluedMap; |
|
import javax.ws.rs.core.Response; |
|
import javax.ws.rs.core.SecurityContext; |
|
|
|
import javax.naming.NamingException; |
|
import jakarta.servlet.ServletContext; |
|
import jakarta.servlet.ServletException; |
|
import jakarta.servlet.http.HttpServletRequest; |
|
import jakarta.servlet.http.HttpServletResponse; |
|
|
|
import com.sun.jersey.api.container.ContainerException; |
|
import com.sun.jersey.api.container.MappableContainerException; |
|
import com.sun.jersey.api.core.ApplicationAdapter; |
|
import com.sun.jersey.api.core.ClasspathResourceConfig; |
|
import com.sun.jersey.api.core.PackagesResourceConfig; |
|
import com.sun.jersey.api.core.ResourceConfig; |
|
import com.sun.jersey.api.core.TraceInformation; |
|
import com.sun.jersey.api.core.servlet.WebAppResourceConfig; |
|
import com.sun.jersey.api.representation.Form; |
|
import com.sun.jersey.core.header.InBoundHeaders; |
|
import com.sun.jersey.core.header.MediaTypes; |
|
import com.sun.jersey.core.reflection.ReflectionHelper; |
|
import com.sun.jersey.core.util.ReaderWriter; |
|
import com.sun.jersey.server.impl.InitialContextHelper; |
|
import com.sun.jersey.server.impl.ThreadLocalInvoker; |
|
import com.sun.jersey.server.impl.application.DeferredResourceConfig; |
|
import com.sun.jersey.server.impl.cdi.CDIComponentProviderFactoryInitializer; |
|
import com.sun.jersey.server.impl.container.servlet.JSPTemplateProcessor; |
|
import com.sun.jersey.server.impl.ejb.EJBComponentProviderFactoryInitilizer; |
|
import com.sun.jersey.server.impl.managedbeans.ManagedBeanComponentProviderFactoryInitilizer; |
|
import com.sun.jersey.server.impl.model.method.dispatch.FormDispatchProvider; |
|
import com.sun.jersey.server.impl.monitoring.GlassFishMonitoringInitializer; |
|
import com.sun.jersey.server.probes.UriRuleProbeProvider; |
|
import com.sun.jersey.spi.container.ContainerListener; |
|
import com.sun.jersey.spi.container.ContainerNotifier; |
|
import com.sun.jersey.spi.container.ContainerRequest; |
|
import com.sun.jersey.spi.container.ContainerResponse; |
|
import com.sun.jersey.spi.container.ContainerResponseWriter; |
|
import com.sun.jersey.spi.container.ReloadListener; |
|
import com.sun.jersey.spi.container.WebApplication; |
|
import com.sun.jersey.spi.container.WebApplicationFactory; |
|
import com.sun.jersey.spi.inject.SingletonTypeInjectableProvider; |
|
import java.security.AccessController; |
|
import java.security.PrivilegedActionException; |
|
|
|
/** |
|
* An abstract Web component that may be extended a Servlet and/or |
|
* Filter implementation, or encapsulated by a Servlet or Filter implementation. |
|
* |
|
* @author Paul Sandoz (paul.sandoz at oracle.com) |
|
*/ |
|
public class WebComponent implements ContainerListener { |
|
/** |
|
* The servlet initialization property whose value is a fully qualified |
|
* class name of a class that implements {@link ResourceConfig} or |
|
* {@link Application}. |
|
*/ |
|
public static final String APPLICATION_CONFIG_CLASS = |
|
"javax.ws.rs.Application"; |
|
|
|
/** |
|
* The servlet initialization property whose value is a fully qualified |
|
* class name of a class that implements {@link ResourceConfig} or |
|
* {@link Application}. |
|
*/ |
|
public static final String RESOURCE_CONFIG_CLASS = |
|
"com.sun.jersey.config.property.resourceConfigClass"; |
|
|
|
/** |
|
* The base path in the Web Pages where JSP templates, associated with |
|
* viewables of resource classes, are located. |
|
* <p/> |
|
* If this property is not set then the base path will be the root path |
|
* of the Web Pages. |
|
*/ |
|
public static final String JSP_TEMPLATES_BASE_PATH = |
|
"com.sun.jersey.config.property.JSPTemplatesBasePath"; |
|
|
|
|
|
private static final Logger LOGGER = |
|
Logger.getLogger(WebComponent.class.getName()); |
|
|
|
private final ThreadLocalInvoker<HttpServletRequest> requestInvoker = |
|
new ThreadLocalInvoker<HttpServletRequest>(); |
|
|
|
private final ThreadLocalInvoker<HttpServletResponse> responseInvoker = |
|
new ThreadLocalInvoker<HttpServletResponse>(); |
|
|
|
|
|
private WebConfig config; |
|
|
|
private ResourceConfig resourceConfig; |
|
|
|
private WebApplication application; |
|
|
|
public WebComponent() { |
|
} |
|
|
|
public WebComponent(Application app) { |
|
if (app == null) |
|
throw new IllegalArgumentException(); |
|
|
|
if (app instanceof ResourceConfig) { |
|
resourceConfig = (ResourceConfig) app; |
|
} else { |
|
resourceConfig = new ApplicationAdapter(app); |
|
} |
|
} |
|
|
|
/** |
|
* Get the Web configuration. |
|
* |
|
* @return the Web configuration. |
|
*/ |
|
public WebConfig getWebConfig() { |
|
return config; |
|
} |
|
|
|
/** |
|
* Get the resource configuration. |
|
* |
|
* @return the resource configuration. |
|
*/ |
|
public ResourceConfig getResourceConfig() { |
|
return resourceConfig; |
|
} |
|
|
|
/** |
|
* Initiate the Web component. |
|
* |
|
* @param webConfig the Web configuration. |
|
* @throws javax.servlet.ServletException in case of any initialization error |
|
*/ |
|
public void init(WebConfig webConfig) throws ServletException { |
|
config = webConfig; |
|
|
|
if (resourceConfig == null) |
|
resourceConfig = createResourceConfig(config); |
|
|
|
load(); |
|
|
|
Object o = resourceConfig.getProperties().get( |
|
ResourceConfig.PROPERTY_CONTAINER_NOTIFIER); |
|
if (o instanceof List) { |
|
List list = (List) o; |
|
for (Object elem : list) { |
|
if (elem instanceof ContainerNotifier) { |
|
ContainerNotifier crf = (ContainerNotifier) elem; |
|
crf.addListener(this); |
|
} |
|
} |
|
} else if (o instanceof ContainerNotifier) { |
|
ContainerNotifier crf = (ContainerNotifier) o; |
|
crf.addListener(this); |
|
} |
|
} |
|
|
|
/** |
|
* Destroy this Web component. |
|
* <p/> |
|
* This will destroy the Web application created by this this Web component. |
|
*/ |
|
public void destroy() { |
|
if (application != null) |
|
application.destroy(); |
|
} |
|
|
|
private final static class Writer extends OutputStream implements ContainerResponseWriter { |
|
final HttpServletResponse response; |
|
|
|
ContainerResponse cResponse; |
|
|
|
long contentLength; |
|
|
|
OutputStream out; |
|
|
|
boolean statusAndHeadersWritten = false; |
|
|
|
Writer(HttpServletResponse response) { |
|
this.response = response; |
|
} |
|
|
|
public OutputStream writeStatusAndHeaders(long contentLength, |
|
ContainerResponse cResponse) throws IOException { |
|
this.contentLength = contentLength; |
|
this.cResponse = cResponse; |
|
this.statusAndHeadersWritten = false; |
|
return this; |
|
} |
|
|
|
public void finish() throws IOException { |
|
if (statusAndHeadersWritten) |
|
return; |
|
|
|
// Note that the writing of headers MUST be performed before |
|
// the invocation of sendError as on some Servlet implementations |
|
// modification of the response headers will have no effect |
|
// after the invocation of sendError. |
|
writeHeaders(); |
|
|
|
|
|
writeStatus(); |
|
} |
|
|
|
private void writeStatus() { |
|
Response.StatusType statusType = cResponse.getStatusType(); |
|
final String reasonPhrase = statusType.getReasonPhrase(); |
|
|
|
// if (reasonPhrase != null) { |
|
// response.setStatus(statusType.getStatusCode(), reasonPhrase); |
|
// } else { |
|
response.setStatus(statusType.getStatusCode()); |
|
// } |
|
} |
|
|
|
public void write(int b) throws IOException { |
|
initiate(); |
|
out.write(b); |
|
} |
|
|
|
@Override |
|
public void write(byte b[]) throws IOException { |
|
if (b.length > 0) { |
|
initiate(); |
|
out.write(b); |
|
} |
|
} |
|
|
|
@Override |
|
public void write(byte b[], int off, int len) throws IOException { |
|
if (len > 0) { |
|
initiate(); |
|
out.write(b, off, len); |
|
} |
|
} |
|
|
|
@Override |
|
public void flush() throws IOException { |
|
writeStatusAndHeaders(); |
|
if (out != null) |
|
out.flush(); |
|
} |
|
|
|
@Override |
|
public void close() throws IOException { |
|
initiate(); |
|
out.close(); |
|
} |
|
|
|
void initiate() throws IOException { |
|
if (out == null) { |
|
writeStatusAndHeaders(); |
|
out = response.getOutputStream(); |
|
} |
|
} |
|
|
|
void writeStatusAndHeaders() { |
|
if (statusAndHeadersWritten) |
|
return; |
|
|
|
writeHeaders(); |
|
writeStatus(); |
|
statusAndHeadersWritten = true; |
|
} |
|
|
|
void writeHeaders() { |
|
if (contentLength != -1 && contentLength < Integer.MAX_VALUE) |
|
response.setContentLength((int) contentLength); |
|
|
|
MultivaluedMap<String, Object> headers = cResponse.getHttpHeaders(); |
|
for (Map.Entry<String, List<Object>> e : headers.entrySet()) { |
|
for (Object v : e.getValue()) { |
|
response.addHeader(e.getKey(), ContainerResponse.getHeaderValue(v)); |
|
} |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* Dispatch client requests to a resource class. |
|
* |
|
* @param baseUri the base URI of the request. |
|
* @param requestUri the URI of the request. |
|
* @param request the {@link HttpServletRequest} object that |
|
* contains the request the client made to |
|
* the Web component. |
|
* @param response the {@link HttpServletResponse} object that |
|
* contains the response the Web component returns |
|
* to the client. |
|
* @return the status code of the response. |
|
* @throws IOException if an input or output error occurs |
|
* while the Web component is handling the |
|
* HTTP request. |
|
* @throws ServletException if the HTTP request cannot |
|
* be handled. |
|
*/ |
|
public int service(URI baseUri, URI requestUri, |
|
final HttpServletRequest request, |
|
final HttpServletResponse response) |
|
throws ServletException, IOException { |
|
// Copy the application field to local instance to ensure that the |
|
// currently loaded web application is used to process |
|
// request |
|
final WebApplication _application = application; |
|
|
|
final ContainerRequest cRequest = createRequest( |
|
_application, |
|
request, |
|
baseUri, |
|
requestUri); |
|
|
|
cRequest.setSecurityContext(new SecurityContext() { |
|
public Principal getUserPrincipal() { |
|
return request.getUserPrincipal(); |
|
} |
|
|
|
public boolean isUserInRole(String role) { |
|
return request.isUserInRole(role); |
|
} |
|
|
|
public boolean isSecure() { |
|
return request.isSecure(); |
|
} |
|
|
|
public String getAuthenticationScheme() { |
|
return request.getAuthType(); |
|
} |
|
}); |
|
|
|
try { |
|
// Check if any servlet filters have consumed a request entity |
|
// of the media type application/x-www-form-urlencoded |
|
// This can happen if a filter calls request.getParameter(...) |
|
filterFormParameters(request, cRequest); |
|
|
|
UriRuleProbeProvider.requestStart(requestUri); |
|
|
|
requestInvoker.set(request); |
|
responseInvoker.set(response); |
|
|
|
final Writer w = new Writer(response); |
|
_application.handleRequest(cRequest, w); |
|
return w.cResponse.getStatus(); |
|
} catch (WebApplicationException ex) { |
|
final Response exResponse = ex.getResponse(); |
|
final String entity = exResponse.getEntity() != null ? exResponse.getEntity().toString() : null; |
|
|
|
response.sendError(exResponse.getStatus(), entity); |
|
|
|
return exResponse.getStatus(); |
|
} catch (MappableContainerException ex) { |
|
traceOnException(cRequest, response); |
|
throw new ServletException(ex.getCause()); |
|
} catch (ContainerException ex) { |
|
traceOnException(cRequest, response); |
|
throw new ServletException(ex); |
|
} catch (RuntimeException ex) { |
|
traceOnException(cRequest, response); |
|
throw ex; |
|
} finally { |
|
UriRuleProbeProvider.requestEnd(); |
|
requestInvoker.set(null); |
|
responseInvoker.set(null); |
|
} |
|
} |
|
|
|
/** |
|
* Extension point for creating your custom container request. |
|
* |
|
* @param app the web app |
|
* @param request the current servlet api request |
|
* @param baseUri the base uri |
|
* @param requestUri the request uri |
|
* @return the request container |
|
* @throws IOException if any error occurs when getting the input stream |
|
*/ |
|
protected ContainerRequest createRequest(WebApplication app, |
|
HttpServletRequest request, URI baseUri, URI requestUri) throws IOException { |
|
return new ContainerRequest( |
|
app, |
|
request.getMethod(), |
|
baseUri, |
|
requestUri, |
|
getHeaders(request), |
|
request.getInputStream()); |
|
} |
|
|
|
private void traceOnException(final ContainerRequest cRequest, final HttpServletResponse response) { |
|
if (cRequest.isTracingEnabled()) { |
|
final TraceInformation ti = (TraceInformation) cRequest.getProperties(). |
|
get(TraceInformation.class.getName()); |
|
|
|
ti.addTraceHeaders(new TraceInformation.TraceHeaderListener() { |
|
public void onHeader(String name, String value) { |
|
response.addHeader(name, value); |
|
} |
|
}); |
|
} |
|
} |
|
|
|
/** |
|
* Create a new instance of a {@link WebApplication}. |
|
* |
|
* @return the {@link WebApplication} instance. |
|
*/ |
|
protected WebApplication create() { |
|
return WebApplicationFactory.createWebApplication(); |
|
} |
|
|
|
/** |
|
* A helper class for creating an injectable provider that supports |
|
* {@link Context} with a type and constant value. |
|
* |
|
* @param <T> the type of the constant value. |
|
*/ |
|
protected static class ContextInjectableProvider<T> extends |
|
SingletonTypeInjectableProvider<Context, T> { |
|
|
|
/** |
|
* Create a new instance. |
|
* |
|
* @param type the type of the constant value. |
|
* @param instance the constant value. |
|
*/ |
|
protected ContextInjectableProvider(Type type, T instance) { |
|
super(type, instance); |
|
} |
|
} |
|
|
|
/** |
|
* Configure the {@link ResourceConfig}. |
|
* <p/> |
|
* The {@link ResourceConfig} is configured such that the following classes |
|
* may be injected onto the field of a root resource class or a parameter |
|
* of a method of root resource class that is annotated with |
|
* {@link javax.ws.rs.core.Context}: {@link HttpServletRequest}, {@link HttpServletResponse} |
|
* , {@link ServletContext} and {@link WebConfig}. |
|
* <p/> |
|
* Any root resource class in registered in the resource configuration |
|
* that is an interface is processed as follows. |
|
* If the class is an interface and there exists a JNDI named object |
|
* with the fully qualified class name as the JNDI name then that named |
|
* object is added as a singleton root resource and the class is removed |
|
* from the set of root resource classes. |
|
* <p/> |
|
* An inheriting class may override this method to configure the |
|
* {@link ResourceConfig} to provide alternative or additional instances |
|
* that are resource or provider classes or instances, and may modify the |
|
* features and properties of the {@link ResourceConfig}. For an inheriting |
|
* class to extend configuration behaviour the overriding method MUST call |
|
* <code>super.configure(servletConfig, rc, wa)</code> as the first statement |
|
* of that method. |
|
* <p/> |
|
* This method will be called only once at initiation. Subsequent |
|
* reloads of the Web application will not result in subsequence calls to |
|
* this method. |
|
* |
|
* @param wc the Web configuration |
|
* @param rc the Resource configuration |
|
* @param wa the Web application |
|
*/ |
|
protected void configure(WebConfig wc, ResourceConfig rc, WebApplication wa) { |
|
configureJndiResources(rc); |
|
|
|
rc.getSingletons().add(new ContextInjectableProvider<HttpServletRequest>( |
|
HttpServletRequest.class, |
|
(HttpServletRequest) Proxy.newProxyInstance( |
|
this.getClass().getClassLoader(), |
|
new Class[]{HttpServletRequest.class}, |
|
requestInvoker))); |
|
|
|
rc.getSingletons().add(new ContextInjectableProvider<HttpServletResponse>( |
|
HttpServletResponse.class, |
|
(HttpServletResponse) Proxy.newProxyInstance( |
|
this.getClass().getClassLoader(), |
|
new Class[]{HttpServletResponse.class}, |
|
responseInvoker))); |
|
|
|
GenericEntity<ThreadLocal<HttpServletRequest>> requestThreadLocal = |
|
new GenericEntity<ThreadLocal<HttpServletRequest>>(requestInvoker.getImmutableThreadLocal()) { |
|
}; |
|
rc.getSingletons().add(new ContextInjectableProvider<ThreadLocal<HttpServletRequest>>( |
|
requestThreadLocal.getType(), requestThreadLocal.getEntity())); |
|
|
|
GenericEntity<ThreadLocal<HttpServletResponse>> responseThreadLocal = |
|
new GenericEntity<ThreadLocal<HttpServletResponse>>(responseInvoker.getImmutableThreadLocal()) { |
|
}; |
|
rc.getSingletons().add(new ContextInjectableProvider<ThreadLocal<HttpServletResponse>>( |
|
responseThreadLocal.getType(), responseThreadLocal.getEntity())); |
|
|
|
rc.getSingletons().add(new ContextInjectableProvider<ServletContext>( |
|
ServletContext.class, |
|
wc.getServletContext())); |
|
|
|
rc.getSingletons().add(new ContextInjectableProvider<WebConfig>( |
|
WebConfig.class, |
|
wc)); |
|
|
|
rc.getClasses().add(JSPTemplateProcessor.class); |
|
|
|
// TODO |
|
// If CDI is enabled should EJB injection be disabled? |
|
EJBComponentProviderFactoryInitilizer.initialize(rc); |
|
|
|
CDIComponentProviderFactoryInitializer.initialize(wc, rc, wa); |
|
|
|
// TODO |
|
// If CDI is enabled then no need to initialize managed beans |
|
ManagedBeanComponentProviderFactoryInitilizer.initialize(rc); |
|
|
|
GlassFishMonitoringInitializer.initialize(); |
|
} |
|
|
|
/** |
|
* Initiate the {@link WebApplication}. |
|
* <p/> |
|
* This method will be called once at initiation and for |
|
* each reload of the Web application. |
|
* <p/> |
|
* An inheriting class may override this method to initiate the |
|
* Web application with different parameters. |
|
* |
|
* @param rc the Resource configuration |
|
* @param wa the Web application |
|
*/ |
|
protected void initiate(ResourceConfig rc, WebApplication wa) { |
|
wa.initiate(rc); |
|
} |
|
|
|
|
|
/** |
|
* Load the Web application. This will create, configure and initiate |
|
* the web application. |
|
*/ |
|
public void load() { |
|
WebApplication _application = create(); |
|
configure(config, resourceConfig, _application); |
|
initiate(resourceConfig, _application); |
|
application = _application; |
|
} |
|
|
|
/** |
|
* Get the default resource configuration if one is not declared in the |
|
* web.xml. |
|
* <p/> |
|
* This implementation returns an instance of {@link WebAppResourceConfig} |
|
* that scans in files and directories as declared by the |
|
* {@link ClasspathResourceConfig#PROPERTY_CLASSPATH} if present, otherwise |
|
* in the "WEB-INF/lib" and "WEB-INF/classes" directories. |
|
* <p/> |
|
* An inheriting class may override this method to supply a different |
|
* default resource configuration implementation. |
|
* |
|
* @param props the properties to pass to the resource configuration. |
|
* @param wc the web configuration. |
|
* @return the default resource configuration. |
|
* @throws javax.servlet.ServletException in case of any issues with providing \ |
|
* the default resource configuration |
|
*/ |
|
protected ResourceConfig getDefaultResourceConfig(Map<String, Object> props, |
|
WebConfig wc) throws ServletException { |
|
return getWebAppResourceConfig(props, wc); |
|
} |
|
|
|
|
|
// ContainerListener |
|
|
|
/** |
|
* Reload the Web application. This will create and initiate the web |
|
* application using the same {@link ResourceConfig} implementation |
|
* that was used to load the Web application. |
|
* <p/> |
|
* This method may be called at runtime, more than once, to reload the |
|
* Web application. For example, if a {@link ResourceConfig} implementation |
|
* is capable of detecting changes to resource classes (addition or removal) |
|
* or providers then this method may be invoked to reload the web |
|
* application for such changes to take effect. |
|
* <p/> |
|
* If this method is called when there are pending requests then such |
|
* requests will be processed using the previously loaded web application. |
|
*/ |
|
public void onReload() { |
|
WebApplication oldApplication = application; |
|
WebApplication newApplication = create(); |
|
initiate(resourceConfig, newApplication); |
|
application = newApplication; |
|
|
|
if (resourceConfig instanceof ReloadListener) |
|
((ReloadListener) resourceConfig).onReload(); |
|
|
|
oldApplication.destroy(); |
|
|
|
} |
|
|
|
|
|
// |
|
|
|
/* package */ ResourceConfig getWebAppResourceConfig(Map<String, Object> props, |
|
WebConfig webConfig) throws ServletException { |
|
// Default to using Web app resource config |
|
return new WebAppResourceConfig(props, webConfig.getServletContext()); |
|
} |
|
|
|
private ResourceConfig createResourceConfig(WebConfig webConfig) |
|
throws ServletException { |
|
final Map<String, Object> props = getInitParams(webConfig); |
|
final ResourceConfig rc = createResourceConfig(webConfig, props); |
|
rc.setPropertiesAndFeatures(props); |
|
return rc; |
|
} |
|
|
|
private ResourceConfig createResourceConfig(WebConfig webConfig, Map<String, Object> props) |
|
throws ServletException { |
|
// Check if the resource config class property is present |
|
String resourceConfigClassName = webConfig.getInitParameter(RESOURCE_CONFIG_CLASS); |
|
// Otherwise check if the JAX-RS application config class property is |
|
// present |
|
if (resourceConfigClassName == null) |
|
resourceConfigClassName = webConfig.getInitParameter(APPLICATION_CONFIG_CLASS); |
|
|
|
// If no resource config class property is present |
|
if (resourceConfigClassName == null) { |
|
// If the packages property is present then |
|
// use the packages resource config |
|
String packages = webConfig.getInitParameter( |
|
PackagesResourceConfig.PROPERTY_PACKAGES); |
|
if (packages != null) { |
|
props.put(PackagesResourceConfig.PROPERTY_PACKAGES, packages); |
|
return new PackagesResourceConfig(props); |
|
} |
|
|
|
ResourceConfig defaultConfig = webConfig.getDefaultResourceConfig(props); |
|
if (defaultConfig != null) |
|
return defaultConfig; |
|
|
|
return getDefaultResourceConfig(props, webConfig); |
|
} |
|
|
|
try { |
|
Class<?> resourceConfigClass = AccessController.doPrivileged(ReflectionHelper. |
|
classForNameWithExceptionPEA(resourceConfigClassName)); |
|
|
|
// TODO add support for WebAppResourceConfig |
|
if (resourceConfigClass == ClasspathResourceConfig.class) { |
|
String[] paths = getPaths(webConfig.getInitParameter( |
|
ClasspathResourceConfig.PROPERTY_CLASSPATH)); |
|
props.put(ClasspathResourceConfig.PROPERTY_CLASSPATH, paths); |
|
return new ClasspathResourceConfig(props); |
|
} else if (ResourceConfig.class.isAssignableFrom(resourceConfigClass)) { |
|
try { |
|
Constructor constructor = resourceConfigClass.getConstructor(Map.class); |
|
if (ClasspathResourceConfig.class.isAssignableFrom(resourceConfigClass)) { |
|
String[] paths = getPaths(webConfig.getInitParameter( |
|
ClasspathResourceConfig.PROPERTY_CLASSPATH)); |
|
props.put(ClasspathResourceConfig.PROPERTY_CLASSPATH, paths); |
|
} |
|
return (ResourceConfig) constructor.newInstance(props); |
|
} catch (NoSuchMethodException ex) { |
|
// Pass through and try the default constructor |
|
} catch (Exception e) { |
|
throw new ServletException(e); |
|
} |
|
|
|
return new DeferredResourceConfig(resourceConfigClass.asSubclass(ResourceConfig.class)); |
|
} else if (Application.class.isAssignableFrom(resourceConfigClass)) { |
|
return new DeferredResourceConfig(resourceConfigClass.asSubclass(Application.class)); |
|
} else { |
|
String message = "Resource configuration class, " + resourceConfigClassName + |
|
", is not a super class of " + Application.class; |
|
throw new ServletException(message); |
|
} |
|
} catch (ClassNotFoundException e) { |
|
String message = "Resource configuration class, " + resourceConfigClassName + |
|
", could not be loaded"; |
|
throw new ServletException(message, e); |
|
} catch (PrivilegedActionException e) { |
|
String message = "Resource configuration class, " + resourceConfigClassName + |
|
", could not be loaded"; |
|
throw new ServletException(message, e.getCause()); |
|
} |
|
} |
|
|
|
private Map<String, Object> getInitParams(WebConfig webConfig) { |
|
Map<String, Object> props = new HashMap<String, Object>(); |
|
Enumeration names = webConfig.getInitParameterNames(); |
|
while (names.hasMoreElements()) { |
|
String name = (String) names.nextElement(); |
|
props.put(name, webConfig.getInitParameter(name)); |
|
} |
|
return props; |
|
} |
|
|
|
private String[] getPaths(String classpath) throws ServletException { |
|
final ServletContext context = config.getServletContext(); |
|
if (classpath == null) { |
|
String[] paths = { |
|
context.getRealPath("/WEB-INF/lib"), |
|
context.getRealPath("/WEB-INF/classes") |
|
}; |
|
if (paths[0] == null && paths[1] == null) { |
|
String message = "The default deployment configuration that scans for " + |
|
"classes in /WEB-INF/lib and /WEB-INF/classes is not supported " + |
|
"for the application server." + |
|
"Try using the package scanning configuration, see the JavaDoc for " + |
|
PackagesResourceConfig.class.getName() + " and the property " + |
|
PackagesResourceConfig.PROPERTY_PACKAGES + "."; |
|
throw new ServletException(message); |
|
} |
|
return paths; |
|
} else { |
|
String[] virtualPaths = classpath.split(";"); |
|
List<String> resourcePaths = new ArrayList<String>(); |
|
for (String virtualPath : virtualPaths) { |
|
virtualPath = virtualPath.trim(); |
|
if (virtualPath.length() == 0) continue; |
|
String path = context.getRealPath(virtualPath); |
|
if (path != null) resourcePaths.add(path); |
|
} |
|
if (resourcePaths.isEmpty()) { |
|
String message = "None of the declared classpath locations, " + |
|
classpath + |
|
", could be resolved. " + |
|
"This could be because the default deployment configuration that scans for " + |
|
"classes in classpath locations is not supported. " + |
|
"Try using the package scanning configuration, see the JavaDoc for " + |
|
PackagesResourceConfig.class.getName() + " and the property " + |
|
PackagesResourceConfig.PROPERTY_PACKAGES + "."; |
|
throw new ServletException(message); |
|
} |
|
return resourcePaths.toArray(new String[resourcePaths.size()]); |
|
} |
|
} |
|
|
|
private void configureJndiResources(ResourceConfig rc) { |
|
// Obtain any instances that are registered in JNDI |
|
// Assumes such instances are singletons |
|
// Registered classes have to be interfaces |
|
javax.naming.Context x = InitialContextHelper.getInitialContext(); |
|
if (x != null) { |
|
Iterator<Class<?>> i = rc.getClasses().iterator(); |
|
while (i.hasNext()) { |
|
Class<?> c = i.next(); |
|
if (!c.isInterface()) continue; |
|
|
|
try { |
|
Object o = x.lookup(c.getName()); |
|
if (o != null) { |
|
i.remove(); |
|
rc.getSingletons().add(o); |
|
LOGGER.log(Level.INFO, |
|
"An instance of the class " + c.getName() + |
|
" is found by JNDI look up using the class name as the JNDI name. " + |
|
"The instance will be registered as a singleton."); |
|
} |
|
} catch (NamingException ex) { |
|
LOGGER.log(Level.CONFIG, |
|
"JNDI lookup failed for Jersey application resource " + c.getName(), |
|
ex); |
|
} |
|
} |
|
} |
|
} |
|
|
|
private void filterFormParameters(HttpServletRequest servletRequest, ContainerRequest containerRequet) throws IOException { |
|
if (MediaTypes.typeEquals(MediaType.APPLICATION_FORM_URLENCODED_TYPE, containerRequet.getMediaType()) |
|
&& !isEntityPresent(containerRequet)) { |
|
Form f = new Form(); |
|
|
|
Enumeration e = servletRequest.getParameterNames(); |
|
while (e.hasMoreElements()) { |
|
String name = (String) e.nextElement(); |
|
String[] values = servletRequest.getParameterValues(name); |
|
|
|
f.put(name, Arrays.asList(values)); |
|
} |
|
|
|
if (!f.isEmpty()) { |
|
containerRequet.getProperties().put(FormDispatchProvider.FORM_PROPERTY, f); |
|
if (LOGGER.isLoggable(Level.WARNING)) { |
|
LOGGER.log(Level.WARNING, |
|
"A servlet request, to the URI " + containerRequet.getRequestUri() + ", " + |
|
"contains form parameters in " + |
|
"the request body but the request body has been consumed " + |
|
"by the servlet or a servlet filter accessing the request " + |
|
"parameters. Only resource methods using @FormParam " + |
|
"will work as expected. Resource methods consuming the " + |
|
"request body by other means will not work as expected."); |
|
} |
|
} |
|
} |
|
} |
|
|
|
private boolean isEntityPresent(ContainerRequest cr) throws IOException { |
|
InputStream in = cr.getEntityInputStream(); |
|
if (!in.markSupported()) { |
|
in = new BufferedInputStream(in, ReaderWriter.BUFFER_SIZE); |
|
cr.setEntityInputStream(in); |
|
} |
|
|
|
in.mark(1); |
|
if (in.read() == -1) |
|
return false; |
|
else { |
|
in.reset(); |
|
return true; |
|
} |
|
} |
|
|
|
private InBoundHeaders getHeaders(HttpServletRequest request) { |
|
InBoundHeaders rh = new InBoundHeaders(); |
|
|
|
for (Enumeration<String> names = request.getHeaderNames(); names.hasMoreElements();) { |
|
String name = names.nextElement(); |
|
List<String> valueList = new LinkedList<String>(); |
|
for (Enumeration<String> values = request.getHeaders(name); values.hasMoreElements();) { |
|
valueList.add(values.nextElement()); |
|
} |
|
rh.put(name, valueList); |
|
} |
|
|
|
return rh; |
|
} |
|
}
|
|
|