/**
* 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;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.restlet.data.Status;
import org.restlet.engine.Engine;
/**
* Uniform class that provides a context and life cycle support. It has many
* subclasses that focus on specific ways to process calls. The context property
* is typically provided by a parent Component as a way to encapsulate access to
* shared features such as logging and client connectors.<br>
* <br>
* Concurrency note: instances of this class or its subclasses can be invoked by
* several threads at the same time and therefore must be thread-safe. You
* should be especially careful when storing state in member variables.
*
* @author Jerome Louvel
*/
public abstract class Restlet implements Uniform {
/** Error message. */
private static final String UNABLE_TO_START = "Unable to start the Restlet";
// [ifndef gwt] method
/**
* Indicates that a Restlet's context has changed.
*
* @param restlet
* The Restlet with a changed context.
* @param context
* The new context.
*/
private static void fireContextChanged(Restlet restlet, Context context) {
if (context != null) {
if (context instanceof org.restlet.engine.util.ChildContext) {
org.restlet.engine.util.ChildContext childContext = (org.restlet.engine.util.ChildContext) context;
if (childContext.getChild() == null) {
childContext.setChild(restlet);
}
} else if (!(restlet instanceof Component)
&& (context instanceof org.restlet.engine.component.ComponentContext)) {
context.getLogger()
.severe("For security reasons, don't pass the component context to child Restlets anymore. Use the Context#createChildContext() method instead. "
+ restlet.getClass());
}
}
}
/** The author(s). */
private volatile String author;
/** The context. */
private volatile Context context;
/** The description. */
private volatile String description;
// [ifndef gwt] member
/** Finder class to instantiate. */
private volatile Class<? extends org.restlet.resource.Finder> finderClass;
/** The display name. */
private volatile String name;
/** The owner(s). */
private volatile String owner;
/** Indicates if the Restlet was started. */
private volatile boolean started;
/**
* Constructor with null context.
*/
public Restlet() {
this(null);
}
/**
* Constructor with the Restlet's context which can be the parent's
* application context, but shouldn't be the parent Component's context for
* security reasons.
*
* @see Context#createChildContext()
*
* @param context
* The context of the Restlet.
*
*/
public Restlet(Context context) {
// [ifndef gwt] instruction
this.context = context;
// [ifdef gwt] instruction uncomment
// this.context = (context != null) ? context : new Context();
this.started = false;
this.name = toString();
this.description = null;
this.author = null;
this.owner = null;
// [ifndef gwt]
this.finderClass = null;
if (Engine.getInstance() == null) {
Context.getCurrentLogger()
.severe("Unable to fully initialize the Restlet. No Restlet engine available.");
throw new RuntimeException(
"Unable to fully initialize the Restlet. No Restlet engine available.");
}
fireContextChanged(this, context);
// [enddef]
}
// [ifndef gwt] method
/**
* Creates a new finder instance based on the "targetClass" property. If
* none is define, the {@link Application#createFinder(Class)} method is
* invoked if available, otherwise the
* {@link org.restlet.resource.Finder#createFinder(Class, Class, Context, Logger)}
* method is called with the {@link org.restlet.resource.Finder} class as
* parameter.
*
* @param resourceClass
* The target {@link org.restlet.resource.ServerResource} class
* to find.
* @return The new finder instance.
* @see org.restlet.resource.Finder#createFinder(Class, Class, Context,
* Logger)
*/
public org.restlet.resource.Finder createFinder(
Class<? extends org.restlet.resource.ServerResource> resourceClass) {
org.restlet.resource.Finder result = null;
if (getFinderClass() != null) {
result = org.restlet.resource.Finder.createFinder(resourceClass,
getFinderClass(), getContext(), getLogger());
} else if ((getApplication() != null) && (getApplication() != this)) {
result = getApplication().createFinder(resourceClass);
} else {
result = org.restlet.resource.Finder.createFinder(resourceClass,
org.restlet.resource.Finder.class, getContext(),
getLogger());
}
return result;
}
/**
* Attempts to {@link #stop()} the Restlet if it is still started.
*/
@Override
protected void finalize() throws Throwable {
if (isStarted()) {
stop();
}
}
// [ifndef gwt] method
/**
* Returns the parent application if it exists, or null.
*
* @return The parent application if it exists, or null.
*/
public Application getApplication() {
return Application.getCurrent();
}
/**
* Returns the author(s).
*
* @return The author(s).
*/
public String getAuthor() {
return this.author;
}
/**
* Returns the context.
*
* @return The context.
*/
public Context getContext() {
return this.context;
}
/**
* Returns the description.
*
* @return The description
*/
public String getDescription() {
return this.description;
}
// [ifndef gwt] method
/**
* Returns the finder class used to instantiate resource classes. By
* default, it returns the {@link org.restlet.resource.Finder} class. This
* property is leveraged by {@link Application#setOutboundRoot(Class)} and
* {@link Application#setInboundRoot(Class)} methods.
*
* @return the finder class to instantiate.
*/
public Class<? extends org.restlet.resource.Finder> getFinderClass() {
return finderClass;
}
/**
* Returns the context's logger.
*
* @return The context's logger.
*/
public Logger getLogger() {
Logger result = null;
Context context = getContext();
if (context == null) {
context = Context.getCurrent();
}
if (context != null) {
result = context.getLogger();
}
if (result == null) {
result = Engine.getLogger(this, "org.restlet");
}
return result;
}
/**
* Returns the display name.
*
* @return The display name.
*/
public String getName() {
return this.name;
}
/**
* Returns the owner(s).
*
* @return The owner(s).
*/
public String getOwner() {
return this.owner;
}
// [ifndef gwt] method
/**
* Handles a call. Creates an empty {@link Response} object and then invokes
* {@link #handle(Request, Response)}.
*
* @param request
* The request to handle.
* @return The returned response.
*/
public final Response handle(Request request) {
Response response = new Response(request);
handle(request, response);
return response;
}
/**
* Handles a call. The default behavior is to initialize the Restlet by
* setting the current context using the {@link Context#setCurrent(Context)}
* method and by attempting to start it, unless it was already started. If
* an exception is thrown during the start action, then the response status
* is set to {@link Status#SERVER_ERROR_INTERNAL}.
* <p>
* Subclasses overriding this method should make sure that they call
* super.handle(request, response) before adding their own logic.
*
* @param request
* The request to handle.
* @param response
* The response to update.
*/
public void handle(Request request, Response response) {
// [ifndef gwt]
// Associate the response to the current thread
Response.setCurrent(response);
// Associate the context to the current thread
if (getContext() != null) {
Context.setCurrent(getContext());
}
// [enddef]
// Check if the Restlet was started
if (isStopped()) {
try {
start();
} catch (Exception e) {
// Occurred while starting the Restlet
if (getContext() != null) {
getContext().getLogger().log(Level.WARNING,
UNABLE_TO_START, e);
} else {
Context.getCurrentLogger().log(Level.WARNING,
UNABLE_TO_START, e);
}
response.setStatus(Status.SERVER_ERROR_INTERNAL);
}
if (!isStarted()) {
// No exception raised but the Restlet somehow couldn't be
// started
getContext().getLogger().log(Level.WARNING, UNABLE_TO_START);
response.setStatus(Status.SERVER_ERROR_INTERNAL);
}
}
}
/**
* Handles a call.
*
* @param request
* The request to handle.
* @param response
* The response to update.
* @param onResponseCallback
* The callback invoked upon response reception.
*/
public final void handle(Request request, Response response,
Uniform onResponseCallback) {
request.setOnResponse(onResponseCallback);
handle(request, response);
}
/**
* Handles a call.
*
* @param request
* The request to handle.
* @param onReceivedCallback
* The callback invoked upon request reception.
*/
public final void handle(Request request, Uniform onReceivedCallback) {
Response response = new Response(request);
handle(request, response, onReceivedCallback);
}
/**
* Indicates if the Restlet is started.
*
* @return True if the Restlet is started.
*/
public boolean isStarted() {
return this.started;
}
/**
* Indicates if the Restlet is stopped.
*
* @return True if the Restlet is stopped.
*/
public boolean isStopped() {
return !this.started;
}
/**
* Sets the author(s).
*
* @param author
* The author(s).
*/
public void setAuthor(String author) {
this.author = author;
}
/**
* Sets the context.
*
* @param context
* The context.
*/
public void setContext(Context context) {
this.context = context;
// [ifndef gwt] instruction
fireContextChanged(this, context);
}
/**
* Sets the description.
*
* @param description
* The description.
*/
public void setDescription(String description) {
this.description = description;
}
// [ifndef gwt] method
/**
* Sets the finder class to instantiate. This property is leveraged by
* {@link Application#setOutboundRoot(Class)} and
* {@link Application#setInboundRoot(Class)} methods.
*
* @param finderClass
* The finder class to instantiate.
*/
public void setFinderClass(
Class<? extends org.restlet.resource.Finder> finderClass) {
this.finderClass = finderClass;
}
/**
* Sets the display name.
*
* @param name
* The display name.
*/
public void setName(String name) {
this.name = name;
}
/**
* Sets the owner(s).
*
* @param owner
* The owner(s).
*/
public void setOwner(String owner) {
this.owner = owner;
}
/**
* Starts the Restlet. By default its only sets "started" internal property
* to true.
*
* WARNING: this method must be called at the end of the starting process by
* subclasses otherwise concurrent threads could enter into the call
* handling logic too early.
*/
public synchronized void start() throws Exception {
this.started = true;
}
/**
* Stops the Restlet. By default its only sets "started" internal property
* to false.
*
* WARNING: this method must be called at the beginning of the stopping
* process by subclasses otherwise concurrent threads could continue to
* (improperly) handle calls.
*/
public synchronized void stop() throws Exception {
this.started = false;
}
}