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 */ 017package org.apache.camel.processor; 018 019import java.util.HashSet; 020import java.util.Map; 021import java.util.Set; 022 023import org.apache.camel.Exchange; 024import org.apache.camel.processor.aggregate.AggregationStrategy; 025import org.apache.camel.util.EndpointHelper; 026import org.apache.camel.util.ObjectHelper; 027import org.apache.camel.util.StringHelper; 028import org.slf4j.Logger; 029import org.slf4j.LoggerFactory; 030 031/** 032 * Default {@link AggregationStrategy} used by the {@link ClaimCheckProcessor} EIP. 033 * <p/> 034 * This strategy supports the following include rules syntax: 035 * <ul> 036 * <li>body</li> - to aggregate the message body 037 * <li>attachments</li> - to aggregate all the message attachments 038 * <li>headers</li> - to aggregate all the message headers 039 * <li>header:pattern</li> - to aggregate all the message headers that matches the pattern. 040 * The pattern syntax is documented by: {@link EndpointHelper#matchPattern(String, String)}. 041 * </ul> 042 * You can specify multiple rules separated by comma. For example to include the message body and all headers starting with foo 043 * <tt>body,header:foo*</tt>. 044 * If the include rule is specified as empty or as wildcard then everything is merged. 045 * If you have configured both include and exclude then exclude take precedence over include. 046 */ 047public class ClaimCheckAggregationStrategy implements AggregationStrategy { 048 049 private static final Logger LOG = LoggerFactory.getLogger(ClaimCheckAggregationStrategy.class); 050 private String filter; 051 052 public ClaimCheckAggregationStrategy() { 053 } 054 055 public String getFilter() { 056 return filter; 057 } 058 059 public void setFilter(String filter) { 060 this.filter = filter; 061 } 062 063 @Override 064 public Exchange aggregate(Exchange oldExchange, Exchange newExchange) { 065 if (newExchange == null) { 066 return oldExchange; 067 } 068 069 if (ObjectHelper.isEmpty(filter) || "*".equals(filter)) { 070 // grab everything 071 oldExchange.getMessage().setBody(newExchange.getMessage().getBody()); 072 LOG.trace("Including: body"); 073 if (newExchange.getMessage().hasHeaders()) { 074 oldExchange.getMessage().getHeaders().putAll(newExchange.getMessage().getHeaders()); 075 LOG.trace("Including: headers"); 076 } 077 if (newExchange.getMessage().hasAttachments()) { 078 oldExchange.getMessage().getAttachments().putAll(newExchange.getMessage().getAttachments()); 079 LOG.trace("Including: attachments"); 080 } 081 return oldExchange; 082 } 083 084 // body is by default often included 085 if (isBodyEnabled()) { 086 oldExchange.getMessage().setBody(newExchange.getMessage().getBody()); 087 LOG.trace("Including: body"); 088 } 089 090 // attachments is by default often included 091 if (isAttachmentsEnabled()) { 092 if (newExchange.getMessage().hasAttachments()) { 093 oldExchange.getMessage().getAttachments().putAll(newExchange.getMessage().getAttachments()); 094 LOG.trace("Including: attachments"); 095 } 096 } 097 098 // headers is by default often included 099 if (isHeadersEnabled()) { 100 if (newExchange.getMessage().hasHeaders()) { 101 oldExchange.getMessage().getHeaders().putAll(newExchange.getMessage().getHeaders()); 102 LOG.trace("Including: headers"); 103 } 104 } 105 106 // filter specific header if they are somehow enabled by the filter 107 if (hasHeaderPatterns()) { 108 boolean excludeOnly = isExcludeOnlyHeaderPatterns(); 109 for (Map.Entry<String, Object> header : newExchange.getMessage().getHeaders().entrySet()) { 110 String key = header.getKey(); 111 if (hasHeaderPattern(key)) { 112 boolean include = isIncludedHeader(key); 113 boolean exclude = isExcludedHeader(key); 114 if (include) { 115 LOG.trace("Including: header:{}", key); 116 oldExchange.getMessage().getHeaders().put(key, header.getValue()); 117 } else if (exclude) { 118 LOG.trace("Excluding: header:{}", key); 119 } else { 120 LOG.trace("Skipping: header:{}", key); 121 } 122 } else if (excludeOnly) { 123 LOG.trace("Including: header:{}", key); 124 oldExchange.getMessage().getHeaders().put(key, header.getValue()); 125 } 126 } 127 } 128 129 // filter body and all headers 130 if (ObjectHelper.isNotEmpty(filter)) { 131 Iterable it = ObjectHelper.createIterable(filter, ","); 132 for (Object k : it) { 133 String part = k.toString(); 134 if (("body".equals(part) || "+body".equals(part)) && !"-body".equals(part)) { 135 oldExchange.getMessage().setBody(newExchange.getMessage().getBody()); 136 LOG.trace("Including: body"); 137 } else if (("headers".equals(part) || "+headers".equals(part)) && !"-headers".equals(part)) { 138 oldExchange.getMessage().getHeaders().putAll(newExchange.getMessage().getHeaders()); 139 LOG.trace("Including: headers"); 140 } 141 } 142 } 143 144 // filter with remove (--) take precedence at the end 145 Iterable it = ObjectHelper.createIterable(filter, ","); 146 for (Object k : it) { 147 String part = k.toString(); 148 if ("--body".equals(part)) { 149 oldExchange.getMessage().setBody(null); 150 } else if ("--headers".equals(part)) { 151 oldExchange.getMessage().getHeaders().clear(); 152 } else if (part.startsWith("--header:")) { 153 // pattern matching for headers, eg header:foo, header:foo*, header:(foo|bar) 154 String after = StringHelper.after(part, "--header:"); 155 Iterable i = ObjectHelper.createIterable(after, ","); 156 Set<String> toRemoveKeys = new HashSet<>(); 157 for (Object o : i) { 158 String pattern = o.toString(); 159 for (Map.Entry<String, Object> header : oldExchange.getMessage().getHeaders().entrySet()) { 160 String key = header.getKey(); 161 boolean matched = EndpointHelper.matchPattern(key, pattern); 162 if (matched) { 163 toRemoveKeys.add(key); 164 } 165 } 166 } 167 for (String key : toRemoveKeys) { 168 LOG.trace("Removing: header:{}", key); 169 oldExchange.getMessage().removeHeader(key); 170 } 171 } 172 } 173 174 return oldExchange; 175 } 176 177 private boolean hasHeaderPatterns() { 178 String[] parts = filter.split(","); 179 for (String pattern : parts) { 180 if (pattern.startsWith("--")) { 181 continue; 182 } 183 if (pattern.startsWith("header:") || pattern.startsWith("+header:") || pattern.startsWith("-header:")) { 184 return true; 185 } 186 } 187 return false; 188 } 189 190 private boolean isExcludeOnlyHeaderPatterns() { 191 String[] parts = filter.split(","); 192 for (String pattern : parts) { 193 if (pattern.startsWith("--")) { 194 continue; 195 } 196 if (pattern.startsWith("header:") || pattern.startsWith("+header:")) { 197 return false; 198 } 199 } 200 return true; 201 } 202 203 private boolean hasHeaderPattern(String key) { 204 String[] parts = filter.split(","); 205 for (String pattern : parts) { 206 if (pattern.startsWith("--")) { 207 continue; 208 } 209 String header = null; 210 if (pattern.startsWith("header:") || pattern.startsWith("+header:")) { 211 header = StringHelper.after(pattern, "header:"); 212 } else if (pattern.startsWith("-header:")) { 213 header = StringHelper.after(pattern, "-header:"); 214 } 215 if (header != null && EndpointHelper.matchPattern(key, header)) { 216 return true; 217 } 218 } 219 return false; 220 } 221 222 private boolean isIncludedHeader(String key) { 223 String[] parts = filter.split(","); 224 for (String pattern : parts) { 225 if (pattern.startsWith("--")) { 226 continue; 227 } 228 if (pattern.startsWith("header:") || pattern.startsWith("+header:")) { 229 pattern = StringHelper.after(pattern, "header:"); 230 } 231 if (EndpointHelper.matchPattern(key, pattern)) { 232 return true; 233 } 234 } 235 return false; 236 } 237 238 private boolean isExcludedHeader(String key) { 239 String[] parts = filter.split(","); 240 for (String pattern : parts) { 241 if (pattern.startsWith("--")) { 242 continue; 243 } 244 if (pattern.startsWith("-header:")) { 245 pattern = StringHelper.after(pattern, "-header:"); 246 } 247 if (EndpointHelper.matchPattern(key, pattern)) { 248 return true; 249 } 250 } 251 return false; 252 } 253 254 private boolean isBodyEnabled() { 255 // body is always enabled unless excluded 256 String[] parts = filter.split(","); 257 258 boolean onlyExclude = true; 259 for (String pattern : parts) { 260 if (pattern.startsWith("--")) { 261 continue; 262 } 263 if ("body".equals(pattern) || "+body".equals(pattern)) { 264 return true; 265 } else if ("-body".equals(pattern)) { 266 return false; 267 } 268 onlyExclude &= pattern.startsWith("-"); 269 } 270 // body is enabled if we only have exclude patterns 271 return onlyExclude; 272 } 273 274 private boolean isAttachmentsEnabled() { 275 // attachments is always enabled unless excluded 276 String[] parts = filter.split(","); 277 278 boolean onlyExclude = true; 279 for (String pattern : parts) { 280 if (pattern.startsWith("--")) { 281 continue; 282 } 283 if ("attachments".equals(pattern) || "+attachments".equals(pattern)) { 284 return true; 285 } else if ("-attachments".equals(pattern)) { 286 return false; 287 } 288 onlyExclude &= pattern.startsWith("-"); 289 } 290 // attachments is enabled if we only have exclude patterns 291 return onlyExclude; 292 } 293 294 private boolean isHeadersEnabled() { 295 // headers may be enabled unless excluded 296 String[] parts = filter.split(","); 297 298 boolean onlyExclude = true; 299 for (String pattern : parts) { 300 if (pattern.startsWith("--")) { 301 continue; 302 } 303 // if there is individual header filters then we cannot rely on this 304 if (pattern.startsWith("header:") || pattern.startsWith("+header:") || pattern.startsWith("-header:")) { 305 return false; 306 } 307 if ("headers".equals(pattern) || "+headers".equals(pattern)) { 308 return true; 309 } else if ("-headers".equals(pattern)) { 310 return false; 311 } 312 onlyExclude &= pattern.startsWith("-"); 313 } 314 // headers is enabled if we only have exclude patterns 315 return onlyExclude; 316 } 317 318}