diff --git a/README.md b/README.md index 320845d..0aba3d7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,24 @@ -quality-day-training-tasks -========================== +#Excersie 2: Asynchronous method call +##Introduction +This lesson is about following skills: +* creating asynchronous methods in enterprise beans +* EJB self invocation +* concurrency -Various tasks and problems to solve +Expected result of this exercise is an EJB application which uses self invoked asynchronous method calculatePi. + +##Before you start, read about... +* Asynchronous invocation: [http://docs.oracle.com/javaee/6/tutorial/doc/gkkqg.html] +* Session Context [http://docs.oracle.com/javaee/6/api/javax/ejb/SessionContext.html#getBusinessObject(java.lang.Class)] + +##The exercise +To complete this exercise you need to follow these steps: +* Refactor calculatePi in `PiCalculator.java` to return promise (read Asynchronous invocation) instead of Object +* Make calculatePi asynchronous +* Annotate Session Context to be injected properly +* In piFullCalculator use sessionContext to recover business object of class `PiCalculator.java` and assign it to the variable +* Use business object for self invocation of asynchronous method calculatePi and pass +iterations as parameter (Important note: You can't invoke private functions) +* Assign return value to existing variable piFuture + +Good luck ! diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..bd6ce55 --- /dev/null +++ b/pom.xml @@ -0,0 +1,95 @@ + + + 4.0.0 + pl.itcrowd.java.exercises + java-exercises + war + 1.0.0-SNAPSHOT + + + + org.jboss.as.plugins + jboss-as-maven-plugin + ${version.jbossas} + + ${project.artifactId} + true + ${project.groupId} + true + + + + maven-war-plugin + + false + + + + + + + org.jboss.spec.javax.ejb + jboss-ejb-api_3.1_spec + 1.0.1.Final + provided + + + javax.annotation + jsr250-api + 1.0 + + + org.slf4j + slf4j-api + 1.7.7 + + + org.slf4j + slf4j-simple + 1.7.7 + + + junit + junit + 4.8.1 + test + + + org.jboss.arquillian.junit + arquillian-junit-container + test + + + org.jboss.as + jboss-as-arquillian-container-remote + 7.1.1.Final + test + + + + + + org.jboss.shrinkwrap.resolver + shrinkwrap-resolver-bom + 2.2.0-beta-1 + import + pom + + + org.jboss.arquillian + arquillian-bom + 1.1.5.Final + import + pom + + + + + 1.7 + UTF-8 + UTF-8 + UTF-8 + 7.1.1.Final + + diff --git a/src/main/java/Main.java b/src/main/java/Main.java new file mode 100644 index 0000000..e1b035d --- /dev/null +++ b/src/main/java/Main.java @@ -0,0 +1,36 @@ +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.PostConstruct; +import javax.ejb.EJB; +import javax.ejb.Singleton; +import javax.ejb.Startup; + +@Startup +@Singleton +public class Main { + + private Logger logger = LoggerFactory.getLogger(Main.class); + + private Double adminPiResult; + + @EJB + private PiCalculator calculator; + + @PostConstruct + public void main() throws Exception + { + setAdminPiResult(calculator.piFullCalculator(10000000)); + logger.info("admin pi precision: " + Double.toString(getAdminPiResult())); + } + + public Double getAdminPiResult() + { + return adminPiResult; + } + + public void setAdminPiResult(Double adminPiResult) + { + this.adminPiResult = adminPiResult; + } +} diff --git a/src/main/java/PiCalculator.java b/src/main/java/PiCalculator.java new file mode 100644 index 0000000..07ef071 --- /dev/null +++ b/src/main/java/PiCalculator.java @@ -0,0 +1,48 @@ +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.ejb.SessionContext; +import javax.ejb.Stateless; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +@Stateless +public class PiCalculator { + + private Logger logger = LoggerFactory.getLogger(PiCalculator.class); + + private SessionContext sessionContext; + + public Double piFullCalculator(int iterations) throws ExecutionException, InterruptedException + { + Long threadId = Thread.currentThread().getId(); + + //You are supposed to instantiate piFuture with promise from calculatePi function(you need to fix calculatePi return value) + final Future piFuture = null; + logger.info("piFullCalculator should appear before calculatePi: " + Long.toString(threadId)); + + final Object monitor = new Object(); + while(!piFuture.isDone()) { + logger.info("Waiting..."); + //noinspection SynchronizationOnLocalVariableOrMethodParameter + synchronized (monitor) { + monitor.wait(1000); + } + } + + return piFuture.get(); + } + + private Object calculatePi(int iterations) + { + double pi = 4; + boolean plus = false; + for (int i = 3; i < iterations; i += 2) { + pi = plus ? (pi + 4.0 / i) : (pi - 4.0 / i); + plus = !plus; + } + Long threadId = Thread.currentThread().getId(); + logger.info("calculatePi, thread id: " + Long.toString(threadId)); + return pi; + } +} diff --git a/src/main/webapp/WEB-INF/jboss-ejb3.xml b/src/main/webapp/WEB-INF/jboss-ejb3.xml new file mode 100644 index 0000000..1a3d44e --- /dev/null +++ b/src/main/webapp/WEB-INF/jboss-ejb3.xml @@ -0,0 +1,11 @@ + + + + + exercise + * + + + + diff --git a/src/test/java/MainTest.java b/src/test/java/MainTest.java new file mode 100644 index 0000000..2dc1558 --- /dev/null +++ b/src/test/java/MainTest.java @@ -0,0 +1,33 @@ +import junit.framework.Assert; +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.WebArchive; +import org.junit.Test; +import org.junit.runner.RunWith; + +import javax.ejb.EJB; +import java.io.File; + +@RunWith(Arquillian.class) +public class MainTest { + + @EJB + private Main main; + + @Deployment + public static WebArchive createDeployment() + { + return ShrinkWrap.create(WebArchive.class) + .addClasses(Main.class, PiCalculator.class) + .addAsWebInfResource(new File("src/main/webapp/WEB-INF/jboss-ejb3.xml")) + .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml"); + } + + @Test + public void should_calculatePi_equal() throws Exception + { + Assert.assertEquals(main.getAdminPiResult(), 3.1415924535897797d); + } +} diff --git a/src/test/java/PiCalculatorTest.java b/src/test/java/PiCalculatorTest.java new file mode 100644 index 0000000..f1ee67b --- /dev/null +++ b/src/test/java/PiCalculatorTest.java @@ -0,0 +1,34 @@ +import junit.framework.Assert; +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.WebArchive; +import org.junit.Test; +import org.junit.runner.RunWith; + +import javax.ejb.EJB; +import java.io.File; +import java.util.concurrent.Future; + +@RunWith(Arquillian.class) +public class PiCalculatorTest { + + @EJB + private PiCalculator piCalculator; + + @Deployment + public static WebArchive createDeployment() + { + return ShrinkWrap.create(WebArchive.class) + .addClasses(PiCalculator.class) + .addAsWebInfResource(new File("src/main/webapp/WEB-INF/jboss-ejb3.xml")) + .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml"); + } + + @Test + public void should_calculatePi_be_called_asynchronously() throws Exception + { + Assert.assertEquals(((Future) piCalculator.calculatePi(10000)).isDone(), false); + } +}