/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.camel.component.spring.batch; import java.time.Instant; import java.util.Date; import java.util.HashMap; import java.util.Map; import org.apache.camel.CamelContext; import org.apache.camel.EndpointInject; import org.apache.camel.FailedToCreateRouteException; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; import org.apache.camel.impl.DefaultCamelContext; import org.apache.camel.impl.JndiRegistry; import org.apache.camel.impl.SimpleRegistry; import org.apache.camel.test.junit4.CamelTestSupport; import org.apache.commons.lang.reflect.FieldUtils; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.batch.core.Job; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.JobParameters; import org.springframework.batch.core.configuration.JobRegistry; import org.springframework.batch.core.launch.JobLauncher; import static org.mockito.BDDMockito.any; import static org.mockito.BDDMockito.eq; import static org.mockito.BDDMockito.mock; import static org.mockito.BDDMockito.verify; import static org.mockito.BDDMockito.when; @RunWith(MockitoJUnitRunner.class) public class SpringBatchEndpointTest extends CamelTestSupport { // Fixtures @Mock JobLauncher jobLauncher; @Mock JobLauncher alternativeJobLauncher; @Mock JobRegistry jobRegistry; @Mock Job job; @Mock Job dynamicMockjob; // Camel fixtures @EndpointInject(uri = "mock:test") MockEndpoint mockEndpoint; @EndpointInject(uri = "mock:error") MockEndpoint errorEndpoint; @Override protected RouteBuilder createRouteBuilder() throws Exception { return new RouteBuilder() { @Override public void configure() throws Exception { from("direct:start").to("spring-batch:mockJob").to("mock:test"); from("direct:dynamic"). to("spring-batch:fake?jobFromHeader=true"). errorHandler(deadLetterChannel("mock:error")). to("mock:test"); from("direct:dynamicWithJobRegistry"). to("spring-batch:fake?jobFromHeader=true&jobRegistry=#jobRegistry"). errorHandler(deadLetterChannel("mock:error")). to("mock:test"); } }; } @Override public JndiRegistry createRegistry() throws Exception { JndiRegistry registry = super.createRegistry(); registry.bind("jobLauncher", jobLauncher); registry.bind("alternativeJobLauncher", alternativeJobLauncher); registry.bind("mockJob", job); registry.bind("dynamicMockjob", dynamicMockjob); registry.bind("jobRegistry", jobRegistry); return registry; } // Tests @Test public void dynamicJobFailsIfHeaderNotPressent() throws Exception { mockEndpoint.expectedMessageCount(0); errorEndpoint.expectedMessageCount(1); //dynamic job should fail as header is not present and the job is dynamic sendBody("direct:dyanmic", "Start the job, please."); mockEndpoint.assertIsSatisfied(); mockEndpoint.assertIsSatisfied(); } @Test public void dynamicJobFailsIfHeaderWithInvalidJobName() throws Exception { mockEndpoint.expectedMessageCount(0); errorEndpoint.expectedMessageCount(1); //dynamic job should fail as header is present but the job does not exists header(SpringBatchConstants.JOB_NAME).append("thisJobDoesNotExsistAtAll" + Date.from(Instant.now())); sendBody("direct:dyanmic", "Start the job, please."); mockEndpoint.assertIsSatisfied(); mockEndpoint.assertIsSatisfied(); } @Test public void dynamicJobWorksIfHeaderPressentWithValidJob() throws Exception { mockEndpoint.expectedMessageCount(1); errorEndpoint.expectedMessageCount(0); final Map<String, Object> headers = new HashMap<>(); headers.put(SpringBatchConstants.JOB_NAME, "dynamicMockjob"); sendBody("direct:dynamic", "Start the job, please.", headers); mockEndpoint.assertIsSatisfied(); errorEndpoint.assertIsSatisfied(); } @Test public void dynamicJobWorksIfHeaderPresentWithValidJobLocatedInJobRegistry() throws Exception { mockEndpoint.expectedMessageCount(1); errorEndpoint.expectedMessageCount(0); Job mockJob = mock(Job.class); when(jobRegistry.getJob(eq("dyanmicMockJobFromJobRegistry"))).thenReturn(mockJob); final Map<String, Object> headers = new HashMap<>(); headers.put(SpringBatchConstants.JOB_NAME, "dyanmicMockJobFromJobRegistry"); headers.put("jobRegistry", "#jobRegistry"); sendBody("direct:dynamicWithJobRegistry", "Start the job, please.", headers); mockEndpoint.assertIsSatisfied(); errorEndpoint.assertIsSatisfied(); } @Test public void shouldInjectJobToEndpoint() throws IllegalAccessException { SpringBatchEndpoint batchEndpoint = getMandatoryEndpoint("spring-batch:mockJob", SpringBatchEndpoint.class); Job batchEndpointJob = (Job) FieldUtils.readField(batchEndpoint, "job", true); assertSame(job, batchEndpointJob); } @Test public void shouldRunJob() throws Exception { // When sendBody("direct:start", "Start the job, please."); // Then verify(jobLauncher).run(eq(job), any(JobParameters.class)); } @Test public void shouldReturnJobExecution() throws Exception { // Given JobExecution jobExecution = mock(JobExecution.class); when(jobLauncher.run(eq(job), any(JobParameters.class))).thenReturn(jobExecution); // When sendBody("direct:start", "Start the job, please."); // Then mockEndpoint.expectedBodiesReceived(jobExecution); } @Test(expected = FailedToCreateRouteException.class) public void shouldThrowExceptionIfUsedAsConsumer() throws Exception { // When context().addRoutes(new RouteBuilder() { @Override public void configure() throws Exception { from("spring-batch:mockJob").to("direct:emptyEndpoint"); } }); } @Test public void shouldConvertHeadersToJobParams() throws Exception { // Given String headerKey = "headerKey"; String headerValue = "headerValue"; // When template.sendBodyAndHeader("direct:start", "Start the job, please.", headerKey, headerValue); // Then ArgumentCaptor<JobParameters> jobParameters = ArgumentCaptor.forClass(JobParameters.class); verify(jobLauncher).run(any(Job.class), jobParameters.capture()); String parameter = jobParameters.getValue().getString(headerKey); assertEquals(parameter, headerValue); } @Test public void setNullValueToJobParams() throws Exception { // Given String headerKey = "headerKey"; Date headerValue = null; // When template.sendBodyAndHeader("direct:start", "Start the job, please.", headerKey, headerValue); // Then ArgumentCaptor<JobParameters> jobParameters = ArgumentCaptor.forClass(JobParameters.class); verify(jobLauncher).run(any(Job.class), jobParameters.capture()); Date parameter = jobParameters.getValue().getDate(headerKey); assertEquals(parameter, headerValue); } @Test public void shouldConvertDateHeadersToJobParams() throws Exception { // Given String headerKey = "headerKey"; Date headerValue = new Date(); // When template.sendBodyAndHeader("direct:start", "Start the job, please.", headerKey, headerValue); // Then ArgumentCaptor<JobParameters> jobParameters = ArgumentCaptor.forClass(JobParameters.class); verify(jobLauncher).run(any(Job.class), jobParameters.capture()); Date parameter = jobParameters.getValue().getDate(headerKey); assertEquals(parameter, headerValue); } @Test public void shouldConvertLongHeadersToJobParams() throws Exception { // Given String headerKey = "headerKey"; Long headerValue = 1L; // When template.sendBodyAndHeader("direct:start", "Start the job, please.", headerKey, headerValue); // Then ArgumentCaptor<JobParameters> jobParameters = ArgumentCaptor.forClass(JobParameters.class); verify(jobLauncher).run(any(Job.class), jobParameters.capture()); Long parameter = jobParameters.getValue().getLong(headerKey); assertEquals(parameter, headerValue); } @Test public void shouldConvertDoubleHeadersToJobParams() throws Exception { // Given String headerKey = "headerKey"; Double headerValue = 1.0; // When template.sendBodyAndHeader("direct:start", "Start the job, please.", headerKey, headerValue); // Then ArgumentCaptor<JobParameters> jobParameters = ArgumentCaptor.forClass(JobParameters.class); verify(jobLauncher).run(any(Job.class), jobParameters.capture()); Double parameter = jobParameters.getValue().getDouble(headerKey); assertEquals(parameter, headerValue); } @Test public void shouldInjectJobLauncherByReferenceName() throws Exception { // Given context().addRoutes(new RouteBuilder() { @Override public void configure() throws Exception { from("direct:launcherRefTest").to("spring-batch:mockJob?jobLauncher=#alternativeJobLauncher"); } }); // When template.sendBody("direct:launcherRefTest", "Start the job, please."); // Then SpringBatchEndpoint batchEndpoint = context().getEndpoint("spring-batch:mockJob?jobLauncher=#alternativeJobLauncher", SpringBatchEndpoint.class); JobLauncher batchEndpointJobLauncher = (JobLauncher) FieldUtils.readField(batchEndpoint, "jobLauncher", true); assertSame(alternativeJobLauncher, batchEndpointJobLauncher); } @Test(expected = FailedToCreateRouteException.class) public void shouldFailWhenThereIsNoJobLauncher() throws Exception { // Given SimpleRegistry registry = new SimpleRegistry(); registry.put("mockJob", job); CamelContext camelContext = new DefaultCamelContext(registry); camelContext.addRoutes(new RouteBuilder() { @Override public void configure() throws Exception { from("direct:start").to("spring-batch:mockJob"); } }); // When camelContext.start(); } @Test(expected = FailedToCreateRouteException.class) public void shouldFailWhenThereIsMoreThanOneJobLauncher() throws Exception { // Given SimpleRegistry registry = new SimpleRegistry(); registry.put("mockJob", job); registry.put("launcher1", jobLauncher); registry.put("launcher2", jobLauncher); CamelContext camelContext = new DefaultCamelContext(registry); camelContext.addRoutes(new RouteBuilder() { @Override public void configure() throws Exception { from("direct:start").to("spring-batch:mockJob"); } }); // When camelContext.start(); } @Test public void shouldResolveAnyJobLauncher() throws Exception { // Given SimpleRegistry registry = new SimpleRegistry(); registry.put("mockJob", job); registry.put("someRandomName", jobLauncher); CamelContext camelContext = new DefaultCamelContext(registry); camelContext.addRoutes(new RouteBuilder() { @Override public void configure() throws Exception { from("direct:start").to("spring-batch:mockJob"); } }); // When camelContext.start(); // Then SpringBatchEndpoint batchEndpoint = camelContext.getEndpoint("spring-batch:mockJob", SpringBatchEndpoint.class); JobLauncher batchEndpointJobLauncher = (JobLauncher) FieldUtils.readField(batchEndpoint, "jobLauncher", true); assertSame(jobLauncher, batchEndpointJobLauncher); } @Test public void shouldUseJobLauncherFromComponent() throws Exception { // Given SpringBatchComponent batchComponent = new SpringBatchComponent(); batchComponent.setJobLauncher(alternativeJobLauncher); context.addComponent("customBatchComponent", batchComponent); // When context().addRoutes(new RouteBuilder() { @Override public void configure() throws Exception { from("direct:startCustom").to("customBatchComponent:mockJob"); } }); // Then SpringBatchEndpoint batchEndpoint = context().getEndpoint("customBatchComponent:mockJob", SpringBatchEndpoint.class); JobLauncher batchEndpointJobLauncher = (JobLauncher) FieldUtils.readField(batchEndpoint, "jobLauncher", true); assertSame(alternativeJobLauncher, batchEndpointJobLauncher); } @Test public void shouldInjectJobRegistryByReferenceName() throws Exception { // Given Job mockJob = mock(Job.class); when(jobRegistry.getJob(eq("mockJob"))).thenReturn(mockJob); context().addRoutes(new RouteBuilder() { @Override public void configure() throws Exception { from("direct:jobRegistryRefTest").to("spring-batch:mockJob?jobRegistry=#jobRegistry"); } }); // When template.sendBody("direct:jobRegistryRefTest", "Start the job, please."); // Then SpringBatchEndpoint batchEndpoint = context().getEndpoint("spring-batch:mockJob?jobRegistry=#jobRegistry", SpringBatchEndpoint.class); JobRegistry batchEndpointJobRegistry = (JobRegistry) FieldUtils.readField(batchEndpoint, "jobRegistry", true); assertSame(jobRegistry, batchEndpointJobRegistry); } @Test public void shouldUseJobRegistryFromComponent() throws Exception { // Given SpringBatchComponent batchComponent = new SpringBatchComponent(); batchComponent.setJobRegistry(jobRegistry); batchComponent.setJobLauncher(jobLauncher); context.addComponent("customBatchComponent", batchComponent); // When context().addRoutes(new RouteBuilder() { @Override public void configure() throws Exception { from("direct:startCustom").to("customBatchComponent:mockJob"); } }); // Then SpringBatchEndpoint batchEndpoint = context().getEndpoint("customBatchComponent:mockJob", SpringBatchEndpoint.class); JobRegistry batchEndpointJobRegistry = (JobRegistry) FieldUtils.readField(batchEndpoint, "jobRegistry", true); assertSame(jobRegistry, batchEndpointJobRegistry); } @Test public void shouldGetJobFromJobRegistry() throws Exception { // Given Job mockJobFromJobRegistry = mock(Job.class); when(jobRegistry.getJob(eq("mockJobFromJobRegistry"))).thenReturn(mockJobFromJobRegistry); // When context().addRoutes(new RouteBuilder() { @Override public void configure() throws Exception { from("direct:jobRegistryTest").to("spring-batch:mockJobFromJobRegistry?jobRegistry=#jobRegistry"); } }); // Then SpringBatchEndpoint batchEndpoint = context().getEndpoint("spring-batch:mockJobFromJobRegistry?jobRegistry=#jobRegistry", SpringBatchEndpoint.class); Job batchEndpointJob = (Job) FieldUtils.readField(batchEndpoint, "job", true); assertSame(mockJobFromJobRegistry, batchEndpointJob); } }