Package org.restlet.routing

Source Code of org.restlet.routing.Redirector

/**
* Copyright 2005-2011 Noelios Technologies.
*
* The contents of this file are subject to the terms of one of the following
* open source licenses: LGPL 3.0 or LGPL 2.1 or CDDL 1.0 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 LGPL 3.0 license at
* http://www.opensource.org/licenses/lgpl-3.0.html
*
* You can obtain a copy of the LGPL 2.1 license at
* http://www.opensource.org/licenses/lgpl-2.1.php
*
* You can obtain a copy of the CDDL 1.0 license at
* http://www.opensource.org/licenses/cddl1.php
*
* You can obtain a copy of the EPL 1.0 license at
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* 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://www.noelios.com/products/restlet-engine
*
* Restlet is a registered trademark of Noelios Technologies.
*/

package org.restlet.routing;

import java.util.logging.Level;

import org.restlet.Application;
import org.restlet.Component;
import org.restlet.Context;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.Restlet;
import org.restlet.data.Reference;
import org.restlet.data.Status;
import org.restlet.engine.header.HeaderConstants;
import org.restlet.representation.Representation;
import org.restlet.util.Resolver;

/**
* Rewrites URIs then redirects the call or the client to a new destination.
* There are various redirection modes that you can choose from: client-side
* redirections ({@link #MODE_CLIENT_FOUND}, {@link #MODE_CLIENT_PERMANENT},
* {@link #MODE_CLIENT_SEE_OTHER}, {@link #MODE_CLIENT_TEMPORARY}) or
* server-side redirections, similar to a reverse proxy (
* {@link #MODE_SERVER_OUTBOUND} and {@link #MODE_SERVER_INBOUND}).<br>
* <br>
* When setting the redirection URIs, you can also used special URI variables to
* reuse most properties from the original request as well as URI template
* variables. For a complete list of properties, please see the {@link Resolver}
* class. For example "/target?referer={fi}" would redirect to the relative URI,
* inserting the referrer URI as a query parameter.<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.
*
* @see org.restlet.routing.Template
* @see <a href="http://wiki.restlet.org/docs_2.1/375-restlet.html">User Guide -
*      URI rewriting and redirection</a>
* @author Jerome Louvel
*/
public class Redirector extends Restlet {
    /**
     * In this mode, the client is permanently redirected to the URI generated
     * from the target URI pattern.<br>
     *
     * @see Status#REDIRECTION_PERMANENT
     */
    public static final int MODE_CLIENT_PERMANENT = 1;

    /**
     * In this mode, the client is simply redirected to the URI generated from
     * the target URI pattern.<br>
     *
     * @see Status#REDIRECTION_FOUND
     */
    public static final int MODE_CLIENT_FOUND = 2;

    /**
     * In this mode, the client is simply redirected to the URI generated from
     * the target URI pattern.<br>
     *
     * @see Status#REDIRECTION_SEE_OTHER
     */
    public static final int MODE_CLIENT_SEE_OTHER = 3;

    /**
     * In this mode, the client is temporarily redirected to the URI generated
     * from the target URI pattern.<br>
     *
     * @see Status#REDIRECTION_TEMPORARY
     */
    public static final int MODE_CLIENT_TEMPORARY = 4;

    /**
     * In this mode, the call is sent to the application's outboundRoot or if
     * null to the context's client dispatcher. Once the selected client
     * connector has completed the request handling, the response is normally
     * returned to the client. In this case, you can view the {@link Redirector}
     * as acting as a transparent server-side proxy Restlet.<br>
     * <br>
     * Remember to add the required connectors to the parent {@link Component}
     * and to declare them in the list of required connectors on the
     * {@link Application#getConnectorService()} property.<br>
     * <br>
     * Note that in this mode, the headers of HTTP requests, stored in the
     * request's attributes, are removed before dispatching. Also, when a HTTP
     * response comes back the headers are also removed.
     *
     * @see Application#getOutboundRoot()
     * @see Context#getClientDispatcher()
     */
    public static final int MODE_SERVER_OUTBOUND = 6;

