Permalink
Show file tree
Hide file tree
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
6 changed files
with
583 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@@ -0,0 +1,89 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!-- | ||
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. | ||
--> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<parent> | ||
<artifactId>batchee-tools</artifactId> | ||
<groupId>org.apache.batchee</groupId> | ||
<version>1.0.1-SNAPSHOT</version> | ||
</parent> | ||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<artifactId>spring-boot-batchee-ui</artifactId> | ||
<name>BatchEE :: Tools :: Spring Boot UI</name> | ||
|
||
<properties> | ||
<spring-boot.version>2.5.6</spring-boot.version> | ||
</properties> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>${project.groupId}</groupId> | ||
<artifactId>batchee-servlet-embedded</artifactId> | ||
<version>${project.version}</version> | ||
</dependency> | ||
<dependency> <!-- for jsp --> | ||
<groupId>org.apache.tomcat</groupId> | ||
<artifactId>tomcat-jasper</artifactId> | ||
<version>9.0.54</version> | ||
</dependency> | ||
|
||
<dependency> | ||
<groupId>org.springframework.batch</groupId> | ||
<artifactId>spring-batch-core</artifactId> | ||
<version>4.3.3</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter</artifactId> | ||
<version>${spring-boot.version}</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-jdbc</artifactId> | ||
<version>${spring-boot.version}</version> | ||
<scope>provided</scope> | ||
<exclusions> | ||
<exclusion> | ||
<groupId>org.slf4j</groupId> | ||
<artifactId>slf4j-api</artifactId> | ||
</exclusion> | ||
</exclusions> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-web</artifactId> | ||
<version>${spring-boot.version}</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
|
||
<!-- for now we still use junit4 so stick to it --> | ||
<dependency> | ||
<groupId>junit</groupId> | ||
<artifactId>junit</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.apache.derby</groupId> | ||
<artifactId>derby</artifactId> | ||
<scope>test</scope> | ||
</dependency> | ||
</dependencies> | ||
</project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@@ -0,0 +1,95 @@ | ||
/* | ||
* 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.batchee.spring.ui; | ||
|
||
import javax.batch.operations.JobOperator; | ||
import javax.batch.operations.JobSecurityException; | ||
import java.io.ByteArrayInputStream; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.net.URL; | ||
import java.net.URLConnection; | ||
import java.net.URLStreamHandler; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.Enumeration; | ||
import java.util.Set; | ||
import java.util.function.Function; | ||
import java.util.stream.Stream; | ||
|
||
import static java.util.Collections.enumeration; | ||
import static java.util.Collections.singleton; | ||
import static java.util.stream.Collectors.toSet; | ||
|
||
public class BatchEEJobOperator extends DelegatingJobOperator { | ||
private final String[] defaultJobNames; | ||
|
||
public BatchEEJobOperator(final JobOperator builtIn, | ||
final String... defaultJobNames) { | ||
super(builtIn); | ||
this.defaultJobNames = defaultJobNames; | ||
} | ||
|
||
@Override | ||
public Set<String> getJobNames() throws JobSecurityException { | ||
final Set<String> builtInNames = super.getJobNames(); | ||
return defaultJobNames.length == 0 ? | ||
builtInNames : | ||
Stream.concat(Stream.of(defaultJobNames), builtInNames.stream()).collect(toSet()); | ||
} | ||
|
||
public static class Context { | ||
private Context() { | ||
// no-op | ||
} | ||
|
||
public static <T> T withFacade(final JobOperator operator, final Function<JobOperator, T> task) { | ||
JobOperatorFacade.current = operator; | ||
final Thread thread = Thread.currentThread(); | ||
final ClassLoader loader = thread.getContextClassLoader(); | ||
thread.setContextClassLoader(new ClassLoader(loader) { | ||
// don't use default spring JobOperator impl, it is not accurate and loads a global context - we don't want it | ||
@Override | ||
public Enumeration<URL> getResources(final String name) throws IOException { | ||
if ("META-INF/services/javax.batch.operations.JobOperator".equals(name)) { | ||
return enumeration(singleton(new URL("embed://", null, -1, "", new URLStreamHandler() { | ||
@Override | ||
protected URLConnection openConnection(final URL u) { | ||
return new URLConnection(u) { | ||
@Override | ||
public void connect() { | ||
// no-op | ||
} | ||
|
||
@Override | ||
public InputStream getInputStream() { | ||
return new ByteArrayInputStream(JobOperatorFacade.class.getName().getBytes(StandardCharsets.UTF_8)); | ||
} | ||
}; | ||
} | ||
}))); | ||
} | ||
return super.getResources(name); | ||
} | ||
}); | ||
try { | ||
return task.apply(operator); | ||
} finally { | ||
thread.setContextClassLoader(loader); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@@ -0,0 +1,109 @@ | ||
/* | ||
* 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.batchee.spring.ui; | ||
|
||
import org.apache.batchee.servlet.JBatchController; | ||
import org.springframework.batch.core.Job; | ||
import org.springframework.batch.core.explore.JobExplorer; | ||
import org.springframework.batch.core.jsr.JsrJobParametersConverter; | ||
import org.springframework.batch.core.jsr.launch.JsrJobOperator; | ||
import org.springframework.batch.core.repository.JobRepository; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | ||
import org.springframework.boot.context.properties.ConfigurationProperties; | ||
import org.springframework.boot.web.servlet.ServletRegistrationBean; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.transaction.PlatformTransactionManager; | ||
|
||
import javax.batch.operations.JobOperator; | ||
import javax.servlet.ServletConfig; | ||
import javax.servlet.ServletException; | ||
import javax.sql.DataSource; | ||
import java.util.List; | ||
|
||
@Configuration(proxyBeanMethods = false) | ||
public class BatchEEUI { | ||
@Bean | ||
@ConditionalOnMissingBean(value = JobOperator.class) | ||
public JobOperator operator(final JobExplorer jobExplorer, | ||
final JobRepository jobRepository, | ||
final PlatformTransactionManager transactionManager, | ||
final DataSource dataSource, | ||
final Properties properties, | ||
@Autowired(required = false) final List<Job> jobs) throws Exception { | ||
final JsrJobParametersConverter jobParametersConverter = new JsrJobParametersConverter(dataSource); | ||
jobParametersConverter.afterPropertiesSet(); | ||
return new BatchEEJobOperator( | ||
new JsrJobOperator(jobExplorer, jobRepository, jobParametersConverter, transactionManager), | ||
getJobNames(properties, jobs)); | ||
} | ||
|
||
@Bean | ||
public ServletRegistrationBean<JBatchController> batcheeUI(final JobOperator operator, final Properties properties) { | ||
final ServletRegistrationBean<JBatchController> registrationBean = new ServletRegistrationBean<>(new JBatchController() { | ||
@Override | ||
public void init(final ServletConfig config) { | ||
// force operator to be this one since spring JsrJobOperator loads an "external" context we don't want at all | ||
BatchEEJobOperator.Context.withFacade(operator, op -> { | ||
try { | ||
super.init(config); | ||
return null; | ||
} catch (final ServletException e) { | ||
throw new IllegalStateException(e); | ||
} | ||
}); | ||
} | ||
}.defaultScan(false).mapping(properties.getMapping()), properties.getMapping()); | ||
registrationBean.setLoadOnStartup(1); | ||
registrationBean.setAsyncSupported(true); | ||
return registrationBean; | ||
} | ||
|
||
private String[] getJobNames(final Properties properties, final List<Job> jobs) { | ||
try { | ||
return jobs != null && properties.getDefaultBatchNames().length == 0 ? | ||
jobs.stream().map(Job::getName).toArray(String[]::new) : | ||
properties.getDefaultBatchNames(); | ||
} catch (final RuntimeException re) { | ||
return properties.getDefaultBatchNames(); | ||
} | ||
} | ||
|
||
@Configuration | ||
@ConfigurationProperties(prefix = "batchee.ui") | ||
public static class Properties { | ||
private String mapping = "/batchee/*"; | ||
private String[] defaultBatchNames = new String[0]; | ||
|
||
public String[] getDefaultBatchNames() { | ||
return defaultBatchNames; | ||
} | ||
|
||
public void setDefaultBatchNames(final String[] defaultBatchNames) { | ||
this.defaultBatchNames = defaultBatchNames; | ||
} | ||
|
||
public void setMapping(final String mapping) { | ||
this.mapping = mapping; | ||
} | ||
|
||
public String getMapping() { | ||
return mapping; | ||
} | ||
} | ||
} |
Oops, something went wrong.