Академический Документы
Профессиональный Документы
Культура Документы
www.jaxenter.com
TM
Originally developed under the name Web Beans, the CDI specification was created to fill the gaps between EJB on the back end, and JSF in the view layer. There are currently three CDI implementations available: JBoss Weld,
Apache OpenWebBeans, and Caucho CanDI. Several libraries already provide CDI extensions, such as Apache DeltaSpike, JBoss Seam 3, and Apache MyFaces CODI. Deep-dive into the world of CDI with this issue of JTJ.
CDI
CDI Introduction
A Hollywood Story
#16
Extension Programming
Implementing Additional Functionality with CDI
Apache DeltaSpike
Creating a CDI Extension
Arquillian
Testable Development of CDI
Editorial
Index
A Hollywood Story
3 9 14 18
CDI Introduction
A Hollywood Story
CDI brings Dependency Injection to a new level. Within the first two years of its existence, CDI quickly rose from an unknown little JSR to now being unanimously considered the star of Java EE 6. This article provides an overview of the major CDI features, explores a sample web application, and outlines some of the basic mechanisms of the framework.
CDI Introduction
Essential CDI
Before we go on to dive into some code, lets take a quick look at some key CDI features: Type Safety: Instead of injecting objects by a (string) name, CDI uses the Java type to resolve injections. When the type is not sufficient, a Qualifier annotation can be used. This allows the compiler to easily detect errors, and provides easy refactoring. POJOs: Almost every Java object can be injected by CDI! This includes EJBs, JNDI resources, Persistence Units and Persistence Contexts, as well as any object which previously would have been created by a factory method. Extensibility: Every CDI container can enhance its functionality by using portable Extensions. The attribute portable means that those CDI Extensions can run on every CDI container and Java EE 6 server, no matter which vendor. This is accomplished by a well-specified SPI (Service Provider Interface) which is part of the JSR-299 specification. Interceptors: It has never been easier to write your own Interceptors. Because of the portable behaviour of JSR-299, they now also run on every EE 6-certified server and on all standalone CDI containers. Decorators: These allow to dynamically extend existing interface implementations with business aspects. Events: CDI specifies a type-safe mechanism to send and receive events with loose coupling. Unified EL integration: EL-2.2 opens a new horizon in regard of flexibility and functionality. CDI provides outof-the-box support for it!
is also application-scoped. Here we see our first injection the configuration field is not set by the applications code, but is injected by CDI. The @Inject annotation tells CDI to perform the injection (listing 1). Our application also identifies the current user (note that it doesnt try to perform any authentication or authorization, we simply trust everyone!), and the current user is scoped to the HTTP session. CDI provides the session scope, which ensures that you will get the same instance of an object per HTTP Session (in a web application). By default, CDI beans are not available for use in JSF via the Unified Expression Language. In order to expose it for use by JSF and EL, we add the @Named annotation (listing 2). Now that we have a working application, lets explore some of the CDI features we are using.
Listing 2
@SessionScoped @Named public class User { public String getName() {..} .. } The web page is implemented with JSF 2. We suggest you use a controller class: @RequestScoped @Named public class Mail { private @Inject MailService mailService; private @Inject User user; private String text; // + getter and setter private String recipient; // + getter and setter public String sendMail() { mailService.send(user.getName(), recipient, text); return "messageSent"; // forward to 'message sent' JSF2 page } }
Lets start by exploring a sample web application. The application allows you to send e-mail via a web form fairly simple. We only provide code fragments, but they should be enough to get the gist of how CDI is used. After showing each part of the application, we will discuss the details in the next chapter. For our mail application, we need an application-scoped MailService. Application-scoped objects are essentially singletons the container will ensure you always get the same instance whenever you inject it into your application. The replyTo address is taken from the ConfigurationService which
Listing 1
@ApplicationScoped public class MyMailService implements MailService { private @Inject CongurationService conguration; public send(String from, String to, String body) { String replyTo = conguration.getReplyToAddress(); ... // send the email } }
The only missing part now is the JSF page itself, sendMail.xhtml: <h:form> <h:outputLabel value="Username" for="username"/> <h:outputText id="username" value="#{user.name}"/><br/> <h:outputLabel value="Recipient" for="recipient"/> <h:inputText id="recipient" value="#{mail.recipient}"/><br/> <h:outputLabel value="Body" for="body"/> <h:inputText id="body" value="#{mail.body}"/><br/> <h:commandButton value="Send" action="#{mail.send}"/> </h:form>
CDI Introduction
Basic mechanisms
Sometimes it's very helpful to look behind the scenes of a framework to understand at least the very basic mechanisms. Thus we like to explain a few of those techniques first. Dependency Injection: DI is sometimes described as the Hollywood Principle don't call us, we call you. It means that we let the container manage the creation of instances and inject it instead of creating them ourselves with the new operator. Of course, even in DI containers, nothing happens without anyone triggering this process. For a JSR-299 container, this trigger is a call to one of the method
T BeanManager#getReference(Bean<T> type, Qualier... qualiers);
Terminus Contextual Instance: Contextual Instances are exactly our singleton instances per scope, our session singletons, request singletons, etc. Usually a user never uses a Contextual Instance directly, but only via its Contextual Reference Terminus Contextual Reference: By default, a CDI container wraps all Contextual Instances via a proxy and only injects those proxies instead of the real instances. In the CDI specification those proxies are called Contextual Reference. There are plenty of reasons why CDI uses proxies by default: Serialisation: Instead of serialising the full object, we only need to serialise the proxy. On de-serialisation, it will automatically connect to the right Contextual Instance again. Scope differences: With proxies, it is possible to inject a @SessionScoped UserSettings into an @ApplicationScoped MailService because the Proxy will connect to the right UserSettings itself. Interceptors and Decorators: a proxy is the perfect way to implement interceptors and decorators in a non-intrusive way.
which returns an instance of the given type T. By doing so, it will not only create the returned instance, but also all of its @Inject-annotated children in a recursive way. Usually the getReference(Bean<T>) method is not called manually, but internally by the Expression Language Resolver. By writing something like
<h:inputText value="#{mailForm.text}"/>
the ELResolver will look for the Bean<T> with the name mailForm and resolve the instance. Scopes, contexts, singletons: Each of a CDI containers managed objects is a singleton in the original sense specified by Ward Cunningham, who invented not only the Wiki but also, together with Kent Beck, introduced design patterns into computer science in 1987. A singleton in this sense means that there is exactly one instance in a well-specified context. In a comment on Refactoring to Patterns by Joshua Kerievsky, Ward noted: There is a proper context for every computation. So much of object-oriented programming is about establishing context, about balancing the lifetimes of variables, causing them to live the right length of time and then die gracefully. CDI is all about creating singletons in a well-specified context. In our case, the lifecycle of instances is defined by their scopes. A @SessionScoped-annotated bean exists exactly once per session. We could also name it a session singleton. If a user accesses our @SessionScoped bean for the first time, it will get created and stored inside the session. Every subsequent access will return exactly this same instance. When the session ends, all the CDI-managed instances stored inside it will also be properly destroyed. If we have a @RequestScoped bean, we could call it a request singleton, a @ConversationScoped bean is a conversation singleton, etc. Terminus Managed Bean: There are a few terms used in the CDI specification which need a short explanation. The term Bean in Java is already pretty well-established and means a POJO (Plain Old Java Object) with getters and setters. The terminus technicus Managed Bean now means something completely different. It doesn't refer to instances of a class but meta-information which can be used to create those instances. It is represented by the interface Bean<T> and will be gathered on container startup via classpath scanning.
Standard scopes
JSR-299 defines the most important scopes to build classic web applications: @ApplicationScoped @SessionScoped @ConversationScoped @RequestScoped Those scopes are meta-annotated as @NormalScope which means they have a well-defined lifecycle. Beside those, there is another non-normal scope: @Dependent. If a class doesn't have any explicit CDI scope annotation or is explicitly annotated with @Dependent, an instance will be created for each and every InjectionPoint and will share the lifecycle of the contextual instances they get injected into. An example: if a @Dependent MySecurityHelper is injected in a @RequestScoped MyBackingBean, then the MySecurityHelper instance will be destroyed along with the MyBack-
CDI Introduction
ingBean instance at the end of the request. If you @Inject the MySecurityHelper into a @SessionScoped UserSettings object, it will also be treated as @SessionScoped.
Qualifiers
If an application needs multiple implementations of one and the same interface, this previously has been solved by giving them different names. The problem with this approach is that this string-based solution is not typesafe and can easily lead to ClassCastExceptions. The CDI specification introduced a typesafe way to achieve the same result with the @Qualifier meta-annotation. A small sample: An application needs to access two different databases with JPA. Thus we need two different EntityManagers. To distinguish between those, we just create two @Qualifier annotations @CustomerDb and @AdminDb (in analogue):
@Target( { TYPE, METHOD, PARAMETER, FIELD }) @Retention(RUNTIME) @Documented @Qualier public @interface CustomerDb {}
In many EE applications it makes sense to cache some information in the session. An example of such information would be user roles and privileges and the menu tree based on those rights. It's usually not necessary to perform this expensive calculation for each and every request. Rather, it can simply be stored in the session. One problem with this approach is that changing user settings during runtime e.g. when a user logs in temporarily as administrator or changes his view language is not easy. By using the CDI event system we can implement this in a very elegant way. Instead of manually cleaning up all depending information, we just send a UserSettingsChanged event and everyone who is interested can react accordingly. The event itself is typesafely represented by a class, which might also contain payload data:
public class UserSettingsChanged { public UserSettingsChanged(String userName) { this.userName = userName; } private String userName; // + getter und setter ... }
Those Qualifiers can now easily be used to inject the appropriate EntityManager:
public @ApplicationScoped class MyService { private @Inject @CustomerDb EntityManager customerEm; private @Inject @AdminDb EntityManager adminEm; ...
Let's now focus on the event source. For firing a UserSettingsChangedEvent, we first need to inject an event-source:
public class MyLoginBean { private @Inject Event<UserSettingsChanged> userChangedEvent; ... public boolean login(String username, String password) { .. do the login stuff userChangedEvent.re(new UserSettingsChanged(username)); ... } }
Producer Methods
In the previous example, we knowingly omitted how those EntityManagers will be created. One possibility would be to use Producer Methods (listing 3). We create @RequestScoped EntityManagers, since an EntityManager is per definition not serialisable thus we cannot store it in the session. We also need to implement a way to properly clean up the EntityManagers at the end of each request. This can be done with disposal methods, using the @Disposes annotation:
// class MyEntityManagerProducers continued public void disposeUdEm(@Disposes @UserData EntityManager em) { em.close(); } public void disposeBoEm(@Disposes @BackOfce EntityManager em) { em.close(); } }
Any class which needs to react on this event can now comfortably observe it via an observer method:
Listing 3
public class MyEntityManagerProducers { @Produces @RequestScoped @CustomerDb public EntityManager createCustomerDbEm() { return Persistence.createEntityManagerFactory(customerDb). createEntityManager(); } @Produces @RequestScoped @AdminDb public EntityManager createAdminEm() { return Persistence.createEntityManagerFactory("adminDb"). createEntityManager(); } ...
Events
The CDI specification defines a flexible but very easily usable eventing mechanism based on the Observer/Observable pattern.
CDI Introduction
public @SessionScoped class MyBackingBean { Locale userLanguage; ... public void refreshLanguage(@Observes UserSettingsChanged usc) { userLanguage = getDefaultLanguageOfUser(usc.getUserName()); } ... }
The second part is the interceptor implementation itself. This class must be annotated as @Interceptor and additionally with its intended interceptor binding. The interceptor functionality itself is implemented in a method which gets annotated as @AroundInvoke (listing 4).
If the UserSettingsChange event gets fired, all observer methods of beans in currently active scopes will get invoked.
Final thoughts
After two years of availability, CDI is already seeing wide adoption. It has proven itself in a broad range of projects, from providing productivity in small start-ups to offering reliability and scalability websites with millions of users per day. The Expert Group is currently working actively on CDI 1.1, a specification which will bring small fixes and improvements, including much-requested standardization of Java SE bootstrap functionality for non-web applications.
Interceptors
CDI provides an easy way to create own custom interceptors, as we will show by creating our own @Transactional interceptor. Instead of having to manage the transactions manually, our small interceptor will do this for us as shown in the following usage example:
@ApplicationScoped public class MyUserService { private @Inject EntityManager em; @Transactional public storeUser(User u) { em.persist(u); } }
For implementing this feature, we need to provide two parts. The first one is obviously the annotation itself. It is metaannotated as @InterceptorBinding which marks it as an annotation that is intended to be used for interceptors:
@Retention(RetentionPolicy.RUNTIME) @Target( { ElementType.TYPE, ElementType.METHOD })
Listing 4
@Interceptor @Transactional public class TransactionalInterceptor { private @Inject EntityManager em; @AroundInvoke public Object invoke(InvocationContext context) throws Exception{ EntityTransaction t =em.getTransaction(); try { if(!t.isActive()) t.begin(); return context.proceed(); } catch(Exception e) { .. rollback and stuff } nally { if(t != null && t.isActive()) t.commit(); } } }
Mark Struberg is a software architect with over 20 years of programming experience. He has been working with Java since 1996 and is actively involved in open source projects in the Java and Linux area. He is Apache Software Foundation member and serves as PMC and Committer for Apache OpenWebBeans, MyFaces, Maven, OpenJPA, BVal, DeltaSpike and other projects. He is also a CDI Expert Group member actively working on the specification. Mark works for the Research Group for Industrial Software (INSO) at the Vienna University of Technology.
Pete leads the Seam, Weld and CDI TCK projects, is an adviser to the RichFaces project, and is a founder of the Arquillian project. He has worked on a number of specifications including JSF 2.0, AtInject and CDI. He is a regular speaker at JUGs and conferences such as Devoxx (Javapolis), JAX, JavaBlend, JSFDays and JBoss World. Pete is currently employed by Red Hat Inc. working on JBoss open source projects. Before working for Red Hat, he used and contributed to Seam whilst working at a UK based staffing agency as IT Development Manager.
CDI Introduction
p r e s e nt s
Developer.Class presents JAX Days. This unique two day training and workshop event is crammed with quality, in-depth training and hands-on Java development activities. Dont miss this amazing opportunity to update your knowledge and introduce yourself to relevant and exciting new topics.
J D UG A 2
Pragmatic Architecture
with Ted Neward
Advanced Neo4j
with Ian Robinson & Jim Webber
CDI Extensions
The first task a CDI container performs is to load all CDI extensions. Next, all of the classes contained within JAR files that have a META-INF/beans.xml marker file will be scanned also. For each of these classes an AnnotatedType will be constructed based on the given class. This object contains metadata about all annotations, constructors, methods, fields, etc. for the processed class. This information can be modified by extensions. Later the CDI container will expose this metadata in Bean<T> instances used to manage contextual objects. The container will start all available contexts after all classes have been scanned and all constraints have been verified.
CDI Extensions
The Container will pick up our extension via the Java ServiceLoader mechanism. Simply place a file named javax.enterprise.inject.spi.Extension (the same name as the interface) within the META-INF/services folder of your JAR and ensure this file contains the fully-qualified name of the Extension implementation. The container will look up the name of the class at runtime and instantiate it via the default constructor (therefore the class needs a default constructor). Before we delve into the content of the extension, we will first look into Quartz, the scheduler we want to integrate with, and how Quartz would be used without our extension.
It would be trivial to change this annotation to enable more advanced features like setting the schedule via dynamic configuration rather than hard-coding it in the jobs source; the annotation needs only to hold the information necessary to let our extension decide how to configure the job. Now its time to look at the extension itself.
Listing 1
import static org.quartz.JobBuilder.*;2 import static org.quartz.TriggerBuilder.*; import static org.quartz.CronScheduleBuilder.*; ... SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory(); Scheduler scheduler = schedFact.getScheduler(); JobDetail job = newJob(MyJob.class) .withIdentity("myJob") .build(); Trigger trigger = newTrigger() .withIdentity("myTrigger") .withSchedule(cronSchedule("0 0/10 * * * ?")) .forJob("myJob") .build(); scheduler.scheduleJob(job, trigger);
Listing 2
@Scheduled("0 0/10 * * * ?") public class MyJob implements Runnable { @Inject private MyService service; @Override public void run() { service.doSomething(); } }
10
CDI Extensions
can not only gather information from the CDI container this way; it can also modify this information and even transmit back new information to the container. As a rule of thumb, all things possible via CDI annotations can also be performed programmatically in an extension. You can very easily add or modify scopes, Interceptors, Decorators, InjectionPoints, Producer methods, ObserverMethods, etc. The defined container lifecycle events are: BeforeBeanDiscovery ProcessAnnotatedType ProcessInjectionTarget and ProcessProducer ProcessBean and ProcessObserverMethod AfterBeanDiscovery AfterDeploymentValidation BeforeShutdown The first article of this series described how to observe custom CDI events. Extensions can observe the container system events in exactly the same way. To find every class that has the @Scheduled annotation, the extension should observe the ProcessAnnotatedType event. This system event is fired for every class scanned by the container during boot. A Quartz job and Quartz trigger can be created and started after the container initialization is complete for any such annotation found (Listing 3). An observer for the BeforeBeanDiscovery event which is fired before the scanning process starts can be used to initialize the scheduler.
public void initScheduler(@Observes BeforeBeanDiscovery event) { scheduler = StdSchedulerFactory.getDefaultScheduler(); }
public void startScheduler(@Observes AfterDeploymentValidation event, BeanManager bm) { beanManager = bm; try { scheduler.start(); LOG.info("Started scheduler."); } catch (SchedulerException se) { throw new RuntimeException(se); } }
Finally, the shutdown() method of the scheduler is called in an observer of the BeforeShutdown event.
public void shutdownScheduler(@Observes BeforeShutdown event) { try {
Listing 3
public void scheduleJob(@Observes ProcessAnnotatedType pat) { AnnotatedType t = pat.getAnnotatedType(); Scheduled schedule = t.getAnnotation(Scheduled.class); if (schedule == null) { //no scheduled job, ignoring this class return; } Class<Runnable> jobClass = t.getJavaClass().asSubclass(Runnable.class); if (jobClass == null) { LOG.error("Can't schedule job " + t); return; } JobDetail job = newJob(CdiJob.class) .usingJobData(CdiJob.JOB_CLASS_NAME, jobClass.getName()) .build(); Trigger trigger = newTrigger() .withSchedule(cronSchedule(schedule.value())) .build(); scheduler.scheduleJob(job, trigger); }
The scheduler also has to be started. This can be done in an AfterDeploymentValidation event observer. That event is fired after the container has validated that there are no deployment problems, so all jobs will be scheduled at this point. Additionally, the BeanManager can be stored in the Extension for later use (e.g. in the CdiJob class).
Listing 4
public class CdiJob implements org.quartz.Job { public nal static String JOB_CLASS_NAME = CDI_JOB_CLASS_NAME; @Override public void execute(JobExecutionContext context) throws JobExecutionException { JobDataMap jobData = context.getJobDetail().getJobDataMap(); String className = jobData.getString(JOB_CLASS_NAME); Class<Runnable> jobClass; try { jobClass = Class.forName(className).asSubclass(Runnable.class); } catch (ClassNotFoundException e) {
throw new JobExecutionException(e); } BeanManager bm = QuartzExtension.getBeanManager(); Set<Bean<?>> jobBeans = bm.getBeans(jobClass); Bean<?> jobBean = bm.resolve(jobBeans); CreationalContext c = bm.createCreationalContext(jobBean); Runnable job = (Runnable) bm.getReference(jobBean, Runnable.class, c); try { job.run(); } nally { jobBean.destroy(job, c); } } }
11
CDI Extensions
Summary
One of the most important features of CDI is the possibility to create extensions. With this mechanism, CDI can be extended in a portable, vendor-independent way to provide new features (like custom scopes), implement application-specific functionality (like loading configuration from an external database) or to integrate other technologies in a CDI-like fashion.
Ronald Steininger is a Senior Software Engineer for the Research Group for Industrial Software (INSO) at the Vienna University of Technology. He spent the last six years working with Java on both rich clients and web applications. He and his colleagues use CDI and Java EE 6 for all of their projects since late 2009. He spends his spare time working on his master thesis about the software architecture of campus management systems. Arne Limburg is Enterprise Architect at open knowledge GmbH in Oldenburg, Germany. He is an experienced developer, architect and trainer in the Enterprise environment and also regularly involved in Android development. He frequently speaks at conferences and conducts workshops in those fields. He is also an active participant in open source projects, e.g. as a committer of Apache OpenWebBeans and as initiator and Project Lead of JPA Security. Mark Struberg is a software architect with over 20 years of programming experience. He has been working with Java since 1996 and is actively involved in open source projects in the Java and Linux area. He is Apache Software Foundation member and serves as PMC and Committer for Apache OpenWebBeans, MyFaces, Maven, OpenJPA, BVal, DeltaSpike and other projects. He is also a CDI Expert Group member actively working on the specification. Mark works for the Research Group for Industrial Software (INSO) at the Vienna University of Technology.
The following class does the work needed, so that a CDImanaged instance of the Runnable (our actual job) we defined is run as a Quartz job. 1. It extracts the job class from the Quartz configuration. 2. Before calling the run() method, it receives the actual CDI-managed instance from the BeanManager. 3. After the run() method returns, the instance is destroyed (Listing 4). Our extension sets up all scheduled jobs to use this class, so that all of this happens in a manner completely transparent to the user. Keep in mind that contexts like the RequestContext and SessionContext are not active for this job (there is no active request nor session). If the injected services need these contexts because they depend on beans like a RequestScoped EntityManager, the contexts can be started and stopped in @PostConstruct and @PreDestroy methods of the job. This is container-specific at the moment, but work is underway within the Apache DeltaSpike project to make this possible in a vendor-independent way.
Imprint
Publisher Software & Support Media GmbH Editorial Office Address Darmstdter Landstrae 108 60598 Frankfurt am Main Germany www.jaxenter.com Editor in Chief: Sales: Cindy Ng +44 (0)20 7401 4837 cindyn@sandsmedia.com Entire contents copyright 2012 Software & Support Media GmbH. All rights reserved. No part of this publication may be reproduced, redistributed, posted online, or reused by any means in any form, including print, electronic, photocopy, internal network, Web or any other method, without prior written permission of Software & Support Media GmbH The views expressed are solely those of the authors and do not reflect the views or position of their firm, any of their clients, or Publisher. Regarding the information, Publisher disclaims all warranties as to the accuracy, completeness, or adequacy of any information, and is not responsible for any errors, omissions, in adequacies, misuse, or the consequences of using any information provided by Pub lisher. Rights of disposal of rewarded articles belong to Publisher. All mentioned trademarks and service marks are copyrighted by their respective owners.
Sebastian Meyen Claudia Frhling, Diana Kupfer Dan Allen, Aslak Knutsen, Arne Limburg, Pete Muir, Gerhard Petraczek, Jason Porter, Andrew L. Rubinger, Ronald Steininger, Mark Struberg Nicole Bechtel, Lisa Pychlau Jens Mainz Tobias Dorn
12
Announcing the
www.jaxconf.com
Apache DeltaSpike
of an application server to avoid vendor lock-in. Java EE 6 is the first Java EE specification which doesnt need an additional framework for painless usage or for replacing manual integrations of different parts of the platform. At least thats a common message of some community members. Java EE 6 is closer to this statement than the former versions. However, it turns out the real world isnt that shiny. A lot of new innovations still happen in the Open Source space first and some features might even be too special to be included into a specification. Therefore it was intended to provide a rich set of extension points from the very beginning. That allows to keep specifications in general and CDI in particular consistent and simple without restricting custom approaches and mechanisms. As mentioned earlier, CDI is provided out-of-the-box by Java EE 6+ servers and therefore a very important aspect of CDI extensions is portability which allows them to be used seamlessly in combination with all implementations of the CDI specification. Apache OpenWebBeans and JBoss Weld are such CDI implementations and they allow to use CDI even with plain Java SE. Therefore, neither CDI nor portable CDI extensions are restricted to Java EE. That allows to create different types of CDI extensions. Apache DeltaSpike is such a collection of portable CDI extensions. To understand the roots of DeltaSpike its important to look at two famous portable CDI extensions - JBoss Seam3 and Apache MyFaces ExtCDI (aka MyFaces CODI).
JBoss Seam3
Seam 3 started shortly after JSR 299 completed. The goal of Seam 3 was not to be a complete migration of all things from Seam 2, but it provides a rich toolbox for building standardsbased web applications tailored for traditional and cloud deployments. Certainly some ideas came from Seam 2, but many more came from the community. Unlike the starting
14
Apache DeltaSpike
of Seam 1 and 2, Seam 3 did not simply want to improve JSF support, but to unite many technologies with CDI. Many of the modules developed in Seam 3 demonstrate this such as Mail, JMS, JCR, Reports and Spring-Integration. All of these modules came from the community as a hole was discovered while using the technologies and seeking a more unified and simpler approach for developers. Other areas in developer productivity were explored in Seam 3 such as type safe logging in Solder, exception handling also in Solder though formerly Seam Catch, XML configuration via Seam Config, now in Solder.
part of the community decided to start a shared CDI extensions project developed and maintained by the community. The Apache Software Foundation was chosen due to its reputation concerning community-driven and vendor-independent projects. Amongst many others, the project management committee of Apache MyFaces was excited about this idea, and the lead developers of MyFaces CODI helped to draft a proposal for such a new Apache project. The hardest part was the name for the project. After some discussions the upcoming community agreed on the intermediate name DeltaSpike. With this name, the community entered the Apache Incubator, which is the entry point for all new Apache projects. Before the project leaves the incubator and graduates to an Apache top level project, the community will vote a final project-name. The initial committers of the DeltaSpike community are representatives of several existing CDI communities which joined the effort from the very beginning. Aside from some other important tasks, the community merges features of the participating extension projects, adds new features and grows a community before DeltaSpike becomes a top level project and leaves the Apache Incubator. Before a feature gets added to DeltaSpike it is discussed by the community.
Progressing fast
Two months after the first e-mail on the mailing list [1], DeltaSpike v0.1 incubating was announced. With this first step, the community started to merge features of Apache MyFaces CODI-Core and JBoss Solder which is a central part of Seam3. Every DeltaSpike release is well tested on multiple containers and many real world projects. Nonetheless it's expected that even APIs will change until version 1.0 based on the feedback of the community and new features and bug fixes will be added quickly. In the next release, the community will add further DeltaSpike-Core features as well as new modules. Besides reviewing, discussing and adding features to DeltaSpike, the community is also working on a great documentation, examples, as well as a test-suite which should ensure true and tested portability across different environments.
15
Apache DeltaSpike
several integration modules for Java EE technologies like JSF, JPA, REST, JMS as well as other frameworks like Spring and Quartz. The goal regarding JBoss Seam3 and Apache MyFaces CODI is to add as much as the majority can agree on. Since the community is optimistic to add and/or merge most features in/into DeltaSpike, it is expected to be easy for users of the original extension projects to migrate to DeltaSpike. Instead of using workarounds for the migration, users are invited to join the effort early and share use cases which might not be supported at the time they try to switch to the corresponding module of DeltaSpike. In the end they should see an even better approach they can use. Version 1.0 should be fast, thoroughly tested, very stable and compatible with all major server platforms which support CDI. The DeltaSpike team is already very active, and all of us are confident that it will grow as fast as DeltaSpike itself to reach the goal of a stable, reliable and truly portable CDI extension and even more important: a great community.
Gerhard Petracek is Apache MyFaces committer and PMC chair; Apache OpenWebBeans committer and PMC member; Apache DeltaSpike committer, PPMC member and mentor; Apache member; Bean-Validation expert group member and a founder of Apache MyFaces Extensions Validator, Apache MyFaces CODI and Apache DeltaSpike. At IRIAN Solutions Gerhard is responsible for Java-EE trainings and the development of modern web-applications for customers such as Credit-Suisse. Furthermore, he holds lectures at an Austrian University of Applied Sciences and is the author of several articles on JSF and Java-EE. Jason Porter is a software engineer currently working in the Java Enterprise Edition Space and Seam at Red Hat. His specialties include JBoss AS, Seam, CDI, JSF, Java EE, Gradle. He has worked with PHP, Ruby (both stand-alone and Rails), Groovy, XSLT the rest of the web language arena (HTML, CSS, JS, etc). His current position as Senior Software Engineer at Red Hat has him work primarily on Seam, however, he also contributes to JBoss Forge, Arquillian and Apache DeltaSpike.
16
JOIN US FOR 4 DAYS CRAMMED FULL OF THE BEST JAVA CONTENT, SPEAKERS, DISCUSSIONS AND NETWORKING
July 9 12, San Francisco, CA
www.jaxconf.com
Java Core Android Java EE Spring Languages Cloud Agile HTML5 Web Architecture Continous Delivery JavaScript Open Source Tools & Framework Java Performance
Technical presentations and tutorials In-depth coverage of the latest technologies Practical implementation techniques Neutral coverage of the most important Java Ecosystem topics
Arquillian
Assumptions
The easiest way to get started with Arquillian is to incorporate it into the test suite of a project build that offers dependency management. Today, the most widely-used build tool in
Respond to the prompts by entering the value shown after each double colon below. Hit the Enter key after each line (as indicated by <ENTER>).
18
Arquillian
Define value for property groupId: : org.arquillian.example <ENTER> Define value for property artifactId: : arquillian-tutorial <ENTER> Define value for property version: : <ENTER> Define value for property package: : <ENTER> Confirm properties configuration: groupId: org.arquillian.example artifactId: arquillian-tutorial version: 1.0-SNAPSHOT package: org.arquillian.example Y: : <ENTER> This command generated a Maven-based Java project inside a new folder named arquillian-tutorial under the current directory. The file structure of the project is shown below:
src/ main/ java/ Place all application Java source files here (under Java package) resources/ Place all application configuration files here test/ java/ Place all test Java source files heres (under Java package) resources/ Place all test configuration files here (e.g., arquillian.xml) pom.xml The Maven build file. Tells Maven how your project should be built. The project is preconfigured to use Java 6 and JUnit 4.8, the minimum required versions of Java and JUnit for using Arquillian, respectively. The generator also created a Java package named org. arquillian.example underneath the two java folders. You should put your Java source files in this package rather than at the root of the java folder. Arquillian also supports TestNG 5. However, we will be using JUnit throughout this guide. Go ahead and open up the pom.xml in your editor. You should see an XML file containing basic project information, a build section and a dependencies section. You can remove all the elements below the JUnit dependency as they arent required. After making the change, you should end up with the contents seen in listing 1 (trimmed for brevity). Were going to be writing Java EE 6 components. Therefore, we also need to add the Java EE 6 API to the classpath so we can compile these components. Open up the pom.xml file once again and add the following XML fragment directly inside the <dependencies> element. Listing 2 shows how the section should look once youre done. We strongly recommend that you do not use the Java EE API artifact with coordinatesjavax:javaee-api. That bundle contains classes with stripped method bodies, which will cause your application to throw strange Absent Code errors if left on the classpath at runtime (even when running tests). Read the FAQ under [5] if you want more background. The foundation of your project is now ready! Skip to the next section to open the project in Eclipse [6] so we can start writing some code!
Listing 1
pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.arquillian.example</groupId> <artifactId>arquillian-tutorial</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <name>arquillian-tutorial</name> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> <conguration> <source>1.6</source> <target>1.6</target> </conguration> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.8.1</version> <scope>test</scope> </dependency> </dependencies> </project>
19
Arquillian
On Unix-based operating systems, adding Forge to your path typically means editing your $HOME/.bashrc or $HOME/.profile; you will need to set the following environment variables:
$ export FORGE_HOME=$HOME/forge/ $ export PATH=$PATH:$FORGE_HOME/bin On Windows, you will need to right-click on Control Panel, then click System Properties, open the Advanced tab, then click Environment Variables and add these two entries visually. We recommended setting User variables for Forge, unless you have placed the unzipped distribution in a folder where all users can access it. Now that Forge is installed (i.e., extracted), open a command prompt (a shell) and run the forge command (figure 1):
$ forge [no project] ~ $
src/ main/ java/ Place all application Java source files here (under Java package) resources/ Place all application configuration files here META-INF/ forge.xml An empty Forge settings file test/ java/ Place all test Java source files heres (under Java package) resources/ Place all test configuration files here (e.g., arquillian.xml) pom.xml The Maven build file. Tells Maven how your project should be built. Forge also makes the project folder your current directory within the Forge shell.
[arquillian-tutorial] arquillian-tutorial $
Thats it! Youve got Forge up and running. Now its time to create the project. Inside the Forge shell, execute the following command to create a blank project, much like we created a project using the Maven Archetype above:
$ new-project --named arquillian-tutorial --topLevelPackage org.arquillian.example
By default, Forge sets up the project to use Java 1.6, the minimum required version of Java to use Arquillian, a nice convenience. What we need to add now is the Java EE APIs. Thats done using the project add-dependency command below:
$ project add-dependency org.jboss.spec:jboss-javaee-6.0:1.0.0.Final:pom:provided
You will also need to add JUnit 4.8, the minimum required version of JUnit to use Arquillian, as a test-scoped dependency:
$ project add-dependency junit:junit:4.8.1:test
This command generates a Maven-based Java project inside a new folder named arquillian-tutorial under the current directory. The file structure of the project Forge generates is shown below:
Listing 2
pom.xml <!-- clip --> <dependencies> <dependency> <groupId>org.jboss.spec</groupId> <artifactId>jboss-javaee-6.0</artifactId> <version>1.0.0.Final</version> <type>pom</type> <scope>provided</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.8.1</version> <scope>test</scope> </dependency> </dependencies> <!-- clip -->
The result of the pom.xml that Forge generates is shown in Listing 3. Arquillian is now distributed in the Maven Central repository, so the JBoss Public repository declaration in the pom.xml is unnecessary and can be removed. Keep in mind, though, you may need it to retrieve other JBoss software not available in Maven Central. If you follow the camp that consider repositories explicitly defined in your projects pom.xml to be an antipattern, then read the instructions under [8] to enable the repository globally in your settings.xml file. The foundation of your project is now ready! Lets now open the project in Eclipse so we can start writing some code!
20
Arquillian
1. Select Help | Eclipse Marketplace... from the main menu 2. Type jboss tools in the Find input field (no quotes) and press Enter 3. Click the Install button next to JBoss Tools (Indigo) 4. Complete the install wizard, then restart Eclipse if prompted JBoss Tools provides a nice environment for developing Java EE applications, including excellent CDI support. Dont worry, its not a heavyweight plugin.
However, if you just want the Maven integration without the extras that JBoss Tools brings, you can follow these steps instead: 1. Select Help | Eclipse Marketplace... from the main menu 2. Type maven in the Find Fig. 2: Eclipse will recognize and open the Maven project in the input field (no quotes) and Project Navigator view. press Enter 3. Click the Install button next to Maven Integration for Eclipse 4. Complete the install wizard, then restart Eclipse if prompted 5. Repeat the steps to install the Maven Integration for Eclipse WTP Once you have the Maven Integration plugin installed, follow these steps to open the project: 1. Select File | Import... from the main menu 2. Type existing maven in the input source field 3. Select the option Existing Maven Projects, then click the Next button 4. Click the Browse button 5. Navigate the project folder on your filesystem, then click the OK button 6. Click the Finish button to open the project Eclipse will recognize the Maven project and open it in the Project Navigator view. If you expand the project, it should look similar to figure 2. Now we can really get down to business!
Listing 3
pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation=" http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> <groupId>org.arquillian.example</groupId> <artifactId>arquillian-tutorial</artifactId> <version>1.0.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.jboss.spec</groupId> <artifactId>jboss-javaee-6.0</artifactId> <version>1.0.0.Final</version> <type>provided</type> <scope>pom</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.8.1</version> <scope>test</scope> </dependency> </dependencies> <repositories> <repository> <id>JBOSS_NEXUS</id> <url>http://repository.jboss.org/nexus/content/groups/public</url> </repository> </repositories> <build> <nalName>arquillian-tutorial</nalName> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> <conguration> <source>1.6</source> <target>1.6</target> </conguration> </plugin> </plugins> </build> </project>
Create a component
In order to write an Arquillian test, we need to have a component for it to test. Lets begin by creating a basic component so that you can learn how to execute an Arquillian test without other distractions. Well gradually move to more complex scenarios. In your IDE, create a new Java class named Greeter in the org.arquillian.example package. Replace the contents of the file with this greeter logic (Listing 4). We want to verify that this class behaves properly when invoked as a CDI bean. Of course, we could simply write a unit test. But lets pretend that the bean uses enterprise services such as dependency injection and messaging and must be used inside a container. (Besides, that way we give it room to grow). To use the class as a CDI bean, well be injecting it into the test using the @Inject annotation. That calls for an Arquillian test, which means its time to add the Arquillian API to the project!
21
Arquillian
XML fragment directly above the <build> element to import the BOM, or version matrix, for Arquillians transitive dependencies.
pom.xml <!-- clip --> <dependencyManagement> <dependencies> <dependency> <groupId>org.jboss.arquillian</groupId> <artifactId>arquillian-bom</artifactId> <version>1.0.0.CR7</version> <scope>import</scope> <type>pom</type> </dependency> </dependencies> </dependencyManagement> <!-- clip -->
can download the file from the gist [11]. Youre all set to write your first Arquillian test!
Next, append the following XML fragment directly under the last <dependency> element to add the Arquillian JUnit integration:
pom.xml <!-- clip --> <dependency> <groupId>org.jboss.arquillian.junit</groupId> <artifactId>arquillian-junit-container</artifactId> <scope>test</scope> </dependency> <!-- clip -->
Now, about that flair. An Arquillian test case must have three things: 1. A @RunWith(Arquillian.class) annotation on the class 2. A static method annotated with @Deployment that returns a ShrinkWrap archive 3. At least one method annotated with @Test The @RunWith annotation tells JUnit to use Arquillian as the test controller. Arquillian then looks for a static method annotated with the @Deployment annotation to retrieve the test archive (i.e., micro-deployment). Then some magic happens and each @Test method is run inside the container environment.
The Arquillian JUnit integration artifact also adds the Arquillian and ShrinkWrap APIs to the test classpath. You need all of these libraries to write and compile a JUnit Arquillian test. To use TestNG instead of JUnit, substitute the Arquillian JUnit integration with the Arquillian TestNG integration. If youre having trouble with the pom.xml up to this point, you
Listing 4
src/main/java/org/arquillian/example/Greeter.java package org.arquillian.example; import java.io.PrintStream; /** * A component for creating personal greetings. */ public class Greeter { public void greet(PrintStream to, String name) { to.println(createGreeting(name)); } public String createGreeting(String name) { return "Hello, " + name + "!"; } }
22
Arquillian
Using ShrinkWrap, weve defined a Java archive (jar) as the deployment that includes the Greeter class that the test will invoke and an empty beans.xml in the META-INF directory to activate CDI in this archive. Now all we need to do is inject the Greeter instance into a field directly above the test method and replace the unimplemented test method with one that asserts the behavior of the bean. To give you that warm fuzzy feeling, well also print the greeting to the console.
src/test/java/org/arquillian/example/GreeterTest.java // clip @Inject Greeter greeter; @Test public void should_create_greeting() { Assert.assertEquals("Hello, Earthling!", greeter.createGreeting("Earthling")); greeter.greet(System.out, "Earthling"); }
Listing 6 shows how the test should look when youre done. Youve written your first Arquillian test! Ah, but youre probably wondering how to run it. If youre thinking, Just like a unit test youre correct! However, we first need to add a container adapter to the classpath.
Listing 6
src/test/java/org/arquillian/example/GreeterTest.java package org.arquillian.example; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.junit.Arquillian; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.EmptyAsset; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.junit.Test; import org.junit.Assert; import org.junit.runner.RunWith; @RunWith(Arquillian.class) public class GreeterTest { @Deployment public static JavaArchive createDeployment() { return ShrinkWrap.create(JavaArchive.class) .addClass(Greeter.class) .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"); } @Inject Greeter greeter; @Test public void should_create_greeting() { Assert.assertEquals("Hello, Earthling!", greeter.createGreeting("Earthling")); greeter.greet(System.out, "Earthling"); } }
Listing 5
src/test/java/org/arquillian/example/GreeterTest.java package org.arquillian.example; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.junit.Arquillian; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.EmptyAsset; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(Arquillian.class) public class GreeterTest { @Deployment public static JavaArchive createDeployment() { return ShrinkWrap.create(JavaArchive.class) .addClass(Greeter.class) .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"); } @Test public void should_create_greeting() { Assert.fail("Not yet implemented"); } }
23
Arquillian
When you run the test, you should see the following lines printed to the console:
26 [main] INFO org.jboss.weld.Version WELD-000900 1.1.1 (Final) Hello, Earthling! You should then see the JUnit view appear, revealing a green bar (g. 4)!
You can also run the test on the commandline using Maven:
$ mvn test
------------------------------------------------------TESTS ------------------------------------------------------Running org.arquillian.example.GreeterTest 21 [main] INFO org.jboss.weld.Version - WELD-000900 1.1.1 (Final) Hello, Earthling! Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.858 sec Congratulations! Youve earned your rst green bar with Arquillian!
Fig. 4: JUnit view revealing a green bar
A closer look
How do you know that CDI really worked? For all you know, Arquillian created a new instance of theGreeter class and injected it into the test without any involvement from CDI. Lets prove its there. Create a new CDI bean named PhraseBuilder in the org.arquillian.example package that can create phrases from templates. Next, open up the Greeter class and create a new constructor that will inject PhraseBuilder using constructor injection. Then, delegate the task of creating the greeting to the injected bean (listing 9) Now, in order for the test to work, an instance of PhraseBuilder must be created, its @PostConstructmethod invoked
Were using an embedded container in this example, so we need the container runtime, Weld. Now back to the test.
Listing 7
pom.xml <!-- clip --> <dependency> <groupId>org.jboss.arquillian.container</groupId> <artifactId>arquillian-weld-ee-embedded-1.1</artifactId> <version>1.0.0.CR3</version> <scope>test</scope> </dependency> <dependency> <groupId>org.jboss.weld</groupId> <artifactId>weld-core</artifactId> <version>1.1.1.Final</version> <scope>test</scope> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.5.10</version> <scope>test</scope> </dependency> <!-- clip -->
Listing 8
src/main/java/org/arquillian/example/PhraseBuilder.java package org.arquillian.example; import java.text.MessageFormat; import java.util.HashMap; import java.util.Map; import javax.annotation.PostConstruct; public class PhraseBuilder { private Map<String, String> templates; public String buildPhrase(String id, Object... args) { return MessageFormat.format(templates.get(id), args); } @PostConstruct public void initialize() { templates = new HashMap<String, String>(); templates.put("hello", "Hello, {0}!"); } }
24
Arquillian
and must be injected into the constructor of Greeter when an instance of Greeter is created. We can be certain CDI is at work if all that comes together. One last step. Because we created a new class, we must be sure that its getting added to the archive returned by the @ Deployment method in the test. Simply change the line:
.addClass(Greeter.class) ...to read: .addClasses(Greeter.class, PhraseBuilder.class)
Run the test again. You should get another green bar! Feels good, doesnt it?
But! Is the embedded container telling the whole story? Will the component work if running inside a full container? One of the perks of Arquillian is that you can run the same test in different compatible containers, whether its another embedded container or a standalone container. If you intend to use multiple containers, read on.
Listing 9
src/main/java/org/arquillian/example/Greeter.java package org.arquillian.example; import java.io.PrintStream; import javax.inject.Inject; public class Greeter { private PhraseBuilder phraseBuilder; @Inject public Greeter(PhraseBuilder phraseBuilder) { this.phraseBuilder = phraseBuilder; } public void greet(PrintStream to, String name) { to.println(createGreeting(name)); } public String createGreeting(String name) { return phraseBuilder.buildPhrase("hello", name); } }
25
Arquillian
Listing 11
pom.xml <!-- clip --> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.jboss.arquillian.junit</groupId> <artifactId>arquillian-junit-container</artifactId> <scope>test</scope> </dependency> </dependencies> <proles> <prole> <id>arquillian-weld-ee-embedded</id> <dependencies> <dependency> <groupId>org.jboss.spec</groupId> <artifactId>jboss-javaee-6.0</artifactId> <version>1.0.0.Final</version> <type>pom</type> <scope>provided</scope> </dependency> <dependency> <groupId>org.jboss.arquillian.container</groupId> <artifactId>arquillian-weld-ee-embedded-1.1</artifactId> <version>1.0.0.CR3</version> <scope>test</scope> </dependency> <dependency> <groupId>org.jboss.weld</groupId> <artifactId>weld-core</artifactId> <version>1.1.1.Final</version> <scope>test</scope> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.5.10</version> <scope>test</scope> </dependency> </dependencies> </prole> </proles> <!-- clip -->
Listing 10
pom.xml <!-- clip --> <proles> <prole> <id>arquillian-weld-ee-embedded</id> <dependencies> <dependency> <groupId>org.jboss.spec</groupId> <artifactId>jboss-javaee-6.0</artifactId> <version>1.0.0.Final</version> <type>pom</type> <scope>provided</scope> </dependency> <dependency> <groupId>org.jboss.arquillian.container</groupId> <artifactId>arquillian-weld-ee-embedded-1.1</artifactId> <version>1.0.0.CR3</version> <scope>test</scope> </dependency> <dependency> <groupId>org.jboss.weld</groupId> <artifactId>weld-core</artifactId> <version>1.1.1.Final</version> <scope>test</scope> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.5.10</version> <scope>test</scope> </dependency> </dependencies> </prole> </proles> <!-- clip -->
26
Arquillian
Listing 12
pom.xml
<!-- clip --> <prole> <id>arquillian-glasssh-embedded</id> <dependencies> <dependency> <groupId>org.jboss.arquillian.container</groupId> <artifactId>arquillian-glasssh-embedded-3.1</artifactId> <version>1.0.0.CR2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.glasssh.extras</groupId> <artifactId>glasssh-embedded-all</artifactId> <version>3.1</version> <scope>provided</scope> </dependency> </dependencies> </prole> <!-- clip -->
Fig. 7: Maven profile selector dialog showing the profile we have activated
Figure 7 displays the Maven profile selector dialog showing the profile weve activated. Once you have activated the profile, you should be able to run the test again successfully. You already know the test works in Weld EE Embedded. Lets switch to GlassFish Embedded by repeating the steps above, this time activating only the arquillian-glassfish-embedded profile. Run the test again. You should see GlassFish start in the consoleand another green bar! Youve now run the same test on two different embedded containers, a CDI container (Weld) and a Java EE container (GlassFish). Both of these executions are in process. To really be sure the component works in a pure environment, we need to use a standalone container. Lets switch to using JBoss AS. To run the test on a standalone instance of JBoss AS, you first need to set it up. You can either: 1. download and unpack it in a location outside the project or 2. you can have Maven download and unpack it during a build. Follow these steps to setup JBoss AS 7 outside the project: 1. Download JBoss AS 7 [16] 2. (be sure the version you select matches the version youve defined in your pom.xml for <artifactId>jboss-as-arquillian-container-managed</artifactId>) 3. Extract the archive 4. (optional) Set the JBOSS_HOME environment variable to the path of the extracted directory To have Maven handle this task for you instead, add the following XML fragment under the <id> element of the arqjbossas-managed profile (listing 13). To target a managed JBoss AS 7 instance, you also need a small bit of Arquillian configuration. Create the following configuration file and assign the value of the jbossHome property to the location where JBoss AS 7 is installed. If youre using the Maven dependency plug-in, the location is target/ jboss-as-7.0.2.Final (listing 14).
27
Arquillian
Now change the active Maven profile to arquillian-jbossasmanaged, then run the test again. You should see JBoss AS starting in the consoleand yet another green bar! The message printed to System.out gets written to the server log instead of the console. Thats the same test, this time running in a standard (nonembedded) Java EE container. Arquillian packages the test, deploys to the container as a Java EE archive, executes the
tests remotely, captures the results and feeds them back to the Eclipse JUnit result view (or in the Maven surefire results). You can read more about how this lifecycle works in the Arquillian reference guide [17].
Andrew L. Rubinger is an advocate for and speaker on testable enterprise Java development and author of Enterprise JavaBeans 3.1 from O'Reilly Media. He is a member of the JBoss Application Server development team and technical lead of the ShrinkWrap project. Andrew is proudly employed by JBoss/Red Hat. Dan is an open source advocate and community catalyst, author and speaker. He's currently pursuing these interests as a Principal Software Engineer at Red Hat. In that role, he serves as a JBoss Community liaison, contributes to several JBoss Community projects, including Arquillian, ShrinkWrap, Seam 3 / DeltaSpike and JBoss Forge, and participates in the JCP on behalf of Red Hat. Dan is the author Seam in Action (Manning, 2008), writes for IBM developerWorks, NFJS magazine and JAXenter and is an internationally recognized speaker. He's presented at major software conference series including JavaOne, Devoxx, NFJS, JAX and Jazoon. After a long conference day, you'll likely find Dan enjoying tech talk with fellow community members while savoring a Belgian Trappist beer. JBoss, Red Hat Arquillian project lead Aslak Knutsen is currently a Senior Software Engineer at JBoss, by Red Hat where he is working on projects such as Arquillian, ShrinkWrap, Weld and Seam 3, one of the founders of the JBoss Testing initiative and a speaker at major industry conferences including Devoxx, Jazoon, JFokus, Geecon, JUDCon and JBoss World. Previously, Aslak was a Senior Consultant at Conduct AS (working with JBoss related technologies) and Senior Developer at EDB ASA (working with electronic billing/banking systems).
Listing 13
pom.xml
<!-- clip --> <build> <plugins> <plugin> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <id>unpack</id> <phase>process-test-classes</phase> <goals> <goal>unpack</goal> </goals> <conguration> <artifactItems> <artifactItem> <groupId>org.jboss.as</groupId> <artifactId>jboss-as-dist</artifactId> <version>7.1.0.Final</version> <type>zip</type> <overWrite>false</overWrite> <outputDirectory>target</outputDirectory> </artifactItem> </artifactItems> </conguration> </execution> </executions> </plugin> </plugins> </build> <!-- clip -->
Listing 14
src/test/resources/arquillian.xml <arquillian xmlns="http://jboss.org/schema/arquillian" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd"> <container qualier="jbossas-7-managed" default="true"> <conguration> <property name="jbossHome">target/jboss-as-7.1.0.Final</property> </conguration> </container> </arquillian>
[6] http://arqpreview-alrubinger.rhcloud.com/guides/getting_started/index. html#open_project_in_eclipse [7] https://docs.jboss.org/author/display/FORGE/Home [8] https://community.jboss.org/wiki/MavenGettingStarted-Users [9] http://eclipse.org/m2e/ [10] http://www.jboss.org/tools [11] https://gist.github.com/1263892 [12] http://www.jboss.org/shrinkwrap [13] http://arqpreview-alrubinger.rhcloud.com/guides/shrinkwrap_introduction [14] http://embedded-glassfish.java.net/ [15] https://gist.github.com/1263934 [16] http://www.jboss.org/jbossas/downloads [17] https://docs.jboss.org/author/display/ARQ/Negotiating+test+execution
28