13
13
import java .util .concurrent .ConcurrentHashMap ;
14
14
import java .util .concurrent .ConcurrentMap ;
15
15
import java .util .concurrent .atomic .AtomicInteger ;
16
+ import java .util .function .Consumer ;
16
17
import java .util .function .Supplier ;
17
18
import java .util .stream .Stream ;
18
19
import org .apache .commons .lang3 .StringUtils ;
20
+ import org .apache .commons .lang3 .tuple .Pair ;
19
21
import org .slf4j .Logger ;
20
22
import org .slf4j .LoggerFactory ;
21
23
import org .testcontainers .containers .GenericContainer ;
28
30
* ContainerFactory is the companion to {@link TestDatabase} and provides it with suitable
29
31
* testcontainer instances.
30
32
*/
31
- public abstract class ContainerFactory <C extends JdbcDatabaseContainer <?>> {
33
+ public abstract class ContainerFactory <C extends GenericContainer <?>> {
32
34
33
35
static private final Logger LOGGER = LoggerFactory .getLogger (ContainerFactory .class );
34
36
35
- private record ContainerKey (Class <? extends ContainerFactory > clazz , DockerImageName imageName , List <String > methods ) {};
37
+ private record ContainerKey < C extends GenericContainer <?>> (Class <? extends ContainerFactory > clazz , DockerImageName imageName , List <NamedContainerModifier < C > > methods ) {};
36
38
37
39
private static class ContainerOrException {
38
40
@@ -67,12 +69,12 @@ GenericContainer<?> container() {
67
69
68
70
}
69
71
70
- private static final ConcurrentMap <ContainerKey , ContainerOrException > SHARED_CONTAINERS = new ConcurrentHashMap <>();
72
+ private final ConcurrentMap <ContainerKey < C > , ContainerOrException > SHARED_CONTAINERS = new ConcurrentHashMap <>();
71
73
private static final AtomicInteger containerId = new AtomicInteger (0 );
72
74
73
- private static final MdcScope .Builder getTestContainerLogMdcBuilder (DockerImageName imageName , List <String > methods ) {
75
+ private final MdcScope .Builder getTestContainerLogMdcBuilder (DockerImageName imageName , List <NamedContainerModifier < C >> containerModifiers ) {
74
76
return new MdcScope .Builder ()
75
- .setLogPrefix ("testcontainer %s (%s[%s]):" .formatted (containerId .incrementAndGet (), imageName , StringUtils .join (methods , "," )))
77
+ .setLogPrefix ("testcontainer %s (%s[%s]):" .formatted (containerId .incrementAndGet (), imageName , StringUtils .join (containerModifiers , "," )))
76
78
.setPrefixColor (LoggingHelper .Color .RED_BACKGROUND );
77
79
}
78
80
@@ -87,7 +89,18 @@ private static final MdcScope.Builder getTestContainerLogMdcBuilder(DockerImageN
87
89
*/
88
90
@ SuppressWarnings ("unchecked" )
89
91
public final C shared (String imageName , String ... methods ) {
90
- final var containerKey = new ContainerKey (getClass (), DockerImageName .parse (imageName ), Stream .of (methods ).toList ());
92
+ return (C ) shared (imageName , Stream .of (methods ).map (n ->new NamedContainerModifier <C >(n , null )).toList ());
93
+ }
94
+
95
+ public final C shared (String imageName , NamedContainerModifier <C >... namedContainerModifiers ) {
96
+ return (C ) shared (imageName , List .of (namedContainerModifiers ));
97
+ }
98
+
99
+ public final C shared (String imageName ) {
100
+ return (C ) shared (imageName , new ArrayList <>());
101
+ }
102
+ public final C shared (String imageName , List <NamedContainerModifier <C >> namedContainerModifiers ) {
103
+ final ContainerKey <C > containerKey = new ContainerKey <>(getClass (), DockerImageName .parse (imageName ), namedContainerModifiers );
91
104
// We deliberately avoid creating the container itself eagerly during the evaluation of the map
92
105
// value.
93
106
// Container creation can be exceedingly slow.
@@ -103,38 +116,69 @@ public final C shared(String imageName, String... methods) {
103
116
*/
104
117
@ SuppressWarnings ("unchecked" )
105
118
public final C exclusive (String imageName , String ... methods ) {
106
- return (C ) createAndStartContainer (DockerImageName .parse (imageName ), Stream .of (methods ).toList ());
119
+ return (C ) createAndStartContainer (DockerImageName .parse (imageName ), Stream .of (methods ).map ( n -> new NamedContainerModifier < C >( n , null )). toList ());
107
120
}
108
121
109
- private GenericContainer <?> createAndStartContainer (DockerImageName imageName , List <String > methodNames ) {
110
- LOGGER .info ("Creating new shared container based on {} with {}." , imageName , methodNames );
111
- try {
112
- GenericContainer <?> container = createNewContainer (imageName );
113
- final var methods = new ArrayList <Method >();
114
- for (String methodName : methodNames ) {
115
- methods .add (getClass ().getMethod (methodName , container .getClass ()));
122
+ public final C exclusive (String imageName ) {
123
+ return (C ) createAndStartContainer (DockerImageName .parse (imageName ), new ArrayList <>());
124
+ }
125
+
126
+ public final C exclusive (String imageName , NamedContainerModifier <C >... namedContainerModifiers ) {
127
+ return (C ) createAndStartContainer (DockerImageName .parse (imageName ), Stream .of (namedContainerModifiers ).toList ());
128
+ }
129
+
130
+ public record NamedContainerModifier <C extends GenericContainer <?>>(String name , Consumer <C > modifier ){
131
+
132
+ @ Override
133
+ public String toString () {
134
+ return name ;
135
+ }
136
+ }
137
+
138
+ private NamedContainerModifier <C > resolveModifierByName (C container , String methodName ) {
139
+ final ContainerFactory <C > self = this ;
140
+ Consumer <C > resolvedMethod = c ->{
141
+ try {
142
+ Class <? extends GenericContainer > containerClass = container .getClass ();
143
+ Method method = self .getClass ().getMethod (methodName , containerClass );
144
+ method .invoke (self , c );
145
+ } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e ) {
146
+ throw new RuntimeException (e );
116
147
}
117
- final var logConsumer = new Slf4jLogConsumer (LOGGER ) {
148
+ };
149
+ return new NamedContainerModifier <>(methodName , resolvedMethod );
150
+ }
118
151
119
- public void accept (OutputFrame frame ) {
120
- if (frame .getUtf8StringWithoutLineEnding ().trim ().length () > 0 ) {
121
- super .accept (frame );
122
- }
123
- }
124
152
125
- };
126
- getTestContainerLogMdcBuilder (imageName , methodNames ).produceMappings (logConsumer ::withMdc );
127
- container .withLogConsumer (logConsumer );
128
- for (Method method : methods ) {
129
- LOGGER .info ("Calling {} in {} on new shared container based on {}." ,
130
- method .getName (), getClass ().getName (), imageName );
131
- method .invoke (this , container );
153
+ private C createAndStartContainer (DockerImageName imageName , List <NamedContainerModifier <C >> namedContainerModifiers ) {
154
+ LOGGER .info ("Creating new shared container based on {} with {}." , imageName , namedContainerModifiers );
155
+ C container = createNewContainer (imageName );
156
+ final List <NamedContainerModifier <C >> resolvedNamedContainerModifiers = new ArrayList <>();
157
+ for (NamedContainerModifier <C > namedContainerModifier : namedContainerModifiers ) {
158
+ if (namedContainerModifier .modifier == null ) {
159
+ resolvedNamedContainerModifiers .add (resolveModifierByName (container , namedContainerModifier .name ));
160
+ } else {
161
+ resolvedNamedContainerModifiers .add (namedContainerModifier );
132
162
}
133
- container .start ();
134
- return container ;
135
- } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e ) {
136
- throw new RuntimeException (e );
137
163
}
164
+ final var logConsumer = new Slf4jLogConsumer (LOGGER ) {
165
+
166
+ public void accept (OutputFrame frame ) {
167
+ if (frame .getUtf8StringWithoutLineEnding ().trim ().length () > 0 ) {
168
+ super .accept (frame );
169
+ }
170
+ }
171
+
172
+ };
173
+ getTestContainerLogMdcBuilder (imageName , resolvedNamedContainerModifiers ).produceMappings (logConsumer ::withMdc );
174
+ container .withLogConsumer (logConsumer );
175
+ for (NamedContainerModifier <C > resolvedNamedContainerModifier : resolvedNamedContainerModifiers ) {
176
+ LOGGER .info ("Calling {} in {} on new shared container based on {}." ,
177
+ resolvedNamedContainerModifier .name (), getClass ().getName (), imageName );
178
+ resolvedNamedContainerModifier .modifier .accept (container );
179
+ }
180
+ container .start ();
181
+ return container ;
138
182
}
139
183
140
184
}
0 commit comments