surefire for spring-boot/surefire incompatibility
diff --git a/pom.xml b/pom.xml
index f892463..bc78af5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -35,7 +35,7 @@
         <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/geronimo-batchee.git</developerConnection>
         <url>https://gitbox.apache.org/repos/asf/geronimo-batchee.git</url>
       <tag>HEAD</tag>
-  </scm>
+    </scm>
 
 
     <properties>
diff --git a/tools/spring-boot-batchee-ui/pom.xml b/tools/spring-boot-batchee-ui/pom.xml
index d85af64..e74fca9 100644
--- a/tools/spring-boot-batchee-ui/pom.xml
+++ b/tools/spring-boot-batchee-ui/pom.xml
@@ -39,15 +39,15 @@
       <version>${project.version}</version>
     </dependency>
     <dependency> <!-- for jsp -->
-      <groupId>org.apache.tomcat</groupId>
-      <artifactId>tomcat-jasper</artifactId>
+      <groupId>org.apache.tomcat.embed</groupId>
+      <artifactId>tomcat-embed-jasper</artifactId>
       <version>9.0.54</version>
     </dependency>
 
     <dependency>
-      <groupId>org.springframework.batch</groupId>
-      <artifactId>spring-batch-core</artifactId>
-      <version>4.3.3</version>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-batch</artifactId>
+      <version>${spring-boot.version}</version>
       <scope>provided</scope>
     </dependency>
     <dependency>
diff --git a/tools/spring-boot-batchee-ui/src/test/java/org/apache/batchee/spring/ui/BatchEEUITest.java b/tools/spring-boot-batchee-ui/src/test/java/org/apache/batchee/spring/ui/BatchEEUITest.java
index e3c2501..18bb1f8 100644
--- a/tools/spring-boot-batchee-ui/src/test/java/org/apache/batchee/spring/ui/BatchEEUITest.java
+++ b/tools/spring-boot-batchee-ui/src/test/java/org/apache/batchee/spring/ui/BatchEEUITest.java
@@ -17,6 +17,7 @@
 package org.apache.batchee.spring.ui;
 
 import com.zaxxer.hikari.HikariDataSource;
+import org.apache.catalina.WebResourceRoot;
 import org.junit.Test;
 import org.springframework.batch.core.BatchStatus;
 import org.springframework.batch.core.Job;
@@ -35,85 +36,113 @@
 import org.springframework.batch.repeat.RepeatStatus;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.boot.web.embedded.tomcat.TomcatContextCustomizer;
 import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext;
 import org.springframework.context.ConfigurableApplicationContext;
 import org.springframework.context.annotation.Bean;
 import org.springframework.http.ResponseEntity;
+import org.springframework.web.client.HttpClientErrorException;
 import org.springframework.web.client.RestTemplate;
 
 import javax.sql.DataSource;
+import java.net.MalformedURLException;
+import java.net.URL;
 import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicReference;
 