    /**
     * In this mode, the call is sent to the context's server dispatcher. Once
     * the selected client connector has completed the request handling, the
     * response is normally returned to the client. In this case, you can view
     * the Redirector as acting as a transparent proxy Restlet.<br>
     * <br>
     * Remember to add the required connectors to the parent {@link Component}
     * and to declare them in the list of required connectors on the
     * {@link Application#getConnectorService()} property.<br>
     * <br>
     * Note that in this mode, the headers of HTTP requests, stored in the
     * request's attributes, are removed before dispatching. Also, when a HTTP
     * response comes back the headers are also removed.
     *
     * @see Context#getServerDispatcher()
     */
    public static final int MODE_SERVER_INBOUND = 7;

    /** The target URI pattern. */
    protected volatile String targetTemplate;

    /** The redirection mode. */
    protected volatile int mode;

    /**
     * Constructor for the client dispatcher mode.
     *
     * @param context
     *            The context.
     * @param targetTemplate
     *            The template to build the target URI.
     * @see org.restlet.routing.Template
     */
    public Redirector(Context context, String targetTemplate) {
        this(context, targetTemplate, MODE_SERVER_OUTBOUND);
    }

    /**
     * Constructor.
     *
     * @param context
     *            The context.
     * @param targetPattern
     *            The pattern to build the target URI (using StringTemplate
     *            syntax and the CallModel for variables).
     * @param mode
     *            The redirection mode.
     */
    public Redirector(Context context, String targetPattern, int mode) {
        super(context);
        this.targetTemplate = targetPattern;
        this.mode = mode;
    }

    /**
     * Returns the redirection mode.
     *
     * @return The redirection mode.
     */
    public int getMode() {
        return this.mode;
    }

    /**
     * Returns the target reference to redirect to by automatically resolving
     * URI template variables found using the {@link Template} class using the
     * request and response as data models.
     *
     * @param request
     *            The request to handle.
     * @param response
     *            The response to update.
     * @return The target reference to redirect to.
     */
    protected Reference getTargetRef(Request request, Response response) {
        // Create the template
        Template rt = new Template(this.targetTemplate);
        rt.setLogger(getLogger());

        // Return the formatted target URI
        if (new Reference(this.targetTemplate).isRelative()) {
            // Be sure to keep the resource's base reference.
            return new Reference(request.getResourceRef(), rt.format(request,
                    response));
        }

        return new Reference(rt.format(request, response));
    }

    /**
     * Returns the target URI pattern.
     *
     * @return The target URI pattern.
     */
    public String getTargetTemplate() {
        return this.targetTemplate;
    }

    /**
     * Handles a call by redirecting using the selected redirection mode.
     *
     * @param request
     *            The request to handle.
     * @param response
     *            The response to update.
     */
    @Override
    public void handle(Request request, Response response) {
        // Generate the target reference
        Reference targetRef = getTargetRef(request, response);

        switch (this.mode) {
        case MODE_CLIENT_PERMANENT:
            if (request.isLoggable()) {
                getLogger().log(Level.INFO,
                        "Permanently redirecting client to: " + targetRef);
            }

            response.redirectPermanent(targetRef);
            break;

        case MODE_CLIENT_FOUND:
            if (request.isLoggable()) {
                getLogger().log(Level.INFO,
                        "Redirecting client to found location: " + targetRef);
            }

            response.setLocationRef(targetRef);
            response.setStatus(Status.REDIRECTION_FOUND);
            break;

        case MODE_CLIENT_SEE_OTHER:
            if (request.isLoggable()) {
                getLogger().log(Level.INFO,
                        "Redirecting client to another location: " + targetRef);
            }

            response.redirectSeeOther(targetRef);
            break;

        case MODE_CLIENT_TEMPORARY:
            if (request.isLoggable()) {
                getLogger().log(Level.INFO,
                        "Temporarily redirecting client to: " + targetRef);
            }

            response.redirectTemporary(targetRef);
            break;

        case MODE_SERVER_OUTBOUND:
            if (request.isLoggable()) {
                getLogger().log(Level.INFO,
                        "Redirecting via client dispatcher to: " + targetRef);
            }

            outboundServerRedirect(targetRef, request, response);
            break;

        case MODE_SERVER_INBOUND:
            if (request.isLoggable()) {
                getLogger().log(Level.INFO,
                        "Redirecting via server dispatcher to: " + targetRef);
            }

            inboundServerRedirect(targetRef, request, response);
            break;
        }
    }

