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: 18539 $
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            getOutFilter().add("Content-Type");
070    
071            // Filter out Content-Length since it can fool Jetty (HttpGenerator) to 
072            // close response output stream prematurely.  (It occurs when the
073            // message size (e.g. with attachment) is large and response content length 
074            // is bigger than request content length.)
075            getOutFilter().add("Content-Length");
076            
077            // Filter Content-Length as it will cause some trouble when the message 
078            // is passed to the other endpoint
079            getInFilter().add("Content-Length");
080    
081            // initialize message header filter map with default SOAP filter
082            messageHeaderFiltersMap = new HashMap<String, MessageHeaderFilter>();
083            addToMessageHeaderFilterMap(new SoapMessageHeaderFilter());
084            
085            // filter headers begin with "Camel" or "org.apache.camel"
086            setOutFilterPattern("(Camel|org\\.apache\\.camel)[\\.|a-z|A-z|0-9]*");
087        }
088    
089        @SuppressWarnings("unchecked")
090        @Override
091        protected boolean extendedFilter(Direction direction, String key, Object value, Exchange exchange) {
092            // Currently only handles Header.HEADER_LIST message header relay/filter
093            if (!Header.HEADER_LIST.equals(key) || value == null) { 
094                return false;
095            }
096            
097            if (!relayHeaders) {
098                // not propagating Header.HEADER_LIST at all
099                return true;
100            }
101            
102            if (relayAllMessageHeaders) {
103                // all message headers will be relayed (no filtering)
104                return false;
105            }
106    
107            // get filter
108            MessageHeaderFilter messageHeaderfilter = getMessageHeaderFilter(exchange);
109            if (messageHeaderfilter == null) {
110                if (LOG.isDebugEnabled()) {
111                    LOG.debug("No CXF Binding namespace can be resolved.  Message headers are intact.");
112                }
113                return false;
114            }
115            
116            if (LOG.isTraceEnabled()) {
117                LOG.trace("messageHeaderfilter = " + messageHeaderfilter);
118            }
119    
120            try {
121                messageHeaderfilter.filter(direction, (List<Header>)value);
122            } catch (Throwable t) {
123                if (LOG.isDebugEnabled()) {
124                    LOG.debug("Failed to cast value to Header<List> due to " + t.toString(), t);
125                }
126            }
127            
128            // return false since the header list (which has been filtered) should be propagated
129            return false;
130        }
131    
132        private void addToMessageHeaderFilterMap(MessageHeaderFilter filter) {
133            for (String ns : filter.getActivationNamespaces()) {
134                if (messageHeaderFiltersMap.containsKey(ns) && messageHeaderFiltersMap.get(ns) 
135                    != messageHeaderFiltersMap && !allowFilterNamespaceClash) {
136                    throw new IllegalArgumentException("More then one MessageHeaderRelay activates "
137                                                       + "for the same namespace: " + ns);
138                }
139                messageHeaderFiltersMap.put(ns, filter);
140            }
141        }
142        
143        private MessageHeaderFilter getMessageHeaderFilter(Exchange exchange) {
144            BindingOperationInfo boi = exchange.getProperty(BindingOperationInfo.class.getName(), 
145                                                            BindingOperationInfo.class);
146            String ns = null;
147            if (boi != null) {
148                BindingInfo b = boi.getBinding();
149                if (b != null) {
150                    ns = b.getBindingId();
151                }
152            }
153            
154            MessageHeaderFilter answer = null;
155            if (ns != null) {
156                answer = messageHeaderFiltersMap.get(ns);
157            }
158            
159            return answer;
160        }
161    
162        /**
163         * @param messageHeaderFilters the messageHeaderFilters to set
164         */
165        public void setMessageHeaderFilters(List<MessageHeaderFilter> messageHeaderFilters) {
166            this.messageHeaderFilters = messageHeaderFilters;
167            // clear the amp to allow removal of default filter
168            messageHeaderFiltersMap.clear();
169            for (MessageHeaderFilter filter : messageHeaderFilters) {
170                addToMessageHeaderFilterMap(filter);
171            }
172        }
173    
174        /**
175         * @return the messageHeaderFilters
176         */
177        public List<MessageHeaderFilter> getMessageHeaderFilters() {
178            return messageHeaderFilters;
179        }
180    
181        /**
182         * @return the allowFilterNamespaceClash
183         */
184        public boolean isAllowFilterNamespaceClash() {
185            return allowFilterNamespaceClash;
186        }
187    
188        /**
189         * @param allowFilterNamespaceClash the allowFilterNamespaceClash to set
190         */
191        public void setAllowFilterNamespaceClash(boolean allowFilterNamespaceClash) {
192            this.allowFilterNamespaceClash = allowFilterNamespaceClash;
193        }
194        
195        /**
196         * @return the messageHeaderFiltersMap
197         */
198        public Map<String, MessageHeaderFilter> getMessageHeaderFiltersMap() {
199            return messageHeaderFiltersMap;
200        }
201    
202        /**
203         * @param relayHeaders the relayHeaders to set
204         */
205        public void setRelayHeaders(boolean relayHeaders) {
206            this.relayHeaders = relayHeaders;
207        }
208    
209        /**
210         * @return the relayHeaders
211         */
212        public boolean isRelayHeaders() {
213            return relayHeaders;
214        }
215    
216        /**
217         * @return the relayAllMessageHeaders
218         */
219        public boolean isRelayAllMessageHeaders() {
220            return relayAllMessageHeaders;
221        }
222    
223        /**
224         * @param relayAllMessageHeaders the relayAllMessageHeaders to set
225         */
226        public void setRelayAllMessageHeaders(boolean relayAllMessageHeaders) {
227            this.relayAllMessageHeaders = relayAllMessageHeaders;
228        }
229    }