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.processor; 018 019 import org.apache.camel.Exchange; 020 import org.apache.camel.PollingConsumer; 021 import org.apache.camel.Processor; 022 import org.apache.camel.impl.ServiceSupport; 023 import org.apache.camel.processor.aggregate.AggregationStrategy; 024 import org.apache.camel.util.ExchangeHelper; 025 import org.apache.camel.util.ServiceHelper; 026 import org.apache.commons.logging.Log; 027 import org.apache.commons.logging.LogFactory; 028 029 import static org.apache.camel.util.ExchangeHelper.copyResultsPreservePattern; 030 031 /** 032 * A content enricher that enriches input data by first obtaining additional 033 * data from a <i>resource</i> represented by an endpoint <code>producer</code> 034 * and second by aggregating input data and additional data. Aggregation of 035 * input data and additional data is delegated to an {@link org.apache.camel.processor.aggregate.AggregationStrategy} 036 * object. 037 * <p/> 038 * Uses a {@link org.apache.camel.PollingConsumer} to obtain the additional data as opposed to {@link Enricher} 039 * that uses a {@link org.apache.camel.Producer}. 040 * 041 * @see Enricher 042 */ 043 public class PollEnricher extends ServiceSupport implements Processor { 044 045 private static final transient Log LOG = LogFactory.getLog(PollEnricher.class); 046 private AggregationStrategy aggregationStrategy; 047 private PollingConsumer consumer; 048 private long timeout; 049 050 /** 051 * Creates a new {@link PollEnricher}. The default aggregation strategy is to 052 * copy the additional data obtained from the enricher's resource over the 053 * input data. When using the copy aggregation strategy the enricher 054 * degenerates to a normal transformer. 055 * 056 * @param consumer consumer to resource endpoint. 057 */ 058 public PollEnricher(PollingConsumer consumer) { 059 this(defaultAggregationStrategy(), consumer, 0); 060 } 061 062 /** 063 * Creates a new {@link PollEnricher}. 064 * 065 * @param aggregationStrategy aggregation strategy to aggregate input data and additional data. 066 * @param consumer consumer to resource endpoint. 067 * @param timeout timeout in millis 068 */ 069 public PollEnricher(AggregationStrategy aggregationStrategy, PollingConsumer consumer, long timeout) { 070 this.aggregationStrategy = aggregationStrategy; 071 this.consumer = consumer; 072 this.timeout = timeout; 073 } 074 075 /** 076 * Sets the aggregation strategy for this poll enricher. 077 * 078 * @param aggregationStrategy the aggregationStrategy to set 079 */ 080 public void setAggregationStrategy(AggregationStrategy aggregationStrategy) { 081 this.aggregationStrategy = aggregationStrategy; 082 } 083 084 /** 085 * Sets the default aggregation strategy for this poll enricher. 086 */ 087 public void setDefaultAggregationStrategy() { 088 this.aggregationStrategy = defaultAggregationStrategy(); 089 } 090 091 /** 092 * Sets the timeout to use when polling. 093 * <p/> 094 * Use 0 or negative to not use timeout and block until data is available. 095 * 096 * @param timeout timeout in millis. 097 */ 098 public void setTimeout(long timeout) { 099 this.timeout = timeout; 100 } 101 102 /** 103 * Enriches the input data (<code>exchange</code>) by first obtaining 104 * additional data from an endpoint represented by an endpoint 105 * <code>producer</code> and second by aggregating input data and additional 106 * data. Aggregation of input data and additional data is delegated to an 107 * {@link org.apache.camel.processor.aggregate.AggregationStrategy} object set at construction time. If the 108 * message exchange with the resource endpoint fails then no aggregation 109 * will be done and the failed exchange content is copied over to the 110 * original message exchange. 111 * 112 * @param exchange input data. 113 */ 114 public void process(Exchange exchange) throws Exception { 115 preCheckPoll(exchange); 116 117 Exchange resourceExchange; 118 if (timeout < 0) { 119 if (LOG.isDebugEnabled()) { 120 LOG.debug("Consumer receive: " + consumer); 121 } 122 resourceExchange = consumer.receive(); 123 } else if (timeout == 0) { 124 if (LOG.isDebugEnabled()) { 125 LOG.debug("Consumer receiveNoWait: " + consumer); 126 } 127 resourceExchange = consumer.receiveNoWait(); 128 } else { 129 if (LOG.isDebugEnabled()) { 130 LOG.debug("Consumer receive with timeout: " + timeout + " ms. " + consumer); 131 } 132 resourceExchange = consumer.receive(timeout); 133 } 134 135 if (LOG.isDebugEnabled()) { 136 if (resourceExchange == null) { 137 LOG.debug("Consumer received no exchange"); 138 } else { 139 LOG.debug("Consumer received: " + resourceExchange); 140 } 141 } 142 143 if (resourceExchange != null && resourceExchange.isFailed()) { 144 // copy resource exchange onto original exchange (preserving pattern) 145 copyResultsPreservePattern(exchange, resourceExchange); 146 } else { 147 prepareResult(exchange); 148 149 // prepare the exchanges for aggregation 150 ExchangeHelper.prepareAggregation(exchange, resourceExchange); 151 Exchange aggregatedExchange = aggregationStrategy.aggregate(exchange, resourceExchange); 152 if (aggregatedExchange != null) { 153 // copy aggregation result onto original exchange (preserving pattern) 154 copyResultsPreservePattern(exchange, aggregatedExchange); 155 // handover any synchronization 156 if (resourceExchange != null) { 157 resourceExchange.handoverCompletions(exchange); 158 } 159 } 160 } 161 } 162 163 /** 164 * Strategy to pre check polling. 165 * <p/> 166 * Is currently used to prevent doing poll enrich from a file based endpoint when the current route also 167 * started from a file based endpoint as that is not currently supported. 168 * 169 * @param exchange the current exchange 170 */ 171 protected void preCheckPoll(Exchange exchange) throws Exception { 172 // noop 173 } 174 175 private static void prepareResult(Exchange exchange) { 176 if (exchange.getPattern().isOutCapable()) { 177 exchange.getOut().copyFrom(exchange.getIn()); 178 } 179 } 180 181 private static AggregationStrategy defaultAggregationStrategy() { 182 return new CopyAggregationStrategy(); 183 } 184 185 @Override 186 public String toString() { 187 return "PollEnrich[" + consumer + "]"; 188 } 189 190 protected void doStart() throws Exception { 191 ServiceHelper.startService(consumer); 192 } 193 194 protected void doStop() throws Exception { 195 ServiceHelper.stopService(consumer); 196 } 197 198 private static class CopyAggregationStrategy implements AggregationStrategy { 199 200 public Exchange aggregate(Exchange oldExchange, Exchange newExchange) { 201 if (newExchange != null) { 202 copyResultsPreservePattern(oldExchange, newExchange); 203 } 204 return oldExchange; 205 } 206 207 } 208 209 }