Skip to content

Commit 9e80313

Browse files
authored
Add support for specifying a custom mapper implementation for a DeployableContainer. (#553)
Fixes #552
1 parent 2bc6e1c commit 9e80313

File tree

5 files changed

+127
-13
lines changed

5 files changed

+127
-13
lines changed

container/impl-base/src/main/java/org/jboss/arquillian/container/impl/ContainerImpl.java

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.jboss.arquillian.config.descriptor.api.ProtocolDef;
2121
import org.jboss.arquillian.container.spi.Container;
2222
import org.jboss.arquillian.container.spi.ServerKillProcessor;
23+
import org.jboss.arquillian.container.spi.client.container.ConfigurationMapper;
2324
import org.jboss.arquillian.container.spi.client.container.ContainerConfiguration;
2425
import org.jboss.arquillian.container.spi.client.container.DeployableContainer;
2526
import org.jboss.arquillian.container.spi.client.container.LifecycleException;
@@ -47,25 +48,25 @@
4748
* @author <a href="mailto:[email protected]">Aslak Knutsen</a>
4849
* @version $Revision: $
4950
*/
50-
public class ContainerImpl implements Container {
51+
public class ContainerImpl<T extends ContainerConfiguration> implements Container<T> {
5152
@Inject
5253
private Event<ContainerEvent> event;
5354

5455
@Inject
5556
@ContainerScoped
56-
private InstanceProducer<Container> containerProducer;
57+
private InstanceProducer<Container<T>> containerProducer;
5758

5859
@Inject
5960
private Instance<ServiceLoader> serviceLoader;
6061

61-
private DeployableContainer<?> deployableContainer;
62+
private DeployableContainer<T> deployableContainer;
6263
private String name;
6364
private State state = State.STOPPED;
6465
private Throwable failureCause;
6566

6667
private ContainerDef containerConfiguration;
6768

68-
public ContainerImpl(String name, DeployableContainer<?> deployableContainer, ContainerDef containerConfiguration) {
69+
public ContainerImpl(String name, DeployableContainer<T> deployableContainer, ContainerDef containerConfiguration) {
6970
Validate.notNull(name, "Name must be specified");
7071
Validate.notNull(deployableContainer, "DeployableContainer must be specified");
7172
Validate.notNull(containerConfiguration, "ConfigurationConfiguration must be specified");
@@ -87,7 +88,7 @@ public String getName() {
8788
* @see org.jboss.arquillian.container.impl.ContainerT#getDeployableContainer()
8889
*/
8990
@Override
90-
public DeployableContainer<?> getDeployableContainer() {
91+
public DeployableContainer<T> getDeployableContainer() {
9192
return deployableContainer;
9293
}
9394

@@ -103,10 +104,15 @@ public ContainerDef getContainerConfiguration() {
103104
* @see org.jboss.arquillian.container.impl.ContainerT#createDeployableConfiguration()
104105
*/
105106
@Override
106-
public ContainerConfiguration createDeployableConfiguration() throws Exception {
107-
ContainerConfiguration config = SecurityActions.newInstance(
108-
deployableContainer.getConfigurationClass(), new Class<?>[0], new Object[0]);
109-
MapObject.populate(config, containerConfiguration.getContainerProperties());
107+
public T createDeployableConfiguration() throws Exception {
108+
Class<T> configClass = (Class<T>) deployableContainer.getConfigurationClass();
109+
T config = SecurityActions.newInstance(configClass, new Class<?>[0], new Object[0]);
110+
ConfigurationMapper<T> mapper = deployableContainer.getConfigurationMapper();
111+
if(mapper != null) {
112+
mapper.populateConfiguration(config, containerConfiguration);
113+
} else {
114+
MapObject.populate(config, containerConfiguration.getContainerProperties());
115+
}
110116
config.validate();
111117
return config;
112118
}

container/impl-base/src/test/java/org/jboss/arquillian/container/impl/ContainerRegistryTestCase.java

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616
*/
1717
package org.jboss.arquillian.container.impl;
1818

19+
import org.jboss.arquillian.config.descriptor.api.ContainerDef;
1920
import org.jboss.arquillian.config.descriptor.impl.ContainerDefImpl;
2021
import org.jboss.arquillian.container.spi.ConfigurationException;
2122
import org.jboss.arquillian.container.spi.Container;
2223
import org.jboss.arquillian.container.spi.ContainerRegistry;
24+
import org.jboss.arquillian.container.spi.client.container.ConfigurationMapper;
2325
import org.jboss.arquillian.container.spi.client.container.ContainerConfiguration;
2426
import org.jboss.arquillian.container.spi.client.container.DeployableContainer;
2527
import org.jboss.arquillian.container.spi.client.deployment.TargetDescription;
@@ -140,6 +142,46 @@ public void shouldBeAbleToCreatePrivateContainerConfiguration() throws Exception
140142
((PrivateDummyContainerConfiguration) container.createDeployableConfiguration()).getProperty());
141143
}
142144

145+
@Test
146+
public void shouldBeAbleToCreateContainerConfigurationCustomMapper() throws Exception {
147+
ServiceLoader serviceLoader = Mockito.mock(ServiceLoader.class);
148+
DeployableContainer<CustomContainerConfiguration> deployableContainer =
149+
Mockito.mock(DeployableContainer.class);
150+
151+
Mockito.when(serviceLoader.onlyOne(Mockito.same(DeployableContainer.class))).thenReturn(deployableContainer);
152+
Mockito.when(deployableContainer.getConfigurationClass()).thenReturn(CustomContainerConfiguration.class);
153+
Mockito.when(deployableContainer.getConfigurationMapper()).thenReturn(new CustomMapper());
154+
155+
String name = "custom-container";
156+
String prop = "prop-value";
157+
String[] hosts = {"host1", "host2", "host3"};
158+
159+
ContainerRegistry registry = new LocalContainerRegistry(injector.get());
160+
ContainerDefImpl containerDef = new ContainerDefImpl(ARQUILLIAN_XML);
161+
containerDef.setContainerName(name);
162+
containerDef.property("property", prop);
163+
containerDef.property("hosts", "host1,host2,host3");
164+
165+
registry.create(containerDef, serviceLoader);
166+
167+
Container<CustomContainerConfiguration> container = registry.getContainer(new TargetDescription(name));
168+
169+
Assert.assertEquals(
170+
"Verify that the only registered container is returned as default",
171+
name, container.getName());
172+
173+
CustomContainerConfiguration config = container.createDeployableConfiguration();
174+
Assert.assertEquals(
175+
"Verify that the custom configuration 'property' was populated",
176+
prop,
177+
config.getProperty());
178+
179+
Assert.assertArrayEquals(
180+
"Verify that the custom configuration 'hosts' was populated",
181+
hosts,
182+
config.getHosts());
183+
}
184+
143185
@Test
144186
public void shouldBeAbleToSpecifyTarget() throws Exception {
145187
String name = "some-name";
@@ -197,4 +239,23 @@ private static class PrivateDummyContainerConfiguration extends DummyContainerCo
197239
private PrivateDummyContainerConfiguration() {
198240
}
199241
}
242+
private static class CustomContainerConfiguration extends DummyContainerConfiguration {
243+
private String[] hosts;
244+
public String[] getHosts() {
245+
return hosts;
246+
}
247+
public void setHosts(String[] hosts) {
248+
this.hosts = hosts;
249+
}
250+
}
251+
private static class CustomMapper implements ConfigurationMapper<CustomContainerConfiguration> {
252+
@Override
253+
public void populateConfiguration(CustomContainerConfiguration containerConfiguration, ContainerDef definition) {
254+
String property = definition.getContainerProperty("property");
255+
containerConfiguration.setProperty(property);
256+
String hostsString = definition.getContainerProperty("hosts");
257+
String[] hosts = hostsString.split(",");
258+
containerConfiguration.setHosts(hosts);
259+
}
260+
}
200261
}

container/spi/src/main/java/org/jboss/arquillian/container/spi/Container.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
* @author <a href="mailto:[email protected]">Aslak Knutsen</a>
3131
* @version $Revision: $
3232
*/
33-
public interface Container {
33+
public interface Container<T extends ContainerConfiguration> {
3434

3535
/**
3636
* @return the name
@@ -40,7 +40,7 @@ public interface Container {
4040
/**
4141
* @return the deployableContainer
4242
*/
43-
DeployableContainer<?> getDeployableContainer();
43+
DeployableContainer<T> getDeployableContainer();
4444

4545
/**
4646
* @return the containerConfiguration
@@ -50,7 +50,7 @@ public interface Container {
5050
/**
5151
* @return the configuration
5252
*/
53-
ContainerConfiguration createDeployableConfiguration() throws Exception;
53+
T createDeployableConfiguration() throws Exception;
5454

5555
boolean hasProtocolConfiguration(ProtocolDescription description);
5656

@@ -73,4 +73,4 @@ public interface Container {
7373
enum State {
7474
SETUP, SETUP_FAILED, STARTED, STARTED_FAILED, STOPPED, STOPPED_FAILED, KILLED, KILLED_FAILED;
7575
}
76-
}
76+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* JBoss, Home of Professional Open Source
3+
* Copyright 2024 Red Hat Inc. and/or its affiliates and other contributors
4+
* by the @authors tag. See the copyright.txt in the distribution for a
5+
* full listing of individual contributors.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.jboss.arquillian.container.spi.client.container;
18+
19+
import org.jboss.arquillian.config.descriptor.api.ContainerDef;
20+
21+
/**
22+
* An interface that {@link DeployableContainer<T>} can use to control how the mapping
23+
* from the externalized container definition, as found in an arquillian.xml file
24+
* for example, is applied to the {@link ContainerConfiguration} instances
25+
*
26+
* @param <T> the type of ContainerConfiguration
27+
*/
28+
public interface ConfigurationMapper<T extends ContainerConfiguration> {
29+
/**
30+
*
31+
* @param containerConfiguration - the deployable container configuration instance to populate
32+
* @param definition - the container definition from the ArquillianDescriptor that
33+
* was parsed
34+
*/
35+
void populateConfiguration(T containerConfiguration, ContainerDef definition);
36+
}

container/spi/src/main/java/org/jboss/arquillian/container/spi/client/container/DeployableContainer.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,17 @@ public interface DeployableContainer<T extends ContainerConfiguration> {
3737
// ControllableContainer
3838
Class<T> getConfigurationClass();
3939

40+
/**
41+
* Provide a mapping instance that takes the {@link org.jboss.arquillian.config.descriptor.api.ContainerDef}
42+
* for the arquillian.xml or other configured descriptor and populates the container configuration
43+
* instance from the descriptor values.
44+
*
45+
* @return A possibly null ConfigurationMapper. If null, the default logic to map from string based
46+
* properties as implemented in org.jboss.arquillian.container.impl.MapObject will be used.
47+
*/
48+
default ConfigurationMapper getConfigurationMapper() {
49+
return null;
50+
}
4051
default void setup(T configuration) {
4152
}
4253

0 commit comments

Comments
 (0)