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 }