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.component.test;
018
019import java.util.ArrayList;
020import java.util.Iterator;
021import java.util.List;
022
023import org.apache.camel.Component;
024import org.apache.camel.Endpoint;
025import org.apache.camel.Exchange;
026import org.apache.camel.Processor;
027import org.apache.camel.WrappedFile;
028import org.apache.camel.component.mock.MockEndpoint;
029import org.apache.camel.spi.Metadata;
030import org.apache.camel.spi.UriEndpoint;
031import org.apache.camel.spi.UriParam;
032import org.apache.camel.spi.UriPath;
033import org.apache.camel.util.EndpointHelper;
034import org.apache.camel.util.ObjectHelper;
035import org.slf4j.Logger;
036import org.slf4j.LoggerFactory;
037
038/**
039 * The test component extends the mock component by on startup to pull messages from another endpoint to set the expected message bodies.
040 *
041 * That is, you use the test endpoint in a route and messages arriving on it will be implicitly compared to some
042 * expected messages extracted from some other location.
043 * So you can use, for example, an expected set of message bodies as files.
044 * This will then set up a properly configured Mock endpoint, which is only valid if the received messages
045 * match the number of expected messages and their message payloads are equal.
046 */
047@UriEndpoint(firstVersion = "1.3.0", scheme = "test", title = "Test", syntax = "test:name", producerOnly = true, label = "core,testing", lenientProperties = true)
048public class TestEndpoint extends MockEndpoint {
049    private static final Logger LOG = LoggerFactory.getLogger(TestEndpoint.class);
050
051    private Endpoint expectedMessageEndpoint;
052
053    @UriPath(description = "Name of endpoint to lookup in the registry to use for polling messages used for testing") @Metadata(required = "true")
054    private String name;
055    @UriParam
056    private boolean anyOrder;
057    @UriParam(defaultValue = "2000")
058    private long timeout = 2000L;
059    @UriParam
060    private boolean split;
061    @UriParam
062    private String delimiter = "\\n|\\r";
063
064    public TestEndpoint(String endpointUri, Component component) {
065        super(endpointUri, component);
066    }
067
068    public void setExpectedMessageEndpoint(Endpoint expectedMessageEndpoint) {
069        this.expectedMessageEndpoint = expectedMessageEndpoint;
070    }
071
072    @Override
073    protected void doStart() throws Exception {
074        LOG.debug("Consuming expected messages from: {}", expectedMessageEndpoint);
075
076        final List<Object> expectedBodies = new ArrayList<Object>();
077        EndpointHelper.pollEndpoint(expectedMessageEndpoint, new Processor() {
078            public void process(Exchange exchange) throws Exception {
079                // if file based we need to load the file into memory as the file may be deleted/moved afterwards
080                Object body = getInBody(exchange);
081                if (body instanceof WrappedFile) {
082                    body = exchange.getIn().getBody(String.class);
083                }
084                if (split) {
085                    // use new lines in both styles
086                    Iterator it = ObjectHelper.createIterator(body, delimiter, false, true);
087                    while (it.hasNext()) {
088                        Object line = it.next();
089                        LOG.trace("Received message body {}", line);
090                        expectedBodies.add(line);
091                    }
092                } else {
093                    expectedBodies.add(body);
094                }
095            }
096        }, timeout);
097
098        LOG.info("Received: {} expected message(s) from: {}", expectedBodies.size(), expectedMessageEndpoint);
099        if (anyOrder) {
100            expectedBodiesReceivedInAnyOrder(expectedBodies);
101        } else {
102            expectedBodiesReceived(expectedBodies);
103        }
104    }
105
106    /**
107     * This method allows us to convert or coerce the expected message body into some other type
108     */
109    protected Object getInBody(Exchange exchange) {
110        return exchange.getIn().getBody();
111    }
112
113    public long getTimeout() {
114        return timeout;
115    }
116
117    /**
118     * The timeout to use when polling for message bodies from the URI
119     */
120    public void setTimeout(long timeout) {
121        this.timeout = timeout;
122    }
123
124    public boolean isAnyOrder() {
125        return anyOrder;
126    }
127
128    /**
129     * Whether the expected messages should arrive in the same order or can be in any order.
130     */
131    public void setAnyOrder(boolean anyOrder) {
132        this.anyOrder = anyOrder;
133    }
134
135    public boolean isSplit() {
136        return split;
137    }
138
139    /**
140     * If enabled the the messages loaded from the test endpoint will be split using new line delimiters
141     * so each line is an expected message.
142     * <br/>
143     * For example to use a file endpoint to load a file where each line is an expected message.
144     */
145    public void setSplit(boolean split) {
146        this.split = split;
147    }
148
149    public String getDelimiter() {
150        return delimiter;
151    }
152
153    /**
154     * The split delimiter to use when split is enabled.
155     * By default the delimiter is new line based.
156     * The delimiter can be a regular expression.
157     */
158    public void setDelimiter(String delimiter) {
159        this.delimiter = delimiter;
160    }
161}