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.Endpoint;
027import org.apache.camel.Processor;
028import org.apache.camel.processor.Enricher;
029import org.apache.camel.processor.aggregate.AggregationStrategy;
030import org.apache.camel.processor.aggregate.AggregationStrategyBeanAdapter;
031import org.apache.camel.spi.Metadata;
032import org.apache.camel.spi.RouteContext;
033import org.apache.camel.util.ObjectHelper;
034
035/**
036 * Enriches a message with data from a secondary resource
037 *
038 * @see Enricher
039 */
040@Metadata(label = "eip,transformation")
041@XmlRootElement(name = "enrich")
042@XmlAccessorType(XmlAccessType.FIELD)
043public class EnrichDefinition extends NoOutputDefinition<EnrichDefinition> implements EndpointRequiredDefinition {
044    @XmlAttribute(name = "uri")
045    private String resourceUri;
046    // TODO: For Camel 3.0 we should remove this ref attribute as you can do that in the uri, by prefixing with ref:
047    @XmlAttribute(name = "ref")
048    @Deprecated
049    private String resourceRef;
050    @XmlAttribute(name = "strategyRef")
051    private String aggregationStrategyRef;
052    @XmlAttribute(name = "strategyMethodName")
053    private String aggregationStrategyMethodName;
054    @XmlAttribute(name = "strategyMethodAllowNull")
055    private Boolean aggregationStrategyMethodAllowNull;
056    @XmlAttribute
057    private Boolean aggregateOnException;
058    @XmlTransient
059    private AggregationStrategy aggregationStrategy;
060    
061    public EnrichDefinition() {
062        this(null, null);
063    }
064
065    public EnrichDefinition(String resourceUri) {
066        this(null, resourceUri);
067    }
068    
069    public EnrichDefinition(AggregationStrategy aggregationStrategy, String resourceUri) {
070        this.aggregationStrategy = aggregationStrategy;
071        this.resourceUri = resourceUri;
072    }
073    
074    @Override
075    public String toString() {
076        return "Enrich[" + description() + " " + aggregationStrategy + "]";
077    }
078    
079    protected String description() {
080        return FromDefinition.description(resourceUri, resourceRef, (Endpoint) null);
081    }
082    
083    @Override
084    public String getLabel() {
085        return "enrich[" + description() + "]";
086    }
087
088    @Override
089    public String getEndpointUri() {
090        if (resourceUri != null) {
091            return resourceUri;
092        } else {
093            return null;
094        }
095    }
096
097    @Override
098    public Processor createProcessor(RouteContext routeContext) throws Exception {
099        if (ObjectHelper.isEmpty(resourceUri) && ObjectHelper.isEmpty(resourceRef)) {
100            throw new IllegalArgumentException("Either uri or ref must be provided for resource endpoint");
101        }
102
103        // lookup endpoint
104        Endpoint endpoint;
105        if (resourceUri != null) {
106            endpoint = routeContext.resolveEndpoint(resourceUri);
107        } else {
108            endpoint = routeContext.resolveEndpoint(null, resourceRef);
109        }
110
111        Enricher enricher = new Enricher(null, endpoint.createProducer());
112        AggregationStrategy strategy = createAggregationStrategy(routeContext);
113        if (strategy == null) {
114            enricher.setDefaultAggregationStrategy();
115        } else {
116            enricher.setAggregationStrategy(strategy);
117        }
118        if (getAggregateOnException() != null) {
119            enricher.setAggregateOnException(getAggregateOnException());
120        }
121        return enricher;
122    }
123
124    private AggregationStrategy createAggregationStrategy(RouteContext routeContext) {
125        AggregationStrategy strategy = getAggregationStrategy();
126        if (strategy == null && aggregationStrategyRef != null) {
127            Object aggStrategy = routeContext.lookup(aggregationStrategyRef, Object.class);
128            if (aggStrategy instanceof AggregationStrategy) {
129                strategy = (AggregationStrategy) aggStrategy;
130            } else if (aggStrategy != null) {
131                AggregationStrategyBeanAdapter adapter = new AggregationStrategyBeanAdapter(aggStrategy, getAggregationStrategyMethodName());
132                if (getAggregationStrategyMethodAllowNull() != null) {
133                    adapter.setAllowNullNewExchange(getAggregationStrategyMethodAllowNull());
134                    adapter.setAllowNullOldExchange(getAggregationStrategyMethodAllowNull());
135                }
136                strategy = adapter;
137            } else {
138                throw new IllegalArgumentException("Cannot find AggregationStrategy in Registry with name: " + aggregationStrategyRef);
139            }
140        }
141
142        if (strategy != null && strategy instanceof CamelContextAware) {
143            ((CamelContextAware) strategy).setCamelContext(routeContext.getCamelContext());
144        }
145
146        return strategy;
147    }
148
149    public String getResourceUri() {
150        return resourceUri;
151    }
152
153    /**
154     * The endpoint uri for the external service to enrich from. You must use either uri or ref.
155     */
156    public void setResourceUri(String resourceUri) {
157        this.resourceUri = resourceUri;
158    }
159
160    public String getResourceRef() {
161        return resourceRef;
162    }
163
164    /**
165     * Refers to the endpoint for the external service to enrich from. You must use either uri or ref.
166     *
167     * @deprecated use uri with ref:uri instead
168     */
169    @Deprecated
170    public void setResourceRef(String resourceRef) {
171        this.resourceRef = resourceRef;
172    }
173
174    public String getAggregationStrategyRef() {
175        return aggregationStrategyRef;
176    }
177
178    /**
179     * Refers to an AggregationStrategy to be used to merge the reply from the external service, into a single outgoing message.
180     * By default Camel will use the reply from the external service as outgoing message.
181     */
182    public void setAggregationStrategyRef(String aggregationStrategyRef) {
183        this.aggregationStrategyRef = aggregationStrategyRef;
184    }
185
186    public String getAggregationStrategyMethodName() {
187        return aggregationStrategyMethodName;
188    }
189
190    /**
191     * This option can be used to explicit declare the method name to use, when using POJOs as the AggregationStrategy.
192     */
193    public void setAggregationStrategyMethodName(String aggregationStrategyMethodName) {
194        this.aggregationStrategyMethodName = aggregationStrategyMethodName;
195    }
196
197    public Boolean getAggregationStrategyMethodAllowNull() {
198        return aggregationStrategyMethodAllowNull;
199    }
200
201    /**
202     * If this option is false then the aggregate method is not used if there was no data to enrich.
203     * If this option is true then null values is used as the oldExchange (when no data to enrich),
204     * when using POJOs as the AggregationStrategy.
205     */
206    public void setAggregationStrategyMethodAllowNull(Boolean aggregationStrategyMethodAllowNull) {
207        this.aggregationStrategyMethodAllowNull = aggregationStrategyMethodAllowNull;
208    }
209
210    public AggregationStrategy getAggregationStrategy() {
211        return aggregationStrategy;
212    }
213
214    /**
215     * Sets the AggregationStrategy to be used to merge the reply from the external service, into a single outgoing message.
216     * By default Camel will use the reply from the external service as outgoing message.
217     */
218    public void setAggregationStrategy(AggregationStrategy aggregationStrategy) {
219        this.aggregationStrategy = aggregationStrategy;
220    }
221
222    public Boolean getAggregateOnException() {
223        return aggregateOnException;
224    }
225
226    /**
227     * If this option is false then the aggregate method is not used if there was an exception thrown while trying
228     * to retrieve the data to enrich from the resource. Setting this option to true allows end users to control what
229     * to do if there was an exception in the aggregate method. For example to suppress the exception
230     * or set a custom message body etc.
231     */
232    public void setAggregateOnException(Boolean aggregateOnException) {
233        this.aggregateOnException = aggregateOnException;
234    }
235}