I spoke a lot of ApplicationComposers on this blog. If you don’t know it yet in two words it is originally a way to fully define its EE application programmatically and then run test against this described application.
However with a trivial main you can make it run your application!
As a quick reminder here is an ApplicationComposer test:
@EnableServices("jax-ws") @RunWith(ApplicationComposer.class) public class MeetingPlannerTest { @RandomPort("http") private int jaxWsPort; @Inject private LazyAgenda agenda; @Module @Classes(cdi = true, value = { MeetingPlannerImpl.class, LazyAgenda.class }) public WebApp war() { return new WebApp() .contextRoot("/demo") .addServlet("jaxws", MeetingPlannerImpl.class.getName(), "/meeting-planner"); } @Test public void book() throws MalformedURLException { final Service service = Service.create( new URL("http://127.0.0.1:" + jaxWsPort + "/demo/meeting-planner?wsdl"), new QName("http://jaxws.example.superbiz.org/", "MeetingPlannerImplService")); final MeetingPlanner planner = service.getPort(MeetingPlanner.class); assertTrue(planner.book(new Date(System.currentTimeMillis() + 1000000))); } @Test public void cdiTest() { assertNotNull(agenda); // etc... } }
What we can see is:
- we described our application – here using the shortcurt @Classes but you can use @JarLocation for dependencies or even fully programmatic way using Beans (represents a beans.xml)
- we used http embedded layer thanks to @EnableServices and thanks to it use JAXWS – JAXRS is as trivial as replacing jaxws by jaxrs
- we were able to get injected components – cdi bean here
So globally we have all we need to run an application excepted a main(String[]).
If you are curious and see how is implemented the JUnit runner you’ll realize it is abstracted in ApplicationComposers class which is test framework independent.
So globally we can write something like:
public static void run(final Class<?> type) throws Exception { ApplicationComposers composer = new ApplicationComposers(type); Object instance = type.newInstance(); composer.before(instance); CountDownLatch latch = new CountDownLatch(1); Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { try { composer.after(); } catch (final Exception e) { // no-op } latch.countDown(); } }); latch.await(); }
Not that hard right? and finally supposing you defined your class in MyApplication you can run it with:
MyRunner.run(MyApplication.class);
That’s globally what was added to coming TomEE 2. Main differences are TomEE version supports:
- String[] constructor for application class to get main args
- public @PostConstruct methods are supported in the application class
This way you can now do:
@SimpleLog @EnableServices("jaxrs") @Classes({ MyService.class, MyResource.class }) public class MyApp { @Module public WebApp app() { return new WebApp().contextRoot("/"); } public static void main(String[] args) { ApplicationComposers.run(MyApp.class, args); } }
This will start the application and deploy MyResource (of course I suppose it is a JAXRS service).
Microservice you said? :p
Hi,
is there an easy way to serve static content too or do I have to register my own servlet/jax-rs component to do so? (for example, testing an angularjs application + rest services)
Thanks,
Xavier
There is an in progress one – ie working for arquillian openejb embedded but not implemented for other cases – using EmbeddedServletContext.ResourceProvider. I didnt get timeyet to implement it for maven structured project but it is the way to go, I even think adding @Webresource to application composer, more to come 😉
Xavier, you can do this today with Spring Boot [1], which also supports JAX-RS, and supports all the good parts of Java EE: [1] http://spring.io/blog/2013/12/19/serving-static-web-content-with-spring-boot and [2] http://spring.io/blog/2014/11/23/bootiful-java-ee-support-in-spring-boot-1-2
@Xavier: https://issues.apache.org/jira/browse/TOMEE-1498
Nice! Thank you.
Does @WebResource work with classpath resources too? (“classpath:META-INF/resources” for example)
it is activated by default so no need to add @WebResource