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 }