001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.camel.component.restlet;
018
019 import java.io.IOException;
020 import java.io.PrintWriter;
021 import java.io.StringWriter;
022 import java.util.Map;
023
024 import javax.xml.transform.dom.DOMSource;
025
026 import org.apache.camel.Exchange;
027 import org.apache.camel.HeaderFilterStrategyAware;
028 import org.apache.camel.Message;
029 import org.apache.camel.RuntimeCamelException;
030 import org.apache.camel.converter.jaxp.StringSource;
031 import org.apache.camel.spi.HeaderFilterStrategy;
032 import org.apache.commons.logging.Log;
033 import org.apache.commons.logging.LogFactory;
034 import org.restlet.data.ChallengeResponse;
035 import org.restlet.data.ChallengeScheme;
036 import org.restlet.data.CharacterSet;
037 import org.restlet.data.Form;
038 import org.restlet.data.MediaType;
039 import org.restlet.data.Parameter;
040 import org.restlet.data.Request;
041 import org.restlet.data.Response;
042 import org.restlet.data.Status;
043
044 /**
045 * Default Restlet binding implementation
046 *
047 * @version $Revision: 753088 $
048 */
049 public class DefaultRestletBinding implements RestletBinding, HeaderFilterStrategyAware {
050 private static final Log LOG = LogFactory.getLog(DefaultRestletBinding.class);
051 private HeaderFilterStrategy headerFilterStrategy;
052
053 /**
054 * Populate Camel message from Restlet request
055 *
056 * @param request message to be copied from
057 * @param exchange to be populated
058 * @throws Exception
059 */
060 public void populateExchangeFromRestletRequest(Request request,
061 Exchange exchange) throws Exception {
062
063 Message inMessage = exchange.getIn();
064 // extract headers from restlet
065 for (Map.Entry<String, Object> entry : request.getAttributes().entrySet()) {
066 if (!headerFilterStrategy.applyFilterToExternalHeaders(entry.getKey(),
067 entry.getValue())) {
068
069 inMessage.setHeader(entry.getKey(), entry.getValue());
070 if (LOG.isDebugEnabled()) {
071 LOG.debug("Populate exchange from Restlet request header: "
072 + entry.getKey() + " value: " + entry.getValue());
073 }
074
075 }
076 }
077
078 // copy query string to header
079 String query = request.getResourceRef().getQuery();
080 if (null != query) {
081 inMessage.setHeader(RestletConstants.QUERY_STRING, query);
082 }
083
084 if (!request.isEntityAvailable()) {
085 return;
086 }
087
088 Form form = new Form(request.getEntity());
089 if (form != null) {
090 for (Map.Entry<String, String> entry : form.getValuesMap().entrySet()) {
091 // extract body added to the form as the key which has null value
092 if (entry.getValue() == null) {
093 inMessage.setBody(entry.getKey());
094 if (LOG.isDebugEnabled()) {
095 LOG.debug("Populate exchange from Restlet request body: " + entry.getValue());
096 }
097 } else {
098 if (!headerFilterStrategy.applyFilterToExternalHeaders(entry.getKey(),
099 entry.getValue())) {
100
101 inMessage.setHeader(entry.getKey(), entry.getValue());
102 if (LOG.isDebugEnabled()) {
103 LOG.debug("Populate exchange from Restlet request user header: "
104 + entry.getKey() + " value: " + entry.getValue());
105 }
106 }
107 }
108 }
109 }
110 }
111
112 /**
113 * Populate Restlet Request from Camel message
114 *
115 * @param request to be populated
116 * @param exchange message to be copied from
117 */
118 public void populateRestletRequestFromExchange(Request request,
119 Exchange exchange) {
120 request.setReferrerRef("camel-restlet");
121 String body = exchange.getIn().getBody(String.class);
122 Form form = new Form();
123 // add the body as the key in the form with null value
124 form.add(body, null);
125
126 if (LOG.isDebugEnabled()) {
127 LOG.debug("Populate Restlet request from exchange body: " + body);
128 }
129
130 // login and password are filtered by header filter strategy
131 String login = (String) exchange.getIn().getHeader(RestletConstants.LOGIN);
132 String password = (String) exchange.getIn().getHeader(RestletConstants.PASSWORD);
133
134 if (login != null && password != null) {
135 ChallengeResponse authentication = new ChallengeResponse(
136 ChallengeScheme.HTTP_BASIC, login, password);
137 request.setChallengeResponse(authentication);
138 if (LOG.isDebugEnabled()) {
139 LOG.debug("Basic HTTP Authentication has been applied");
140 }
141 }
142
143 for (Map.Entry<String, Object> entry : exchange.getIn().getHeaders().entrySet()) {
144 if (!headerFilterStrategy.applyFilterToCamelHeaders(entry.getKey(),
145 entry.getValue())) {
146 if (entry.getKey().startsWith("org.restlet.")) {
147 // put the org.restlet headers in attributes
148 request.getAttributes().put(entry.getKey(), entry.getValue());
149 } else {
150 // put the user stuff in the form
151 form.add(entry.getKey(), entry.getValue().toString());
152 }
153 if (LOG.isDebugEnabled()) {
154 LOG.debug("Populate Restlet request from exchange header: "
155 + entry.getKey() + " value: " + entry.getValue());
156 }
157 }
158 }
159
160 request.setEntity(form.getWebRepresentation());
161 }
162
163 /**
164 * Populate Restlet request from Camel message
165 *
166 * @param exchange message to be copied from
167 * @param response to be populated
168 */
169 public void populateRestletResponseFromExchange(Exchange exchange,
170 Response response) {
171
172 Message out = null;
173 if (exchange.isFailed()) {
174 // 500 for internal server error which can be overridden by response code in header
175 response.setStatus(Status.valueOf(500));
176 out = exchange.getFault(false);
177 if (out == null) {
178 Throwable t = exchange.getException();
179 if (t != null) {
180 StringWriter sw = new StringWriter();
181 PrintWriter pw = new PrintWriter(sw);
182 t.printStackTrace(pw);
183 response.setEntity(sw.toString(), MediaType.TEXT_PLAIN);
184 return;
185 }
186 }
187 } else {
188 out = exchange.getOut();
189 }
190
191 // get content type
192 MediaType mediaType = out.getHeader(RestletConstants.MEDIA_TYPE, MediaType.class);
193 if (mediaType == null) {
194 Object body = out.getBody();
195 mediaType = MediaType.TEXT_PLAIN;
196 if (body instanceof String) {
197 mediaType = MediaType.TEXT_PLAIN;
198 } else if (body instanceof StringSource || body instanceof DOMSource) {
199 mediaType = MediaType.TEXT_XML;
200 }
201 }
202
203 // get response code
204 Integer responseCode = out.getHeader(RestletConstants.RESPONSE_CODE, Integer.class);
205 if (responseCode != null) {
206 response.setStatus(Status.valueOf(responseCode));
207 }
208
209 for (Map.Entry<String, Object> entry : out.getHeaders().entrySet()) {
210 if (!headerFilterStrategy.applyFilterToCamelHeaders(entry.getKey(),
211 entry.getValue())) {
212 response.getAttributes().put(entry.getKey(), entry.getValue());
213 if (LOG.isDebugEnabled()) {
214 LOG.debug("Populate Restlet response from exchange header: "
215 + entry.getKey() + " value: " + entry.getValue());
216 }
217 }
218 }
219
220 String text = out.getBody(String.class);
221 if (LOG.isDebugEnabled()) {
222 LOG.debug("Populate Restlet response from exchange body: " + text);
223 }
224 response.setEntity(text, mediaType);
225
226 if (exchange.getProperty(Exchange.CHARSET_NAME) != null) {
227 response.getEntity().setCharacterSet(CharacterSet.valueOf(exchange.getProperty(Exchange.CHARSET_NAME,
228 String.class)));
229 }
230 }
231
232 /**
233 * Populate Camel message from Restlet response
234 *
235 * @param exchange to be populated
236 * @param response message to be copied from
237 * @throws IOException
238 */
239 public void populateExchangeFromRestletResponse(Exchange exchange,
240 Response response) throws IOException {
241
242 for (Map.Entry<String, Object> entry : response.getAttributes().entrySet()) {
243 if (!headerFilterStrategy.applyFilterToExternalHeaders(entry.getKey(),
244 entry.getValue())) {
245 exchange.getOut().setHeader(entry.getKey(), entry.getValue());
246 if (LOG.isDebugEnabled()) {
247 LOG.debug("Populate exchange from Restlet response header: "
248 + entry.getKey() + " value: " + entry.getValue());
249 }
250 }
251 }
252
253 String text = response.getEntity().getText();
254 if (LOG.isDebugEnabled()) {
255 LOG.debug("Populate exchange from Restlet response: " + text);
256 }
257
258 if (exchange.getPattern().isOutCapable()) {
259 exchange.getOut().setBody(text);
260 } else {
261 throw new RuntimeCamelException("Exchange is incapable of receiving response: "
262 + exchange);
263 }
264 }
265
266 public HeaderFilterStrategy getHeaderFilterStrategy() {
267 return headerFilterStrategy;
268 }
269
270 public void setHeaderFilterStrategy(HeaderFilterStrategy strategy) {
271 headerFilterStrategy = strategy;
272 }
273 }