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    }