/**
* Copyright 2005-2014 Restlet
*
* The contents of this file are subject to the terms of one of the following
* open source licenses: Apache 2.0 or or EPL 1.0 (the "Licenses"). You can
* select the license that you prefer but you may not use this file except in
* compliance with one of these Licenses.
*
* You can obtain a copy of the Apache 2.0 license at
* http://www.opensource.org/licenses/apache-2.0
*
* You can obtain a copy of the EPL 1.0 license at
* http://www.opensource.org/licenses/eclipse-1.0
*
* See the Licenses for the specific language governing permissions and
* limitations under the Licenses.
*
* Alternatively, you can obtain a royalty free commercial license with less
* limitations, transferable or non-transferable, directly at
* http://restlet.com/products/restlet-framework
*
* Restlet is a registered trademark of Restlet S.A.S.
*/
package org.restlet.ext.servlet;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Enumeration;
import java.util.List;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.restlet.Application;
import org.restlet.Client;
import org.restlet.Component;
import org.restlet.Context;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.Server;
import org.restlet.data.Method;
import org.restlet.data.Protocol;
import org.restlet.engine.Engine;
import org.restlet.engine.adapter.HttpServerHelper;
import org.restlet.engine.adapter.ServerCall;
import org.restlet.engine.component.ComponentContext;
import org.restlet.ext.servlet.internal.ServletCall;
import org.restlet.ext.servlet.internal.ServletWarClient;
import org.restlet.routing.Route;
import org.restlet.routing.TemplateRoute;
import org.restlet.routing.VirtualHost;
/**
* Servlet acting like an HTTP server connector. This Servlet can deploy
* multiple Restlet applications or components. This allows you to reuse an
* existing standalone Restlet Component, potentially containing several
* applications, and declaring client connectors, for example for the CLAP, FILE
* or HTTP protocols.<br>
* <br>
* There are three separate ways to configure the deployment using this Servlet.
* Please note that you can also combine the two first of them whereas the last
* one is a full alternative. They are described below by order of priority:
* <table>
* <tr>
* <th>Mode</th>
* <th>Description</th>
* </tr>
* <tr>
* <td><b>1</b></td>
* <td>A "/WEB-INF/restlet.xml" file exists and contains a valid XML
* configuration as described in the documentation of the {@link Component}
* class. It is used to instantiate and attach the described component,
* contained applications and connectors. Please note that you can combine the
* usage of such configuration file and method 2.</td>
* </tr>
* <tr>
* <td><b>2</b></td>
* <td>The "/WEB-INF/web.xml" file contains a parameter named
* "org.restlet.component". Its value must be the path of a class that inherits
* from {@link Component}. It is used to instantiate and attach the described
* component, contained applications and connectors. Please note that you can
* combine the definition of your own custom Component subclass and method 1.</td>
* </tr>
* <tr>
* <td><b>3</b></td>
* <td>The "/WEB-INF/web.xml" file contains a parameter named
* "org.restlet.application". Its value must be the path of a class that
* inherits from {@link Application}. It is used to instantiate the application
* and to attach it to an implicit Restlet Component.</td>
* </tr>
* </table>
* <br>
* In deployment mode 3, you can also add an optional "org.restlet.clients"
* context parameter that contains a space separated list of client protocols
* supported by the underlying component. For each one, a new client connector
* is added to the implicit {@link Component} instance.<br>
*
* Here is an example configuration to attach two separate applications:
*
* <pre>
* <?xml version="1.0" encoding="UTF-8"?>
* <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
* xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
* xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
* id="WebApp_ID" version="2.5">
*
* <display-name>Restlet adapters</display-name>
*
* <servlet>
* <servlet-name>Restlet1</servlet-name>
* <servlet-class>org.restlet.ext.servlet.ServerServlet</servlet-class>
* <init-param>
* <param-name>org.restlet.application</param-name>
* <param-value>test.MyApplication1</param-value>
* </init-param>
* </servlet>
*
* <servlet-mapping>
* <servlet-name>Restlet1</servlet-name>
* <url-pattern>/1/*</url-pattern>
* </servlet-mapping>
*
* <servlet>
* <servlet-name>Restlet2</servlet-name>
* <servlet-class>org.restlet.ext.servlet.ServerServlet</servlet-class>
* <init-param>
* <param-name>org.restlet.application</param-name>
* <param-value>test.MyApplication2</param-value>
* </init-param>
* </servlet>
*
* <servlet-mapping>
* <servlet-name>Restlet2</servlet-name>
* <url-pattern>/2/*</url-pattern>
* </servlet-mapping>
*
* </web-app>
* </pre>
*
* Now, here is a more detailed template configuration showing you more
* configuration options:
*
* <pre>
* <?xml version="1.0" encoding="UTF-8"?>
* <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
* xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
* xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
* id="WebApp_ID" version="2.5">
*
* <display-name>Restlet adapters</display-name>
*
* <!-- Servlet to Restlet adapter declaration (Mandatory) -->
* <servlet>
* <servlet-name>RestletAdapter</servlet-name>
* <servlet-class>org.restlet.ext.servlet.ServerServlet</servlet-class>
*
* <!-- Your component class name (Optional - For mode 2) -->
* <init-param>
* <param-name>org.restlet.component</param-name>
* <param-value>test.MyComponent</param-value>
* </init-param>
*
* <!-- Your application class name (Optional - For mode 3) -->
* <init-param>
* <param-name>org.restlet.application</param-name>
* <param-value>test.MyApplication</param-value>
* </init-param>
*
* <!-- List of supported client protocols (Optional - Only in mode 3) -->
* <init-param>
* <param-name>org.restlet.clients</param-name>
* <param-value>HTTP HTTPS FILE</param-value>
* </init-param>
*
* <!-- Add the Servlet context path to routes (Optional) -->
* <init-param>
* <param-name>org.restlet.autoWire</param-name>
* <param-value>true</param-value>
* </init-param>
* </servlet>
*
* <!-- Mapping catching all requests on a given path (Mandatory) -->
* <servlet-mapping>
* <servlet-name>RestletAdapter</servlet-name>
* <url-pattern>/*</url-pattern>
* </servlet-mapping>
*
* </web-app>
* </pre>
*
* Note that the enumeration of "initParameters" of your Servlet will be copied
* to the "context.parameters" property of your Restlet Application. This way,
* you can pass additional initialization parameters to your application, and
* maybe share them with other Servlets.<br>
* <br>
* An additional boolean parameter called "org.restlet.autoWire" allows you to
* control the way your customized Component fits in the context of the wrapping
* Servlet. The root cause is that both your Servlet Container and your Restlet
* Component handle part of the URI routing, respectively to the right Servlet
* and to the right virtual host and Restlets (most of the time Application
* instances).<br>
* <br>
* When a request reaches the Servlet container, it is first routed according to
* its web.xml configuration (i.e. declared virtual hosts and webapp context
* path which is generally the name of the webapp WAR file). Once the incoming
* request reaches the ServerServlet and the wrapped Restlet Component, its URI
* is, for the second time, entirely subject to a separate routing chain. It
* begins with the virtual hosts, then continues to the URI pattern used when
* attaching Restlets to the host. The important conclusion is that both routing
* configurations must be consistent in order to work fine.<br>
* <br>
* In deployment mode 3, the context path of the servlet is automatically added.
* That's what we call the auto-wire feature. This is the default case, and is
* equivalent to setting the value "true" for the "org.restlet.autoWire"
* parameter as described above. In modes 1 or 2, if you want to manually
* control the URI wiring, you can disable the auto-wiring by setting the
* property to "false".<br>
* <br>
* Also, a WAR client connector is automatically attached to the parent Restlet
* component. It lets you access to resources inside your WAR using the uniform
* interface. Here is an example of WAR URI that can be resolved by this client:
* "war:///WEB-INF/web.xml". In order to use it, just call the
* {@link Context#getClientDispatcher()} in your application.<br>
* <br>
* Also, the ServletContext is copied into an
* "org.restlet.ext.servlet.ServletContext" attribute of the Restlet application
* in case you need access to it.<br>
* <br>
* Finally, an "org.restlet.ext.servlet.offsetPath" attribute, containing the
* computed offset path used to attach applications when (and only when) the
* auto-wiring feature is set, is added to the component's context.
*
* @see <a href="http://www.oracle.com/technetwork/java/javaee/">J2EE home
* page</a>
* @author Jerome Louvel
*/
public class ServerServlet extends HttpServlet {
/**
* Name of the attribute key containing a reference to the current
* application.
*/
private static final String APPLICATION_KEY = "org.restlet.application";
/**
* The Servlet context initialization parameter's name containing a boolean
* value. "true" indicates that all applications will be attached to the
* Component's virtual hosts with the Servlet Context path value.
*/
private static final String AUTO_WIRE_KEY = "org.restlet.autoWire";
/** The default value for the AUTO_WIRE_KEY parameter. */
private static final String AUTO_WIRE_KEY_DEFAULT = "true";
/**
* Name of the attribute key containing a list of supported client
* protocols.
*/
private static final String CLIENTS_KEY = "org.restlet.clients";
/**
* Name of the attribute key containing a reference to the current
* component.
*/
private static final String COMPONENT_KEY = "org.restlet.component";
/**
* The Servlet context initialization parameter's name containing the name
* of the Servlet context attribute that should be used to store the Restlet
* Application instance.
*/
private static final String NAME_APPLICATION_ATTRIBUTE = "org.restlet.attribute.application";
/** The default value for the NAME_APPLICATION_ATTRIBUTE parameter. */
private static final String NAME_APPLICATION_ATTRIBUTE_DEFAULT = "org.restlet.ext.servlet.ServerServlet.application";
/**
* The Servlet context initialization parameter's name containing the name
* of the Servlet context attribute that should be used to store the Restlet
* Component instance.
*/
private static final String NAME_COMPONENT_ATTRIBUTE = "org.restlet.attribute.component";
/** The default value for the NAME_COMPONENT_ATTRIBUTE parameter. */
private static final String NAME_COMPONENT_ATTRIBUTE_DEFAULT = "org.restlet.ext.servlet.ServerServlet.component";
/**
* Name of the attribute containing the computed offset path used to attach
* applications when (and only when) the auto-wiring feature is set, is
* added to the component's context.
*/
private static final String NAME_OFFSET_PATH_ATTRIBUTE = "org.restlet.ext.servlet.offsetPath";
/**
* The Servlet context initialization parameter's name containing the name
* of the Servlet context attribute that should be used to store the HTTP
* server connector instance.
*/
private static final String NAME_SERVER_ATTRIBUTE = "org.restlet.attribute.server";
/** The default value for the NAME_SERVER_ATTRIBUTE parameter. */
private static final String NAME_SERVER_ATTRIBUTE_DEFAULT = "org.restlet.ext.servlet.ServerServlet.server";
/** Serial version identifier. */
private static final long serialVersionUID = 1L;
/** The associated Restlet application. */
private volatile transient Application application;
/** The associated Restlet component. */
private volatile transient Component component;
/** The associated HTTP server helper. */
private volatile transient HttpServerHelper helper;
/**
* Constructor.
*/
public ServerServlet() {
this.application = null;
this.component = null;
this.helper = null;
}
/**
* Creates the single Application used by this Servlet.
*
* @param parentContext
* The parent component context.
*
* @return The newly created Application or null if unable to create
*/
protected Application createApplication(Context parentContext) {
Application application = null;
// Try to instantiate a new target application
// First, find the application class name
String applicationClassName = getInitParameter(APPLICATION_KEY, null);
// Load the application class using the given class name
if (applicationClassName != null) {
try {
Class<?> targetClass = loadClass(applicationClassName);
try {
// Instantiate an application with the default constructor
// then invoke the setContext method.
application = (Application) targetClass.getConstructor()
.newInstance();
// Set the context based on the Servlet's context
application.setContext(parentContext.createChildContext());
} catch (NoSuchMethodException e) {
log("[Restlet] ServerServlet couldn't invoke the constructor of the target class. Please check this class has a constructor without parameter. The constructor with a parameter of type Context will be used instead.");
// The constructor with the Context parameter does not
// exist. Create a new instance of the application class by
// invoking the constructor with the Context parameter.
application = (Application) targetClass.getConstructor(
Context.class).newInstance(
parentContext.createChildContext());
}
} catch (ClassNotFoundException e) {
log("[Restlet] ServerServlet couldn't find the target class. Please check that your classpath includes "
+ applicationClassName, e);
} catch (InstantiationException e) {
log("[Restlet] ServerServlet couldn't instantiate the target class. Please check this class has an empty constructor "
+ applicationClassName, e);
} catch (IllegalAccessException e) {
log("[Restlet] ServerServlet couldn't instantiate the target class. Please check that you have to proper access rights to "
+ applicationClassName, e);
} catch (NoSuchMethodException e) {
log("[Restlet] ServerServlet couldn't invoke the constructor of the target class. Please check this class has a constructor with a single parameter of Context "
+ applicationClassName, e);
} catch (InvocationTargetException e) {
log("[Restlet] ServerServlet couldn't instantiate the target class. An exception was thrown while creating "
+ applicationClassName, e);
}
}
return application;
}
/**
* Creates a new Servlet call wrapping a Servlet request/response couple and
* a Server connector.
*
* @param server
* The Server connector.
* @param request
* The Servlet request.
* @param response
* The Servlet response.
* @return The new ServletCall instance.
*/
protected ServerCall createCall(Server server, HttpServletRequest request,
HttpServletResponse response) {
return new ServletCall(server, request, response);
}
/**
* Creates the single Component used by this Servlet.
*
* @return The newly created Component or null if unable to create.
*/
protected Component createComponent() {
// Detect customized Component
String componentClassName = getInitParameter(COMPONENT_KEY, null);
Class<?> targetClass = null;
Component component = null;
if (componentClassName != null) {
try {
targetClass = loadClass(componentClassName);
} catch (ClassNotFoundException e) {
log("[Restlet] ServerServlet couldn't find the target component class. Please check that your classpath includes "
+ componentClassName, e);
}
}
log("[Restlet] ServerServlet: component class is " + componentClassName);
if (targetClass != null) {
try {
@SuppressWarnings("unchecked")
Constructor<? extends Component> ctor = ((Class<? extends Component>) targetClass)
.getConstructor();
log("[Restlet] ServerServlet: instantiating custom component");
component = (Component) ctor.newInstance();
} catch (IllegalAccessException e) {
log("[Restlet] ServerServlet couldn't instantiate the target class. Please check that you have proper access rights to "
+ componentClassName, e);
} catch (InvocationTargetException e) {
log("[Restlet] ServerServlet encountered an exception instantiating the target class "
+ componentClassName, e.getTargetException());
} catch (InstantiationException e) {
log(String.format(
"[Restlet] ServerServlet couldn't instantiate the target class. Please check that %s has %s.",
componentClassName, "an empty constructor"), e);
} catch (NoSuchMethodException e) {
log(String.format(
"[Restlet] ServerServlet couldn't instantiate the target class. Please check that %s has %s.",
componentClassName, "an empty constructor"), e);
}
}
// Create the default Component
if (component == null) {
component = new Component();
// Define the list of supported client protocols.
final String clientProtocolsString = getInitParameter(CLIENTS_KEY,
null);
if (clientProtocolsString != null) {
final String[] clientProtocols = clientProtocolsString
.split(" ");
Client client;
for (final String clientProtocol : clientProtocols) {
client = new Client(clientProtocol);
if (client.isAvailable()) {
component.getClients().add(client);
} else {
log("[Restlet] Couldn't find a client connector for protocol "
+ clientProtocol);
}
}
}
}
return component;
}
/**
* Creates the associated HTTP server handling calls.
*
* @param request
* The HTTP Servlet request.
* @return The new HTTP server handling calls.
*/
protected HttpServerHelper createServer(HttpServletRequest request) {
HttpServerHelper result = null;
Component component = getComponent();
if (component != null) {
// First, let's create a pseudo server
Server server = new Server(component.getContext()
.createChildContext(), (List<Protocol>) null,
this.getLocalAddr(request), this.getLocalPort(request),
component);
result = new HttpServerHelper(server);
// Change the default adapter
Context serverContext = server.getContext();
serverContext.getParameters().add("adapter",
"org.restlet.ext.servlet.internal.ServletServerAdapter");
// Attach the hosted application(s) to the right path
String uriPattern = this.getContextPath(request)
+ request.getServletPath();
if (isDefaultComponent()) {
if (getApplication() != null) {
log("[Restlet] Attaching application: " + getApplication()
+ " to URI: " + uriPattern);
component.getDefaultHost().attach(uriPattern,
getApplication());
}
} else {
// According to the mode, configure correctly the component.
String autoWire = getInitParameter(AUTO_WIRE_KEY,
AUTO_WIRE_KEY_DEFAULT);
if (AUTO_WIRE_KEY_DEFAULT.equalsIgnoreCase(autoWire)) {
// Translate all defined routes as much as possible
// with the context path only or the full servlet path.
// 1- get the offset
boolean addContextPath = false;
boolean addFullServletPath = false;
if (component.getDefaultHost().getRoutes().isEmpty()) {
// Case where the default host has a default route (with
// an empty pattern).
addFullServletPath = component.getDefaultHost()
.getDefaultRoute() != null;
} else {
for (Route route : component.getDefaultHost()
.getRoutes()) {
if (route instanceof TemplateRoute) {
TemplateRoute templateRoute = (TemplateRoute) route;
if (templateRoute.getTemplate().getPattern() == null) {
addFullServletPath = true;
continue;
}
if (!templateRoute.getTemplate().getPattern()
.startsWith(uriPattern)) {
if (!templateRoute
.getTemplate()
.getPattern()
.startsWith(
request.getServletPath())) {
addFullServletPath = true;
} else {
addContextPath = true;
break;
}
}
}
}
}
if (!addContextPath) {
for (VirtualHost virtualHost : component.getHosts()) {
if (virtualHost.getRoutes().isEmpty()) {
// Case where the default host has a default
// route (with an empty pattern).
addFullServletPath = virtualHost
.getDefaultRoute() != null;
} else {
for (Route route : virtualHost.getRoutes()) {
if (route instanceof TemplateRoute) {
TemplateRoute templateRoute = (TemplateRoute) route;
if (templateRoute.getTemplate()
.getPattern() == null) {
addFullServletPath = true;
continue;
}
if (!templateRoute.getTemplate()
.getPattern()
.startsWith(uriPattern)) {
if (!templateRoute
.getTemplate()
.getPattern()
.startsWith(
request.getServletPath())) {
addFullServletPath = true;
} else {
addContextPath = true;
break;
}
}
}
}
}
if (addContextPath) {
break;
}
}
}
// 2- Translate all routes.
if (addContextPath || addFullServletPath) {
String offsetPath = null;
if (addContextPath) {
offsetPath = this.getContextPath(request);
} else {
offsetPath = uriPattern;
}
if (offsetPath != null) {
getComponent()
.getContext()
.getAttributes()
.put(NAME_OFFSET_PATH_ATTRIBUTE, offsetPath);
}
// Shift the default route (if any) of the default host
Route defaultRoute = component.getDefaultHost()
.getDefaultRoute();
if (defaultRoute != null) {
if (defaultRoute instanceof TemplateRoute) {
TemplateRoute defaultTemplateRoute = (TemplateRoute) defaultRoute;
defaultTemplateRoute.getTemplate().setPattern(
offsetPath
+ defaultTemplateRoute
.getTemplate()
.getPattern());
log("[Restlet] Attaching restlet: "
+ defaultRoute.getNext()
+ " to URI: "
+ offsetPath
+ defaultTemplateRoute.getTemplate()
.getPattern());
} else {
log("[Restlet] Attaching restlet: "
+ defaultRoute.getNext());
}
}
// Shift the routes of the default host
for (Route route : component.getDefaultHost()
.getRoutes()) {
if (route instanceof TemplateRoute) {
TemplateRoute templateRoute = (TemplateRoute) route;
log("[Restlet] Attaching restlet: "
+ route.getNext()
+ " to URI: "
+ offsetPath
+ templateRoute.getTemplate()
.getPattern());
templateRoute.getTemplate().setPattern(
offsetPath
+ templateRoute.getTemplate()
.getPattern());
} else {
log("[Restlet] Attaching restlet: "
+ defaultRoute.getNext());
}
}
for (VirtualHost virtualHost : component.getHosts()) {
// Shift the default route (if any) of the virtual
// host
defaultRoute = virtualHost.getDefaultRoute();
if (defaultRoute != null) {
if (defaultRoute instanceof TemplateRoute) {
TemplateRoute defaultTemplateRoute = (TemplateRoute) defaultRoute;
defaultTemplateRoute
.getTemplate()
.setPattern(
offsetPath
+ defaultTemplateRoute
.getTemplate()
.getPattern());
log("[Restlet] Attaching restlet: "
+ defaultRoute.getNext()
+ " to URI: "
+ offsetPath
+ defaultTemplateRoute
.getTemplate().getPattern());
} else {
log("[Restlet] Attaching restlet: "
+ defaultRoute.getNext());
}
}
// Shift the routes of the virtual host
for (Route route : virtualHost.getRoutes()) {
if (route instanceof TemplateRoute) {
TemplateRoute templateRoute = (TemplateRoute) route;
log("[Restlet] Attaching restlet: "
+ route.getNext()
+ " to URI: "
+ offsetPath
+ templateRoute.getTemplate()
.getPattern());
templateRoute.getTemplate().setPattern(
offsetPath
+ templateRoute
.getTemplate()
.getPattern());
} else {
log("[Restlet] Attaching restlet: "
+ route.getNext());
}
}
}
}
}
}
}
return result;
}
/**
* Creates a new client for the WAR protocol.
*
* @param context
* The parent context.
* @param config
* The Servlet config.
* @return The new WAR client instance.
*/
protected Client createWarClient(Context context, ServletConfig config) {
return new ServletWarClient(context, config.getServletContext());
}
@Override
public void destroy() {
if ((getComponent() != null) && (getComponent().isStarted())) {
try {
getComponent().stop();
} catch (Exception e) {
log("Error during the stopping of the Restlet component", e);
}
}
super.destroy();
}
/**
* Returns the application. It creates a new one if none exists.
*
* @return The application.
*/
public Application getApplication() {
// Lazy initialization with double-check.
Application result = this.application;
if (result == null) {
synchronized (this) {
result = this.application;
if (result == null) {
// In case a component is explicitly defined, it cannot be
// completed.
if (isDefaultComponent()) {
// Find the attribute name to use to store the
// application
String applicationAttributeName = getInitParameter(
NAME_APPLICATION_ATTRIBUTE,
NAME_APPLICATION_ATTRIBUTE_DEFAULT + "."
+ getServletName());
// Look up the attribute for a target
result = (Application) getServletContext()
.getAttribute(applicationAttributeName);
if (result == null) {
result = createApplication(getComponent()
.getContext());
init(result);
getServletContext().setAttribute(
applicationAttributeName, result);
}
this.application = result;
}
}
}
}
return result;
}
/**
* Returns the component. It creates a new one if none exists.
*
* @return The component.
*/
public Component getComponent() {
// Lazy initialization with double-check.
Component result = this.component;
if (result == null) {
synchronized (this) {
result = this.component;
if (result == null) {
// Find the attribute name to use to store the component
final String componentAttributeName = getInitParameter(
NAME_COMPONENT_ATTRIBUTE,
NAME_COMPONENT_ATTRIBUTE_DEFAULT + "."
+ getServletName());
// Look up the attribute for a target
result = (Component) getServletContext().getAttribute(
componentAttributeName);
if (result == null) {
result = createComponent();
init(result);
getServletContext().setAttribute(
componentAttributeName, result);
}
}
this.component = result;
}
}
return result;
}
/**
* Intercepter method need for subclasses such as XdbServerServlet.
*
* @param request
* The Servlet request.
* @return The portion of the request URI that indicates the context of the
* request.
*/
protected String getContextPath(HttpServletRequest request) {
return request.getContextPath();
}
/**
* Returns the value of a given initialization parameter, first from the
* Servlet configuration, then from the Web Application context.
*
* @param name
* The parameter name.
* @param defaultValue
* The default to use in case the parameter is not found.
* @return The value of the parameter or null.
*/
public String getInitParameter(String name, String defaultValue) {
String result = getServletConfig().getInitParameter(name);
if (result == null) {
result = getServletConfig().getServletContext().getInitParameter(
name);
}
if (result == null) {
result = defaultValue;
}
return result;
}
/**
* Intercepter method need for subclasses such as XdbServerServlet.
*
* @param request
* The Servlet request.
* @return The Internet Protocol (IP) address of the interface on which the
* request was received.
*/
protected String getLocalAddr(HttpServletRequest request) {
return request.getLocalAddr();
}
/**
* Intercepter method need for subclasses such as XdbServerServlet.
*
* @param request
* The Servlet request.
* @return The Internet Protocol (IP) port number of the interface on which
* the request was received
*/
protected int getLocalPort(HttpServletRequest request) {
return request.getLocalPort();
}
/**
* Returns the associated HTTP server handling calls. It creates a new one
* if none exists.
*
* @param request
* The HTTP Servlet request.
* @return The HTTP server handling calls.
*/
public HttpServerHelper getServer(HttpServletRequest request) {
// Lazy initialization with double-check.
HttpServerHelper result = this.helper;
if (result == null) {
synchronized (this) {
result = this.helper;
if (result == null) {
// Find the attribute name to use to store the server
// reference
final String serverAttributeName = getInitParameter(
NAME_SERVER_ATTRIBUTE,
NAME_SERVER_ATTRIBUTE_DEFAULT + "."
+ getServletName());
// Look up the attribute for a target
result = (HttpServerHelper) getServletContext()
.getAttribute(serverAttributeName);
if (result == null) {
result = createServer(request);
getServletContext().setAttribute(serverAttributeName,
result);
}
this.helper = result;
}
}
}
return result;
}
@Override
public void init() throws ServletException {
if ((getComponent() != null) && (getComponent().isStopped())) {
try {
getComponent().start();
} catch (Exception e) {
log("Error during the starting of the Restlet Application", e);
}
}
}
/**
* Initialize a application. Copies Servlet parameters into the component's
* context. Copies the ServletContext into an
* "org.restlet.ext.servlet.ServletContext" attribute.
*
* @param application
* The application to configure.
*/
protected void init(Application application) {
if (application != null) {
Context applicationContext = application.getContext();
if (applicationContext != null) {
// Copies the ServletContext into an attribute
applicationContext.getAttributes().put(
"org.restlet.ext.servlet.ServletContext",
getServletContext());
// Copy all the servlet parameters into the context
String initParam;
// Copy all the Servlet component initialization parameters
javax.servlet.ServletConfig servletConfig = getServletConfig();
for (Enumeration<String> enum1 = servletConfig
.getInitParameterNames(); enum1.hasMoreElements();) {
initParam = enum1.nextElement();
applicationContext.getParameters().add(initParam,
servletConfig.getInitParameter(initParam));
}
// Copy all the Servlet application initialization parameters
for (Enumeration<String> enum1 = getServletContext()
.getInitParameterNames(); enum1.hasMoreElements();) {
initParam = enum1.nextElement();
applicationContext.getParameters().add(initParam,
getServletContext().getInitParameter(initParam));
}
}
}
}
/**
* Initialize a component. Adds a default WAR client and copies Servlet
* parameters into the component's context. Copies the ServletContext into
* an "org.restlet.ext.servlet.ServletContext" attribute.
*
* @param component
* The component to configure.
*/
protected void init(Component component) {
if (component != null) {
// Complete the configuration of the Component
// Add the WAR client
component.getClients()
.add(createWarClient(component.getContext(),
getServletConfig()));
// Copy all the servlet parameters into the context
ComponentContext componentContext = (ComponentContext) component
.getContext();
// Copies the ServletContext into an attribute
componentContext.getAttributes().put(
"org.restlet.ext.servlet.ServletContext",
getServletContext());
// Copy all the Servlet container initialization parameters
String initParam;
javax.servlet.ServletConfig servletConfig = getServletConfig();
for (Enumeration<String> enum1 = servletConfig
.getInitParameterNames(); enum1.hasMoreElements();) {
initParam = enum1.nextElement();
componentContext.getParameters().add(initParam,
servletConfig.getInitParameter(initParam));
}
// Copy all the Servlet application initialization parameters
for (Enumeration<String> enum1 = getServletContext()
.getInitParameterNames(); enum1.hasMoreElements();) {
initParam = enum1.nextElement();
componentContext.getParameters().add(initParam,
getServletContext().getInitParameter(initParam));
}
// Copy all Servlet's context attributes
String attributeName;
for (Enumeration<String> namesEnum = getServletContext()
.getAttributeNames(); namesEnum.hasMoreElements();) {
attributeName = namesEnum.nextElement();
componentContext.getAttributes().put(attributeName,
getServletContext().getAttribute(attributeName));
}
}
}
/**
* Indicates if the Component hosted by this Servlet is the default one or
* one provided by the user.
*
* @return True if the Component is the default one, false otherwise.
*/
private boolean isDefaultComponent() {
// The Component is provided via an XML configuration file.
Client client = createWarClient(new Context(), getServletConfig());
Response response = client.handle(new Request(Method.GET,
"war:///WEB-INF/restlet.xml"));
if (response.getStatus().isSuccess() && response.isEntityAvailable()) {
return false;
}
// The Component is provided via a context parameter in the "web.xml"
// file.
String componentAttributeName = getInitParameter(COMPONENT_KEY, null);
if (componentAttributeName != null) {
return false;
}
return true;
}
/**
* Returns a class for a given qualified class name.
*
* @param className
* The class name to lookup.
* @return The class object.
* @throws ClassNotFoundException
*/
protected Class<?> loadClass(String className)
throws ClassNotFoundException {
return Engine.loadClass(className);
}
/**
* Services a HTTP Servlet request as an uniform call.
*
* @param request
* The HTTP Servlet request.
* @param response
* The HTTP Servlet response.
*/
@Override
public void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpServerHelper helper = getServer(request);
if (helper != null) {
helper.handle(createCall(helper.getHelped(), request, response));
} else {
log("[Restlet] Unable to get the Restlet HTTP server connector. Status code 500 returned.");
response.sendError(500);
}
}
}