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