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    
018    package org.apache.camel.component.cxf.jaxrs;
019    
020    import java.lang.reflect.Method;
021    import java.util.HashMap;
022    import java.util.List;
023    import java.util.Map;
024    
025    import javax.ws.rs.core.MultivaluedMap;
026    import javax.ws.rs.core.Response;
027    
028    import org.apache.camel.Exchange;
029    import org.apache.camel.Message;
030    import org.apache.camel.component.cxf.CxfConstants;
031    import org.apache.camel.spi.HeaderFilterStrategy;
032    import org.apache.camel.spi.HeaderFilterStrategyAware;
033    import org.apache.commons.logging.Log;
034    import org.apache.commons.logging.LogFactory;
035    import org.apache.cxf.jaxrs.impl.MetadataMap;
036    import org.apache.cxf.jaxrs.model.OperationResourceInfoStack;
037    import org.apache.cxf.message.MessageContentsList;
038    
039    /**
040     * Default strategy  to bind between Camel and CXF exchange for RESTful resources.
041     *
042     *
043     * @version $Revision: 18533 $
044     */
045    public class DefaultCxfRsBinding implements CxfRsBinding, HeaderFilterStrategyAware {
046        private static final Log LOG = LogFactory.getLog(DefaultCxfRsBinding.class);
047    
048        protected Map<String, String> camelToCxfHeaderMap = new HashMap<String, String>();
049        protected Map<String, String> cxfToCamelHeaderMap = new HashMap<String, String>();
050        
051        private HeaderFilterStrategy headerFilterStrategy;
052        
053        public DefaultCxfRsBinding() {
054            // initialize mappings between Camel and CXF header names
055            
056            camelToCxfHeaderMap.put(Exchange.HTTP_URI, org.apache.cxf.message.Message.REQUEST_URI);
057            camelToCxfHeaderMap.put(Exchange.HTTP_METHOD, org.apache.cxf.message.Message.HTTP_REQUEST_METHOD);
058            camelToCxfHeaderMap.put(Exchange.HTTP_PATH, org.apache.cxf.message.Message.PATH_INFO);
059            camelToCxfHeaderMap.put(Exchange.CONTENT_TYPE, org.apache.cxf.message.Message.CONTENT_TYPE);
060            camelToCxfHeaderMap.put(Exchange.HTTP_CHARACTER_ENCODING, org.apache.cxf.message.Message.ENCODING);
061            camelToCxfHeaderMap.put(Exchange.HTTP_QUERY, org.apache.cxf.message.Message.QUERY_STRING);
062            camelToCxfHeaderMap.put(Exchange.ACCEPT_CONTENT_TYPE, org.apache.cxf.message.Message.ACCEPT_CONTENT_TYPE);
063        
064            cxfToCamelHeaderMap.put(org.apache.cxf.message.Message.REQUEST_URI, Exchange.HTTP_URI);
065            cxfToCamelHeaderMap.put(org.apache.cxf.message.Message.HTTP_REQUEST_METHOD, Exchange.HTTP_METHOD);
066            cxfToCamelHeaderMap.put(org.apache.cxf.message.Message.PATH_INFO, Exchange.HTTP_PATH);
067            cxfToCamelHeaderMap.put(org.apache.cxf.message.Message.CONTENT_TYPE, Exchange.CONTENT_TYPE);
068            cxfToCamelHeaderMap.put(org.apache.cxf.message.Message.ENCODING, Exchange.HTTP_CHARACTER_ENCODING);
069            cxfToCamelHeaderMap.put(org.apache.cxf.message.Message.QUERY_STRING, Exchange.HTTP_QUERY);
070            cxfToCamelHeaderMap.put(org.apache.cxf.message.Message.ACCEPT_CONTENT_TYPE, Exchange.ACCEPT_CONTENT_TYPE);
071        }
072        
073        public Object populateCxfRsResponseFromExchange(Exchange camelExchange,
074                                                        org.apache.cxf.message.Exchange cxfExchange) throws Exception {
075            if (camelExchange.isFailed()) {
076                throw camelExchange.getException();
077            }
078            
079            return camelExchange.getOut().getBody();
080        }
081    
082        
083        public void populateExchangeFromCxfRsRequest(org.apache.cxf.message.Exchange cxfExchange,
084                                                     Exchange camelExchange, Method method, Object[] paramArray) {
085            Message camelMessage = camelExchange.getIn();        
086            //Copy the CXF message header into the Camel inMessage
087            org.apache.cxf.message.Message cxfMessage = cxfExchange.getInMessage();
088            
089            // TODO use header filter strategy and cxfToCamelHeaderMap
090            
091            copyMessageHeader(cxfMessage, camelMessage, org.apache.cxf.message.Message.REQUEST_URI, Exchange.HTTP_URI);
092           
093            copyMessageHeader(cxfMessage, camelMessage, org.apache.cxf.message.Message.HTTP_REQUEST_METHOD, Exchange.HTTP_METHOD);
094            
095            // We need remove the BASE_PATH from the PATH_INFO
096            String pathInfo = (String)cxfMessage.get(org.apache.cxf.message.Message.PATH_INFO);
097            String basePath = (String)cxfMessage.get(org.apache.cxf.message.Message.BASE_PATH);
098            if (pathInfo != null && basePath != null && pathInfo.startsWith(basePath)) {
099                pathInfo = pathInfo.substring(basePath.length());
100            }
101            if (pathInfo != null) {
102                camelMessage.setHeader(Exchange.HTTP_PATH, pathInfo);
103            }
104            
105            copyMessageHeader(cxfMessage, camelMessage, org.apache.cxf.message.Message.CONTENT_TYPE, Exchange.CONTENT_TYPE);
106            
107            copyMessageHeader(cxfMessage, camelMessage, org.apache.cxf.message.Message.ENCODING, Exchange.HTTP_CHARACTER_ENCODING);
108            
109            copyMessageHeader(cxfMessage, camelMessage, org.apache.cxf.message.Message.QUERY_STRING, Exchange.HTTP_QUERY);
110            
111            copyMessageHeader(cxfMessage, camelMessage, org.apache.cxf.message.Message.ACCEPT_CONTENT_TYPE, Exchange.ACCEPT_CONTENT_TYPE);
112            
113            camelMessage.setHeader(CxfConstants.CAMEL_CXF_RS_RESPONSE_CLASS, method.getReturnType());
114            
115            camelMessage.setHeader(CxfConstants.CAMEL_CXF_RS_RESPONSE_GENERIC_TYPE, method.getGenericReturnType());
116                   
117            copyOperationResourceInfoStack(cxfMessage, camelMessage);
118            
119            camelMessage.setHeader(CxfConstants.OPERATION_NAME, method.getName());
120            
121            camelMessage.setBody(new MessageContentsList(paramArray));        
122        }
123    
124        
125        public MultivaluedMap<String, String> bindCamelHeadersToRequestHeaders(Map<String, Object> camelHeaders,
126                                                                               Exchange camelExchange)
127            throws Exception {
128    
129            MultivaluedMap<String, String> answer = new MetadataMap<String, String>();
130            for (Map.Entry<String, Object> entry : camelHeaders.entrySet()) {
131                
132                if (headerFilterStrategy.applyFilterToCamelHeaders(entry.getKey(), entry.getValue(), camelExchange)) {
133                    if (LOG.isTraceEnabled()) {
134                        LOG.trace("Drop Camel header: " + entry.getKey() + "=" + entry.getValue());
135                    }
136                    continue;
137                }
138                
139                String mappedHeaderName = camelToCxfHeaderMap.get(entry.getKey());
140                if (mappedHeaderName == null) {
141                    mappedHeaderName = entry.getKey();
142                }
143                
144                if (LOG.isTraceEnabled()) {
145                    LOG.trace("Propagate Camel header: " + entry.getKey() + "=" + entry.getValue() + " as " 
146                              + mappedHeaderName);
147                }
148                
149                answer.putSingle(mappedHeaderName, entry.getValue().toString());
150            }
151            return answer;
152        }
153    
154        /**
155         * This method call Message.getBody({@link MessageContentsList}) to allow
156         * an appropriate converter to kick in even through we only read the first
157         * element off the MessageContextList.  If that returns null, we check  
158         * the body to see if it is a List or an array and then return the first 
159         * element.  If that fails, we will simply return the object.
160         */
161        public Object bindCamelMessageBodyToRequestBody(Message camelMessage, Exchange camelExchange)
162            throws Exception {
163    
164            Object request = camelMessage.getBody(MessageContentsList.class);
165            if (request != null) {
166                return ((MessageContentsList)request).get(0);
167            } 
168    
169            request = camelMessage.getBody();
170            if (request instanceof List) {
171                request = ((List<?>)request).get(0);
172            } else if (request.getClass().isArray()) {
173                request = ((Object[])request)[0];
174            }
175    
176            return request;
177    
178        }
179    
180        /**
181         * We will return an empty Map unless the response parameter is a {@link Response} object. 
182         */
183        public Map<String, Object> bindResponseHeadersToCamelHeaders(Object response, Exchange exchange)
184            throws Exception {
185            
186            Map<String, Object> answer = new HashMap<String, Object>();
187            if (response instanceof Response) {
188                
189                for (Map.Entry<String, List<Object>> entry : ((Response)response).getMetadata().entrySet()) {
190                    if (!headerFilterStrategy.applyFilterToExternalHeaders(entry.getKey(), 
191                                                                           entry.getValue(), exchange)) {
192                        
193                        String mappedHeaderName = cxfToCamelHeaderMap.get(entry.getKey());
194                        if (mappedHeaderName == null) {
195                            mappedHeaderName = entry.getKey();
196                        }
197                        
198                        if (LOG.isTraceEnabled()) {
199                            LOG.trace("Populate external header " + entry.getKey() + "=" + entry.getValue()
200                                      + " as " + mappedHeaderName);
201                        }
202                        
203                        answer.put(mappedHeaderName, entry.getValue().get(0));
204    
205                    } else {
206                        if (LOG.isTraceEnabled()) {
207                            LOG.trace("Drop external header " + entry.getKey() + "=" + entry.getValue());
208                        }
209                    }
210                }
211                
212                // put response code in Camel message header
213                answer.put(Exchange.HTTP_RESPONSE_CODE, ((Response)response).getStatus());
214            }
215            
216            return answer;
217        }
218    
219        /**
220         *  By default, we just return the response object. 
221         */
222        public Object bindResponseToCamelBody(Object response, Exchange camelExchange) throws Exception {
223            return response;
224        }
225        
226        public HeaderFilterStrategy getHeaderFilterStrategy() {        
227            return headerFilterStrategy;
228        }
229    
230    
231        public void setHeaderFilterStrategy(HeaderFilterStrategy strategy) {
232            headerFilterStrategy = strategy;        
233        }
234        
235        public Map<String, String> getCamelToCxfHeaderMap() {
236            return camelToCxfHeaderMap;
237        }
238    
239        public void setCamelToCxfHeaderMap(Map<String, String> camelToCxfHeaderMap) {
240            this.camelToCxfHeaderMap = camelToCxfHeaderMap;
241        }
242    
243        public Map<String, String> getCxfToCamelHeaderMap() {
244            return cxfToCamelHeaderMap;
245        }
246    
247        public void setCxfToCamelHeaderMap(Map<String, String> cxfToCamelHeaderMap) {
248            this.cxfToCamelHeaderMap = cxfToCamelHeaderMap;
249        }
250        
251        protected void copyMessageHeader(org.apache.cxf.message.Message cxfMessage, Message camelMessage, String cxfKey, String camelKey) {
252            if (cxfMessage.get(cxfKey) != null) {
253                camelMessage.setHeader(camelKey, cxfMessage.get(cxfKey));
254            }
255        }
256        
257        protected void copyOperationResourceInfoStack(org.apache.cxf.message.Message cxfMessage, Message camelMessage) {
258            OperationResourceInfoStack stack = cxfMessage.get(OperationResourceInfoStack.class);
259            if (stack != null) {
260                // make a copy of the operation resource info for looking up the sub resource location
261                OperationResourceInfoStack copyStack = (OperationResourceInfoStack)stack.clone();
262                camelMessage.setHeader(CxfConstants.CAMEL_CXF_RS_OPERATION_RESOURCE_INFO_STACK, copyStack);
263                            
264            }
265          
266        }
267    
268    }