Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Types and some EJB spec related fixes #114

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion Chapter02-EnablingTechnologies.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,7 @@ Maven.resolver().loadPomFromClassLoaderResource("/path/to/pom.xml")
.resolve("G:A:P:?").withTransitivity().asFile();
----

2. Resolving artifacts defined in effective POM. ShrinkWrap Resolvers allows you to artifacts defined with specific scope into list of artifacts to be resolved. This way, you don't need to alter your tests if you change dependencies of your application. You can either use +importDependencies(ScopeType...)+ or convenience methods, that cover the most frequent usages (+importRuntimeDependencies()+, +importTestDependencies()+ and +importRuntimeAndTestDependencies()+:
2. Resolving artifacts defined in effective POM. ShrinkWrap Resolvers allows you to artifacts defined with specific scope into list of artifacts to be resolved. This way, you don't need to alter your tests if you change dependencies of your application. You can either use +importDependencies(ScopeType...)+ or convenience methods, that cover the most frequent usages (+importRuntimeDependencies()+, +importTestDependencies()+ and +importRuntimeAndTestDependencies())+:
+
[source,java]
----
Expand Down
2 changes: 1 addition & 1 deletion Chapter04-RequirementsAndExampleApplication.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ image:images/ch04-requirements_example_app/clone.png["GitHub URI to Clone"]
Then simply move to a directory in which you'd like to place your local clone, and issue the +git clone+ command, passing in the URI to your GitHub repository. For instance:

----
$> git clone [email protected]:ALRubinger/continuous-enterprise-development.git
$> git clone [email protected]:arquillian/continuous-enterprise-development.git
Cloning into 'continuous-enterprise-development'...
remote: Counting objects: 2661, done.
remote: Compressing objects: 100% (1170/1170), done.
Expand Down
10 changes: 5 additions & 5 deletions Chapter07-BusinessLogicServicesLayer.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,15 @@ Web-based applications offer few avenues to push information to their users once

Our GeekSeek application will therefore introduce the requirement: "Send an Email to the new User upon successful Signup"

At first blush, this seems like a farily trivial problems to solve. The http://www.oracle.com/technetwork/java/javamail/index.html[JavaMail] API is straightforward enough to use (though a bit dated), and is included as part of the Java EE platform.
At first blush, this seems like a fairly trivial problems to solve. The http://www.oracle.com/technetwork/java/javamail/index.html[JavaMail] API is straightforward enough to use (though a bit dated), and is included as part of the Java EE platform.

Unfortunately, there are many issues to consider beyond the boilerplate code required to send the email itself.

_Should we block (wait) while the mail message is sent to the SMTP server?_ Connecting to an external service can take some time, depending upon how it handles open connections. The delivery of the email isn't designed to be immediate, so there's not much sense forcing the user to wait while we connect to an SMTP server, construct a +MimeMessage+, and send.

_What if sending the email fails? Should the enclosing user registration action which called the email service fail, too?_ Sending the email is, in our case, part of a welcome operation. A new user registration doesn't strictly *need* this to succeed as we won't be relying upon email to validate the user's identity. Still, we'd like to make every available effort to ensure that the email goes through, independent of the user registration action. And we'd like to have some notification and options to handle emails that were attempted to be sent, but have failed.

_How do we test to ensure that the emails we've sent are received? How do we validate the email's contents are correct?_ Even if we don't dispatch the communication with the SMTP server to a new Thread, interacting with this external process makes for an asynchronous action. Asynchronous testing is not always the most simple process to set up, but this does not excuse us from the responsibility of ensuring that our email service is worked as designed.
_How do we test to ensure that the emails we've sent are received? How do we validate the email's contents are correct?_ Even if we don't dispatch the communication with the SMTP server to a new Thread, interacting with this external process makes for an asynchronous action. Asynchronous testing is not always the most simple process to set up, but this does not excuse us from the responsibility of ensuring that our email service is working as designed.

=== Implementation

