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.PrintWriter;
020    import java.io.StringWriter;
021    import java.util.Map;
022    
023    import javax.xml.transform.dom.DOMSource;
024    
025    import org.apache.camel.Exchange;
026    import org.apache.camel.Message;
027    import org.apache.camel.RuntimeCamelException;
028    import org.apache.camel.converter.jaxp.StringSource;
029    import org.apache.camel.spi.HeaderFilterStrategy;
030    import org.apache.camel.spi.HeaderFilterStrategyAware;
031    import org.apache.commons.logging.Log;
032    import org.apache.commons.logging.LogFactory;
033    import org.restlet.data.ChallengeResponse;
034    import org.restlet.data.ChallengeScheme;
035    import org.restlet.data.CharacterSet;
036    import org.restlet.data.Form;
037    import org.restlet.data.MediaType;
038    import org.restlet.data.Method;
039    import org.restlet.data.Request;
040    import org.restlet.data.Response;
041    import org.restlet.data.Status;
042    
043    /**
044     * Default Restlet binding implementation
045     *
046     * @version $Revision: 19261 $
047     */
048    public class DefaultRestletBinding implements RestletBinding, HeaderFilterStrategyAware {
049        private static final Log LOG = LogFactory.getLog(DefaultRestletBinding.class);
050        private HeaderFilterStrategy headerFilterStrategy;
051    
052        public void populateExchangeFromRestletRequest(Request request, Exchange exchange) throws Exception {
053            Message inMessage = exchange.getIn();
054    
055            // extract headers from restlet
056            for (Map.Entry<String, Object> entry : request.getAttributes().entrySet()) {
057                if (!headerFilterStrategy.applyFilterToExternalHeaders(entry.getKey(), entry.getValue(), exchange)) {
058                    inMessage.setHeader(entry.getKey(), entry.getValue());
059                    if (LOG.isDebugEnabled()) {
060                        LOG.debug("Populate exchange from Restlet request header: " 
061                                + entry.getKey() + " value: " + entry.getValue());
062                    }
063                }
064            }
065            
066            // copy query string to header
067            String query = request.getResourceRef().getQuery();
068            if (query != null) {
069                inMessage.setHeader(Exchange.HTTP_QUERY, query);
070            }
071            
072            // copy URI to header
073            inMessage.setHeader(Exchange.HTTP_URI, request.getResourceRef().getIdentifier(true));
074            
075            // copy HTTP method to header
076            inMessage.setHeader(Exchange.HTTP_METHOD, request.getMethod().toString());
077    
078            if (!request.isEntityAvailable()) {
079                return;
080            }
081            
082            
083    
084            // only deal with the form if the content type is "application/x-www-form-urlencoded"
085            if (request.getEntity().getMediaType().equals(MediaType.APPLICATION_WWW_FORM)) {            
086                Form form = new Form(request.getEntity());
087                for (Map.Entry<String, String> entry : form.getValuesMap().entrySet()) {
088                    if (entry.getValue() == null) {
089                        inMessage.setBody(entry.getKey());
090                        if (LOG.isDebugEnabled()) {
091                            LOG.debug("Populate exchange from Restlet request body: " + entry.getValue());
092                        }
093                    } else {
094                        if (!headerFilterStrategy.applyFilterToExternalHeaders(entry.getKey(), entry.getValue(), exchange)) {
095                            inMessage.setHeader(entry.getKey(), entry.getValue());
096                            if (LOG.isDebugEnabled()) {
097                                LOG.debug("Populate exchange from Restlet request user header: "
098                                        + entry.getKey() + " value: " + entry.getValue());
099                            }
100                        }
101                    }
102                }
103            } else {
104                inMessage.setBody(request.getEntity().getStream());
105            }
106            
107        }
108    
109        public void populateRestletRequestFromExchange(Request request, Exchange exchange) {
110            request.setReferrerRef("camel-restlet");
111            String body = exchange.getIn().getBody(String.class);
112            Form form = new Form();
113            // add the body as the key in the form with null value
114            form.add(body, null);
115            
116            MediaType mediaType = exchange.getIn().getHeader(Exchange.CONTENT_TYPE, MediaType.class);
117            if (mediaType == null) {
118                mediaType = MediaType.APPLICATION_WWW_FORM;
119            }
120            
121            if (LOG.isDebugEnabled()) {
122                LOG.debug("Populate Restlet request from exchange body: " + body + " using media type " + mediaType);
123            }
124            
125            // login and password are filtered by header filter strategy
126            String login = exchange.getIn().getHeader(RestletConstants.RESTLET_LOGIN, String.class);
127            String password = exchange.getIn().getHeader(RestletConstants.RESTLET_PASSWORD, String.class);
128              
129            if (login != null && password != null) {
130                ChallengeResponse authentication = new ChallengeResponse(ChallengeScheme.HTTP_BASIC, login, password);
131                request.setChallengeResponse(authentication);
132                if (LOG.isDebugEnabled()) {
133                    LOG.debug("Basic HTTP Authentication has been applied");
134                }
135            }
136            
137            for (Map.Entry<String, Object> entry : exchange.getIn().getHeaders().entrySet()) {
138                if (!headerFilterStrategy.applyFilterToCamelHeaders(entry.getKey(), entry.getValue(), exchange)) {
139                    // Use forms only for GET and POST/x-www-form-urlencoded
140                    if (request.getMethod() == Method.GET || (request.getMethod() == Method.POST && mediaType == MediaType.APPLICATION_WWW_FORM)) {
141                        if (entry.getKey().startsWith("org.restlet.")) {
142                            // put the org.restlet headers in attributes
143                            request.getAttributes().put(entry.getKey(), entry.getValue());
144                        } else {
145                            // put the user stuff in the form
146                            form.add(entry.getKey(), entry.getValue().toString());   
147                        }
148                    } else {
149                        // For non-form post put all the headers in attributes
150                        request.getAttributes().put(entry.getKey(), entry.getValue());
151                    }
152                    if (LOG.isDebugEnabled()) {
153                        LOG.debug("Populate Restlet request from exchange header: " 
154                                + entry.getKey() + " value: " + entry.getValue());
155                    }
156                }
157            }
158            
159            if (LOG.isDebugEnabled()) {
160                LOG.debug("Using Content Type: " 
161                        + mediaType + " for POST data:  " + body);
162            }
163    
164            // Only URL Encode for GET and form POST
165            if (request.getMethod() == Method.GET || (request.getMethod() == Method.POST && mediaType == MediaType.APPLICATION_WWW_FORM)) {
166                request.setEntity(form.getWebRepresentation());
167            } else {
168                request.setEntity(body, mediaType);
169            }
170        }
171    
172        public void populateRestletResponseFromExchange(Exchange exchange, Response response) {
173            
174            Message out;
175            if (exchange.isFailed()) {
176                // 500 for internal server error which can be overridden by response code in header
177                response.setStatus(Status.valueOf(500));
178                if (exchange.hasOut() && exchange.getOut().isFault()) {
179                    out = exchange.getOut();
180                } else {
181                    // print exception as message and stacktrace
182                    Exception t = exchange.getException();
183                    StringWriter sw = new StringWriter();
184                    PrintWriter pw = new PrintWriter(sw);
185                    t.printStackTrace(pw);
186                    response.setEntity(sw.toString(), MediaType.TEXT_PLAIN);
187                    return;
188                }
189            } else {
190                out = exchange.getOut();
191            }
192                 
193            // get content type
194            MediaType mediaType = out.getHeader(Exchange.CONTENT_TYPE, MediaType.class);
195            if (mediaType == null) {
196                Object body = out.getBody();
197                mediaType = MediaType.TEXT_PLAIN;
198                if (body instanceof String) {
199                    mediaType = MediaType.TEXT_PLAIN;
200                } else if (body instanceof StringSource || body instanceof DOMSource) {
201                    mediaType = MediaType.TEXT_XML;
202                }
203            }
204                    
205            // get response code
206            Integer responseCode = out.getHeader(Exchange.HTTP_RESPONSE_CODE, Integer.class);
207            if (responseCode != null) {
208                response.setStatus(Status.valueOf(responseCode));
209            }
210    
211            for (Map.Entry<String, Object> entry : out.getHeaders().entrySet()) {
212                if (!headerFilterStrategy.applyFilterToCamelHeaders(entry.getKey(), entry.getValue(), exchange)) {
213                    response.getAttributes().put(entry.getKey(), entry.getValue());
214                    if (LOG.isDebugEnabled()) {
215                        LOG.debug("Populate Restlet response from exchange header: " 
216                                + entry.getKey() + " value: " + entry.getValue());
217                    }
218                }
219            }
220            
221            String text = out.getBody(String.class);
222            if (LOG.isDebugEnabled()) {
223                LOG.debug("Populate Restlet response from exchange body: " + text);
224            }
225            response.setEntity(text, mediaType);
226            
227            if (exchange.getProperty(Exchange.CHARSET_NAME) != null) {
228                response.getEntity().setCharacterSet(CharacterSet.valueOf(exchange.getProperty(Exchange.CHARSET_NAME, 
229                                                                                               String.class)));
230            } 
231        }
232    
233        public void populateExchangeFromRestletResponse(Exchange exchange, Response response) throws Exception {
234            
235            for (Map.Entry<String, Object> entry : response.getAttributes().entrySet()) {
236                if (!headerFilterStrategy.applyFilterToExternalHeaders(entry.getKey(), entry.getValue(), exchange)) {
237                    exchange.getOut().setHeader(entry.getKey(), entry.getValue());
238                    if (LOG.isDebugEnabled()) {
239                        LOG.debug("Populate exchange from Restlet response header: " 
240                                + entry.getKey() + " value: " + entry.getValue());
241                    }
242                }
243            }
244    
245            String text = response.getEntity().getText();
246            if (LOG.isDebugEnabled()) {
247                LOG.debug("Populate exchange from Restlet response: " + text);
248            }
249            
250            if (exchange.getPattern().isOutCapable()) {
251                exchange.getOut().setBody(text);
252            } else {
253                throw new RuntimeCamelException("Exchange is incapable of receiving response: " + exchange + " with pattern: " + exchange.getPattern());
254            }
255        }
256    
257        public HeaderFilterStrategy getHeaderFilterStrategy() {
258            return headerFilterStrategy;
259        }
260    
261        public void setHeaderFilterStrategy(HeaderFilterStrategy strategy) {
262            headerFilterStrategy = strategy;
263        }
264    }