    /**
     * Redirects a given call to a target reference. In the default
     * implementation, the request HTTP headers, stored in the request's
     * attributes, are removed before dispatching. After dispatching, the
     * response HTTP headers are also removed to prevent conflicts with the main
     * call.
     *
     * @param targetRef
     *            The target reference with URI variables resolved.
     * @param request
     *            The request to handle.
     * @param response
     *            The response to update.
     */
    protected void inboundServerRedirect(Reference targetRef, Request request,
            Response response) {
        serverRedirect(getContext().getServerDispatcher(), targetRef, request,
                response);
    }

    /**
     * Redirects a given call to a target reference. In the default
     * implementation, the request HTTP headers, stored in the request's
     * attributes, are removed before dispatching. After dispatching, the
     * response HTTP headers are also removed to prevent conflicts with the main
     * call.
     *
     * @param targetRef
     *            The target reference with URI variables resolved.
     * @param request
     *            The request to handle.
     * @param response
     *            The response to update.
     */
    protected void outboundServerRedirect(Reference targetRef, Request request,
            Response response) {
        Restlet next = (getApplication() == null) ? null : getApplication()
                .getOutboundRoot();

        if (next == null) {
            next = getContext().getClientDispatcher();
        }

        serverRedirect(next, targetRef, request, response);
        if (response.getEntity() != null
                && !request.getResourceRef().getScheme()
                        .equalsIgnoreCase(targetRef.getScheme())) {
            // Distinct protocol, this data cannot be exposed.
            response.getEntity().setLocationRef((Reference) null);
        }
    }

    /**
     * Optionally rewrites the response entity returned in the
     * {@link #MODE_SERVER_INBOUND} and {@link #MODE_SERVER_OUTBOUND} modes. By
     * default, it just returns the initial entity without any modification.
     *
     * @param initialEntity
     *            The initial entity returned.
     * @return The rewritten entity.
     */
    protected Representation rewrite(Representation initialEntity) {
        return initialEntity;
    }

    /**
     * Redirects a given call on the server-side to a next Restlet with a given
     * target reference. In the default implementation, the request HTTP
     * headers, stored in the request's attributes, are removed before
     * dispatching. After dispatching, the response HTTP headers are also
     * removed to prevent conflicts with the main call.
     *
     * @param next
     *            The next Restlet to forward the call to.
     * @param targetRef
     *            The target reference with URI variables resolved.
     * @param request
     *            The request to handle.
     * @param response
     *            The response to update.
     */
    protected void serverRedirect(Restlet next, Reference targetRef,
            Request request, Response response) {
        if (next == null) {
            getLogger().warning(
                    "No next Restlet provided for server redirection to "
                            + targetRef);
        } else {
            // Save the base URI if it exists as we might need it for
            // redirections
            Reference resourceRef = request.getResourceRef();
            Reference baseRef = resourceRef.getBaseRef();

            // Reset the protocol and let the dispatcher handle the protocol
            request.setProtocol(null);

            // Update the request to cleanly go to the target URI
            request.setResourceRef(targetRef);
            request.getAttributes().remove(HeaderConstants.ATTRIBUTE_HEADERS);
            next.handle(request, response);

            // Allow for response rewriting and clean the headers
            response.setEntity(rewrite(response.getEntity()));
            response.getAttributes().remove(HeaderConstants.ATTRIBUTE_HEADERS);
            request.setResourceRef(resourceRef);

            // In case of redirection, we may have to rewrite the redirect URI
            if (response.getLocationRef() != null) {
                Template rt = new Template(this.targetTemplate);
                rt.setLogger(getLogger());
                int matched = rt.parse(response.getLocationRef().toString(),
                        request);

                if (matched > 0) {
                    String remainingPart = (String) request.getAttributes()
                            .get("rr");

                    if (remainingPart != null) {
                        response.setLocationRef(baseRef.toString()
                                + remainingPart);
                    }
                }
            }
        }
    }

    /**
     * Sets the redirection mode.
     *
     * @param mode
     *            The redirection mode.
     */
    public void setMode(int mode) {
        this.mode = mode;
    }

    /**
     * Sets the target URI pattern.
     *
     * @param targetTemplate
     *            The target URI pattern.
     */
    public void setTargetTemplate(String targetTemplate) {
        this.targetTemplate = targetTemplate;
    }

}
TOP

Related Classes of org.restlet.routing.Redirector

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.