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 }