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.cxf;
018    
019    import java.util.HashMap;
020    import java.util.List;
021    import java.util.Map;
022    
023    import org.apache.camel.Exchange;
024    import org.apache.camel.impl.DefaultHeaderFilterStrategy;
025    import org.apache.commons.logging.Log;
026    import org.apache.commons.logging.LogFactory;
027    import org.apache.cxf.endpoint.Client;
028    import org.apache.cxf.headers.Header;
029    import org.apache.cxf.message.Message;
030    import org.apache.cxf.service.model.BindingInfo;
031    import org.apache.cxf.service.model.BindingOperationInfo;
032    
033    /**
034     * The default CXF header filter strategy.
035     * 
036     * @version $Revision: 17642 $
037     */
038    public class CxfHeaderFilterStrategy extends DefaultHeaderFilterStrategy {
039        private static final Log LOG = LogFactory.getLog(CxfHeaderFilterStrategy.class);
040        private Map<String, MessageHeaderFilter> messageHeaderFiltersMap;
041     
042        private List<MessageHeaderFilter> messageHeaderFilters;
043    
044        private boolean relayHeaders = true;
045        private boolean allowFilterNamespaceClash;
046        private boolean relayAllMessageHeaders;
047    
048        public CxfHeaderFilterStrategy() {
049            initialize();  
050        }
051    
052        protected void initialize() {
053            getOutFilter().add(CxfConstants.OPERATION_NAME);
054            getOutFilter().add(CxfConstants.OPERATION_NAMESPACE);
055            
056            // Request and response context Maps will be passed to CXF Client APIs
057            getOutFilter().add(Client.REQUEST_CONTEXT);
058            getOutFilter().add(Client.RESPONSE_CONTEXT);
059    
060            // protocol headers are stored as a Map.  DefaultCxfBinding
061            // read the Map and send each entry to the filter.  Therefore,
062            // we need to filter the header of this name.
063            getOutFilter().add(Message.PROTOCOL_HEADERS);
064            getInFilter().add(Message.PROTOCOL_HEADERS);
065            
066            // Since CXF can take the content-type from the protocol header
067            // we need to filter this header of this name.
068            getOutFilter().add("content-type");
069            
070            // Filter out Content-Length since it can fool Jetty (HttpGenerator) to 
071            // close response output stream prematurely.  (It occurs when the
072            // message size (e.g. with attachment) is large and response content length 
073            // is bigger than request content length.)
074            getOutFilter().add("Content-Length");
075    
076            // initialize message header filter map with default SOAP filter
077            messageHeaderFiltersMap = new HashMap<String, MessageHeaderFilter>();
078            addToMessageHeaderFilterMap(new SoapMessageHeaderFilter());
079            
080            // filter headers begin with "Camel" or "org.apache.camel"
081            setOutFilterPattern("(Camel|org\\.apache\\.camel)[\\.|a-z|A-z|0-9]*");
082        }
083    
084        @SuppressWarnings("unchecked")
085        @Override
086        protected boolean extendedFilter(Direction direction, String key, Object value, Exchange exchange) {
087            // Currently only handles Header.HEADER_LIST message header relay/filter
088            if (!Header.HEADER_LIST.equals(key) || value == null) { 
089                return false;
090            }
091            
092            if (!relayHeaders) {
093                // not propagating Header.HEADER_LIST at all
094                return true;
095            }
096            
097            if (relayAllMessageHeaders) {
098                // all message headers will be relayed (no filtering)
099                return false;
100            }
101    
102            // get filter
103            MessageHeaderFilter messageHeaderfilter = getMessageHeaderFilter(exchange);
104            if (messageHeaderfilter == null) {
105                if (LOG.isDebugEnabled()) {
106                    LOG.debug("No CXF Binding namespace can be resolved.  Message headers are intact.");
107                }
108                return false;
109            }
110            
111            if (LOG.isTraceEnabled()) {
112                LOG.trace("messageHeaderfilter = " + messageHeaderfilter);
113            }
114    
115            try {
116                messageHeaderfilter.filter(direction, (List<Header>)value);
117            } catch (Throwable t) {
118                if (LOG.isDebugEnabled()) {
119                    LOG.debug("Failed to cast value to Header<List> due to " + t.toString(), t);
120                }
121            }
122            
123            // return false since the header list (which has been filtered) should be propagated
124            return false;
125        }
126    
127        private void addToMessageHeaderFilterMap(MessageHeaderFilter filter) {
128            for (String ns : filter.getActivationNamespaces()) {
129                if (messageHeaderFiltersMap.containsKey(ns) && messageHeaderFiltersMap.get(ns) 
130                    != messageHeaderFiltersMap && !allowFilterNamespaceClash) {
131                    throw new IllegalArgumentException("More then one MessageHeaderRelay activates "
132                                                       + "for the same namespace: " + ns);
133                }
134                messageHeaderFiltersMap.put(ns, filter);
135            }
136        }
137        
138        private MessageHeaderFilter getMessageHeaderFilter(Exchange exchange) {
139            BindingOperationInfo boi = exchange.getProperty(BindingOperationInfo.class.getName(), 
140                                                            BindingOperationInfo.class);
141            String ns = null;
142            if (boi != null) {
143                BindingInfo b = boi.getBinding();
144                if (b != null) {
145                    ns = b.getBindingId();
146                }
147            }
148            
149            MessageHeaderFilter answer = null;
150            if (ns != null) {
151                answer = messageHeaderFiltersMap.get(ns);
152            }
153            
154            return answer;
155        }
156    
157        /**
158         * @param messageHeaderFilters the messageHeaderFilters to set
159         */
160        public void setMessageHeaderFilters(List<MessageHeaderFilter> messageHeaderFilters) {
161            this.messageHeaderFilters = messageHeaderFilters;
162            // clear the amp to allow removal of default filter
163            messageHeaderFiltersMap.clear();
164            for (MessageHeaderFilter filter : messageHeaderFilters) {
165                addToMessageHeaderFilterMap(filter);
166            }
167        }
168    
169        /**
170         * @return the messageHeaderFilters
171         */
172        public List<MessageHeaderFilter> getMessageHeaderFilters() {
173            return messageHeaderFilters;
174        }
175    
176        /**
177         * @return the allowFilterNamespaceClash
178         */
179        public boolean isAllowFilterNamespaceClash() {
180            return allowFilterNamespaceClash;
181        }
182    
183        /**
184         * @param allowFilterNamespaceClash the allowFilterNamespaceClash to set
185         */
186        public void setAllowFilterNamespaceClash(boolean allowFilterNamespaceClash) {
187            this.allowFilterNamespaceClash = allowFilterNamespaceClash;
188        }
189        
190        /**
191         * @return the messageHeaderFiltersMap
192         */
193        public Map<String, MessageHeaderFilter> getMessageHeaderFiltersMap() {
194            return messageHeaderFiltersMap;
195        }
196    
197        /**
198         * @param relayHeaders the relayHeaders to set
199         */
200        public void setRelayHeaders(boolean relayHeaders) {
201            this.relayHeaders = relayHeaders;
202        }
203    
204        /**
205         * @return the relayHeaders
206         */
207        public boolean isRelayHeaders() {
208            return relayHeaders;
209        }
210    
211        /**
212         * @return the relayAllMessageHeaders
213         */
214        public boolean isRelayAllMessageHeaders() {
215            return relayAllMessageHeaders;
216        }
217    
218        /**
219         * @param relayAllMessageHeaders the relayAllMessageHeaders to set
220         */
221        public void setRelayAllMessageHeaders(boolean relayAllMessageHeaders) {
222            this.relayAllMessageHeaders = relayAllMessageHeaders;
223        }
224    }