ApplicationComposer API is mainly contained in org.apache.openejb.testing
package
(historically, today we would have called the package org.apache.tomee.applicationcomposer
).
To start using ApplicationComposer you need to add some dependencies.
The minimum required one is openejb-core
:
<dependency>
<groupId>org.apache.openejb</groupId>
<artifactId>openejb-core</artifactId>
<version>${openejb.version></version>
</dependency>
If you need JAXRS services you'll add (or replace thanks to transitivity of maven) openejb-cxf-rs
:
<dependency>
<groupId>org.apache.openejb</groupId>
<artifactId>openejb-cxf-rs</artifactId>
<version>${openejb.version></version>
</dependency>
If you need JAXWS services you'll add (or replace thanks to transitivity of maven) openejb-cxf
:
<dependency>
<groupId>org.apache.openejb</groupId>
<artifactId>openejb-cxf</artifactId>
<version>${openejb.version></version>
</dependency>
etc...
An ApplicationComposer needs at minimum a module (the application you need to deploy).
To do so you have two cases:
@Module
@Classes
directly on the ApplicationComposer class as a shortcut for:
@Module public WebApp app() { return new WebApp(); }
The expected returned type of these methods are in org.apache.openejb.jee
package:
Application
: entry point to create an earWebApp
: a web applicationEjbJar
: an ejb moduleEnterpriseBean
children: a simple EJBPersistence
: a persistence module with multiple unitsPersistenceUnit
: a simple unit (automatically wrapped in a Persistence
)Connector
: a JCA connector moduleBeans
: a CDI module,Class[]
or Class
: a set of classes scanned to discover annotationsNote that for easiness @Classes
was added to be able to describe a module and some scanned classes. For instance the
following snippet will create a web application with classes C1, C2 as CDI beans and E1 as an EJB automatically:
@Module
@Classes(cdi = true, value = { C1.class, C2.class, E1.class })
public WebApp app() {
return new WebApp();
}
Often you need to customize a bit the container or at least create some resources like test databases.
To do so you can create a method returning Properties
which will be the container properties.
Note: to simplify writing properties you can use PropertiesBuilder
util class which is just a fluent API
to write properties.
In these properties you can reuse OpenEJB/TomEE property syntax for resources.
Here is a sample:
@Configuration
public Properties configuration() {
return new PropertiesBuilder()
.p("db", "new://Resource?type=DataSource")
.p("db.JdbcUrld", "jdbc:hsqldb:mem:test")
.build();
}
Since TomEE 2.x you can also put properties on ApplicationComposer class using @ContainerProperties
API:
@ContainerProperties({
@ContainerProperties.Property(name = "db", value = "new://Resource?type=DataSource"),
@ContainerProperties.Property(name = "db.JdbcUrl", value = "jdbc:hsqldb:mem:test")
})
public class MyAppComposer() {
// ...
}
Sometimes you need to customize a container component. The most common use case is the security service to mock a little bit authorization if you don't care in your test.
To do so just write a method decorated with @Component
returning the instance you desire.
Components in TomEE are stored in a container Map and the key needs to be a Class
. This one is deduced from the returned
type of the @Component
method:
@Component
public SecurityService mockSecurity() {
return new MySecurityService();
}
If you use JUnit you have mainly 2 solutions to run you "model" using the ApplicationComposer:
ApplicationComposer
runner:
@RunWith(ApplicationComposer.class) public class MyTest { // ... }
ApplicationComposerRule
rule:
public class MyTest { @Rule // or @ClassRule if you want the container/application lifecycle be bound to the class and not test methods public final ApplicationComposerRule rule = new ApplicationComposerRule(this); }
Tip: since TomEE 2.x ApplicationComposerRule is decomposed in 2 rules if you need: ContainerRule
and DeployApplication
.
Using JUnit RuleChain
you can chain them to get the samebehavior as ApplicationComposerRule
or better deploy
multiple ApplicationComposer models and controlling their deployment ordering (to mock a remote service for instance).
Finally just write @Test
method using test class injections as if the test class was a managed bean!
TestNG integration is quite simple today and mainly ApplicationComposerListener
class you can configure
as a listener to get ApplicationComposer features.
Finally just write TestNG @Test
method using test class injections as if the test class was a managed bean!
Since TomEE 2.x you can also use ApplicationComposers
to directly run you ApplicationComposer model
as a standalone application:
public class MyApp {
public static void main(String[] args) {
ApplicationComposers.run(MyApp.class, args);
}
// @Module, @Configuration etc...
}
Tip: if MyApp
has @PostConstruct
methods they will be respected and if MyApp
has a constructor taking an array
of String it will be instantiated getting the second parameter as argument (ie you can propagate your main parameter
to your model to modify your application depending it!)
@Classes(cdi = true, value = { MyService.class, MyOtherService.class })
@ContainerProperties(@ContainerProperties.Property(name = "myDb", value = "new://Resource?type=DataSource"))
@RunWith(ApplicationComposer.class)
public class MyTest {
@Resource(name = "myDb")
private DataSource ds;
@Inject
private MyService service;
@Test
public void myTest() {
// do test using injections
}
}
If you want to learn more about ApplicationComposer see Advanced page.