• Source
    1.  
    2.  
    3. /* Name of the class has to be "Main" only if the class is public. */
    4. /**
    5.  * Licensed to the Apache Software Foundation (ASF) under one or more
    6.  * contributor license agreements. See the NOTICE file distributed with
    7.  * this work for additional information regarding copyright ownership.
    8.  * The ASF licenses this file to You under the Apache License, Version 2.0
    9.  * (the "License"); you may not use this file except in compliance with
    10.  * the License. You may obtain a copy of the License at
    11.  *
    12.  * http://www.apache.org/licenses/LICENSE-2.0
    13.  *
    14.  * Unless required by applicable law or agreed to in writing, software
    15.  * distributed under the License is distributed on an "AS IS" BASIS,
    16.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    17.  * See the License for the specific language governing permissions and
    18.  * limitations under the License.
    19.  */
    20. package org.apache.camel.test.junit4;
    21.  
    22. import java.io.ByteArrayInputStream;
    23. import java.io.File;
    24. import java.io.FileOutputStream;
    25. import java.io.InputStream;
    26. import java.io.OutputStream;
    27. import java.util.ArrayList;
    28. import java.util.Collections;
    29. import java.util.Comparator;
    30. import java.util.HashMap;
    31. import java.util.Hashtable;
    32. import java.util.List;
    33. import java.util.Map;
    34. import java.util.Properties;
    35. import java.util.Set;
    36. import java.util.concurrent.TimeUnit;
    37. import java.util.stream.Collectors;
    38. import javax.management.AttributeNotFoundException;
    39. import javax.management.InstanceNotFoundException;
    40. import javax.management.MBeanException;
    41. import javax.management.MBeanServer;
    42. import javax.management.MalformedObjectNameException;
    43. import javax.management.ObjectName;
    44. import javax.management.ReflectionException;
    45. import javax.naming.Context;
    46. import javax.naming.InitialContext;
    47.  
    48. import org.apache.camel.CamelContext;
    49. import org.apache.camel.ConsumerTemplate;
    50. import org.apache.camel.Endpoint;
    51. import org.apache.camel.Exchange;
    52. import org.apache.camel.Expression;
    53. import org.apache.camel.FluentProducerTemplate;
    54. import org.apache.camel.Message;
    55. import org.apache.camel.NoSuchEndpointException;
    56. import org.apache.camel.Predicate;
    57. import org.apache.camel.Processor;
    58. import org.apache.camel.ProducerTemplate;
    59. import org.apache.camel.Route;
    60. import org.apache.camel.RoutesBuilder;
    61. import org.apache.camel.Service;
    62. import org.apache.camel.ServiceStatus;
    63. import org.apache.camel.api.management.mbean.ManagedCamelContextMBean;
    64. import org.apache.camel.api.management.mbean.ManagedProcessorMBean;
    65. import org.apache.camel.api.management.mbean.ManagedRouteMBean;
    66. import org.apache.camel.builder.AdviceWithRouteBuilder;
    67. import org.apache.camel.builder.RouteBuilder;
    68. import org.apache.camel.component.mock.MockEndpoint;
    69. import org.apache.camel.component.properties.PropertiesComponent;
    70. import org.apache.camel.impl.BreakpointSupport;
    71. import org.apache.camel.impl.DefaultCamelBeanPostProcessor;
    72. import org.apache.camel.impl.DefaultCamelContext;
    73. import org.apache.camel.impl.DefaultDebugger;
    74. import org.apache.camel.impl.InterceptSendToMockEndpointStrategy;
    75. import org.apache.camel.impl.JndiRegistry;
    76. import org.apache.camel.management.JmxSystemPropertyKeys;
    77. import org.apache.camel.model.ModelCamelContext;
    78. import org.apache.camel.model.ProcessorDefinition;
    79. import org.apache.camel.spi.Language;
    80. import org.apache.camel.util.IOHelper;
    81. import org.apache.camel.util.StopWatch;
    82. import org.apache.camel.util.TimeUtils;
    83. import org.junit.After;
    84. import org.junit.AfterClass;
    85. import org.junit.Before;
    86. import org.junit.Rule;
    87. import org.slf4j.Logger;
    88. import org.slf4j.LoggerFactory;
    89.  
    90. /**
    91.  * A useful base class which creates a {@link org.apache.camel.CamelContext} with some routes
    92.  * along with a {@link org.apache.camel.ProducerTemplate} for use in the test case
    93.  *
    94.  * @version
    95.  */
    96. public abstract class CamelTestSupport extends TestSupport {
    97. private static final Logger LOG = LoggerFactory.getLogger(CamelTestSupport.class);
    98. private static final ThreadLocal<Boolean> INIT = new ThreadLocal<Boolean>();
    99. private static ThreadLocal<ModelCamelContext> threadCamelContext = new ThreadLocal<ModelCamelContext>();
    100. private static ThreadLocal<ProducerTemplate> threadTemplate = new ThreadLocal<ProducerTemplate>();
    101. private static ThreadLocal<FluentProducerTemplate> threadFluentTemplate = new ThreadLocal<FluentProducerTemplate>();
    102. private static ThreadLocal<ConsumerTemplate> threadConsumer = new ThreadLocal<ConsumerTemplate>();
    103. private static ThreadLocal<Service> threadService = new ThreadLocal<Service>();
    104. protected volatile ModelCamelContext context;
    105. protected volatile ProducerTemplate template;
    106. protected volatile FluentProducerTemplate fluentTemplate;
    107. protected volatile ConsumerTemplate consumer;
    108. protected volatile Service camelContextService;
    109. protected boolean dumpRouteStats;
    110. private boolean useRouteBuilder = true;
    111. private final DebugBreakpoint breakpoint = new DebugBreakpoint();
    112. private final StopWatch watch = new StopWatch();
    113. private final Map<String, String> fromEndpoints = new HashMap<String, String>();
    114. private CamelTestWatcher camelTestWatcher = new CamelTestWatcher();
    115.  
    116. /**
    117.   * Use the RouteBuilder or not
    118.   * @return <tt>true</tt> then {@link CamelContext} will be auto started,
    119.   * <tt>false</tt> then {@link CamelContext} will <b>not</b> be auto started (you will have to start it manually)
    120.   */
    121. public boolean isUseRouteBuilder() {
    122. return useRouteBuilder;
    123. }
    124.  
    125. public void setUseRouteBuilder(boolean useRouteBuilder) {
    126. this.useRouteBuilder = useRouteBuilder;
    127. }
    128.  
    129. /**
    130.   * Whether to dump route coverage stats at the end of the test.
    131.   * <p/>
    132.   * This allows tooling or manual inspection of the stats, so you can generate a route trace diagram of which EIPs
    133.   * have been in use and which have not. Similar concepts as a code coverage report.
    134.   *
    135.   * @return <tt>true</tt> to write route coverage status in an xml file in the <tt>target/camel-route-coverage</tt> directory after the test has finished.
    136.   */
    137. public boolean isDumpRouteCoverage() {
    138. return false;
    139. }
    140.  
    141. /**
    142.   * Override when using <a href="http://camel.apache.org/advicewith.html">advice with</a> and return <tt>true</tt>.
    143.   * This helps knowing advice with is to be used, and {@link CamelContext} will not be started before
    144.   * the advice with takes place. This helps by ensuring the advice with has been property setup before the
    145.   * {@link CamelContext} is started
    146.   * <p/>
    147.   * <b>Important:</b> Its important to start {@link CamelContext} manually from the unit test
    148.   * after you are done doing all the advice with.
    149.   *
    150.   * @return <tt>true</tt> if you use advice with in your unit tests.
    151.   */
    152. public boolean isUseAdviceWith() {
    153. return false;
    154. }
    155.  
    156. /**
    157.   * Override to control whether {@link CamelContext} should be setup per test or per class.
    158.   * <p/>
    159.   * By default it will be setup/teardown per test (per test method). If you want to re-use
    160.   * {@link CamelContext} between test methods you can override this method and return <tt>true</tt>
    161.   * <p/>
    162.   * <b>Important:</b> Use this with care as the {@link CamelContext} will carry over state
    163.   * from previous tests, such as endpoints, components etc. So you cannot use this in all your tests.
    164.   * <p/>
    165.   * Setting up {@link CamelContext} uses the {@link #doPreSetup()}, {@link #doSetUp()}, and {@link #doPostSetup()}
    166.   * methods in that given order.
    167.   *
    168.   * @return <tt>true</tt> per class, <tt>false</tt> per test.
    169.   */
    170. public boolean isCreateCamelContextPerClass() {
    171. return false;
    172. }
    173.  
    174. /**
    175.   * Override to enable auto mocking endpoints based on the pattern.
    176.   * <p/>
    177.   * Return <tt>*</tt> to mock all endpoints.
    178.   *
    179.   * @see org.apache.camel.util.EndpointHelper#matchEndpoint(String, String)
    180.   */
    181. public String isMockEndpoints() {
    182. return null;
    183. }
    184.  
    185. /**
    186.   * Override to enable auto mocking endpoints based on the pattern, and <b>skip</b> sending
    187.   * to original endpoint.
    188.   * <p/>
    189.   * Return <tt>*</tt> to mock all endpoints.
    190.   *
    191.   * @see org.apache.camel.util.EndpointHelper#matchEndpoint(String, String)
    192.   */
    193. public String isMockEndpointsAndSkip() {
    194. return null;
    195. }
    196.  
    197. public void replaceRouteFromWith(String routeId, String fromEndpoint) {
    198. fromEndpoints.put(routeId, fromEndpoint);
    199. }
    200.  
    201. /**
    202.   * Override to enable debugger
    203.   * <p/>
    204.   * Is default <tt>false</tt>
    205.   */
    206. public boolean isUseDebugger() {
    207. return false;
    208. }
    209.  
    210. public Service getCamelContextService() {
    211. return camelContextService;
    212. }
    213.  
    214. public Service camelContextService() {
    215. return camelContextService;
    216. }
    217.  
    218. public CamelContext context() {
    219. return context;
    220. }
    221.  
    222. public ProducerTemplate template() {
    223. return template;
    224. }
    225.  
    226. public FluentProducerTemplate fluentTemplate() {
    227. return fluentTemplate;
    228. }
    229.  
    230. public ConsumerTemplate consumer() {
    231. return consumer;
    232. }
    233.  
    234. /**
    235.   * Allows a service to be registered a separate lifecycle service to start
    236.   * and stop the context; such as for Spring when the ApplicationContext is
    237.   * started and stopped, rather than directly stopping the CamelContext
    238.   */
    239. public void setCamelContextService(Service service) {
    240. camelContextService = service;
    241. threadService.set(camelContextService);
    242. }
    243.  
    244. @Before
    245. public void setUp() throws Exception {
    246. log.info("********************************************************************************");
    247. log.info("Testing: " + getTestMethodName() + "(" + getClass().getName() + ")");
    248. log.info("********************************************************************************");
    249.  
    250. if (isCreateCamelContextPerClass()) {
    251. // test is per class, so only setup once (the first time)
    252. boolean first = INIT.get() == null;
    253. if (first) {
    254. doPreSetup();
    255. doSetUp();
    256. doPostSetup();
    257. } else {
    258. // and in between tests we must do IoC and reset mocks
    259. postProcessTest();
    260. resetMocks();
    261. }
    262. } else {
    263. // test is per test so always setup
    264. doPreSetup();
    265. doSetUp();
    266. doPostSetup();
    267. }
    268.  
    269. // only start timing after all the setup
    270. watch.restart();
    271. }
    272.  
    273. /**
    274.   * Strategy to perform any pre setup, before {@link CamelContext} is created
    275.   */
    276. protected void doPreSetup() throws Exception {
    277. // noop
    278. }
    279.  
    280. /**
    281.   * Strategy to perform any post setup after {@link CamelContext} is created
    282.   */
    283. protected void doPostSetup() throws Exception {
    284. // noop
    285. }
    286.  
    287. private void doSetUp() throws Exception {
    288. log.debug("setUp test");
    289. // jmx is enabled if we have configured to use it, or if dump route coverage is enabled (it requires JMX)
    290. boolean jmx = useJmx() || isDumpRouteCoverage();
    291. if (jmx) {
    292. enableJMX();
    293. } else {
    294. disableJMX();
    295. }
    296.  
    297. context = (ModelCamelContext)createCamelContext();
    298. threadCamelContext.set(context);
    299.  
    300. assertNotNull("No context found!", context);
    301.  
    302. // reduce default shutdown timeout to avoid waiting for 300 seconds
    303. context.getShutdownStrategy().setTimeout(getShutdownTimeout());
    304.  
    305. // set debugger if enabled
    306. if (isUseDebugger()) {
    307. if (context.getStatus().equals(ServiceStatus.Started)) {
    308. log.info("Cannot setting the Debugger to the starting CamelContext, stop the CamelContext now.");
    309. // we need to stop the context first to setup the debugger
    310. context.stop();
    311. }
    312. context.setDebugger(new DefaultDebugger());
    313. context.getDebugger().addBreakpoint(breakpoint);
    314. // note: when stopping CamelContext it will automatic remove the breakpoint
    315. }
    316.  
    317. template = context.createProducerTemplate();
    318. template.start();
    319. fluentTemplate = context.createFluentProducerTemplate();
    320. fluentTemplate.start();
    321. consumer = context.createConsumerTemplate();
    322. consumer.start();
    323.  
    324. threadTemplate.set(template);
    325. threadFluentTemplate.set(fluentTemplate);
    326. threadConsumer.set(consumer);
    327.  
    328. // enable auto mocking if enabled
    329. String pattern = isMockEndpoints();
    330. if (pattern != null) {
    331. context.addRegisterEndpointCallback(new InterceptSendToMockEndpointStrategy(pattern));
    332. }
    333. pattern = isMockEndpointsAndSkip();
    334. if (pattern != null) {
    335. context.addRegisterEndpointCallback(new InterceptSendToMockEndpointStrategy(pattern, true));
    336. }
    337.  
    338. // configure properties component (mandatory for testing)
    339. PropertiesComponent pc = context.getComponent("properties", PropertiesComponent.class);
    340. Properties extra = useOverridePropertiesWithPropertiesComponent();
    341. if (extra != null && !extra.isEmpty()) {
    342. pc.setOverrideProperties(extra);
    343. }
    344. Boolean ignore = ignoreMissingLocationWithPropertiesComponent();
    345. if (ignore != null) {
    346. pc.setIgnoreMissingLocation(ignore);
    347. }
    348.  
    349. postProcessTest();
    350.  
    351. if (isUseRouteBuilder()) {
    352. RoutesBuilder[] builders = createRouteBuilders();
    353. for (RoutesBuilder builder : builders) {
    354. log.debug("Using created route builder: " + builder);
    355. context.addRoutes(builder);
    356. }
    357. replaceFromEndpoints();
    358. boolean skip = "true".equalsIgnoreCase(System.getProperty("skipStartingCamelContext"));
    359. if (skip) {
    360. log.info("Skipping starting CamelContext as system property skipStartingCamelContext is set to be true.");
    361. } else if (isUseAdviceWith()) {
    362. log.info("Skipping starting CamelContext as isUseAdviceWith is set to true.");
    363. } else {
    364. startCamelContext();
    365. }
    366. } else {
    367. replaceFromEndpoints();
    368. log.debug("Using route builder from the created context: " + context);
    369. }
    370. log.debug("Routing Rules are: " + context.getRoutes());
    371.  
    372. assertValidContext(context);
    373.  
    374. INIT.set(true);
    375. }
    376.  
    377. private void replaceFromEndpoints() throws Exception {
    378. for (final Map.Entry<String, String> entry : fromEndpoints.entrySet()) {
    379. context.getRouteDefinition(entry.getKey()).adviceWith(context, new AdviceWithRouteBuilder() {
    380. @Override
    381. public void configure() throws Exception {
    382. replaceFromWith(entry.getValue());
    383. }
    384. });
    385. }
    386. }
    387.  
    388. @After
    389. public void tearDown() throws Exception {
    390. long time = watch.stop();
    391.  
    392. log.info("********************************************************************************");
    393. log.info("Testing done: " + getTestMethodName() + "(" + getClass().getName() + ")");
    394. log.info("Took: " + TimeUtils.printDuration(time) + " (" + time + " millis)");
    395.  
    396. // if we should dump route stats, then write that to a file
    397. if (isDumpRouteCoverage()) {
    398. String className = this.getClass().getSimpleName();
    399. String dir = "target/camel-route-coverage";
    400. String name = className + "-" + getTestMethodName() + ".xml";
    401.  
    402. ManagedCamelContextMBean managedCamelContext = context.getManagedCamelContext();
    403. if (managedCamelContext == null) {
    404. log.warn("Cannot dump route coverage to file as JMX is not enabled. Override useJmx() method to enable JMX in the unit test classes.");
    405. } else {
    406. logCoverageSummary(managedCamelContext);
    407.  
    408. String xml = managedCamelContext.dumpRoutesCoverageAsXml();
    409. String combined = "<camelRouteCoverage>\n" + gatherTestDetailsAsXml() + xml + "\n</camelRouteCoverage>";
    410.  
    411. File file = new File(dir);
    412. // ensure dir exists
    413. file.mkdirs();
    414. file = new File(dir, name);
    415.  
    416. log.info("Dumping route coverage to file: " + file);
    417. InputStream is = new ByteArrayInputStream(combined.getBytes());
    418. OutputStream os = new FileOutputStream(file, false);
    419. IOHelper.copyAndCloseInput(is, os);
    420. IOHelper.close(os);
    421. }
    422. }
    423. log.info("********************************************************************************");
    424.  
    425. if (isCreateCamelContextPerClass()) {
    426. // we tear down in after class
    427. return;
    428. }
    429.  
    430. LOG.debug("tearDown test");
    431. doStopTemplates(consumer, template, fluentTemplate);
    432. doStopCamelContext(context, camelContextService);
    433. }
    434.  
    435. /**
    436.   * Logs route coverage summary:
    437.   * - which routes are uncovered
    438.   * - what is the coverage of each processor in each route
    439.   */
    440. private void logCoverageSummary(ManagedCamelContextMBean managedCamelContext) throws Exception {
    441. StringBuilder builder = new StringBuilder("\nCoverage summary\n");
    442.  
    443. int routes = managedCamelContext.getTotalRoutes();
    444.  
    445. long contextExchangesTotal = managedCamelContext.getExchangesTotal();
    446.  
    447. List<String> uncoveredRoutes = new ArrayList<>();
    448.  
    449. StringBuilder routesSummary = new StringBuilder();
    450. routesSummary.append("\tProcessor coverage\n");
    451.  
    452. MBeanServer server = context.getManagementStrategy().getManagementAgent().getMBeanServer();
    453.  
    454. Map<String, List<ManagedProcessorMBean>> processorsForRoute = findProcessorsForEachRoute(server);
    455.  
    456. // log processor coverage for each route
    457. for (Route route : context.getRoutes()) {
    458. ManagedRouteMBean managedRoute = context.getManagedRoute(route.getId(), ManagedRouteMBean.class);
    459. if (managedRoute.getExchangesTotal() == 0) {
    460. uncoveredRoutes.add(route.getId());
    461. }
    462.  
    463. long routeCoveragePercentage = Math.round((double) managedRoute.getExchangesTotal() / contextExchangesTotal * 100);
    464. routesSummary.append("\t\tRoute ").append(route.getId()).append(" total: ").append(managedRoute.getExchangesTotal()).append(" (").append(routeCoveragePercentage).append("%)\n");
    465.  
    466. if (server != null) {
    467. List<ManagedProcessorMBean> processors = processorsForRoute.get(route.getId());
    468. if (processors != null) {
    469. for (ManagedProcessorMBean managedProcessor : processors) {
    470. String processorId = managedProcessor.getProcessorId();
    471. long processorExchangesTotal = managedProcessor.getExchangesTotal();
    472. long processorCoveragePercentage = Math.round((double) processorExchangesTotal / contextExchangesTotal * 100);
    473. routesSummary.append("\t\t\tProcessor ").append(processorId).append(" total: ").append(processorExchangesTotal).append(" (").append(processorCoveragePercentage).append("%)\n");
    474. }
    475. }
    476. }
    477. }
    478.  
    479. int used = routes - uncoveredRoutes.size();
    480.  
    481. long contextPercentage = Math.round((double) used / routes * 100);
    482. builder.append("\tRoute coverage: ").append(used).append(" out of ").append(routes).append(" routes used (").append(contextPercentage).append("%)\n");
    483. builder.append("\t\tCamelContext (").append(managedCamelContext.getCamelId()).append(") total: ").append(contextExchangesTotal).append("\n");
    484.  
    485. if (uncoveredRoutes.size() > 0) {
    486. builder.append("\t\tUncovered routes: ").append(uncoveredRoutes.stream().collect(Collectors.joining(", "))).append("\n");
    487. }
    488.  
    489. builder.append(routesSummary);
    490. log.info(builder.toString());
    491.  
    492. }
    493.  
    494. /**
    495.   * Groups all processors from Camel context by route id
    496.   */
    497. private Map<String, List<ManagedProcessorMBean>> findProcessorsForEachRoute(MBeanServer server)
    498. throws MalformedObjectNameException, MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException {
    499. String domain = context.getManagementStrategy().getManagementAgent().getMBeanServerDefaultDomain();
    500.  
    501. Map<String, List<ManagedProcessorMBean>> processorsForRoute = new HashMap<>();
    502.  
    503. ObjectName processorsObjectName = new ObjectName(domain + ":context=" + context.getManagementName() + ",type=processors,name=*");
    504. Set<ObjectName> objectNames = server.queryNames(processorsObjectName, null);
    505.  
    506. for (ObjectName objectName : objectNames) {
    507. String routeId = server.getAttribute(objectName, "RouteId").toString();
    508. String name = objectName.getKeyProperty("name");
    509. name = ObjectName.unquote(name);
    510.  
    511. ManagedProcessorMBean managedProcessor = context.getManagedProcessor(name, ManagedProcessorMBean.class);
    512.  
    513. if (managedProcessor != null) {
    514. if (processorsForRoute.get(routeId) == null) {
    515. List<ManagedProcessorMBean> processorsList = new ArrayList<>();
    516. processorsList.add(managedProcessor);
    517.  
    518. processorsForRoute.put(routeId, processorsList);
    519. } else {
    520. processorsForRoute.get(routeId).add(managedProcessor);
    521. }
    522. }
    523. }
    524.  
    525. // sort processors by position in route definition
    526. for (Map.Entry<String, List<ManagedProcessorMBean>> entry : processorsForRoute.entrySet()) {
    527. Collections.sort(entry.getValue(), Comparator.comparing(ManagedProcessorMBean::getIndex));
    528. }
    529.  
    530. return processorsForRoute;
    531. }
    532.  
    533. @AfterClass
    534. public static void tearDownAfterClass() throws Exception {
    535. INIT.remove();
    536. LOG.debug("tearDownAfterClass test");
    537. doStopTemplates(threadConsumer.get(), threadTemplate.get(), threadFluentTemplate.get());
    538. doStopCamelContext(threadCamelContext.get(), threadService.get());
    539. }
    540.  
    541. /**
    542.   * Gathers test details as xml
    543.   */
    544. private String gatherTestDetailsAsXml() {
    545. StringBuilder sb = new StringBuilder();
    546. sb.append("<test>\n");
    547. sb.append(" <class>").append(getClass().getName()).append("</class>\n");
    548. sb.append(" <method>").append(getTestMethodName()).append("</method>\n");
    549. sb.append(" <time>").append(getCamelTestWatcher().timeTaken()).append("</time>\n");
    550. sb.append("</test>\n");
    551. return sb.toString();
    552. }
    553.  
    554. /**
    555.   * Returns the timeout to use when shutting down (unit in seconds).
    556.   * <p/>
    557.   * Will default use 10 seconds.
    558.   *
    559.   * @return the timeout to use
    560.   */
    561. protected int getShutdownTimeout() {
    562. return 10;
    563. }
    564.  
    565. /**
    566.   * Whether or not JMX should be used during testing.
    567.   *
    568.   * @return <tt>false</tt> by default.
    569.   */
    570. protected boolean useJmx() {
    571. return false;
    572. }
    573.  
    574. /**
    575.   * Whether or not type converters should be lazy loaded (notice core converters is always loaded)
    576.   *
    577.   * @return <tt>false</tt> by default.
    578.   */
    579. @Deprecated
    580. protected boolean isLazyLoadingTypeConverter() {
    581. return false;
    582. }
    583.  
    584. /**
    585.   * Override this method to include and override properties
    586.   * with the Camel {@link PropertiesComponent}.
    587.   *
    588.   * @return additional properties to add/override.
    589.   */
    590. protected Properties useOverridePropertiesWithPropertiesComponent() {
    591. return null;
    592. }
    593.  
    594. @Rule
    595. public CamelTestWatcher getCamelTestWatcher() {
    596. return camelTestWatcher;
    597. }
    598.  
    599. /**
    600.   * Whether to ignore missing locations with the {@link PropertiesComponent}.
    601.   * For example when unit testing you may want to ignore locations that are
    602.   * not available in the environment you use for testing.
    603.   *
    604.   * @return <tt>true</tt> to ignore, <tt>false</tt> to not ignore, and <tt>null</tt> to leave as configured
    605.   * on the {@link PropertiesComponent}
    606.   */
    607. protected Boolean ignoreMissingLocationWithPropertiesComponent() {
    608. return null;
    609. }
    610.  
    611. protected void postProcessTest() throws Exception {
    612. context = threadCamelContext.get();
    613. template = threadTemplate.get();
    614. fluentTemplate = threadFluentTemplate.get();
    615. consumer = threadConsumer.get();
    616. camelContextService = threadService.get();
    617. applyCamelPostProcessor();
    618. }
    619.  
    620. /**
    621.   * Applies the {@link DefaultCamelBeanPostProcessor} to this instance.
    622.   *
    623.   * Derived classes using IoC / DI frameworks may wish to turn this into a NoOp such as for CDI
    624.   * we would just use CDI to inject this
    625.   */
    626. protected void applyCamelPostProcessor() throws Exception {
    627. // use the default bean post processor from camel-core
    628. DefaultCamelBeanPostProcessor processor = new DefaultCamelBeanPostProcessor(context);
    629. processor.postProcessBeforeInitialization(this, getClass().getName());
    630. processor.postProcessAfterInitialization(this, getClass().getName());
    631. }
    632.  
    633. protected void stopCamelContext() throws Exception {
    634. doStopCamelContext(context, camelContextService);
    635. }
    636.  
    637. private static void doStopCamelContext(CamelContext context, Service camelContextService) throws Exception {
    638. if (camelContextService != null) {
    639. if (camelContextService == threadService.get()) {
    640. threadService.remove();
    641. }
    642. camelContextService.stop();
    643. } else {
    644. if (context != null) {
    645. if (context == threadCamelContext.get()) {
    646. threadCamelContext.remove();
    647. }
    648. context.stop();
    649. }
    650. }
    651. }
    652.  
    653. private static void doStopTemplates(ConsumerTemplate consumer, ProducerTemplate template, FluentProducerTemplate fluentTemplate) throws Exception {
    654. if (consumer != null) {
    655. if (consumer == threadConsumer.get()) {
    656. threadConsumer.remove();
    657. }
    658. consumer.stop();
    659. }
    660. if (template != null) {
    661. if (template == threadTemplate.get()) {
    662. threadTemplate.remove();
    663. }
    664. template.stop();
    665. }
    666. if (fluentTemplate != null) {
    667. if (fluentTemplate == threadFluentTemplate.get()) {
    668. threadFluentTemplate.remove();
    669. }
    670. fluentTemplate.stop();
    671. }
    672. }
    673.  
    674. protected void startCamelContext() throws Exception {
    675. if (camelContextService != null) {
    676. camelContextService.start();
    677. } else {
    678. if (context instanceof DefaultCamelContext) {
    679. DefaultCamelContext defaultCamelContext = (DefaultCamelContext)context;
    680. if (!defaultCamelContext.isStarted()) {
    681. defaultCamelContext.start();
    682. }
    683. } else {
    684. context.start();
    685. }
    686. }
    687. }
    688.  
    689. @SuppressWarnings("deprecation")
    690. protected CamelContext createCamelContext() throws Exception {
    691. CamelContext context = new DefaultCamelContext(createRegistry());
    692. context.setLazyLoadTypeConverters(isLazyLoadingTypeConverter());
    693. return context;
    694. }
    695.  
    696. protected JndiRegistry createRegistry() throws Exception {
    697. return new JndiRegistry(createJndiContext());
    698. }
    699.  
    700. protected Context createJndiContext() throws Exception {
    701. Properties properties = new Properties();
    702.  
    703. // jndi.properties is optional
    704. InputStream in = getClass().getClassLoader().getResourceAsStream("jndi.properties");
    705. if (in != null) {
    706. log.debug("Using jndi.properties from classpath root");
    707. properties.load(in);
    708. } else {
    709. properties.put("java.naming.factory.initial", "org.apache.camel.util.jndi.CamelInitialContextFactory");
    710. }
    711. return new InitialContext(new Hashtable<Object, Object>(properties));
    712. }
    713.  
    714. /**
    715.   * Factory method which derived classes can use to create a {@link RouteBuilder}
    716.   * to define the routes for testing
    717.   */
    718. protected RoutesBuilder createRouteBuilder() throws Exception {
    719. return new RouteBuilder() {
    720. @Override
    721. public void configure() {
    722. // no routes added by default
    723. }
    724. };
    725. }
    726.  
    727. /**
    728.   * Factory method which derived classes can use to create an array of
    729.   * {@link org.apache.camel.builder.RouteBuilder}s to define the routes for testing
    730.   *
    731.   * @see #createRouteBuilder()
    732.   */
    733. protected RoutesBuilder[] createRouteBuilders() throws Exception {
    734. return new RoutesBuilder[] {createRouteBuilder()};
    735. }
    736.  
    737. /**
    738.   * Resolves a mandatory endpoint for the given URI or an exception is thrown
    739.   *
    740.   * @param uri the Camel <a href="">URI</a> to use to create or resolve an endpoint
    741.   * @return the endpoint
    742.   */
    743. protected Endpoint resolveMandatoryEndpoint(String uri) {
    744. return resolveMandatoryEndpoint(context, uri);
    745. }
    746.  
    747. /**
    748.   * Resolves a mandatory endpoint for the given URI and expected type or an exception is thrown
    749.   *
    750.   * @param uri the Camel <a href="">URI</a> to use to create or resolve an endpoint
    751.   * @return the endpoint
    752.   */
    753. protected <T extends Endpoint> T resolveMandatoryEndpoint(String uri, Class<T> endpointType) {
    754. return resolveMandatoryEndpoint(context, uri, endpointType);
    755. }
    756.  
    757. /**
    758.   * Resolves the mandatory Mock endpoint using a URI of the form <code>mock:someName</code>
    759.   *
    760.   * @param uri the URI which typically starts with "mock:" and has some name
    761.   * @return the mandatory mock endpoint or an exception is thrown if it could not be resolved
    762.   */
    763. protected MockEndpoint getMockEndpoint(String uri) {
    764. return getMockEndpoint(uri, true);
    765. }
    766.  
    767. /**
    768.   * Resolves the {@link MockEndpoint} using a URI of the form <code>mock:someName</code>, optionally
    769.   * creating it if it does not exist.
    770.   *
    771.   * @param uri the URI which typically starts with "mock:" and has some name
    772.   * @param create whether or not to allow the endpoint to be created if it doesn't exist
    773.   * @return the mock endpoint or an {@link NoSuchEndpointException} is thrown if it could not be resolved
    774.   * @throws NoSuchEndpointException is the mock endpoint does not exists
    775.   */
    776. protected MockEndpoint getMockEndpoint(String uri, boolean create) throws NoSuchEndpointException {
    777. if (create) {
    778. return resolveMandatoryEndpoint(uri, MockEndpoint.class);
    779. } else {
    780. Endpoint endpoint = context.hasEndpoint(uri);
    781. if (endpoint instanceof MockEndpoint) {
    782. return (MockEndpoint) endpoint;
    783. }
    784. throw new NoSuchEndpointException(String.format("MockEndpoint %s does not exist.", uri));
    785. }
    786. }
    787.  
    788. /**
    789.   * Sends a message to the given endpoint URI with the body value
    790.   *
    791.   * @param endpointUri the URI of the endpoint to send to
    792.   * @param body the body for the message
    793.   */
    794. protected void sendBody(String endpointUri, final Object body) {
    795. template.send(endpointUri, new Processor() {
    796. public void process(Exchange exchange) {
    797. Message in = exchange.getIn();
    798. in.setBody(body);
    799. }
    800. });
    801. }
    802.  
    803. /**
    804.   * Sends a message to the given endpoint URI with the body value and specified headers
    805.   *
    806.   * @param endpointUri the URI of the endpoint to send to
    807.   * @param body the body for the message
    808.   * @param headers any headers to set on the message
    809.   */
    810. protected void sendBody(String endpointUri, final Object body, final Map<String, Object> headers) {
    811. template.send(endpointUri, new Processor() {
    812. public void process(Exchange exchange) {
    813. Message in = exchange.getIn();
    814. in.setBody(body);
    815. for (Map.Entry<String, Object> entry : headers.entrySet()) {
    816. in.setHeader(entry.getKey(), entry.getValue());
    817. }
    818. }
    819. });
    820. }
    821.  
    822. /**
    823.   * Sends messages to the given endpoint for each of the specified bodies
    824.   *
    825.   * @param endpointUri the endpoint URI to send to
    826.   * @param bodies the bodies to send, one per message
    827.   */
    828. protected void sendBodies(String endpointUri, Object... bodies) {
    829. for (Object body : bodies) {
    830. sendBody(endpointUri, body);
    831. }
    832. }
    833.  
    834. /**
    835.   * Creates an exchange with the given body
    836.   */
    837. protected Exchange createExchangeWithBody(Object body) {
    838. return createExchangeWithBody(context, body);
    839. }
    840.  
    841. /**
    842.   * Asserts that the given language name and expression evaluates to the
    843.   * given value on a specific exchange
    844.   */
    845. protected void assertExpression(Exchange exchange, String languageName, String expressionText, Object expectedValue) {
    846. Language language = assertResolveLanguage(languageName);
    847.  
    848. Expression expression = language.createExpression(expressionText);
    849. assertNotNull("No Expression could be created for text: " + expressionText + " language: " + language, expression);
    850.  
    851. assertExpression(expression, exchange, expectedValue);
    852. }
    853.  
    854. /**
    855.   * Asserts that the given language name and predicate expression evaluates
    856.   * to the expected value on the message exchange
    857.   */
    858. protected void assertPredicate(String languageName, String expressionText, Exchange exchange, boolean expected) {
    859. Language language = assertResolveLanguage(languageName);
    860.  
    861. Predicate predicate = language.createPredicate(expressionText);
    862. assertNotNull("No Predicate could be created for text: " + expressionText + " language: " + language, predicate);
    863.  
    864. assertPredicate(predicate, exchange, expected);
    865. }
    866.  
    867. /**
    868.   * Asserts that the language name can be resolved
    869.   */
    870. protected Language assertResolveLanguage(String languageName) {
    871. Language language = context.resolveLanguage(languageName);
    872. assertNotNull("No language found for name: " + languageName, language);
    873. return language;
    874. }
    875.  
    876. /**
    877.   * Asserts that all the expectations of the Mock endpoints are valid
    878.   */
    879. protected void assertMockEndpointsSatisfied() throws InterruptedException {
    880. MockEndpoint.assertIsSatisfied(context);
    881. }
    882.  
    883. /**
    884.   * Asserts that all the expectations of the Mock endpoints are valid
    885.   */
    886. protected void assertMockEndpointsSatisfied(long timeout, TimeUnit unit) throws InterruptedException {
    887. MockEndpoint.assertIsSatisfied(context, timeout, unit);
    888. }
    889.  
    890. /**
    891.   * Reset all Mock endpoints.
    892.   */
    893. protected void resetMocks() {
    894. MockEndpoint.resetMocks(context);
    895. }
    896.  
    897. protected void assertValidContext(CamelContext context) {
    898. assertNotNull("No context found!", context);
    899. }
    900.  
    901. protected <T extends Endpoint> T getMandatoryEndpoint(String uri, Class<T> type) {
    902. T endpoint = context.getEndpoint(uri, type);
    903. assertNotNull("No endpoint found for uri: " + uri, endpoint);
    904. return endpoint;
    905. }
    906.  
    907. protected Endpoint getMandatoryEndpoint(String uri) {
    908. Endpoint endpoint = context.getEndpoint(uri);
    909. assertNotNull("No endpoint found for uri: " + uri, endpoint);
    910. return endpoint;
    911. }
    912.  
    913. /**
    914.   * Disables the JMX agent. Must be called before the {@link #setUp()} method.
    915.   */
    916. protected void disableJMX() {
    917. System.setProperty(JmxSystemPropertyKeys.DISABLED, "true");
    918. }
    919.  
    920. /**
    921.   * Enables the JMX agent. Must be called before the {@link #setUp()} method.
    922.   */
    923. protected void enableJMX() {
    924. System.setProperty(JmxSystemPropertyKeys.DISABLED, "false");
    925. }
    926.  
    927. /**
    928.   * Single step debugs and Camel invokes this method before entering the given processor
    929.   */
    930. protected void debugBefore(Exchange exchange, Processor processor, ProcessorDefinition<?> definition,
    931. String id, String label) {
    932. }
    933.  
    934. /**
    935.   * Single step debugs and Camel invokes this method after processing the given processor
    936.   */
    937. protected void debugAfter(Exchange exchange, Processor processor, ProcessorDefinition<?> definition,
    938. String id, String label, long timeTaken) {
    939. }
    940.  
    941. /**
    942.   * To easily debug by overriding the <tt>debugBefore</tt> and <tt>debugAfter</tt> methods.
    943.   */
    944. private class DebugBreakpoint extends BreakpointSupport {
    945.  
    946. @Override
    947. public void beforeProcess(Exchange exchange, Processor processor, ProcessorDefinition<?> definition) {
    948. CamelTestSupport.this.debugBefore(exchange, processor, definition, definition.getId(), definition.getLabel());
    949. }
    950.  
    951. @Override
    952. public void afterProcess(Exchange exchange, Processor processor, ProcessorDefinition<?> definition, long timeTaken) {
    953. CamelTestSupport.this.debugAfter(exchange, processor, definition, definition.getId(), definition.getLabel(), timeTaken);
    954. }
    955. }
    956.  
    957. }