Expand Down Expand Up @@ -120,7 +120,7 @@ public MailMessage build() throws IllegalStateException {

It's this immutable +MailMessageBuilder.MailMessage+ which will be safely passed between our services.

With our value object defined, we can now create our +SMTPMailService+. We know that we'll need to connect to some external SMTP server via the +JavaMail+ API, and Java EE allows injection of these via the +@Resource+ annotation (though the mechanics of exactly where some services are bound is vendor-dependent.). Also, we know that this +SMTPMailService+ is meant to be shared by all users running the application, and won't have any session-specific state. For these reasons, we'll implement the +SMTPMailService+ as a Singleton Session EJB. Note that a a Stateless Session Bean (for use of a pool of instances) might work in an equally-appropriate fashion.
With our value object defined, we can now create our +SMTPMailService+. We know that we'll need to connect to some external SMTP server via the +JavaMail+ API, and Java EE allows injection of these via the +@Resource+ annotation (though the mechanics of exactly where some services are bound is vendor-dependent.). Also, we know that this +SMTPMailService+ is meant to be shared by all users running the application, and won't have any session-specific state. For these reasons, we'll implement the +SMTPMailService+ as a Singleton Session EJB. Note that a Stateless Session Bean (for use of a pool of instances) might work in an equally-appropriate fashion.

[source,java]
----
Expand All @@ -134,7 +134,7 @@ The above is our Singleton bean declaration. Of particular note is the +Transac

An SMTP server is an external resource which is not transactionally-aware. Therefore, we'll have to make note of any exceptions and ensure that if we want a transaction rolled back, we either explicitly tell that to the +TransactionManager+ or throw an unchecked exception which will signal the EJB container to mark any currently-executing transaction for rollback.

We're making a general-purpose SMTP service here, so we may not always know the appropriate actions to take with regards to transactions. The default for EJB is +@TransactionAttributeType.MANDATORY+, which creates a transaction if one is not already in flight. That's not really appropriate here; the SMTP server with which we interact is not transactional, it it'd be silly to sacrifice the overhead of starting a transaction when we're not even dealing with a resource which will respect its semantics! [email protected]+, which we've used here, will accept existing transactions if one is in play, or do nothing if we're invoked outside of a transactional context.
We're making a general-purpose SMTP service here, so we may not always know the appropriate actions to take with regards to transactions. The default for EJB is +@TransactionAttributeType.REQUIRED+, which creates a transaction if one is not already in flight. That's not really appropriate here; the SMTP server with which we interact is not transactional, it it'd be silly to sacrifice the overhead of starting a transaction when we're not even dealing with a resource which will respect its semantics! [email protected]+, which we've used here, will accept existing transactions if one is in play, or do nothing if we're invoked outside of a transactional context.

Now we need to define a method to do the dirty work: accept our +MailMessage+ as a parameter and send it along to the SMTP server. The +JavaMail+ API will act as our conduit to connect to the SMTP server, so we'll take advantage of Java EE's +@Resource+ annotation to inject some relevant supporting services into our +SMTPMailService+.

Expand Down Expand Up @@ -244,7 +244,7 @@ public void queueMailForDelivery(final MailMessageBuilder.MailMessage mailMessag
try {
final Connection connection = connectionFactory.createConnection();
final javax.jms.Session session = connection
.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);
.createSession(true, javax.jms.Session.SESSION_TRANSACTED);
final MessageProducer producer = session.createProducer(smtpQueue);
final ObjectMessage jmsMessage = session.createObjectMessage(mailMessage);
producer.send(jmsMessage);
Expand Down
12 changes: 12 additions & 0 deletions code/application/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,18 @@
<artifactId>rest-assured</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>httpmime</artifactId>
<groupId>org.apache.httpcomponents</groupId>
</exclusion>
<exclusion>
<artifactId>httpcore</artifactId>
<groupId>org.apache.httpcomponents</groupId>
</exclusion>
<exclusion>
<artifactId>httpclient</artifactId>
<groupId>org.apache.httpcomponents</groupId>
</exclusion>
<exclusion>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import java.util.concurrent.TimeUnit;

import org.jboss.arquillian.core.spi.LoadableExtension;
import org.jboss.arquillian.drone.spi.Enhancer;
import org.jboss.arquillian.drone.spi.DroneInstanceEnhancer;
import org.jboss.arquillian.drone.spi.InstanceOrCallableInstance;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
Expand All @@ -16,10 +17,10 @@ public class AngularJSDroneExtension implements LoadableExtension {

@Override
public void register(ExtensionBuilder builder) {
builder.service(Enhancer.class, AngularJSEnhancer.class);
builder.service(DroneInstanceEnhancer.class, AngularJSEnhancer.class);
}

public static class AngularJSEnhancer implements Enhancer<WebDriver> {
public static class AngularJSEnhancer implements DroneInstanceEnhancer<WebDriver> {

private WebDriverEventListener listener;

Expand All @@ -29,8 +30,9 @@ public int getPrecedence() {
}

@Override
public boolean canEnhance(Class<?> type, Class<? extends Annotation> qualifier) {
return WebDriver.class.isAssignableFrom(type);
public boolean canEnhance(InstanceOrCallableInstance instance,
Class<?> droneType, Class<? extends Annotation> qualifier) {
return WebDriver.class.isAssignableFrom(droneType);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
cedj.geekseek.test.functional.arquillian.ArquillianSuiteExtension
cedj.geekseek.test.functional.arquillian.AngularJSDroneExtension
org.cedj.geekseek.test.functional.arquillian.ArquillianSuiteExtension
org.cedj.geekseek.test.functional.arquillian.AngularJSDroneExtension
8 changes: 4 additions & 4 deletions code/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@

<version.arquillian_core>1.1.1.Final</version.arquillian_core>
<version.arquillian_persistence>1.0.0.Alpha6</version.arquillian_persistence>
<version.arquillian_drone>1.2.0.Beta2</version.arquillian_drone>
<version.arquillian_graphene>2.0.0.Beta2</version.arquillian_graphene>
<version.arquillian_drone>1.2.1.Final</version.arquillian_drone>
<version.arquillian_graphene>2.0.0.Final</version.arquillian_graphene>
<version.arquillian_warp>1.0.0.Beta1-SNAPSHOT</version.arquillian_warp>
<version.arquillian_warp_rest>1.0.0.Final-SNAPSHOT</version.arquillian_warp_rest>
<version.arquillian_qunit>1.0.0.Alpha1</version.arquillian_qunit>
<version.restassured>1.8.1</version.restassured>
<version.wildfly>8.0.0.Beta1</version.wildfly>
<version.restassured>2.3.0</version.restassured>
<version.wildfly>8.0.0.Final</version.wildfly>
<version.jboss_as>7.1.1.Final</version.jboss_as>
<version.jboss_eap>7.2.0.Final</version.jboss_eap>
<version.jboss_eap_managed>eap-6.1.0</version.jboss_eap_managed>
Expand Down
36 changes: 36 additions & 0 deletions code/application/service/security/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,24 @@
<groupId>org.jboss.shrinkwrap.resolver</groupId>
<artifactId>shrinkwrap-resolver-impl-maven-archive</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>httpmime</artifactId>
<groupId>org.apache.httpcomponents</groupId>
</exclusion>
<exclusion>
<artifactId>httpcore</artifactId>
<groupId>org.apache.httpcomponents</groupId>
</exclusion>
<exclusion>
<artifactId>httpclient</artifactId>
<groupId>org.apache.httpcomponents</groupId>
</exclusion>
<exclusion>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jboss.shrinkwrap.descriptors</groupId>
Expand All @@ -127,6 +145,24 @@
<groupId>com.jayway.restassured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>httpmime</artifactId>
<groupId>org.apache.httpcomponents</groupId>
</exclusion>
<exclusion>
<artifactId>httpcore</artifactId>
<groupId>org.apache.httpcomponents</groupId>
</exclusion>
<exclusion>
<artifactId>httpclient</artifactId>
<groupId>org.apache.httpcomponents</groupId>
</exclusion>
<exclusion>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class TwitterAuthLoginTestCase {
@Deployment(testable = false)
public static WebArchive deploy() {
return ShrinkWrap.create(WebArchive.class)
//.addAsResource("auth.properties")
.addAsResource("Twitter.properties")
.addPackages(false,
AuthServlet.class.getPackage(),
OAuthAuthenticator.class.getPackage(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.cedj.geekseek.service.security.test.model.SetupAuth;
import org.cedj.geekseek.service.security.test.model.TestApplication;
import org.cedj.geekseek.service.security.test.model.TestCurrentUserProducer;
import org.cedj.geekseek.service.security.test.model.TestUserRepository;
import org.cedj.geekseek.web.rest.core.test.integration.RestCoreDeployments;
import org.cedj.geekseek.web.rest.user.test.integration.UserRestDeployments;
import org.jboss.arquillian.container.test.api.Deployment;
Expand All @@ -40,7 +41,8 @@ public static WebArchive deploy() {
WhoAmIResource.class,
SetupAuth.class,
TestApplication.class,
TestCurrentUserProducer.class)
TestCurrentUserProducer.class,
TestUserRepository.class)
.addAsLibraries(RestCoreDeployments.root())
.addAsLibraries(UserDeployments.domain())
.addAsLibraries(UserRestDeployments.module())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.cedj.geekseek.service.security.test.model;

import org.cedj.geekseek.domain.Repository;
import org.cedj.geekseek.domain.user.model.User;

public class TestUserRepository implements Repository<User> {

@Override
public Class<User> getType() {
return null;
}

@Override
public User store(User entity) {
return null;
}

@Override
public User get(String id) {
return null;
}

@Override
public void remove(User entity) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.STEPS;

import java.io.File;
import java.net.InetAddress;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
Expand All @@ -22,7 +21,7 @@
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.junit.InSequence;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.arquillian.container.ManagementClient;
import org.jboss.as.controller.client.ModelControllerClient;
import org.jboss.as.controller.operations.common.Util;
import org.jboss.dmr.ModelNode;
Expand Down Expand Up @@ -61,7 +60,8 @@ public static WebArchive getApplicationDeployment() {
addClasses(SMTPMailService.class, MailMessageBuilder.class,
SMTPMailServiceConstants.class, SMTPMessageConsumer.class, SMTPServerService.class)
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
.addAsWebInfResource("META-INF/geekseek-smtp-queue-jms.xml");
.addAsWebInfResource("META-INF/geekseek-smtp-queue-jms.xml")
.addClass(ManagementClient.class);
System.out.println(war.toString(true));
return war;
}
Expand Down Expand Up @@ -93,15 +93,15 @@ public static WebArchive getApplicationDeployment() {
@RunAsClient
@InSequence(value = 1)
@Test
public void configureAppServer() throws Exception {
public void configureAppServer(@ArquillianResource ManagementClient managementClient) throws Exception {

/*
* First configure a JavaMail Session for the Server to bind into JNDI; this
* will be used by our MailService EJB. In a production environment, we'll likely have configured
* the server before it was started to point to a real SMTP server
*/

final ModelControllerClient client = ModelControllerClient.Factory.create(InetAddress.getLoopbackAddress(), 9999);
final ModelControllerClient client = managementClient.getControllerClient();

final ModelNode composite = Util.getEmptyOperation(COMPOSITE, new ModelNode());
final ModelNode steps = composite.get(STEPS);
Expand Down Expand Up @@ -135,8 +135,6 @@ public void configureAppServer() throws Exception {

System.out.println("Configure mail service :" + client.execute(composite));

client.close();

/*
* With the config all set and dependencies in place, now we can deploy
*/
Expand All @@ -148,9 +146,9 @@ public void configureAppServer() throws Exception {
@RunAsClient
@InSequence(value = 3)
@Test
public void resetAppServerConfig()
public void resetAppServerConfig(@ArquillianResource ManagementClient managementClient)
throws Exception {
final ModelControllerClient client = ModelControllerClient.Factory.create(InetAddress.getLoopbackAddress(), 9999);
final ModelControllerClient client = managementClient.getControllerClient();

deployer.undeploy(DEPLOYMENT_NAME);

Expand All @@ -175,8 +173,6 @@ public void resetAppServerConfig()
// Find from the WildFly team a better notification mechanism upon which to wait
// https://github.com/arquillian/continuous-enterprise-development/issues/66
//no longer needed since WildFly 8 CR1

client.close();
}

/*
Expand Down