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