+import static java.lang.Thread.sleep;
+import static org.apache.catalina.Lifecycle.CONFIGURE_START_EVENT;
+import static org.apache.catalina.WebResourceRoot.ResourceSetType.RESOURCE_JAR;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 public class BatchEEUITest {
     @Test
-    public void ensureUIRuns() {
+    public void ensureUIRuns() throws InterruptedException {
         try (final ConfigurableApplicationContext ctx = runBatch()) {
-            assertUIIsThere(ctx);
+            final int maxRetries = 10;
+            for (int i = 1; i <= maxRetries; i++) {
+                try {
+                    assertUIIsThere(ctx);
+                    break;
+                } catch (final HttpClientErrorException.NotFound ae) {
+                    if (i == maxRetries) {
+                        throw ae;
+                    }
+                    sleep(250);
+                }
+            }
         }
     }
 
     private ConfigurableApplicationContext runBatch() {
         final SpringApplication app = new SpringApplication(BatchApp.class);
-        final AtomicReference<JobExecution> done = new AtomicReference<>(null);
-        final AtomicReference<ConfigurableApplicationContext> context = new AtomicReference<>(null);
-        app.addListeners(applicationEvent -> {
-            if (!ApplicationReadyEvent.class.isInstance(applicationEvent)) {
-                return;
-            }
+        final JobExecution done;
+        final ConfigurableApplicationContext ctx = app.run("--server.port=0", "--server.tomcat.basedir=target/tomcat");
+        try {
+            done = ctx.getBean(JobLauncher.class).run(ctx.getBean(Job.class), new JobParameters());
+            ctx.getBean(Awaiter.class).latch.await();
+        } catch (final JobExecutionAlreadyRunningException | JobRestartException |
+                JobInstanceAlreadyCompleteException | JobParametersInvalidException |
+                InterruptedException e) {
+            throw new IllegalStateException(e);
+        }
 
-            final ConfigurableApplicationContext ctx = ApplicationReadyEvent.class.cast(applicationEvent)
-                    .getApplicationContext();
-            context.set(ctx);
-            try {
-                done.set(ctx.getBean(JobLauncher.class).run(ctx.getBean(Job.class), new JobParameters()));
-                ctx.getBean(Awaiter.class).latch.await();
-            } catch (final JobExecutionAlreadyRunningException | JobRestartException |
-                    JobInstanceAlreadyCompleteException | JobParametersInvalidException |
-                    InterruptedException e) {
-                throw new IllegalStateException(e);
-            }
-        });
-        app.run("--server.port=0", "--server.tomcat.basedir=target/tomcat");
-
-        final JobExecution exec = done.get();
-        assertNotNull(exec);
-
-        final JobExecution jobExecution = context.get().getBean(JobExplorer.class).getJobExecution(exec.getJobId());
+        final JobExecution jobExecution = ctx.getBean(JobExplorer.class).getJobExecution(done.getJobId());
         assertNotNull(jobExecution);
         assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
-        return context.get();
+        return ctx;
     }
 
-    // snaity checks to ensure the UI is properly rendered
+    // sanity checks to ensure the UI is properly rendered
     private void assertUIIsThere(final ConfigurableApplicationContext ctx) {
         final int port = ServletWebServerApplicationContext.class.cast(ctx).getWebServer().getPort();
-        { // home
-            final ResponseEntity<String> home = new RestTemplate().getForEntity("http://localhost:" + port + "/batchee/", String.class);
-            assertEquals(200, home.getStatusCodeValue());
-            final String body = home.getBody();
-            assertNotNull(body);
-            assertTrue(body, body.contains("<h3 class=\"text-muted\">Apache JBatch GUI</h3>"));
-            assertTrue(body, body.contains("<a href=\"/batchee/executions/BatchEEUITestBatch\">BatchEEUITestBatch</a>"));
-        }
-        { // detail for the batch
-            final ResponseEntity<String> home = new RestTemplate().getForEntity("http://localhost:" + port + "/batchee/executions/BatchEEUITestBatch", String.class);
-            assertEquals(200, home.getStatusCodeValue());
-            final String body = home.getBody();
-            assertNotNull(body);
-            assertTrue(body, body.contains("<td>COMPLETED</td>"));
-            assertTrue(body, body.contains("<td><a href=\"/batchee/step-executions/1\">1</a></td>"));
-        }
+        assertHome(port);
+        assertDetails(port);
+    }
+
+    private void assertDetails(final int port) {
+        final ResponseEntity<String> home = new RestTemplate().getForEntity("http://localhost:" + port + "/batchee/executions/BatchEEUITestBatch", String.class);
+        assertEquals(200, home.getStatusCodeValue());
+        final String body = home.getBody();
+        assertNotNull(body);
+        assertTrue(body, body.contains("<td>COMPLETED</td>"));
+        assertTrue(body, body.contains("<td><a href=\"/batchee/step-executions/1\">1</a></td>"));
+    }
+
+    private void assertHome(final int port) {
+        final ResponseEntity<String> home = new RestTemplate().getForEntity("http://localhost:" + port + "/batchee/", String.class);
+        assertEquals(200, home.getStatusCodeValue());
+        final String body = home.getBody();
+        assertNotNull(body);
+        assertTrue(body, body.contains("<h3 class=\"text-muted\">Apache JBatch GUI</h3>"));
+        assertTrue(body, body.contains("<a href=\"/batchee/executions/BatchEEUITestBatch\">BatchEEUITestBatch</a>"));
     }
 
     @EnableBatchProcessing
     @SpringBootApplication
     public static class BatchApp {
+        @Bean // StaticResourceJars fails with surefire classloader so let's workaround it for the test
+        public TomcatContextCustomizer surefireStaticResourceJarsWorkaround() {
+            return context -> context.addLifecycleListener(event -> {
+                if (!event.getType().equals(CONFIGURE_START_EVENT)) {
+                    return;
+                }
+
+                final URL url = Thread.currentThread().getContextClassLoader()
+                        .getResource("META-INF/resources/internal/batchee/layout.jsp");
+                final WebResourceRoot resources = context.getResources();
+                try {
+                    final URL cleanUrl = new URL(
+                            url.getProtocol(), url.getHost(), url.getPort(),
+                            url.getFile().substring(0, url.getFile().length() - "META-INF/resources/internal/batchee/layout.jsp".length()));
+                    resources.createWebResourceSet(RESOURCE_JAR, "/", cleanUrl, "/META-INF/resources");
+                } catch (final MalformedURLException e) {
+                    throw new IllegalStateException(e);
+                }
+            });
+        }
+
         @Bean(destroyMethod = "close")
         public DataSource dataSource() {
             final HikariDataSource dataSource = new HikariDataSource();