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.model; 018 019import javax.xml.bind.annotation.XmlAccessType; 020import javax.xml.bind.annotation.XmlAccessorType; 021import javax.xml.bind.annotation.XmlAttribute; 022import javax.xml.bind.annotation.XmlRootElement; 023import javax.xml.bind.annotation.XmlTransient; 024 025import org.apache.camel.CamelContextAware; 026import org.apache.camel.Processor; 027import org.apache.camel.processor.ClaimCheckProcessor; 028import org.apache.camel.processor.aggregate.AggregationStrategy; 029import org.apache.camel.processor.aggregate.AggregationStrategyBeanAdapter; 030import org.apache.camel.spi.Metadata; 031import org.apache.camel.spi.RouteContext; 032import org.apache.camel.util.EndpointHelper; 033import org.apache.camel.util.ObjectHelper; 034 035/** 036 * The Claim Check EIP allows you to replace message content with a claim check (a unique key), 037 * which can be used to retrieve the message content at a later time. 038 */ 039@Metadata(label = "eip,routing") 040@XmlRootElement(name = "claimCheck") 041@XmlAccessorType(XmlAccessType.FIELD) 042public class ClaimCheckDefinition extends NoOutputDefinition<ClaimCheckDefinition> { 043 044 @XmlAttribute(required = true) 045 private ClaimCheckOperation operation; 046 @XmlAttribute 047 private String key; 048 @XmlAttribute 049 private String filter; 050 @XmlAttribute(name = "strategyRef") @Metadata(label = "advanced") 051 private String aggregationStrategyRef; 052 @XmlAttribute(name = "strategyMethodName") @Metadata(label = "advanced") 053 private String aggregationStrategyMethodName; 054 @XmlTransient 055 private AggregationStrategy aggregationStrategy; 056 057 public ClaimCheckDefinition() { 058 } 059 060 @Override 061 public String toString() { 062 if (operation != null) { 063 return "ClaimCheck[" + operation + "]"; 064 } else { 065 return "ClaimCheck"; 066 } 067 } 068 069 @Override 070 public String getShortName() { 071 return "claimCheck"; 072 } 073 074 @Override 075 public String getLabel() { 076 return "claimCheck"; 077 } 078 079 @Override 080 public Processor createProcessor(RouteContext routeContext) throws Exception { 081 ObjectHelper.notNull(operation, "operation", this); 082 083 ClaimCheckProcessor claim = new ClaimCheckProcessor(); 084 claim.setOperation(operation.name()); 085 claim.setKey(getKey()); 086 claim.setFilter(getFilter()); 087 088 AggregationStrategy strategy = createAggregationStrategy(routeContext); 089 if (strategy != null) { 090 claim.setAggregationStrategy(strategy); 091 } 092 093 // only filter or aggregation strategy can be configured not both 094 if (getFilter() != null && strategy != null) { 095 throw new IllegalArgumentException("Cannot use both filter and custom aggregation strategy on ClaimCheck EIP"); 096 } 097 098 // validate filter, we cannot have both +/- at the same time 099 if (getFilter() != null) { 100 Iterable it = ObjectHelper.createIterable(filter, ","); 101 boolean includeBody = false; 102 boolean excludeBody = false; 103 for (Object o : it) { 104 String pattern = o.toString(); 105 if ("body".equals(pattern) || "+body".equals(pattern)) { 106 includeBody = true; 107 } else if ("-body".equals(pattern)) { 108 excludeBody = true; 109 } 110 } 111 if (includeBody && excludeBody) { 112 throw new IllegalArgumentException("Cannot have both include and exclude body at the same time in the filter: " + filter); 113 } 114 boolean includeHeaders = false; 115 boolean excludeHeaders = false; 116 for (Object o : it) { 117 String pattern = o.toString(); 118 if ("headers".equals(pattern) || "+headers".equals(pattern)) { 119 includeHeaders = true; 120 } else if ("-headers".equals(pattern)) { 121 excludeHeaders = true; 122 } 123 } 124 if (includeHeaders && excludeHeaders) { 125 throw new IllegalArgumentException("Cannot have both include and exclude headers at the same time in the filter: " + filter); 126 } 127 boolean includeHeader = false; 128 boolean excludeHeader = false; 129 for (Object o : it) { 130 String pattern = o.toString(); 131 if (pattern.startsWith("header:") || pattern.startsWith("+header:")) { 132 includeHeader = true; 133 } else if (pattern.startsWith("-header:")) { 134 excludeHeader = true; 135 } 136 } 137 if (includeHeader && excludeHeader) { 138 throw new IllegalArgumentException("Cannot have both include and exclude header at the same time in the filter: " + filter); 139 } 140 } 141 142 return claim; 143 } 144 145 private AggregationStrategy createAggregationStrategy(RouteContext routeContext) { 146 AggregationStrategy strategy = getAggregationStrategy(); 147 if (strategy == null && aggregationStrategyRef != null) { 148 Object aggStrategy = routeContext.lookup(aggregationStrategyRef, Object.class); 149 if (aggStrategy instanceof AggregationStrategy) { 150 strategy = (AggregationStrategy) aggStrategy; 151 } else if (aggStrategy != null) { 152 strategy = new AggregationStrategyBeanAdapter(aggStrategy, getAggregationStrategyMethodName()); 153 } else { 154 throw new IllegalArgumentException("Cannot find AggregationStrategy in Registry with name: " + aggregationStrategyRef); 155 } 156 } 157 158 if (strategy instanceof CamelContextAware) { 159 ((CamelContextAware) strategy).setCamelContext(routeContext.getCamelContext()); 160 } 161 162 return strategy; 163 } 164 165 // Fluent API 166 //------------------------------------------------------------------------- 167 168 /** 169 * The claim check operation to use. 170 * The following operations is supported: 171 * <ul> 172 * <li>Get</li> - Gets (does not remove) the claim check by the given key. 173 * <li>GetAndRemove</li> - Gets and remove the claim check by the given key. 174 * <li>Set</li> - Sets a new (will override if key already exists) claim check with the given key. 175 * <li>Push</li> - Sets a new claim check on the stack (does not use key). 176 * <li>Pop</li> - Gets the latest claim check from the stack (does not use key). 177 * </ul> 178 */ 179 public ClaimCheckDefinition operation(ClaimCheckOperation operation) { 180 setOperation(operation); 181 return this; 182 } 183 184 /** 185 * To use a specific key for claim check id. 186 */ 187 public ClaimCheckDefinition key(String key) { 188 setKey(key); 189 return this; 190 } 191 192 /** 193 * Specified a filter to control what data gets merging data back from the claim check repository. 194 * 195 * The following syntax is supported: 196 * <ul> 197 * <li>body</li> - to aggregate the message body 198 * <li>attachments</li> - to aggregate all the message attachments 199 * <li>headers</li> - to aggregate all the message headers 200 * <li>header:pattern</li> - to aggregate all the message headers that matches the pattern. 201 * The pattern syntax is documented by: {@link EndpointHelper#matchPattern(String, String)}. 202 * </ul> 203 * You can specify multiple rules separated by comma. For example to include the message body and all headers starting with foo 204 * <tt>body,header:foo*</tt>. 205 * The syntax supports the following prefixes which can be used to specify include,exclude, or remove 206 * <ul> 207 * <li>+</li> - to include (which is the default mode) 208 * <li>-</li> - to exclude (exclude takes precedence over include) 209 * <li>--</li> - to remove (remove takes precedence) 210 * </ul> 211 * For example to exclude a header name foo, and remove all headers starting with bar 212 * <tt>-header:foo,--headers:bar*</tt> 213 * Note you cannot have both include and exclude <tt>header:pattern</tt> at the same time. 214 */ 215 public ClaimCheckDefinition filter(String filter) { 216 setFilter(filter); 217 return this; 218 } 219 220 /** 221 * To use a custom {@link AggregationStrategy} instead of the default implementation. 222 * Notice you cannot use both custom aggregation strategy and configure data at the same time. 223 */ 224 public ClaimCheckDefinition aggregationStrategy(AggregationStrategy aggregationStrategy) { 225 setAggregationStrategy(aggregationStrategy); 226 return this; 227 } 228 229 /** 230 * To use a custom {@link AggregationStrategy} instead of the default implementation. 231 * Notice you cannot use both custom aggregation strategy and configure data at the same time. 232 */ 233 public ClaimCheckDefinition aggregationStrategyRef(String aggregationStrategyRef) { 234 setAggregationStrategyRef(aggregationStrategyRef); 235 return this; 236 } 237 238 /** 239 * This option can be used to explicit declare the method name to use, when using POJOs as the AggregationStrategy. 240 */ 241 public ClaimCheckDefinition aggregationStrategyMethodName(String aggregationStrategyMethodName) { 242 setAggregationStrategyMethodName(aggregationStrategyMethodName); 243 return this; 244 } 245 246 // Properties 247 //------------------------------------------------------------------------- 248 249 public String getKey() { 250 return key; 251 } 252 253 public void setKey(String key) { 254 this.key = key; 255 } 256 257 public ClaimCheckOperation getOperation() { 258 return operation; 259 } 260 261 public void setOperation(ClaimCheckOperation operation) { 262 this.operation = operation; 263 } 264 265 public String getFilter() { 266 return filter; 267 } 268 269 public void setFilter(String filter) { 270 this.filter = filter; 271 } 272 273 public String getAggregationStrategyRef() { 274 return aggregationStrategyRef; 275 } 276 277 public void setAggregationStrategyRef(String aggregationStrategyRef) { 278 this.aggregationStrategyRef = aggregationStrategyRef; 279 } 280 281 public String getAggregationStrategyMethodName() { 282 return aggregationStrategyMethodName; 283 } 284 285 public void setAggregationStrategyMethodName(String aggregationStrategyMethodName) { 286 this.aggregationStrategyMethodName = aggregationStrategyMethodName; 287 } 288 289 public AggregationStrategy getAggregationStrategy() { 290 return aggregationStrategy; 291 } 292 293 public void setAggregationStrategy(AggregationStrategy aggregationStrategy) { 294 this.aggregationStrategy = aggregationStrategy; 295 } 296}