Skip to content

Commit 3e0d0f2

Browse files
committed
Add Docker integration test for SAP HANA
1 parent 612f27c commit 3e0d0f2

File tree

4 files changed

+243
-0
lines changed

4 files changed

+243
-0
lines changed

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@
9292
<h2.version>1.4.200</h2.version>
9393
<sqlite.version>3.34.0</sqlite.version>
9494
<derby.version>10.14.2.0</derby.version>
95+
<hana.version>2.9.12</hana.version>
9596
<activemq.version>5.15.14</activemq.version>
9697
<jaxb-api.version>2.3.1</jaxb-api.version>
9798
<jaxb-core.version>2.3.0.1</jaxb-core.version>

spring-batch-core/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,12 @@
184184
<version>${derby.version}</version>
185185
<scope>test</scope>
186186
</dependency>
187+
<dependency>
188+
<groupId>com.sap.cloud.db.jdbc</groupId>
189+
<artifactId>ngdbc</artifactId>
190+
<version>${hana.version}</version>
191+
<scope>test</scope>
192+
</dependency>
187193
<dependency>
188194
<groupId>commons-io</groupId>
189195
<artifactId>commons-io</artifactId>
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/*
2+
* Copyright 2020-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.batch.core.test.repository;
17+
18+
import java.time.Duration;
19+
import java.time.temporal.ChronoUnit;
20+
import java.util.Arrays;
21+
import java.util.HashMap;
22+
import java.util.HashSet;
23+
import java.util.Map;
24+
import java.util.Set;
25+
26+
import org.testcontainers.containers.JdbcDatabaseContainer;
27+
import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy;
28+
import org.testcontainers.utility.DockerImageName;
29+
import org.testcontainers.utility.LicenseAcceptance;
30+
31+
import com.github.dockerjava.api.model.Ulimit;
32+
33+
/**
34+
* @author Jonathan Bregler
35+
*/
36+
public class HANAContainer<SELF extends HANAContainer<SELF>> extends JdbcDatabaseContainer<SELF> {
37+
38+
private static final Integer PORT = 39041;
39+
40+
private static final String SYSTEM_USER = "SYSTEM";
41+
private static final String SYSTEM_USER_PASSWORD = "HXEHana1";
42+
43+
public HANAContainer(DockerImageName image) {
44+
45+
super( image );
46+
47+
addExposedPorts( 39013, 39017, 39041, 39042, 39043, 39044, 39045, 1128, 1129, 59013, 59014 );
48+
49+
// create ulimits
50+
Ulimit[] ulimits = new Ulimit[]{ new Ulimit( "nofile", 1048576L, 1048576L ) };
51+
52+
// create sysctls Map.
53+
Map<String, String> sysctls = new HashMap<String, String>();
54+
55+
sysctls.put( "kernel.shmmax", "1073741824" );
56+
sysctls.put( "net.ipv4.ip_local_port_range", "40000 60999" );
57+
58+
// Apply mounts, ulimits and sysctls.
59+
this.withCreateContainerCmdModifier( it -> it.getHostConfig().withUlimits( ulimits ).withSysctls( sysctls ) );
60+
61+
// Arguments for Image.
62+
this.withCommand( "--master-password " + SYSTEM_USER_PASSWORD + " --agree-to-sap-license" );
63+
64+
// Determine if container is ready.
65+
this.waitStrategy = new LogMessageWaitStrategy().withRegEx( ".*Startup finished!*\\s" ).withTimes( 1 )
66+
.withStartupTimeout( Duration.of( 600, ChronoUnit.SECONDS ) );
67+
}
68+
69+
@Override
70+
protected void configure() {
71+
/*
72+
* Enforce that the license is accepted - do not remove. License available at:
73+
* https://www.sap.com/docs/download/cmp/2016/06/sap-hana-express-dev-agmt-and- exhibit.pdf
74+
*/
75+
76+
// If license was not accepted programmatically, check if it was accepted via
77+
// resource file
78+
if ( !getEnvMap().containsKey( "AGREE_TO_SAP_LICENSE" ) ) {
79+
LicenseAcceptance.assertLicenseAccepted( this.getDockerImageName() );
80+
acceptLicense();
81+
}
82+
}
83+
84+
/**
85+
* Accepts the license for the SAP HANA Express container by setting the AGREE_TO_SAP_LICENSE=Y Calling this method
86+
* will automatically accept the license at:
87+
* https://www.sap.com/docs/download/cmp/2016/06/sap-hana-express-dev-agmt-and-exhibit.pdf
88+
*
89+
* @return The container itself with an environment variable accepting the SAP HANA Express license
90+
*/
91+
public SELF acceptLicense() {
92+
addEnv( "AGREE_TO_SAP_LICENSE", "Y" );
93+
return self();
94+
}
95+
96+
@Override
97+
protected Set<Integer> getLivenessCheckPorts() {
98+
return new HashSet<>( Arrays.asList( new Integer[]{ getMappedPort( PORT ) } ) );
99+
}
100+
101+
@Override
102+
protected void waitUntilContainerStarted() {
103+
getWaitStrategy().waitUntilReady( this );
104+
}
105+
106+
@Override
107+
public String getDriverClassName() {
108+
return "com.sap.db.jdbc.Driver";
109+
}
110+
111+
@Override
112+
public String getUsername() {
113+
return SYSTEM_USER;
114+
}
115+
116+
@Override
117+
public String getPassword() {
118+
return SYSTEM_USER_PASSWORD;
119+
}
120+
121+
@Override
122+
public String getTestQueryString() {
123+
return "SELECT 1 FROM SYS.DUMMY";
124+
}
125+
126+
@Override
127+
public String getJdbcUrl() {
128+
return "jdbc:sap://" + getContainerIpAddress() + ":" + getMappedPort( PORT ) + "/";
129+
}
130+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Copyright 2020-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.batch.core.test.repository;
17+
18+
import javax.sql.DataSource;
19+
20+
import org.junit.Assert;
21+
import org.junit.Before;
22+
import org.junit.ClassRule;
23+
import org.junit.Test;
24+
import org.junit.runner.RunWith;
25+
import org.springframework.batch.core.ExitStatus;
26+
import org.springframework.batch.core.Job;
27+
import org.springframework.batch.core.JobExecution;
28+
import org.springframework.batch.core.JobParameters;
29+
import org.springframework.batch.core.JobParametersBuilder;
30+
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
31+
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
32+
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
33+
import org.springframework.batch.core.launch.JobLauncher;
34+
import org.springframework.batch.repeat.RepeatStatus;
35+
import org.springframework.beans.factory.annotation.Autowired;
36+
import org.springframework.context.annotation.Bean;
37+
import org.springframework.context.annotation.Configuration;
38+
import org.springframework.core.io.ClassPathResource;
39+
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
40+
import org.springframework.test.context.ContextConfiguration;
41+
import org.springframework.test.context.junit4.SpringRunner;
42+
import org.testcontainers.utility.DockerImageName;
43+
44+
import com.sap.db.jdbcext.HanaDataSource;
45+
46+
/**
47+
* @author Jonathan Bregler
48+
*/
49+
@RunWith(SpringRunner.class)
50+
@ContextConfiguration
51+
public class HANAJobRepositoryIntegrationTests {
52+
53+
private static final DockerImageName HANA_IMAGE = DockerImageName.parse( "store/saplabs/hanaexpress:2.00.054.00.20210603.1" );
54+
55+
@ClassRule
56+
public static HANAContainer<?> hana = new HANAContainer<>( HANA_IMAGE ).acceptLicense();
57+
58+
@Autowired
59+
private DataSource dataSource;
60+
@Autowired
61+
private JobLauncher jobLauncher;
62+
@Autowired
63+
private Job job;
64+
65+
@Before
66+
public void setUp() {
67+
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
68+
databasePopulator.addScript( new ClassPathResource( "/org/springframework/batch/core/schema-hana.sql" ) );
69+
databasePopulator.execute( this.dataSource );
70+
}
71+
72+
@Test
73+
public void testJobExecution() throws Exception {
74+
// given
75+
JobParameters jobParameters = new JobParametersBuilder().toJobParameters();
76+
77+
// when
78+
JobExecution jobExecution = this.jobLauncher.run( this.job, jobParameters );
79+
80+
// then
81+
Assert.assertNotNull( jobExecution );
82+
Assert.assertEquals( ExitStatus.COMPLETED, jobExecution.getExitStatus() );
83+
}
84+
85+
@Configuration
86+
@EnableBatchProcessing
87+
static class TestConfiguration {
88+
89+
@Bean
90+
public DataSource dataSource() throws Exception {
91+
HanaDataSource dataSource = new HanaDataSource();
92+
dataSource.setUser( hana.getUsername() );
93+
dataSource.setPassword( hana.getPassword() );
94+
dataSource.setUrl( hana.getJdbcUrl() );
95+
return dataSource;
96+
}
97+
98+
@Bean
99+
public Job job(JobBuilderFactory jobs, StepBuilderFactory steps) {
100+
return jobs.get( "job" )
101+
.start( steps.get( "step" ).tasklet( (contribution, chunkContext) -> RepeatStatus.FINISHED ).build() )
102+
.build();
103+
}
104+
105+
}
106+
}

0 commit comments

Comments
 (0)