001: /*
002: * Copyright 2005-2007 Noelios Consulting.
003: *
004: * The contents of this file are subject to the terms of the Common Development
005: * and Distribution License (the "License"). You may not use this file except in
006: * compliance with the License.
007: *
008: * You can obtain a copy of the license at
009: * http://www.opensource.org/licenses/cddl1.txt See the License for the specific
010: * language governing permissions and limitations under the License.
011: *
012: * When distributing Covered Code, include this CDDL HEADER in each file and
013: * include the License file at http://www.opensource.org/licenses/cddl1.txt If
014: * applicable, add the following below this CDDL HEADER, with the fields
015: * enclosed by brackets "[]" replaced with your own identifying information:
016: * Portions Copyright [yyyy] [name of copyright owner]
017: */
018:
019: package org.restlet;
020:
021: import java.util.logging.Level;
022:
023: import org.restlet.data.Reference;
024: import org.restlet.data.Request;
025: import org.restlet.data.Response;
026: import org.restlet.data.Status;
027: import org.restlet.resource.Representation;
028: import org.restlet.util.Template;
029:
030: /**
031: * Rewrites URIs then redirects the call or the client to a new destination.
032: *
033: * @see org.restlet.util.Template
034: * @see <a href="http://www.restlet.org/documentation/1.0/tutorial#part10">Tutorial: URI rewriting
035: * and redirection</a>
036: * @author Jerome Louvel (contact@noelios.com)
037: */
038: public class Redirector extends Restlet {
039: /**
040: * In this mode, the client is permanently redirected to the URI generated
041: * from the target URI pattern.<br/> See
042: * org.restlet.data.Status.REDIRECTION_PERMANENT.
043: */
044: public static final int MODE_CLIENT_PERMANENT = 1;
045:
046: /**
047: * In this mode, the client is simply redirected to the URI generated from
048: * the target URI pattern.<br/> See
049: * org.restlet.data.Status.REDIRECTION_FOUND.
050: */
051: public static final int MODE_CLIENT_FOUND = 2;
052:
053: /**
054: * In this mode, the client is simply redirected to the URI generated from
055: * the target URI pattern.<br/> See
056: * org.restlet.data.Status.REDIRECTION_SEE_OTHER.
057: */
058: public static final int MODE_CLIENT_SEE_OTHER = 3;
059:
060: /**
061: * In this mode, the client is temporarily redirected to the URI generated
062: * from the target URI pattern.<br/> See
063: * org.restlet.data.Status.REDIRECTION_TEMPORARY.
064: */
065: public static final int MODE_CLIENT_TEMPORARY = 4;
066:
067: /**
068: * In this mode, the call is sent to the context's dispatcher. Once the
069: * selected client connector has completed the request handling, the
070: * response is normally returned to the client. In this case, you can view
071: * the Redirector as acting as a transparent proxy Restlet.<br>
072: * <br>
073: * Remember to add the required connectors to the parent Component and to
074: * declare them in the list of required connectors on the
075: * Application.connectorService property.<br>
076: * <br>
077: * Note that in this mode, the headers of HTTP requests, stored in the
078: * request's attributes, are removed before dispatching. Also, when a HTTP
079: * response comes back the headers are also removed.
080: */
081: public static final int MODE_DISPATCHER = 5;
082:
083: /** The target URI pattern. */
084: protected String targetTemplate;
085:
086: /** The redirection mode. */
087: protected int mode;
088:
089: /**
090: * Constructor for the dispatcher mode.
091: *
092: * @param context
093: * The context.
094: * @param targetTemplate
095: * The template to build the target URI.
096: * @see org.restlet.util.Template
097: */
098: public Redirector(Context context, String targetTemplate) {
099: this (context, targetTemplate, MODE_DISPATCHER);
100: }
101:
102: /**
103: * Constructor.
104: *
105: * @param context
106: * The context.
107: * @param targetPattern
108: * The pattern to build the target URI (using StringTemplate
109: * syntax and the CallModel for variables).
110: * @param mode
111: * The redirection mode.
112: */
113: public Redirector(Context context, String targetPattern, int mode) {
114: super (context);
115: this .targetTemplate = targetPattern;
116: this .mode = mode;
117: }
118:
119: /**
120: * Handles a call to a resource or a set of resources.
121: *
122: * @param request
123: * The request to handle.
124: * @param response
125: * The response to update.
126: */
127: public void handle(Request request, Response response) {
128: // Generate the target reference
129: Reference targetRef = getTargetRef(request, response);
130:
131: switch (this .mode) {
132: case MODE_CLIENT_PERMANENT:
133: getLogger().log(Level.INFO,
134: "Permanently redirecting client to: " + targetRef);
135: response.redirectPermanent(targetRef);
136: break;
137:
138: case MODE_CLIENT_FOUND:
139: getLogger().log(
140: Level.INFO,
141: "Redirecting client to found location: "
142: + targetRef);
143: response.setRedirectRef(targetRef);
144: response.setStatus(Status.REDIRECTION_FOUND);
145: break;
146:
147: case MODE_CLIENT_SEE_OTHER:
148: getLogger().log(
149: Level.INFO,
150: "Redirecting client to another location: "
151: + targetRef);
152: response.setRedirectRef(targetRef);
153: response.setStatus(Status.REDIRECTION_SEE_OTHER);
154: break;
155:
156: case MODE_CLIENT_TEMPORARY:
157: getLogger().log(Level.INFO,
158: "Temporarily redirecting client to: " + targetRef);
159: response.redirectTemporary(targetRef);
160: break;
161:
162: case MODE_DISPATCHER:
163: getLogger()
164: .log(
165: Level.INFO,
166: "Redirecting via client connector to: "
167: + targetRef);
168: redirectDispatcher(targetRef, request, response);
169: break;
170: }
171: }
172:
173: /**
174: * Redirects a given call to a target reference. In the default
175: * implementation, the request HTTP headers, stored in the request's
176: * attributes, are removed before dispatching. After dispatching, the
177: * response HTTP headers are also removed to prevent conflicts with the main
178: * call.
179: *
180: * @param targetRef
181: * The target reference with URI variables resolved.
182: * @param request
183: * The request to handle.
184: * @param response
185: * The response to update.
186: */
187: protected void redirectDispatcher(Reference targetRef,
188: Request request, Response response) {
189: // Save the base URI if it exists as we might need it for redirections
190: Reference baseRef = request.getResourceRef().getBaseRef();
191:
192: // Update the request to cleanly go to the target URI
193: request.setResourceRef(targetRef);
194: request.getAttributes().remove("org.restlet.http.headers");
195: getContext().getDispatcher().handle(request, response);
196:
197: // Allow for response rewriting and clean the headers
198: response.setEntity(rewrite(response.getEntity()));
199: response.getAttributes().remove("org.restlet.http.headers");
200:
201: // In case of redirection, we may have to rewrite the redirect URI
202: if (response.getRedirectRef() != null) {
203: Template rt = new Template(getLogger(), this .targetTemplate);
204: int matched = rt.parse(
205: response.getRedirectRef().toString(), request);
206:
207: if (matched > 0) {
208: String remainingPart = (String) request.getAttributes()
209: .get("rr");
210:
211: if (remainingPart != null) {
212: response.setRedirectRef(baseRef.toString()
213: + remainingPart);
214: }
215: }
216: }
217: }
218:
219: /**
220: * Returns the target reference to redirect to.
221: *
222: * @param request
223: * The request to handle.
224: * @param response
225: * The response to update.
226: * @return The target reference to redirect to.
227: */
228: protected Reference getTargetRef(Request request, Response response) {
229: // Create the template
230: Template rt = new Template(getLogger(), this .targetTemplate);
231:
232: // Return the formatted target URI
233: return new Reference(rt.format(request, response));
234: }
235:
236: /**
237: * Optionnaly rewrites the response entity returned in the MODE_CONNECTOR
238: * mode. By default, it just returns the initial entity without any
239: * modification.
240: *
241: * @param initialEntity
242: * The initial entity returned.
243: * @return The rewritten entity.
244: */
245: protected Representation rewrite(Representation initialEntity) {
246: return initialEntity;
247: }
248:
249: }
|