diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..916a30c --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +/starting-template/movieplex7/target/ +/starting-template/tmp/movieplex7/target/ diff --git a/docs/chapters/acks.adoc b/docs/chapters/acks.adoc new file mode 100644 index 0000000..b5e2240 --- /dev/null +++ b/docs/chapters/acks.adoc @@ -0,0 +1,21 @@ +:imagesdir: ../images + +== Acknowledgements + +The following Java EE community members graciously reviewed and contributed to this hands-on lab: + +* Antonio Goncalves (http://twitter.com/agoncal[@agoncal]) +* Markus Eisele (http://twitter.com/myfear[@myfear]) +* Craig Sharpe (http://twitter.com/dapugs[@dapugs]) +* Marcus Vinicius Margarites (http://twitter.com/mvfm[@mvfm]) +* David Delabasse (http://twitter.com/delabasse[@delabasse]) +* John Clingan (http://twitter.com/jclingan[@jclingan]) +* Reza Rahman (http://twitter.com/reza_rahman[@reza_rahman]) +* Marian Muller (http://twitter.com/mullermarian[@mullermarian]) +* Jason Porter (http://twitter.com/lightguardjp[@lightguardjp]) +* Dan Allen (http://twitter.com/mojavelinux[@mojavelinux]) +* Andrey Cheptsov (http://twitter.com/andrey_cheptsov[@andrey_cheptsov]) + +Thank you very much for providing the valuable feedback! + + diff --git a/docs/chapters/appendix.adoc b/docs/chapters/appendix.adoc new file mode 100755 index 0000000..2cac0e4 --- /dev/null +++ b/docs/chapters/appendix.adoc @@ -0,0 +1,461 @@ +:imagesdir: ../images +:experimental: +ifndef::server-glassfish[] +ifndef::server-wildfly[] +:server-wildfly: +endif::server-wildfly[] +endif::server-glassfish[] + +[appendix] +== Appendix + +ifdef::server-wildfly[] +[[appendix-wildfly-netbeans]] +=== Configure WildFly 8 in NetBeans + +[[install-wildfly-plugin]] +==== Install WildFly plugin + +. In NetBeans, click on `Tools', `Plugins', `Available Plugins', type ``wildfly'' in `Search:' box, and select the plugin by clicking on the checkbox in `Install' column. ++ +image::16-netbeans-available-plugins-wildfly.png[title="Available Plugins in NetBeans"] ++ +The exact plugin version and the date may be different. +. Click the btn:[Install] button, then btn:[Next >], accept the license agreement by clicking on the checkbox, then click the btn:[Install] button to install the plugin. Click the btn:[Finish] button to restart the IDE and complete installation. + +==== Configure WildFly 8 + +. In NetBeans, click on `Services' tab. ++ +. Right-click on Servers, choose `Add Server...' in the pop-up menu. ++ +image::netbeans-addserver.png[title="Add Server in NetBeans"] ++ +. Select `WildFly Application Server' in the Add Server Instance wizard, set the +name to `WildFly 8' and click btn:[Next >]. ++ +image::16-netbeans-add-instance-wildfly.png[title="Add WildFly instance to NetBeans"] ++ +. Click on btn:[Browse...] for `Server Location' and select the directory that got created +when WildFly archive was unzipped. Click on btn:[Browse...] for `Server Configuration' and +select the `standalone/configuration/standalone-full.xml' file in the unzipped WildFly +archive. ++ +image::16-netbeans-wildfly-full-platform.png[title="Configure WildFly full instance in NetBeans"] ++ +Click on btn:[Next] and then btn:[Finish]. The `Services' should show the WildFly instance. ++ +image::16-netbeans-wildfly-server.png[title="WildFly instance in NetBeans Services tab"] + +[[appendix-wildfly-idea]] +// === Configure WildFly 8 in IntelliJ IDEA +=== Prepare IntelliJ IDEA for working with WildFly 8 + +To be able to perform the exercises discussed in this tutorial, you need the Ultimate Edition of IntelliJ IDEA. Keep that in mind when downloading IntelliJ IDEA from http://www.jetbrains.com/idea/download/. + +When the appropriate edition of IntelliJ IDEA is installed, you can start preparing the IDE for the exercises: + +. <> ++ +. <> ++ +. <> ++ +. <> ++ +. <> + +[[specify-jdk-wildfly-idea]] +==== Specify the JDK + +First of all, you should specify the JDK that you are going to use. In IntelliJ IDEA, this is done in the *Project Structure* dialog: + +. Start IntelliJ IDEA. If, as a result, a project opens, close the project (menu:File[Close Project]). ++ +. On the Welcome screen, under *Quick Start*, click *Configure*. ++ +image::i13-welcome-configure.png[title="Welcome to IntelliJ IDEA"] ++ +. Under *Configure*, click *Project Defaults*, and then, under *Project Defaults*, click *Project Structure*. ++ +. In the left-hand pane of the *Project Structure* dialog, under *Platform Settings*, select *SDKs*. Click image:i13-plus-icon.png[title="Plus icon in IntelliJ IDEA"] and select *JDK*. ++ +image::i13-plus-jdk.png[title="Add JDK in IntelliJ IDEA"] ++ +. In the *Select Home Directory for JDK* dialog, select the folder in which the JDK that you are going to use is installed, and click btn:[OK]. ++ +image::i13-jdk-home.png[title="JDK home in IntelliJ IDEA"] ++ +. In the *Project Structure* dialog, click btn:[Apply]. ++ +image::i13-jdk-defined.png[title="JDK defined in IntelliJ IDEA"] ++ +Now, let's make the JDK that we have specified the default SDK. ++ +. In the left-hand pane, under *Project Settings*, select *Project*. In the right-hand part of the dialog, under *Project SDK*, select the JDK from the list. ++ +image::i13-project-sdk.png[title="Project SDK in IntelliJ IDEA"] ++ +. Click btn:[OK]. + +[[define-wildfly-idea]] +==== Define WildFly + +Defining an application server in IntelliJ IDEA, normally, is just telling the IDE where the server is installed. The servers are defined in the *Settings* dialog. (On OSX, this dialog is called *Preferences*.) + +. On the Welcome screen, to the left of *Project Defaults*, click *Back* image:i13-back-icon.png[title="Back icon in IntelliJ IDEA"]. ++ +. Under *Configure*, click *Settings*. ++ +. In the left-hand pane of the *Settings* (*Preferences*) dialog, under *IDE Settings*, select *Application Servers*. On the *Application Servers* page, click image:i13-plus-icon.png[title="Plus icon in IntelliJ IDEA"] and select *JBoss Server*. (WildFly is a server from the "JBoss family".) ++ +image::i13-plus-jboss.png[title="Add WildFly in IntelliJ IDEA"] ++ +. In the *JBoss Server* dialog, click image:i13-ellipsis-button.png[title="Ellipsis button in IntelliJ IDEA"] to the right of the *JBoss Home* field. ++ +image::i13-jboss-server-dialog-initial.png[title="WildFly server dialog in IntelliJ IDEA"] ++ +. In the *JBoss Home Directory* dialog, select the folder in which you have the WildFly server installed, and click btn:[OK]. ++ +image::i13-jboss-home-directory.png[title="WildFly home in IntelliJ IDEA"] ++ +. Click *OK* in the *JBoss Server* dialog. ++ +image::i13-jboss-server-dialog-final.png[title="WildFly final dialog in IntelliJ IDEA"] ++ +. In the *Settings* (*Preferences*) dialog, click btn:[OK]. ++ +image::i13-jboss-defined.png[title="WildFly defined in IntelliJ IDEA"] + +[[create-project-wildfly-idea]] +==== Create a project + +The sample application is supplied as a Maven project with an associated http://maven.apache.org/pom.html[pom.xml] file that contains all the necessary project definitions. The corresponding IntelliJ IDEA project in such a case can be created by simply "opening" the +pom.xml+ file. (Obviously, this isn't the only way to create projects in IDEA. You can create projects for existing collections of source files, import Eclipse and Flash Builder projects, and Gradle build scripts. Finally, you can create projects from scratch.) + +. On the Welcome screen, to the left of *Configure*, click *Back* image:i13-back-icon.png[title="Back icon in IntelliJ IDEA"]. ++ +. Under *Quick Start*, click *Open Project*. ++ +image::i13-open-project.png[title="Open project in IntelliJ IDEA"] ++ +. In the *Open Project* dialog, select the +pom.xml+ file associated with the sample application, and click btn:[OK]. ++ +image::i13-select-pom.png[title="Select pom in IntelliJ IDEA"] ++ +Wait while IntelliJ IDEA is processing +pom.xml+ and creating the project. When this process is complete, the following message is shown: ++ +image::i13-jpa-detected.png[title="Configure JPA in IntelliJ IDEA"] ++ +. Click *Configure* in the message box. (If by now the message has disappeared, click image:i13-exclamation-mark-icon.png[title="Mark icon in IntelliJ IDEA"] on the Status bar. ++ +image::i13-jpa-detected-status-bar.png[title="JPA detected in status bar in IntelliJ IDEA"] ++ +The *Event Log* tool window will open. Click *Configure* in this window.) ++ +image::i13-jpa-detected-event-log.png[title="JPA detected event log in IntelliJ IDEA"] ++ +. In the *Setup Frameworks* dialog, just click btn:[OK]. (By doing so you confirm that the file +persistence.xml+ found in the project belongs to the JPA framework.) ++ +image::i13-setup-frameworks-jpa.png[title="Setup frameworks in IntelliJ IDEA"] ++ +Now, as an intermediate check, make sure that the project structure looks something similar to this: ++ +image::i13-initial-project-structure.png[title="Project structure in IntelliJ IDEA"] + +[[create-run-config-wildfly-idea]] +==== Create a run/debug configuration + +Applications in IntelliJ IDEA are run and debugged according to what is called run/debug configurations. Now we are going to create the configuration for running and debugging the sample application in the context of WildFly. + +. In the main menu, select menu:Run[Edit Configurations...]. ++ +image::i13-run-edit-configurations.png[title="Edit configurations in IntelliJ IDEA"] ++ +. In the *Run/Debug Configurations* dialog, click image:i13-plus-icon.png[title="Plus icon in IntelliJ IDEA"], select *JBoss Server*, and then select *Local*. ++ +image::i13-run-configs-plus-jboss.png[title="WildFly configuration in IntelliJ IDEA"] ++ +As a result, the run/debug configuration for the WildFly server is created and its settings are shown in the right-hand part of the dialog. ++ +. Change the name of the run/debug configuration to +WildFly8+ (optional). ++ +. In the lower part of the dialog, within the line _Warning: No artifacts marked for deployment_, click btn:[Fix] and select *movieplex7:war exploded*. (Artifacts in IntelliJ IDEA are deployment-ready project outputs and also the configurations according to which such outputs are produced. In our case, there are two configurations for the sample application (_movieplex7:war_ and _movieplex7:war exploded_). Both configurations represent a format suitable for deployment onto a Java EE 7-enabled application server. _movieplex7:war_ corresponds to a Web archive (WAR). _movieplex7:war exploded_ corresponds to the sample application directory structure (a decompressed archive). The second of the formats is more suitable at the development stage because manipulations with it are faster.) ++ +image::i13-jboss-fix-deployment.png[title="Fixing deployment warning in IntelliJ IDEA"] ++ +. Within the line _Error: Artifact $$'movieplex7: exploded'$$ has invalid extension_, click btn:[Fix]. ++ +image::i13-jboss-invalid-extension.png[title="Invalid extension error message in IntelliJ IDEA"] ++ +. In the *Project Structure* dialog, add +.war+ at the end of the output directory path, and click btn:[OK]. (For the servers of the JBoss family, the application root directory has to have +.war+ at the end.) ++ +image::i13-jboss-fix-extension.png[title="Extension error fix in IntelliJ IDEA"] ++ +. In the *Run/Debug Configurations* dialog, switch to the *Server* tab. In the field for the application starting page URL, replace +$$http://localhost:8080/movieplex7-1/$$+ with +$$http://localhost:8080/movieplex7-1.0-SNAPSHOT/$$+ and click btn:[OK]. ++ +image::i13-jboss-url-fixed.png[title="Fixing application URL in IntelliJ IDEA"] + +The *Application Servers* tool window opens in the lower part of the workspace. Shown in this window are the server run/debug configuration and the associated deployment artifact. Now you are ready to run the application. + +[[run-app-wildfly-idea]] +==== Run the application + +In the *Application Servers* tool window, select the server run/debug configuration (_WildFly8 [local]_) and click *Run* image:i13-run-icon.png[title="Run icon in IntelliJ IDEA"]. + +image::i13-run-wildfly.png[title="Run WildFly in IntelliJ IDEA"] + +IntelliJ IDEA compiles the code, builds the artifact, starts WildFly and deploys the artifact to the server. You can monitor this process in the *Run* tool window that opens in the lower part of the workspace. + +image::i13-run-tool-window-wildfly.png[title="Run tool window in IntelliJ IDEA"] + +Finally, your default Web browser opens and the starting page of the application is shown. + +image::i13-starting-page-in-browser.png[title="Starting page in browser from IntelliJ IDEA"] + +At this step IntelliJ IDEA is fully prepared for your development work, and you can continue with your exercises. + +endif::server-wildfly[] +ifdef::server-glassfish[] +[[appendix-glassfish4-netbeans]] +=== Configure GlassFish 4 in NetBeans + +. In NetBeans, click on `Services' tab. ++ +. Right-click on Servers, choose `Add Server...' in the pop-up menu. ++ +image::netbeans-addserver.png[title="Add Server in NetBeans"] ++ +. Select `GlassFish Server' in the Add Server Instance wizard, set the +name to `GlassFish 4.0' and click btn:[Next >]. ++ +. Click on `Browse …' and browse to where you unzipped the GlassFish +build and point to the `glassfish4' directory that got created when you +unzipped the above archive. Click on ”Finish”. + +[[appendix-glassfish4-idea]] +=== Prepare IntelliJ IDEA for working with GlassFish 4 + +To be able to perform the exercises discussed in this tutorial, you need the Ultimate Edition of IntelliJ IDEA. Keep that in mind when downloading IntelliJ IDEA from http://www.jetbrains.com/idea/download/. + +When the appropriate edition of IntelliJ IDEA is installed, you can start preparing the IDE for the exercises: + +. <> ++ +. <> ++ +. <> ++ +. <> ++ +. <> + +[[specify-jdk-glassfish-idea]] +==== Specify the JDK + +First of all, you should specify the JDK that you are going to use. In IntelliJ IDEA, this is done in the *Project Structure* dialog: + +. Start IntelliJ IDEA. If, as a result, a project opens, close the project (menu:File[Close Project]). ++ +. On the Welcome screen, under *Quick Start*, click *Configure*. ++ +image::i13-welcome-configure.png[image] ++ +. Under *Configure*, click *Project Defaults*, and then, under *Project Defaults*, click *Project Structure*. ++ +. In the left-hand pane of the *Project Structure* dialog, under *Platform Settings*, select *SDKs*. Click image:i13-plus-icon.png[image] and select *JDK*. ++ +image::i13-plus-jdk.png[image] ++ +. In the *Select Home Directory for JDK* dialog, select the folder in which the JDK that you are going to use is installed, and click btn:[OK]. ++ +image::i13-jdk-home.png[image] ++ +. In the *Project Structure* dialog, click btn:[Apply]. ++ +image::i13-jdk-defined.png[image] ++ +Now, let's make the JDK that we have specified the default SDK. ++ +. In the left-hand pane, under *Project Settings*, select *Project*. In the right-hand part of the dialog, under *Project SDK*, select the JDK from the list. ++ +image::i13-project-sdk.png[image] ++ +. Click *OK*. + +[[define-glassfish-idea]] +==== Define GlassFish + +Defining an application server in IntelliJ IDEA, normally, is just telling the IDE where the server is installed. The servers are defined in the *Settings* dialog. (On OSX, this dialog is called *Preferences*.) + +. On the Welcome screen, to the left of *Project Defaults*, click *Back* image:i13-back-icon.png[image]. ++ +. Under *Configure*, click *Settings*. ++ +. In the left-hand pane of the *Settings* (*Preferences*) dialog, under *IDE Settings*, select *Application Servers*. On the *Application Servers* page, click image:i13-plus-icon.png[image] and select *GlassFish Server*. ++ +image::i13-plus-glassfish.png[image] ++ +. In the *GlassFish Server* dialog, click image:i13-ellipsis-button.png[image] to the right of the *GlassFish Home* field. ++ +image::i13-glassfish-server-dialog-initial.png[image] ++ +. In the *GlassFish Home Directory* dialog, select the folder in which you have the GlassFish server installed, and click btn:[OK]. ++ +image::i13-glassfish-home-directory.png[image] ++ +. Click *OK* in the *GlassFish Server* dialog. ++ +image::i13-glassfish-server-dialog-final.png[image] ++ +. In the *Settings* (*Preferences*) dialog, click btn:[OK]. ++ +image::i13-glassfish-defined.png[image] + +[[create-project-glassfish-idea]] +==== Create a project + +The sample application is supplied as a Maven project with an associated http://maven.apache.org/pom.html[pom.xml] file that contains all the necessary project definitions. The corresponding IntelliJ IDEA project in such a case can be created by simply "opening" the +pom.xml+ file. (Obviously, this isn't the only way to create projects in IDEA. You can create projects for existing collections of source files, import Eclipse and Flash Builder projects, and Gradle build scripts. Finally, you can create projects from scratch.) + +. On the Welcome screen, to the left of *Configure*, click *Back* image:i13-back-icon.png[image]. ++ +. Under *Quick Start*, click *Open Project*. ++ +image::i13-open-project.png[image] ++ +. In the *Open Project* dialog, select the +pom.xml+ file associated with the sample application, and click btn:[OK]. ++ +image::i13-select-pom.png[image] ++ +Wait while IntelliJ IDEA is processing +pom.xml+ and creating the project. When this process is complete, the following message is shown: ++ +image::i13-jpa-detected.png[image] ++ +. Click *Configure* in the message box. (If by now the message has disappeared, click image:i13-exclamation-mark-icon.png[image] on the Status bar. ++ +image::i13-jpa-detected-status-bar.png[image] ++ +The *Event Log* tool window will open. Click *Configure* in this window.) ++ +image::i13-jpa-detected-event-log.png[image] ++ +. In the *Setup Frameworks* dialog, just click btn:[OK]. (By doing so you confirm that the file +persistence.xml+ found in the project belongs to the JPA framework.) ++ +image::i13-setup-frameworks-jpa.png[image] ++ +Now, as an intermediate check, make sure that the project structure looks something similar to this: ++ +image::i13-initial-project-structure.png[image] + +[[create-run-config-glassfish-idea]] +==== Create a run/debug configuration + +Applications in IntelliJ IDEA are run and debugged according to what is called run/debug configurations. Now we are going to create the configuration for running and debugging the sample application in the context of GlassFish. + +. In the main menu, select menu:Run[Edit Configurations...]. ++ +image::i13-run-edit-configurations.png[image] ++ +. In the *Run/Debug Configurations* dialog, click image:i13-plus-icon.png[image], select *GlassFish Server*, and then select *Local*. ++ +image::i13-run-configs-plus-glassfish.png[image] ++ +As a result, the run/debug configuration for the GlassFish server is created and its settings are shown in the right-hand part of the dialog. ++ +. Change the name of the run/debug configuration to +GlassFish4+ (optional). ++ +. Note the error message in the lower part of the dialog: _Error: Domain not specified_. To fix this, select *domain1* from the *Server Domain* list. ++ +image::i13-glassfish-fix-domain.png[image] ++ +. In the lower part of the dialog, within the line _Warning: No artifacts marked for deployment_, click btn:[Fix] and select *movieplex7:war exploded*. (Artifacts in IntelliJ IDEA are deployment-ready project outputs and also the configurations according to which such outputs are produced. In our case, there are two configurations for the sample application (_movieplex7:war_ and _movieplex7:war exploded_). Both configurations represent a format suitable for deployment onto a Java EE 7-enabled application server. _movieplex7:war_ corresponds to a Web archive (WAR). _movieplex7:war exploded_ corresponds to the sample application directory structure (a decompressed archive). The second of the formats is more suitable at the development stage because manipulations with it are faster.) ++ +image::i13-glassfish-fix-deployment.png[image] ++ +. Switch to the *Server* tab. In the field for the application starting page URL, replace +$$http://localhost:8080/movieplex7-1/$$+ with +$$http://localhost:8080/movieplex7-1.0-SNAPSHOT/$$+ and click btn:[OK]. ++ +image::i13-glassfish-fix-url.png[image] + +The *Application Servers* tool window opens in the lower part of the workspace. Shown in this window are the server run/debug configuration and the associated deployment artifact. Now you are ready to run the application. + +[[run-app-glassfish-idea]] +==== Run the application + +Before executing the run/debug configuration you have to make sure that the GlassFish server's database is running. + +(The sample application requires a database. The GlassFish server's database doesn't start automatically when the server is started. So the database has to be started separately.) + +You can start the database right from IntelliJ IDEA by running the +asadmin start-database+ command in the *Terminal* tool window. (The +asadmin+ utility is located in the +$$<$$GlassFish_installation_folder$$>$$\bin+ directory.) + +. Open the *Terminal* tool window. You can do that, for example, like this: point to image:i13-show-tool-windows-icon.png[image] on the Status bar and select *Terminal*. ++ +image::i13-open-terminal.png[image] ++ +. Run the +asadmin start-database+ command. ++ +image::i13-glassfish-start-database.png[image] ++ +As a result, the database will start, or you will be told that the database is already running. ++ +. Execute the run/debug configuration. You can do that, for example, by selecting menu:Run[Run {apos}GlassFish4{apos}] from the main menu. ++ +image::i13-run-glassfish.png[image] ++ +IntelliJ IDEA compiles the code, builds the artifact, starts FlassFish and deploys the artifact to the server. You can monitor this process in the *Run* tool window that opens in the lower part of the workspace. ++ +image::i13-run-tool-window-glassfish.png[image] ++ +Finally, your default Web browser opens and the starting page of the application is shown. ++ +image::i13-starting-page-in-browser.png[image] + +At this step IntelliJ IDEA is fully prepared for your development work, and you can continue with your exercises. + +//// + +=== Configure GlassFish 4 in IntelliJ IDEA + +. Create an IntelliJ IDEA project in the movieplex7 directory and build the WAR file: ++ + mvn idea:idea + mvn clean package ++ +. Open the project in IntelliJ IDEA. If your IDEA version is new it will need to use the new project format. In that case IDEA will ask you to convert the project. Just confirm that with `Convert'. ++ +image::idea-convertproject.png[Convert Project] ++ +. Once the project was opened IDEA will detect the JPA framework usage and offer you to configure it. Click on `Configure'. ++ +image::idea-configure-jpa.png[Configure Frameworks] ++ +. In the dialog box that shows up make sure the only detected file in there says `persistence.xml` and is checked and confirm it with `Ok'. ++ +image::idea-configure-jpa-dialogbox.png[Setup Frameworks] ++ +. As a next step we need to build the project. Open the ”Maven Projects” pane on the right-hand side of your IDEA window and click on the two arrows (top left-hand side) pointing at each other. The Maven project will be detected and it will ask you if the project may be reopened now due to a language level change. Confirm with `Yes'. ++ +image::idea-open-mavenprojects-pane.png[Find Maven Project] ++ +. When the project is reloaded go to the `Maven Projects' pane again and have Maven build and package the project by selecting `Java EE 7 Hands-on Lab > Lifecycle > package' and clicking on the green `play' arrow. When you do that you might have to configure your Maven installation - in that case just choose the Maven home directory in the configuration dialog that is offered. Afterwards also click on `Enable Auto-Import' if a green hint pops up. ++ +image::idea-mavenprojects-run-package-command.png[Run `maven package` Command] ++ +. In the menu click on menu:Run[Edit Configurations..]. ++ +. In the dialog box that comes up click on the Plus-sign in the top-left corner and at the bottom select the entry `(17 more items)`. Your mileage may vary here, depending on your IntelliJ IDEA setup. A configuration option for `GlassFish Server' should show up. ++ +image::idea-add-glassfish-server-configuration.png[Add GlassFish Server Configuration] ++ +. Pick `Local' and in the upcoming dialog box enter a name (e.g. `GlassFish Server 4.0.0` - depending on your GlassFish Server version) and uncheck `After launch' so the browser doesn't get opened after each redeploy. In the textfield for ”Server Domain” enter `domain1` as the name of the domain. Leave the `Username' field at `admin` and the `Password` field empty. Then click `Configure' next to the `Application server' drop down list, in the upcoming dialog box click on the Plus-sign in the top-left corner and enter the root path of your GlassFish Server installation. If you also have NetBeans 7.4 on your computer then it will show up under the NetBeans folder. Confirm this dialog box to have it closed. ++ +image::idea-edit-glassfish-server-configuration-servertab.png[Configure GlassFish Server] ++ +. Now click on the `Deployment' tab, then click on the Plus-sign underneath the large empty white area labeled `Deploy at the server startup' and choose `Artifact`. Choose the entry `movieplex7:war` and click `Ok'. Click `Ok' again to close the entire configuration dialog. We're now done. ++ +image::idea-edit-glassfish-server-configuration-deploymenttab.png[Configure Deployment] ++ +. As a final step we need to start the database. For NetBeans users this happens automagically but we'll have to do that manually when using IDEA. Just go to your GlassFish Server installation folder's `bin/`-directory and enter the following command `asadmin start-database`, or for OSX/Linux users: `./asadmin start-database` and you're good to go. + +. In the menu now choose menu:Run[Run GlassFish Server 4.0.0] (or whatever you named your GlassFish Server configuration) and your GlassFish Server will start up and deploy the project. + +. Open `http://localhost:8080/movieplex7-1.0-SNAPSHOT/` in your browser to see the (mostly empty) starter template. +endif::server-glassfish[] diff --git a/docs/chapters/batch.adoc b/docs/chapters/batch.adoc new file mode 100644 index 0000000..baa7a2d --- /dev/null +++ b/docs/chapters/batch.adoc @@ -0,0 +1,420 @@ +:imagesdir: ../images + +[[batch]] +== Ticket Sales (Batch Applications for the Java Platform) + +*Purpose*: Read the total sales for each show and populate the database. +In doing so several new features of Java API for Batch Processing 1.0 +will be introduced and demonstrated by using them in the application. + +*Estimated Time*: 30-45 mins + +Batch Processing is execution of series of `jobs' that is suitable for +non-interactive, bulk-oriented and long-running tasks. Batch +Applications for the Java Platform (JSR 352) will define a programming +model for batch applications and a runtime for scheduling and executing +jobs. + +image::5.0-batch-intro.png[title="Introduction to Batch"] + +The core concepts of Batch Processing are: + +* A *Job* is an instance that encapsulates an entire batch process. A +job is typically put together using a Job Specification Language and +consists of multiple steps. The Job Specification Language for JSR 352 +is implemented with XML and is referred as `Job XML'. +* A *Step* is a domain object that encapsulates an independent, +sequential phase of a job. A step contains all of the information +necessary to define and control the actual batch processing. +* *JobOperator* provides an interface to manage all aspects of job +processing, including operational commands, such as start, restart, and +stop, as well as job repository commands, such as retrieval of job and +step executions. +* *JobRepository* holds information about jobs current running and jobs +that run in the past. JobOperator provides access to this repository. +* Reader-Processor-Writer pattern is the primary pattern and is called +as *Chunk-oriented** **Processing*. In this, *ItemReader* reads one item +at a time, *ItemProcessor* processes the item based upon the business +logic, such as calculate account balance and hands it +to *ItemWriter* for aggregation. Once the 'chunk' numbers of items are +aggregated, they are written out, and the transaction is committed. + +This section will read the cumulative sales for each show from a CSV +file and populate them in a database. + +. Right-click on Source Packages, select `New', `Java Package', +specify the value as `org.javaee7.movieplex7.batch', and click on +`Finish'. ++ +. Right-click on newly created package, select `New', `Java Class', +specify the name as `SalesReader'. Make this class extend from +`AbstractItemReader' by changing the class definition and add: ++ +[source, java] +extends AbstractItemReader ++ +`AbstractItemReader` is an abstract class that implements `ItemReader` +interface. The `ItemReader` interface defines methods that read a stream +of items for chunk processing. This reader implementation returns a +String item type as indicated in the class definition. ++ +Add `@Named` as a class-level annotations and it allows the bean to be +injected in Job XML. Add `@Dependent` as another class-level annotation to +mark this bean as a bean defining annotation so that this bean is +available for injection. ++ +Resolve the imports. ++ +. Override `open()` method to initialize the reader by adding the following code: ++ +[source, java] +---- +private BufferedReader reader; + +public void open(Serializable checkpoint) throws Exception { + reader = new BufferedReader( + new InputStreamReader( + Thread.currentThread() + .getContextClassLoader() + .getResourceAsStream("META-INF/sales.csv"))); +} +---- ++ +This method initializes a BufferedReader from `META-INF/sales.csv' that +is bundled with the application. ++ +Sampling of the first few lines from `sales.csv' is shown below: ++ +[source,csv] +1,500.00 +2,660.00 +3,80.00 +4,470.00 +5,1100.x0 ++ +Each line has a show identifier comma separated by the total sales for +that show. Note that the last line (5^th^ record in the sample) has an +intentional typo. In addition, 17^th^ record also has an additional +typo. The lab will use these lines to demonstrate how to handle parsing +errors. ++ +. Override the following method from the abstract class: ++ +[source,java] +---- +@Override +public String readItem() { + String string = null; + try { + string = reader.readLine(); + } catch (IOException ex) { + ex.printStackTrace(); + } + return string; +} +---- ++ +The `readItem` method returns the next item from the stream. It returns +`null` to indicate end of stream. Note end of stream indicates end of chunk, +so the current chunk will be committed and the step will end. ++ +Resolve the imports. ++ +. Right-click on `org.javaee7.movieplex7.batch' package, select +`New', `Java Class', specify the name as `SalesProcessor'. Change the +class definition and add: ++ +[source, java] +implements ItemProcessor ++ +`ItemProcessor` is an interface that defines a method that is used to +operate on an input item and produce an output item. This processor +accepts a String input item from the reader, `SalesReader` in our case, +and returns a `Sales` instance to the writer (coming shortly). `Sales` is +the pre-packaged JPA entity with the application starter source code. ++ +Add `@Named` and `@Dependent` as class-level annotations so that it allows +the bean to be injected in Job XML. ++ +Resolve the imports. ++ +. Add implementation of the abstract method from the interface as: ++ +[source,java] +---- +@Override +public Sales processItem(Object s) { + Sales sales = new Sales(); + StringTokenizer tokens = new StringTokenizer((String)s, ","); + sales.setId(Integer.parseInt(tokens.nextToken())); + sales.setAmount(Float.parseFloat(tokens.nextToken())); + + return sales; +} +---- ++ +This method takes a `String` parameter coming from the `SalesReader`, parses +the value, populates them in the `Sales` instance, and returns it. This is +then aggregated with the writer. ++ +The method can return null indicating that the item should not be +aggregated. For example, the parsing errors can be handled within the +method and return null if the values are not correct. However this +method is implemented where any parsing errors are thrown as exception. +Job XML can be instructed to skip these exceptions and thus that +particular record is skipped from aggregation as well (shown later). ++ +Resolve the imports. ++ +. Right-click on `org.javaee7.movieplex7.batch` package, select +`New', `Java Class', specify the name as `SalesWriter'. Change the +class definition and add: ++ +[source, java] +extends AbstractItemWriter ++ +`AbstractItemWriter` is an abstract class that implements `ItemWriter` +interface. The ItemWriter interface defines methods that write to a +stream of items for chunk processing. This writer writes a list of `Sales` +items. ++ +Add `@Named` and `@Dependent` as class-level annotations so that it allows +the bean to be injected in Job XML. ++ +Resolve the imports. ++ +. Inject `EntityManager` as: ++ +[source, java] +@PersistenceContext EntityManager em; ++ +Override `writeItems` method from the abstract class by adding the following code: ++ +[source, java] +---- +@Override +@Transactional +public void writeItems(List list) { + for (Sales s : (List)list) { + em.persist(s); + } +} +---- ++ +Batch runtime aggregates the list of `Sales` instances returned from the +`SalesProessor` and makes it available as List in this method. This method +iterates over the list and persist each item in the database. ++ +The method also specifies `@Transactional` as a method level annotation. +This is a new annotation introduced by JTA 1.2 that provides the ability +to control transaction boundaries on CDI managed beans. This provides +the semantics of EJB transaction attributes in CDI beans without +dependencies such as RMI. This support is implemented via an +implementation of a CDI interceptor that conducts the necessary +suspending, resuming, etc.  ++ +In this case, a transaction is automatically started before the method +is called, committed if no checked exceptions are thrown, and rolled +back if runtime exceptions are thrown. This behavior can be overridden +using `rollbackOn` and `dontRollbackOn` attributes of the annotation. ++ +[NOTE] +================= +Each chunk is processed within a container-managed transaction already. +There is really no need for `@Transactional` on `writeItems` method but +shows a usage for the annotation. +================= +Resolve the imports. ++ +. Create Job XML that defines the job, step, and chunk. ++ +In `Files' tab, expand the project -> `src' -> `main' -> `resources', +right-click on `META-INF', select `New', `Folder', specify +the name as `batch-jobs', and click on `Finish'. ++ +Right-click on the newly created folder, select `New', `Other', select +`XML', `XML Document', click on `Next >', give the name as `eod-sales', +click on `Next', take the default, and click on `Finish'. ++ +Replace contents of the file with the following: ++ +[source, xml] +---- + + + + + + + + + + + + +---- ++ +This code shows that the job has one step of chunk type. The ``, +``, and `` elements define the CDI bean name of the +implementations of `ItemReader`, `ItemProcessor`, and `ItemWriter` interfaces. +The `item-count` attribute defines that 3 items are +read/processed/aggregated and then given to the writer. The entire +reader/processor/writer cycle is executed within a transaction. ++ +The `` element specifies a set of exceptions to +be skipped by chunk processing. ++ +CSV file used for this lab has intentionally introduced couple of typos +that would generate `NumberFormatException`. Specifying this element +allows skipping the exception, ignore that particular element, and +continue processing. If this element is not specified then the batch +processing will halt. The `skip-limit` attribute specifies the number of +exceptions a step will skip. ++ +. Lets invoke the batch job. ++ +In `Projects' tab, right-click on `org.javaee7.movieplex7.batch' package, select `New', +`Java Class'. Enter the name as `SalesBean' and click on `Finish' +button. ++ +Add the following code to the bean: ++ +[source, java] +---- +public void runJob() { + try { + JobOperator jo = BatchRuntime.getJobOperator(); + long jobId = jo.start("eod-sales", new Properties()); + System.out.println("Started job: with id: " + jobId); + } catch (JobStartException ex) { + ex.printStackTrace(); + } +} +---- ++ +This method uses `BatchRuntime` to get an instance of `JobOperator`, which +is then used to start the job. `JobOperator` is the interface for +operating on batch jobs. It can be used to start, stop, and restart +jobs. It can additionally inspect job history, to discover what jobs are +currently running and what jobs have previously run. ++ +Add `@Named` and `@RequestScoped` as class-level annotations. This allows +the bean to be injectable in an EL expression. ++ +Resolve the imports. ++ +image::5.10-imports.png[title="RequestScoped import"] ++ +. Inject `EntityManagerFactory` in the class as: ++ +[source, java] +@PersistenceUnit EntityManagerFactory emf; ++ +and add the following method: ++ +[source, java] +---- +public List getSalesData() { + return emf. + createEntityManager(). + createNamedQuery("Sales.findAll", Sales.class). + getResultList(); +} +---- ++ +This method uses a pre-defined `@NamedQuery` to query the database and +return all the rows from the table. ++ +Resolve the imports. ++ +. Right-click on `Web Pages', select `New', `Folder', specify the +name as `batch', and click on `Finish'. ++ +Right-click on the newly created folder, select `New', `Other', +`JavaServer Faces', `Facelets Template Client', and click on `Next >'. ++ +Give the File Name as `sales'. Click on `Browse' next to `Template:', +expand `Web Pages', `WEB-INF', select `template.xhtml', and click on +`Select File'. Click on `Finish'. ++ +In this file, remove `` sections where name attribute value is +`top' and `left'. These sections are inherited from the template. ++ +Replace `` section with `content' name such that it looks like: ++ +[source, xml] +---- + + +

Movie Sales

+ + + + + + + #{s.id} + + + + + + #{s.amount} + + + + + +
+
+---- ++ +This code displays the show identifier and sales from that show in a +table by invoking `SalesBean.getSalesData()`. First command button allows +invoking the job that processes the CSV file and populates the database. +The second command button refreshes the page. ++ +Right-click on the yellow bulb to fix namespace prefix/URI mapping for `h:`. This +needs to be repeated for `f:` prefix. ++ +. Add the following code in `template.xhtml` along with other s: ++ +[source, xml] +---- +

+ Sales + +---- ++ +. Run the project to see the output as shown. ++ +image::5.14-sales.png[title="Sales link on main page"] ++ +Notice, a new `Sales' entry is displayed in the left navigation bar. ++ +. Click on `Sales' to see the output as shown. ++ +image::5.15-sales.png[title="Movie Sales page"] ++ +The empty table indicates that there is no sales data in the database. ++ +. Click on `Run Job' button to initiate data processing of CSV +file. Look for `Waiting for localhost' in the browser status bar, +wait for a couple of seconds for the processing to finish, and then +click on `Refresh' button to see the updated output as shown. ++ +image::5.16-sales-output.png[title="Movie Sales output page"] ++ +Now the table is populated with the sales data. ++ +Note that record 5 is missing from the table, as this records did not +have correct numeric entries for the sales total. The Job XML for the +application explicitly mentioned to skip such errors. diff --git a/docs/chapters/conclusion.adoc b/docs/chapters/conclusion.adoc new file mode 100644 index 0000000..6d52464 --- /dev/null +++ b/docs/chapters/conclusion.adoc @@ -0,0 +1,50 @@ +:imagesdir: ../images + +== Conclusion + +This hands-on lab built a trivial 3-tier web application using Java EE 7 +and demonstrated the following features of the platform: + + +. Java EE 7 Platform +.. Maven coordinates +.. Default DataSource +.. Default JMSConnectionFactory +. Java API for WebSocket 1.0 +.. Annotated server endpoint +.. JavaScript client +. Batch Applications for the Java Platform 1.0 +.. Chunk-style processing +.. Exception handling +. Java API for JSON Processing 1.0 +.. Streaming API for generating JSON +.. Streaming API for consuming JSON +. Java API for RESTful Web Services 2.0 +.. Client API +.. Custom Entity Providers +. Java Message Service 2.0 +.. Default ConnectionFactory +.. Injecting JMSContext +.. Synchronous message send and receive +. Contexts and Dependency Injection 1.1 +.. Automatic discovery of beans +.. Injection of beans +. JavaServer Faces 2.2 +.. Faces Flow +. Bean Validation 1.1 +.. Integration with JavaServer Faces +. Java Transaction API 1.2 +.. @Transactional +. Java Persistence API 2.1 +.. Schema generation properties + +Hopefully this has raised your interest enough in trying out Java EE 7 applications using +ifdef::server-glassfish[] +GlassFish 4. +endif::server-glassfish[] +ifdef::server-wildfly[] +WildFly 8. +endif::server-wildfly[] + +Send us feedback or file issues at http://github.com/javaee-samples/javaee7-hol. + diff --git a/docs/chapters/introduction.adoc b/docs/chapters/introduction.adoc new file mode 100644 index 0000000..af91d1e --- /dev/null +++ b/docs/chapters/introduction.adoc @@ -0,0 +1,65 @@ +:imagesdir: ../images + +== Introduction + +The Java EE 7 platform continues the ease of development push that +characterized prior releases by bringing further simplification to +enterprise development. It adds new and important APIs such as the REST +client API in JAX-RS 2.0 and the long awaited Batch Processing API. Java +Message Service 2.0 has undergone an extreme makeover to align with the +improvements in the Java language. There are plenty of improvements to +several other components. Newer web standards like HTML 5, WebSocket, +and JSON processing are embraced to build modern web applications. + +This hands-on lab will build a typical 3-tier end-to-end application +using the following Java EE 7 technologies: + +* Java API for WebSocket 1.0 (JSR 356) +* Batch Applications for the Java Platform 1.0 (JSR 352) +* Java API for JSON Processing 1.0 (JSR 353) +* Java API for RESTful Web Services 2.0 (JSR 339) +* Java Message Service 2.0 (JSR 343) +* Java Persistence API 2.1 (JSR 338) +* JavaServer Faces 2.2 (JSR 344) +* Contexts and Dependency Injection 1.1 (JSR 346) +* Bean Validation 1.1 (JSR 349) +* Java Transaction API 1.2 (JSR 907) + +Together these APIs will allow you to be more productive by simplifying enterprise development. + +The latest version of this document can be downloaded from https://github.com/javaee-samples/javaee7-hol/blob/master/docs/asciidoc/javaee7-hol.html[javaee7-hol.html]. Please file issues or send pull requests for any updates. + +=== Software Requirement + +The following software needs to be downloaded and installed: + +* JDK 7 from +http://www.oracle.com/technetwork/java/javase/downloads/index.html[http://www.oracle.com/technetwork/java/javase/downloads/index.html]. +* *Application Server*: This lab can use WildFly 8 or GlassFish 4 as the application server. This document provides instructions for +ifdef::server-wildfly[WildFly 8.] +ifdef::server-glassfish[GlassFish.] +* *IDE*: NetBeans 8.0+, JBoss Developer Studio (Eclipse-based), or IntelliJ IDEA 13 can be used. This document provides instructions for +ifdef::ide-netbeans[] +NetBeans 8. ++ +Download ``All'' or ``Java EE'' version from +http://netbeans.org/downloads/[http://netbeans.org/downloads/]. A +snapshot of the downloads page is shown and highlights the exact +`Download' button to be clicked. ++ +.NetBeans download +image::1.1-netbeans-download.png[] +endif::ide-netbeans[] ++ +ifdef::server-glassfish[] +GlassFish 4 comes pre-bundled with NetBeans 7.4+ and does not need to be downloaded explicitly. But if you want to download GlassFish 4 then can do so from http://glassfish.org/[glassfish.org]. ++ +TIP: If you have downloaded GlassFish 4 separately or using a pre-installed version of GlassFish 4, then configure it in NetBeans IDE following the instructions in <>. ++ +TIP: <> explains how to configure GlassFish in IntelliJ IDEA. +endif::server-glassfish[] +ifdef::server-wildfly[] +WildFly 8 needs to be downloaded from http://wildfly.org/downloads/[wildfly.org] and configured in NetBeans IDE following the instructions in <>. ++ +TIP: <> explains how to configure WildFly in IntelliJ IDEA. +endif::server-wildfly[] diff --git a/docs/chapters/jaxrs.adoc b/docs/chapters/jaxrs.adoc new file mode 100644 index 0000000..d4e41f2 --- /dev/null +++ b/docs/chapters/jaxrs.adoc @@ -0,0 +1,305 @@ +:imagesdir: ../images +:experimental: + +[[jaxrs]] +== View and Delete Movie (Java API for RESTful Web Services) + +*Purpose*: View, and delete a movie. In doing so several new features of +JAX-RS 2 will be introduced and demonstrated by using them in the +application. + +*Estimated Time*: 30-45 mins + +JAX-RS 2 defines a standard API to create, publish, and invoke a REST +endpoint. JAX-RS 2 adds several new features to the API: + +* Client API that can be used to access Web resources and provides +integration with JAX-RS Providers. Without this API, the users need to +use a low-level HttpUrlConnection to access the REST endpoint. +* Asynchronous processing capabilities in Client and Server that enables +more scalable applications. +* Message Filters and Entity Interceptors as well-defined extension +points to extend the capabilities of an implementation. +* Validation constraints can be specified to validate the parameters and +return type. + +This section will provide the ability to view all the movies, details of +a selected movie, and delete an existing movie using the JAX-RS Client +API. + +. Right-click on `Source Packages', select `New', `Java Class'. +Give the class name as `MovieClientBean', package as +`org.javaee7.movieplex7.client', and click on `Finish'. ++ +This bean will be used to invoke the REST endpoint. ++ +. Add `@Named` and `@RequestScoped` class-level annotations. This allows +the class to be injected in an EL expression and also defines the bean +to be automatically activated and passivated with the request. ++ +Resolve the imports. ++ +WARNING: Make sure to pick `javax.enterprise.context.RequestScoped` class. ++ +image::6.2-imports.png[title="RequestScoped import"] ++ +. Add the following code to the class: ++ +[source, java] +---- +Client client; +WebTarget target; + +@Inject HttpServletRequest httpServletRequest; + +@PostConstruct +public void init() { + client = ClientBuilder.newClient(); + target = client + .target("http://" + + httpServletRequest.getLocalName() + + ":" + + httpServletRequest.getLocalPort() + + "/" + + httpServletRequest.getContextPath() + + "/webresources/movie/"); + +} + +@PreDestroy +public void destroy() { + client.close(); +} +---- ++ +`ClientBuilder` is the main entry point to the Client API. It uses a +fluent builder API to invoke REST endpoints. A new `Client` instance is +created using the default client builder implementation provided by the +JAX-RS implementation provider. Client are heavy-weight objects that +manage the client-side communication infrastructure. It is highly +recommended to create only required number of instances of Client and +close it appropriately. ++ +In this case, `Client` instance is created and destroyed in the lifecycle +callback methods. The endpoint URI is set on this instance by calling +the target method. Note that the endpoint address is dynamically created +by injecting an instance of `HttpServletRequest`. This is a new feature +added in CDI 1.1 ++ +. Add the following method to the class: ++ +[source, java] +---- +public Movie[] getMovies() { + return target + .request() + .get(Movie[].class); +} +---- ++ +A request is prepared by calling the request method. HTTP GET method is +invoked by calling get method. The response type is specified in the +last method call and so return value is of the type `Movie[]`. ++ +. Right-click on `Web Pages', select `New', `Folder', specify the +name as `client', and click on `Finish'. ++ +Right-click on the newly created folder, select `New', `Other', +`JavaServer Faces', `Facelets Template Client', and click on `Next >'. ++ +Give the File Name as `movies'. Click on `Browse' next to `Template:', +expand `Web Pages', `WEB-INF', select `template.xhtml', and click on +`Select File'. Click on `Finish'. ++ +. In this file, remove `` sections where name attribute value is +`top' and `left'. These sections are inherited from the template. ++ +Replace `` section with `content' name such that it looks like: ++ +[source, xml] +---- + + + + + + + + + + + + +---- ++ +This code fragment invokes `getMovies` method from `MovieClientBean`, +iterates over the response in a for loop, and display the name of each +movie with a radio button. The selected radio button value is bound to +the EL expression `#{movieBackingBean.movieId}`. ++ +The code also has a button with `Details' label and looks for +`movie.xhtml' in the same directory. We will create this file later. ++ +Click on the yellow bulb in the left bar to resolve the namespace +prefix-to-URI resolution. This needs to be completed for `h:`, `c:`, +and `f:` prefixes. ++ +image::6.6-imports.png[title="Namespace prefix imports"] ++ +. Right-click on `org.javaee7.movieplex7.client' package, select +`New', `Java Class', specify the value as `MovieBackingBean' and click +on `Finish'. ++ +Add the following field: ++ +[source, java] +int movieId; ++ +Add getters/setters by right-clicking on the editor pane and selecting +`Insert Code' (kbd:[Ctrl+I] shortcut on OSX). Select the field and click on +`Generate'. ++ +Add `@Named` and `@SessionScoped` class-level annotations and implements +`Serializable`. ++ +Resolve the imports. ++ +WARNING: Make sure to import `javax.enterprise.context.SessionScoped`. ++ +. In `template.xhtml', add the following code along with other s: ++ +[source, xml] +---- +

+ Movies + +---- ++ +Running the project (kbd:[Fn+F6] shortcut on OSX) and clicking on `Movies' +in the left navigation bar shows the output as shown. ++ +image::6.8-output.png[title="List of movies output page"] ++ +The list of all the movies with a radio button next to them is +displayed. ++ +. In `MovieClientBean`, inject `MovieBackingBean` to read the value +of selected movie from the page. Add the following code: ++ +[source, java] +---- +@Inject +MovieBackingBean bean; +---- ++ +. In `MovieClientBean`, add the following method: ++ +[source, java] +---- +public Movie getMovie() { + Movie m = target + .path("{movie}") + .resolveTemplate("movie", bean.getMovieId()) + .request() + .get(Movie.class); + return m; +} +---- ++ +This code reuses the `Client` and `WebTarget` instances created in +`@PostConstruct`. It also adds a variable part to the URI of the REST +endpoint, defined using `{movie}`, and binds it to a concrete value using +`resolveTemplate` method. The return type is specified as a parameter to +the get method. ++ +. Right-click on `client' folder, select `New', `Facelets Template +Client', give the File Name as `movie'. Click on `Browse' next to +`Template:', expand `Web Pages', `WEB-INF', select `template.xhtml', and +click on `Select File'. Click on `Finish'. ++ +. In this file, remove `` sections where name attribute value is +`top' and `left'. These sections are inherited from the template. ++ +Replace `` with `content' name such that it looks like: ++ +[source, xml] +---- + +

Movie Details

+ + + + + + + + + + + + + + +
Movie Id:#{movieClientBean.movie.id}
Movie Name:#{movieClientBean.movie.name}
Movie Actors:#{movieClientBean.movie.actors}
+ +
+
+---- ++ +Click on the yellow-bulb to resolve the namespace prefix-URI mapping for +`h:`. ++ +The output values are displayed by calling the `getMovie` method and +using the `id`, `name`, and `actors` property values. ++ +. Run the project, select `Movies' in the left navigation bar, +select a radio button next to any movie, and click on details to see the +output as shown. ++ +image::6.12-output.png[title="Movie Details page"] ++ +Click on the `Back' button to select another movie. ++ +. Add the ability to delete a movie. In `movies.xhtml', add the +following code with other . ++ +[source, xml] +---- + +---- ++ +This button displays a label `Delete', invokes the method deleteMovie +from `MovieClientBean', and then renders `movies.xhtml'. ++ +. Add the following code to `MovieClientBean': ++ +[source, java] +---- +public void deleteMovie() { + target + .path("{movieId}") + .resolveTemplate("movieId", bean.getMovieId()) + .request() + .delete(); +} +---- ++ +This code again reuses the `Client` and `WebTarget` instances created in +`@PostConstruct`. It also adds a variable part to the URI of the REST +endpoint, defined using `{movieId}`, and binds it to a concrete value +using `resolveTemplate` method. The URI of the resource to be deleted is +prepared and then delete method is called to delete the resource. ++ +Make sure to resolve the imports. ++ +Running the project shows the output shown. ++ +image::6.14-output.png[title="Delete button"] ++ +Select a movie and click on Delete button. This deletes the movie from +the database and refreshes list on the page. Note that a redeploy of the +project will delete all the movies anyway and add them all back. diff --git a/docs/chapters/jms.adoc b/docs/chapters/jms.adoc new file mode 100644 index 0000000..cf6bf89 --- /dev/null +++ b/docs/chapters/jms.adoc @@ -0,0 +1,312 @@ +:imagesdir: ../images +:experimental: + +[[jms]] +== Movie Points (Java Message Service) + +*Purpose*: Customers accrue points for watching a movie. + +*Estimated Time*: 30-45 mins + +Java Message Service 2.0 allows sending and receiving messages between +distributed systems. JMS 2 introduced several improvements over the +previous version such as: + +* New `JMSContext` interface +* AutoCloseable `JMSContext`, `Connection`, and `Session` +* Use of runtime exceptions +* Method chaining on `JMSProducer` +* Simplified message sending + +This section will provide a page to simulate submission of movie points +accrued by a customer. These points are submitted to a JMS queue that is +then read synchronously by another bean. JMS queue for further +processing, possibly storing in the database using JPA. + +. Right-click on Source Packages, select `New', `Java Class', +specify the name as `SendPointsBean', package as `org.javaee7.movieplex7.points', +and click on `Finish'. ++ +Add the following class-level annotations: ++ +[source, java] +---- +@Named +@RequestScoped +---- ++ +This makes the bean to be EL-injectable and automatically activated and +passivated with the request. ++ +Resolve the imports. ++ +image::8.2-imports.png[title="RequestScoped import"] ++ +. A message to a JMS Queue is sent after the customer has bought the +tickets. Another bean will then retrieve this message and update the +points for that customer. This allows the two systems, one generating +the data about tickets purchased and the other about crediting the +account with the points, completely decoupled. ++ +This lab will mimic the sending and consuming of a message by an +explicit call to the bean from a JSF page. ++ +Add the following field to the class: ++ +[source, java] +---- +@NotNull +@Pattern(regexp = "^\\d{2},\\d{2}", + message = "Message format must be 2 digits, comma, 2 digits, e.g.12,12") +private String message; +---- ++ +This field contains the message sent to the queue. This field’s value is +bound to an inputText in a JSF page (created later). Constraints have +been specified on this bean that enable validation of data on form +submit. It requires the data to consists of two numerical digits, followed +by a comma, and then two more numerical digits. If the message does not +meet the validation criteria then the error message to be displayed is +specified using message attribute. ++ +This could be thought as conveying the customer identifier and the +points accrued by that customer. ++ +Generate getter/setters for this field. Right-click in the editor pane, +select `Insert Code' (kbd:[Ctrl+I] shortcut on OSX), select `Getter and +Setter', select the field, and click on `Generate'. ++ +. Add the following code to the class: ++ +[source, java] +---- +@Inject +JMSContext context; + +@Resource(lookup = "java:global/jms/pointsQueue") +Queue pointsQueue; + +public void sendMessage() { + System.out.println("Sending message: " + message); + context.createProducer().send(pointsQueue, message); +} +---- ++ +The Java EE Platform requires a pre-configured JMS connection factory +under the JNDI name `java:comp/DefaultJMSConnectionFactory`. If no +connection factory is specified then the pre-configured connection +factory is used. In a Java EE environment, where CDI is enabled by +default anyway, a container-managed `JMSContext` can be injected as: ++ +[source, java] +---- +@Inject +JMSContext context; +---- ++ +This code uses the default factory to inject an instance of +container-managed `JMSContext`. ++ +`JMSContext` is a new interface introduced in JMS 2. This combines in a +single object the functionality of two separate objects from the JMS 1.1 +API: a `Connection` and a `Session`. ++ +When an application needs to send messages it use the `createProducer` +method to create a `JMSProducer` that provides methods to configure and +send messages. Messages may be sent either synchronously or +asynchronously. ++ +When an application needs to receive messages it uses one of several +`createConsumer` or `createDurableConsumer` methods to create a `JMSConsumer`. +A `JMSConsumer` provides methods to receive messages either synchronously +or asynchronously. ++ +All messages are then sent to a `Queue` instance (created later) +identified by `java:global/jms/pointsQueue` JNDI name. The actual message +is obtained from the value entered in the JSF page and bound to the +message field. ++ +Resolve the imports. ++ +WARNING: Make sure `Queue` class is imported from `javax.jms.Queue` instead of the +default `java.util.Queue`. ++ +Click on `OK'. ++ +. Right-click on `org.javaee7.movieplex7.points' package, select +`New', `Java Class', specify the name as `ReceivePointsBean'. ++ +Add the following class-level annotations: ++ +[source, java] +---- +@JMSDestinationDefinition(name = "java:global/jms/pointsQueue", +interfaceName = "javax.jms.Queue") +@Named +@RequestScoped +---- ++ +This allows the bean to refered from an EL expression. It also activates +and passivates the bean with the request. ++ +`JMSDestinationDefinition` is a new annotation introduced in JMS 2. It is +used by the application to provision the required resources and allow an +application to be deployed into a Java EE environment with minimal +administrative configuration. This code will create Queue with the JNDI +name `java:global/jms/pointsQueue`. ++ +. Add the following code to the class: ++ +[source, java] +---- +@Inject +JMSContext context; + +@Resource(lookup="java:global/jms/pointsQueue") +Queue pointsQueue; + +public String receiveMessage() { + try (JMSConsumer consumer = context.createConsumer(pointsQueue)) { + String message = consumer.receiveBody(String.class); + System.out.println("Received message: " + message); + return message; + } +} +---- ++ +This code creates `JMSConsumer` in a try-with-resources block +which is then used to synchronously receive a message. Note that `JMSConsumer` +is created as an auto-managed resource and so is closed automatically after +receiving each message. Alternatively asynchronous message delivery can also be setup +using Message Driven Beans. However that is not covered in this lab. ++ +. Add the following method to the class: ++ +[source, java] +---- +public int getQueueSize() { + int count = 0; + try { + QueueBrowser browser = context.createBrowser(pointsQueue); + Enumeration elems = browser.getEnumeration(); + while (elems.hasMoreElements()) { + elems.nextElement(); + count++; + } + } catch (JMSException ex) { + ex.printStackTrace(); + } + return count; +} +---- ++ +This code creates a `QueueBrowser` to look at the messages on a queue +without removing them. It calculates and returns the total number of +messages in the queue. ++ +Make sure to resolve the import from `javax.jms.Queue`, take all other +defaults. ++ +. Right-click on `Web Pages', select `New', `Folder', specify the +name as `points', and click on `Finish'. ++ +In `Web Pages', right-click on newly created folder, select `Facelets +Template Client', give the File Name as `points'. Click on `Browse' +next to `Template:', expand `Web Pages', `WEB-INF', select +`template.xhtml', and click on `Select File'. Click on `Finish'. ++ +. In this file, remove `` sections where name attribute value is +`top' and `left'. These sections are inherited from the template. ++ +Replace the `` section with `content' name such that it looks like: ++ +[source, xml] +---- + + +

Points

+ + Queue size: +

+ + + + + + + + +---- ++ +Click on the yellow bulb to resolve namespace prefix/URI mapping for `h:` +prefix. ++ +This page displays the number of messages in the current queue. It +provides a text box for entering the message that can be sent to the +queue. The first command button invokes `sendMessage` method from +`SendPointsBean` and refreshes the page. Updated queue count, incremented +by 1 in this case, is displayed. The second command button invokes +`receiveMessage` method from `ReceivePointsBean` and refreshes the page. The +queue count is updated again, decremented by 1 in this case. ++ +If the message does not meet the validation criteria then the error +message is displayed on the screen. ++ +. Add the following code in `template.xhtml' along with other +s: ++ +[source, xml] +---- +

+ Points + +---- ++ +. Run the project. The update page looks like as shown: ++ +image::8.10-output.png[title="Points link on main page"] ++ +Click on `Points' to see the output as: ++ +image::8.10-output2.png[title="Points output page"] ++ +The output shows that the queue has 0 messages. Enter a message `1212' +in the text box and click on `Send Message' to see the output as shown. ++ +image::8.10-output3.png[title="Validation message on Points page"] ++ +This message is not meeting the validation criteria and so the error +message is displayed. ++ +Enter a message as `12,12' in the text box and click on `Send Message' +button to see the output as: ++ +image::8.10-output4.png[title="Correct input for Points page - Send Message (queue size=1)"] ++ +The updated count now shows that there is 1 message in the queue. Click +on `Receive Message' button to see output as: ++ +image::8.10-output5.png[title="Correct input for Points page - Receive Message (queue size=0)"] ++ +The updated count now shows that the message has been consumed and the +queue has 0 messages. ++ +Click on `Send Message' 4 times to see the output as: ++ +image::8.10-output6.png[title="Correct input for Points page - Send Message (queue size=4)"] ++ +The updated count now shows that the queue has 4 messages. Click on +`Receive Message' 2 times to see the output as: ++ +image::8.10-output7.png[title="Correct input for Points page - Receive Message (queue size=2)"] ++ +The count is once again updated to reflect the 2 consumed and 2 +remaining messages in the queue. + diff --git a/docs/chapters/jsf.adoc b/docs/chapters/jsf.adoc new file mode 100644 index 0000000..7f4fb6e --- /dev/null +++ b/docs/chapters/jsf.adoc @@ -0,0 +1,405 @@ +:imagesdir: ../images + +[[jsf]] +== Show Booking (JavaServer Faces) + +*Purpose*: Build pages that allow a user to book a particular movie show +in a theater. In doing so a new feature of JavaServer Faces 2.2 will be +introduced and demonstrated by using in the application. + +*Estimated Time*: 30-45 mins + +JavaServer Faces 2.2 introduces a new feature called _Faces Flow_ that +provides an encapsulation of related views/pages with application +defined entry and exit points. Faces Flow borrows core concepts from ADF +TaskFlow, Spring Web Flow, and Apache MyFaces CODI. + +It introduces `@FlowScoped` CDI annotation for flow-local storage and +`@FlowDefinition` to define the flow using CDI producer methods. There are +clearly defined entry and exit points with well-defined parameters. This +allows the flow to be packaged together as a JAR or ZIP file and be +reused. The application thus becomes a collection of flows and non-flow +pages. Usually the objects in a flow are designed to allow the user to +accomplish a task that requires input over a number of different views. + +This application will build a flow that allows the user to make a movie +reservation. The flow will contain four pages: + +* Display the list of movies +* Display the list of available show timings +* Confirm the choices +* Make the reservation and show the ticket + +Lets build the application. + +. Items in a flow are logically related to each other and so it is +required to keep them together in a directory. In NetBeans, right-click +on the `Web Pages', select `New', `Folder', specify the folder name +`booking', and click on `Finish'. ++ +. Right-click on the newly created folder, select `New', `Facelets +Template Client', give the File Name as `booking'. Click on `Browse' +next to `Template:', expand `Web Pages', `WEB-INF', select +`template.xhtml', and click on `Select File'. Click on `Finish'. ++ +. `booking.xhtml' is the entry point to the flow (more on this later). ++ +In this file, remove `` sections with `top' and `left' name +attributes. These sections are inherited from the template. ++ +Replace `` section with `content' name such that it looks like: ++ +[source, xml] +---- + + +

Pick a movie

+ + + + + + +
+
+---- ++ +The code builds an HTML form that displays the list of movies as radio +button choices. The chosen movie is bound to `#{booking.movieId}` which +will be defined as a flow-scoped bean. The value of action attribute on +commandButton refers to the next view in the flow, i.e. +`showtimes.xhtml' in the same directory in our case. ++ +Click on the yellow bulb as shown and click on the suggestion to +add namespace prefix/URI mapping for `h:`. Repeat the same for `f:` prefix as well. ++ +image::9.3-imports.png[title="Namespace prefix mapping imports"] ++ +. Right-click on `Source Packages', select `New', `Java Class'. +Specify the class name as `Booking' and the package name as +`org.javaee7.movieplex7.booking'. ++ +Add `@Named` class-level annotation to make the class EL-injectable. ++ +Add `@FlowScoped("booking")` to define the scope of bean as the flow. The bean is automatically activated and passivated as the flow is entered or exited. ++ +Add `implements Serializable` to the class as beans with `@FlowScoped` annotation need to be passivation capable, and thus serializable. ++ +Add the following field: ++ +[source, java] +int movieId; ++ +and generate getters/setters by going to `Source', `Insert Code', +selecting `Getter and Setter', and select the field. ++ +Inject `EntityManager` in this class by adding the following code: ++ +[source, java] +---- +@PersistenceContext +EntityManager em; +---- ++ +Add the following convenience method: ++ +[source, java] +---- +public String getMovieName() { + try { + return em.createNamedQuery("Movie.findById", Movie.class) + .setParameter("id", movieId) + .getSingleResult() + .getName(); + } catch (NoResultException e) { + return ""; + } +} +---- ++ +This method will return the movie name based upon the selected movie. ++ +Alternatively, movie id and name may be passed from the selected radio +button and parsed in the backing bean. This will reduce an extra trip to +the database. ++ +Resolve the imports. ++ +. Create `showtimes.xhtml' in the `booking' folder following the +steps used to create `booking.xhtml'. ++ +In this file, remove `` sections with `top' and `left' name +attributes. These sections are inherited from the template. ++ +Replace `` section with `content' name such that it looks like: ++ +[source, xml] +---- + + +

Show Timings for #{booking.movieName}

+ + + + + + + + + +
+
+---- ++ +This code builds an HTML form that displays the chosen movie name and +all the show times. `#{timeslotFacadeREST.all}` returns the list of all +the movies and iterates over them using a `c:forEach` loop. The id and +start time of the selected show are bound to `#{booking.startTime}`. +Command button with value `Back' allows going back to the previous page and +the other command button with value `Confirm' takes to the next view in the +flow, `confirm.xhtml' in our case. ++ +Typically a user will expect the show times only for the selected movie +but all the show times are shown here. This allows us to demonstrate +going back and forth within a flow if an incorrect show time for a movie +is chosen. A different query may be written that displays only the shows +available for this movie; however this is not part of the application. ++ +Right-click on the yellow bulb to fix namespace prefix/URI mapping for +`h:`. This needs to be repeated for `c:` and `f:` prefix as well. ++ +. Add the following fields to the `Booking` class: ++ +[source, java] +---- +String startTime; +int startTimeId; +---- ++ +And the following methods: ++ +[source, java] +---- +public String getStartTime() { + return startTime; +} + +public void setStartTime(String startTime) { + StringTokenizer tokens = new StringTokenizer(startTime, ","); + startTimeId = Integer.parseInt(tokens.nextToken()); + this.startTime = tokens.nextToken(); +} + +public int getStartTimeId() { + return startTimeId; +} +---- ++ +These methods will parse the values received from the form. Also add the +following method: ++ +[source, java] +---- +public String getTheater() { + // for a movie and show + try { + + // Always return the first theater + List list = + em.createNamedQuery("ShowTiming.findByMovieAndTimingId", + ShowTiming.class) + .setParameter("movieId", movieId) + .setParameter("timingId", startTimeId) + .getResultList(); + + if (list.isEmpty()) + return "none"; + + return list + .get(0) + .getTheaterId() + .getId() + .toString(); + } catch (NoResultException e) { + return "none"; + } +} +---- ++ +This method will find the first theater available for the chosen movie +and show the timing. ++ +Additionally a list of theaters offering that movie may be shown in a +separate page. ++ +Resolve the imports. ++ +. Create `confirm.xhtml' page in the `booking' folder by following +the steps used to create ‘booking.xhtml’. ++ +In this file, remove `` sections wht `top' and `left' name +attributes. These sections are inherited from the template. ++ +Replace `' section with `content' name such that it looks like: ++ +[source, xml] +---- + + + + +

No theater found, choose a different time

+ + Movie name: #{booking.movieName}

+ Starts at: #{booking.startTime}

+ + + + +

Confirm ?

+ + Movie name: #{booking.movieName}

+ Starts at: #{booking.startTime}

+ Theater: #{booking.theater}

+ + + + + + + +---- ++ +The code displays the selected movie, show timing, and theater if +available. The reservation can proceed if all three are available. +`print.xhtml' is the last page that shows the confirmed reservation +and is shown when `Book' commandButton is clicked. ++ +`actionListener` can be added to `commandButton` to invoke the business +logic for making the reservation. Additional pages may be added to take +the credit card details and email address. ++ +Right-click on the yellow bulb to fix namespace prefix/URI mapping for ‘c:’. +This needs to be repeated for ‘h:’ prefix as well. ++ +. Create `print.xhtml' page in the `booking' folder by following the +steps used to create ‘booking.xhtml’. ++ +In this file, remove `` sections wht `top' and `left' name +attributes. These sections are inherited from the template. ++ +Replace `` section with `content' name such that it looks like: ++ +[source, xml] +---- + + +

Reservation Confirmed

+ + Movie name: #{booking.movieName}

+ Starts at: #{booking.startTime}

+ Theater: #{booking.theater}

+

+ + + +---- ++ +This code displays the movie name, show timings, and the selected +theater. ++ +Right-click on the yellow bulb to fix namespace prefix/URI mapping for ‘h:’. ++ +The `commandButton` initiates exit from the flow. The `action` attribute +defines a navigation rule that will be defined in the next step. ++ +. `booking.xhtml', `showtimes.xhtml', `confirm.xhtml', and +`print.xhtml' are all in the same directory. Now the runtime needs to be +informed that the views in this directory are to be treated as view +nodes in a flow. This can be done declaratively by adding `booking/booking-flow.xml' +or programmatically by having a class with a method with the following annotations: ++ +[source, java] +@Produces @FlowDefinition ++ +This lab takes the declarative approach. ++ +Right-click on `Web Pages/booking' folder, select `New', `Other', `XML', +`XML Document', give the name as `booking-flow', click on `Next>', take +the default of `Well-formed Document', and click on `Finish'. ++ +Replace the generated code with the following: ++ +[source, xml] +---- + + + + /index + + + +---- ++ +This defines the flow graph. It uses the parent element used in +a standard `faces-config.xml` but defines a `` inside it. ++ +`` defines a return node in a flow graph. `` +contains the node value, or an EL expression that defines the node, to +return to. In this case, the navigation returns to the home page. ++ +. Finally, invoke the flow by editing `WEB-INF/template.xhtml' and +changing: ++ +[source, xml] +Item 1 ++ +to ++ +[source, xml] +Book a movie ++ +`commandLink` renders an HTML anchor tag that behaves like a form submit +button. The action attribute points to the directory where all views for +the flow are stored. This directory already contains `booking-flow.xml' +which defines the flow of the pages. ++ +. Run the project by right clicking on the project and selecting +`Run'. The browser shows the updated output. ++ +image::9.11-output.png[title="Book a movie link on main page"] ++ +Click on `Book a movie' to see the page as shown. ++ +image::9.11-output2.png[title="Book a movie page"] ++ +Select a movie, say `The Shiningr and click on `Pick a time' to see the +page output as shown. ++ +image::9.11-output3.png[title="Show Timings page"] ++ +Pick a time slot, say `04:00', click on `Confirm' to see the output as shown. ++ +image::9.11-output4.png[title="Confirm? page"] ++ +Click on `Book' to confirm and see the output as: ++ +image::9.11-output5.png[title="Reservation Confirmed page"] ++ +Feel free to enter other combinations, go back and forth in the flow and +notice how the values in the bean are preserved. ++ +Click on `home' takes to the main application page. + diff --git a/docs/chapters/json.adoc b/docs/chapters/json.adoc new file mode 100644 index 0000000..4140903 --- /dev/null +++ b/docs/chapters/json.adoc @@ -0,0 +1,301 @@ +:imagesdir: ../images + +[[json]] +== Add Movie (Java API for JSON Processing) + +*Purpose*: Add a new movie. In doing so several new features of the Java +API for JSON Processing 1.0 will be introduced and demonstrated by using +them in the application. + +*Estimated Time*: 30-45 mins + +Java API for JSON Processing provides a standard API to parse and +generate JSON so that the applications can rely upon a portable API. +This API will provide: + +* Produce/Consume JSON in a streaming fashion (similar to StAX API for XML) +* Build a Java Object Model for JSON (similar to DOM API for XML) + +This section will define a JAX-RS Entity Providers that will allow +reading and writing JSON for a Movie POJO. The JAX-RS Client API will +request this JSON representation. + +JAX-RS Entity Providers supply mapping services between on-the-wire +representations and their associated Java types.  Several standard Java +types such as `String`, `byte[]`, `javax.xml.bind.JAXBElement`, +`java.io.InputStream`, `java.io.File`, and others have a pre-defined mapping +and is required by the specification. Applications may provide their own +mapping to custom types using `MessageBodyReader` and `MessageBodyWriter` +interfaces. + +This section will provide the ability to add a new movie to the +application. Typically, this functionality will be available after +proper authentication and authorization. + +. Right-click on Source Packages, select `New', `Java Class', +specify the name as `MovieReader', package as `org.javaee7.movieplex7.json' +and click on `Finish'. Add the following class-level annotations: ++ +. Right-click on newly created package, select `New', `Java Class', +specify the name as `MovieReader', and click on `Finish'. Add the +following class-level annotations: ++ +[source, java] +---- +@Provider +@Consumes(MediaType.APPLICATION_JSON) +---- ++ +`@Provider` allows this implementation to be discovered by the JAX-RS +runtime during the provider scanning phase. `@Consumes` indicates that +this implementation will consume a JSON representation of the resource. ++ +Make sure to resolve imports from the appropriate package as shown. ++ +image::7.2-imports.png[title="Provider import"] ++ +. Make the class implements `MessageBodyReader`. ++ +image::7.3-implements.png[title="Implement abstract methods for MessageBodyReader"] ++ +Click on the hint (shown as yellow bulb) on the class definition and +select `Implement all abstract methods'. ++ +. Change implementation of the `isReadable` method as: ++ +[source, java] +return Movie.class.isAssignableFrom(type); ++ +This method ascertains if the `MessageBodyReader` can produce an instance +of a particular type. ++ +. Replace the `readFrom` method with: ++ +[source, java] +---- +@Override +public Movie readFrom( + Class type, + Type type1, + Annotation[] antns, + MediaType mt, + MultivaluedMap mm, + InputStream in) + throws IOException, WebApplicationException { + + Movie movie = new Movie(); + JsonParser parser = Json.createParser(in); + while (parser.hasNext()) { + switch (parser.next()) { + case KEY_NAME: + String key = parser.getString(); + parser.next(); + switch (key) { + case "id": + movie.setId(parser.getInt()); + break; + case "name": + movie.setName(parser.getString()); + break; + case "actors": + movie.setActors(parser.getString()); + break; + default: + break; + } + break; + default: + break; + } + } + return movie; +} +---- ++ +This code reads a type from the input stream in. `JsonParser`, a streaming +parser, is created from the input stream. Key values are read from the +parser and a `Movie` instance is populated and returned. ++ +Resolve the imports. ++ +. Right-click on `org.javaee7.movieplex7.json' package, select `New', `Java Class', +specify the name as `MovieWriter', and click on `Finish'. Add the +following class-level annotations: ++ +[source, java] +---- +@Provider +@Produces(MediaType.APPLICATION_JSON) +---- ++ +`@Provider` allows this implementation to be discovered by the JAX-RS +runtime during the provider scanning phase. `@Produces` indicates that +this implementation will produce a JSON representation of the resource. ++ +Resolve the imports as shown. ++ +image::7.6-imports.png[title="Provider import"] ++ +. Make this class implement `MessageBodyWriter` interface by adding the following code: +[source, java] +implements MessageBodyWriter ++ +Resolve the imports. ++ +The IDE provide a hint to implement abstract methods as: ++ +image::7.7-implements.png[title="Implement abstract methods for MessageBodyWriter"] ++ +Click on the hint (show as yellow bulb) on the class definition and +select `Implement all abstract methods'. ++ +. Change implementation of the `isWritable` method to: ++ +[source, java] +return Movie.class.isAssignableFrom(type); ++ +This method ascertains if the `MessageBodyWriter` supports a particular +type. ++ +. Add implementation of the `getSize` method as: ++ +[source, java] +return -1; ++ +Originally, this method was called to ascertain the length in bytes of +the serialized form of `t`. In JAX-RS 2.0, this method is deprecated and +the value returned by the method is ignored by a JAX-RS runtime. All +`MessageBodyWriter` implementations are advised to return -1. ++ +. Change implementation of the `writeTo` method to: ++ +[source, java] +---- +JsonGenerator gen = Json.createGenerator(entityStream); +gen.writeStartObject() + .write("id", t.getId()) + .write("name", t.getName()) + .write("actors", t.getActors()) + .writeEnd(); + gen.flush(); +---- ++ +This method writes a type to an HTTP message. `JsonGenerator` writes JSON +data to an output stream in a streaming way. Overloaded write methods +are used to write different data types to the stream. ++ +Resolve the imports. ++ +. In `Web Pages', right-click on `client' folder, select `New', +`Facelets Template Client'. Give the File Name as `addmovie'. +Click on `Browse' next to `Template:', expand `Web Pages', +`WEB-INF', select `template.xhtml', and click on `Select File'. +Click on `Finish'. ++ +. In this file, remove `` sections where name attribute value is +`top' and `left'. These sections are inherited from the template. ++ +Replace `` section with `content' name such that it looks like: ++ +[source, xml] +---- + + +

Add a New Movie

+ + + + + + + + + + + + + + +
Movie Id:
Movie Name:
Movie Actors:
+ +
+
+
+---- ++ +This code creates a form to accept input of `id`, `name`, and `actors` of a +movie. These values are bound to fields in `MovieBackingBean`. The click +of command button invokes the addMovie method from `MovieClientBean` and +then renders `movies.xhtml'. ++ +Click on the hint (show as yellow bulb) to resolve the namespace +prefix/URI mapping as shown. ++ +image::7.11-imports.png[title="Namespace prefix mapping imports"] ++ +. Add `movieName` and `actors` field to `MovieBackingBean` as: ++ +[source, java] +---- +String movieName; +String actors; +---- ++ +Generate getters and setters by clicking on the menu item `Source' and +then `Insert Code'. ++ +. Add the following code to `movies.xhtml' ++ +[source, xml] + ++ +along with rest of the s. ++ +. Add the following method in `MovieClientBean`: ++ +[source, java] +---- +public void addMovie() { + Movie m = new Movie(); + m.setId(bean.getMovieId()); + m.setName(bean.getMovieName()); + m.setActors(bean.getActors()); + target + .register(MovieWriter.class) + .request() + .post(Entity.entity(m, MediaType.APPLICATION_JSON)); +} +---- ++ +This method creates a new `Movie` instance, populates it with the values +from the backing bean, and POSTs the bean to the REST endpoint. The +register method registers a MovieWriter that provides conversion from +the POJO to JSON. Media type of `application/json` is specified using `MediaType.APPLICATION_JSON`. ++ +Resolve the imports as shown ++ +image::7.14-imports.png[title="Entity import"] ++ +. Run the project to see the updated main page as: ++ +image::7.15-output.png[title="New Movie button"] ++ +A new movie can be added by clicking on `New Movie' button. ++ +. Enter the details as shown: ++ +image::7.16-output.png[title="Add a New Movie page"] ++ +Click on `Add' button. The `Movie Id' value has to be greater than 20 +otherwise the primary key constraint will be violated. The table +definition may be updated to generate the primary key based upon a +sequence; however this is not done in the application. ++ +The updated page looks like as shown ++ +image::7.16-output2.png[title="Newly added movie"] ++ +Note that the newly added movie is now displayed. diff --git a/docs/chapters/revision.adoc b/docs/chapters/revision.adoc new file mode 100644 index 0000000..7037523 --- /dev/null +++ b/docs/chapters/revision.adoc @@ -0,0 +1,9 @@ +:imagesdir: ../images + +== Revision History + +. Cleaned up NetBeans instructions and some other typos from DevoxxUK (Jun 25, 2014) +. Added IntelliJ IDEA specific instructions (Jan 22, 2014) +. Added macros to generate WildFly and GlassFish-server specific instructions. Also enabled IntelliJ and Eclipse specific macros (Jan 10, 2014) +. Moving the source document from Pages to AsciiDoc (Dec 3, 2013) + diff --git a/docs/chapters/solutions.adoc b/docs/chapters/solutions.adoc new file mode 100644 index 0000000..ae2741a --- /dev/null +++ b/docs/chapters/solutions.adoc @@ -0,0 +1,6 @@ +:imagesdir: ../images + +== Completed Solutions + +The completed solution for this lab can be downloaded from https://github.com/javaee-samples/javaee7-hol/blob/master/solution/movieplex7-solution.zip[javaee7-hol]. + diff --git a/docs/chapters/statement.adoc b/docs/chapters/statement.adoc new file mode 100644 index 0000000..19201a0 --- /dev/null +++ b/docs/chapters/statement.adoc @@ -0,0 +1,208 @@ +:imagesdir: ../images +:experimental: + +== Problem Statement + +This hands-on lab builds a typical 3-tier Java EE 7 Web application that +allows customers to view the show timings for a movie in a 7-theater +Cineplex and make reservations. Users can add new movies and delete +existing movies. Customers can discuss the movie in a chat room. Total +sales from each showing are calculated at the end of the day. Customers +also accrue points for watching movies. + +.Architecture diagram +image::2.0-problem-statement.png[] + +This figure shows the key components of the application. The User +Interface initiates all the flows in the application. Show Booking, +Add/Delete Movie and Ticket Sales interact with the database; Movie +Points may interact with the database, however, this is out of scope for +this application; and Chat Room does not interact with the database. + +The different functions of the application, as detailed above, utilize +various Java technologies and web standards in their implementation. The +following figure shows how Java EE technologies are used in different +flows. + +.Technologies used in the application +image::2.0-technologies.png[] + +The table below details the components and the selected technology used +in its’ implementation. + +[cols="2,8"] +|=== +|Flow | Description + +|User Interface +|Written entirely in _JavaServer Faces_ (JSF) + +|Chat Room +|Utilizes client-side JavaScript and JSON to communicate with a _WebSocket_ endpoint + +|Ticket Sales +|Uses _Batch Applications for the Java Platform_ to calculate the total +sales and persist to the database. + +|Add/Delete Movie +|Implemented using RESTful Web Services. JSON is used as on-the-wire data format + +|Movie Points +|Uses _Java Message Service_ (JMS) to update and obtain loyalty reward +points; an optional implementation using database technology may be +performed + +|Show Booking +|Uses lightweight _Enterprise JavaBeans_ to communicate with the database +using Java Persistence API +|=== + +This document is not a comprehensive tutorial of Java EE. The attendees +are expected to know the basic Java EE concepts such as EJB, JPA, +JAX-RS, and CDI. The http://docs.oracle.com/javaee/7/tutorial/doc/[Java +EE 7 Tutorial] is a good place to learn all these concepts. However +enough explanation is provided in this guide to get you started with the +application. + +WARNING: This is a sample application and the code may not be +following the best practices to prevent SQL injection, cross-side +scripting attacks, escaping parameters, and other similar features +expected of a robust enterprise application. This is intentional such as +to stay focused on explaining the technology. It is highly recommended +to make sure that the code copied from this sample application is +updated to meet those requirements. + +=== Lab Flow + +The attendees will start with an existing maven application and by +following the instructions and guidance provided by this lab they will: + +* Read existing source code to gain an understanding of the structure of +the application and use of the selected platform technologies. +* Add new and update existing code with provided fragments in order to +demonstrate usage of different technology stacks in the Java EE 7 +platform. + +While you are copy/pasting the code from this document into NetBeans, +here are couple of tips that will be really useful and make your +experience enjoyable! + +Source Code Formatting:: +NetBeans provides capability to neatly format the source code +following conventions. This can be done for any type of source code, +whether its XML or Java or something else. It is highly recommended to +use this functionality after the code is copy/pasted from this document +to the editor. This keeps the code legible. ++ +-- +This functionality can be accessed by right-clicking in the editor pane +and selecting “Format” as shown. + +.Format code in NetBeans +image::2.1-format.png[] + +This functionality is also accessible using the following keyboard +shortcuts: + +[width="50%"] +|=== +|Shortcut | Operating System + +|kbd:[Ctrl+Shift+F] +|OSX + +|kbd:[Alt+Shift+F] +|Windows + +|kbd:[Alt+Shift+F] +|Linux +|=== +-- + +Automatic Imports:: +Copy/pasting the Java code from this document in NetBeans editor does +not auto-import the classes. This is required to be done manually in +order for the classes to compile. This can be fixed for each missing +import statement by clicking on the yellow bulb shown in the side bar. ++ +-- +.ServerEndpoint import +image::2.1-server-endpoint.png[] + +Alternatively all the imports can be resolved by right-clicking on the +editor pane and selecting ``Fix Imports'' as shown. + +.Fix Imports in NetBeans +image::2.1-fix-imports.png[] + +This functionality is also accessible using the following keyboard +shortcuts: + +[width="50%"] +|=== +|Shortcut | Operating System + +|kbd:[Command+Shift+I] +|OSX + +|kbd:[Ctrl+Shift+I] +|Windows + +|kbd:[Ctrl+Shift+I] +|Linux +|=== + +The defaults may work in most of the cases. Choices are shown in case a +class is available to import from multiple packages. If multiple +packages are available then specific packages to import from are clearly +marked in the document. +-- + +=== Estimated Time + +Following the complete instructions in this document can take any where +from two to four hours. The wide time range accommodates for learning +the new technologies, finding your way in NetBeans, copy/pasting the +code, and debugging the errors. + +The recommended flow is where you follow through the instructions in all +sections in the listed sequence. Alternatively, you may like to cover +section <> through <> in an order of your choice, based upon your +interest and preference of the technology. However section <> is a +pre-requisite for <>. + +Here is an approximate time estimate for each section: + +[cols="4,2"] +|=== +|Section Title |Estimated Time + +|<> +|15 - 30 mins + +|<> +|30 - 45 mins + +|<> +|30 - 45 mins + +|<> +|30 - 45 mins + +|<> +|30 - 45 mins + +|<> +|30 - 45 mins + +|<> +|30 - 45 mins +|=== + +The listed time for each section is only an estimate and by no means +restrict you within that. These sections have been completed in much +shorter time, and you can do it too! + +TIP: The listed time for each section also allows you to create a custom +version of the lab depending upon your target audience and available +time. diff --git a/docs/chapters/todo.adoc b/docs/chapters/todo.adoc new file mode 100644 index 0000000..c0d1944 --- /dev/null +++ b/docs/chapters/todo.adoc @@ -0,0 +1,11 @@ +:imagesdir: ../images + +== TODO + +. Add the following use cases: +.. Concurrency Utilities for Java EE +.. WebSocket Java Client +. Disable errors in persistence.xml +. Add icons for Fix Imports, Format, Fix namespaces, Run the Project. +. Change logging to use java.util.Logging. + diff --git a/docs/chapters/troubleshooting.adoc b/docs/chapters/troubleshooting.adoc new file mode 100644 index 0000000..7faf3c6 --- /dev/null +++ b/docs/chapters/troubleshooting.adoc @@ -0,0 +1,37 @@ +:imagesdir: ../images + +== Troubleshooting + +[qanda] +How can I start/stop/restart the application server from within the IDE ?:: +In the `Services' tab, right-click on +ifdef::server-glassfish[] +`GlassFish Server 4'. +endif::server-glassfish[] +ifdef::server-wildfly[] +`WildFly 8'. +endif::server-wildfly[] +All the commands to start, stop, and restart are available from the pop-up menu. + +I accidentally closed the output log window. How do I bring it back ?:: +In “Services” tab of NetBeans, expand `Servers', choose the application server +node, and select +ifdef::server-glassfish[] +`View Domain Server Log'. ++ +image::netbeans-view-log.png[title="View GlassFish server log in NetBeans"] +endif::server-glassfish[] +ifdef::server-wildfly[] +`View Server Log'. ++ +image::11-wildfly-server-log.png[title="View WildFly server log in NetBeans"] +endif::server-wildfly[] ++ +In addition, the web-based administration console can be seen by clicking on +ifdef::server-glassfish[] +`View Domain Admin Console'. +endif::server-glassfish[] +ifdef::server-wildfly[] +`View Admin Console'. +endif::server-wildfly[] + diff --git a/docs/chapters/walkthrough.adoc b/docs/chapters/walkthrough.adoc new file mode 100644 index 0000000..807e162 --- /dev/null +++ b/docs/chapters/walkthrough.adoc @@ -0,0 +1,314 @@ +:imagesdir: ../images + +[[walk-through]] +== Walk-through of Sample Application + +*Purpose*: This section will download the sample application to be used +in this hands-on lab. A walk-through of the application will be +performed to provide an understanding of the application architecture. + +*Estimated Time*: 15-30 mins + +. Download the sample application from +https://github.com/javaee-samples/javaee7-hol/blob/master/starting-template/movieplex7-starting-template.zip?raw=true[movieplex7-starting-template.zip] +and unzip. This will create a `movieplex7' directory and unzips all the +content there. + +. In NetBeans IDE, select `File', `Open Project', select the +unzipped directory, and click on `Open Project'. The project structure +is shown. ++ +.Project structure in NetBeans +image::3.2-project-structure.png[] + +. Maven Coordinates: Expand `Project Files' and double click on +`pom.xml'. In the `pom.xml', the Java EE 7 API is specified as a +: ++ +-- +[source,xml] + + + javax + javaee-api + 7.0 + provided + + + +This will ensure that Java EE 7 APIs are retrieved from the central +Maven repository. + +[NOTE] +================= +The Java EE 6 platform introduced the notion of `profiles'. A profile is +a configuration of the Java EE platform targeted at a specific class of +applications. All Java EE profiles share a set of common features, such +as naming and resource injection, packaging rules, security +requirements, etc. A profile may contain a proper subset or superset of +the technologies contained in the platform. + +The Java EE Web Profile is a profile of the Java EE Platform +specifically targeted at modern web applications. The complete set of +specifications defined in the Web Profile is defined in the Java EE 7 +Web Profile Specification. +================= +ifdef::server-glassfish[] +GlassFish can be downloaded in two different flavors – Full Platform or Web Profile. + +IMPORTANT: This lab requires Full Platform download. All technologies used in this +lab, except Java Message Service and Batch Applications for the Java +Platform, can be deployed on Web Profile. +endif::server-glassfish[] +ifdef::server-wildfly[] +WildFly can be started in Full Platform or Web Profile. +endif::server-wildfly[] + +-- +. *Default Data Source*: Expand `Other Sources', +`src/main/resources', `META-INF', and double-click on `persistence.xml'. +By default, NetBeans opens the file in Design View. Click on `Source' tab +to view the XML source. ++ +-- +.persistence.xml +image::3.2-persistence-xml.png[] + +It looks like: + +[source,xml] + + + + + + + + + + + + + + + + +Notice `` is commented out, i.e. no data source element +is specified. This element identifies the JDBC resource to connect to in +the runtime environment of the underlying application server. + +The Java EE 7 platform defines a new default data source that must be +provided by the runtime. This pre-configured data source is accessible +under the JNDI name + +[source,java] +java:comp/DefaultDataSource + +The JPA 2.1 specification says if neither `jta-data-source` nor +`non-jta-data-source` elements are specified, the deployer must specify a +JTA data source or the default JTA data source must be provided by the +container. + +ifdef::server-wildfly[] +For WildFly 8, the default data source is bound to the JDBC resource `what name`. +endif::server-wildfly[] +ifdef::server-glassfish[] +For GlassFish 4, the default data source is bound to the JDBC resource +`jdbc/__default`. +endif::server-glassfish[] + +Clicking back and forth between `Design' and `Source' view may prompt +the error shown below: + +.Missing server error from persistence.xml +image::3.4-missing-server.png[] + +This will get resolved when we run the application. Click on `OK' to +dismiss the dialog. +-- + +. *Schema Generation:* JPA 2.1 defines a new set of +`javax.persistence.schema-generation.*` properties that can be used to +generate database artifacts like tables, indexes, and constraints in a +database schema. This helps in prototyping of your application where the +required artifacts are generated either prior to application deployment +or as part of `EntityManagerFactory` creation. This feature will allow +your JPA domain object model to be directly generated in a database. The +generated schema may need to be tuned for actual production environment. ++ +-- +The ``persistence.xml'' in the application has the following +`javax.persistence.schema-generation.*` properties. Their meaning and +possible values are explained: + +|=== +|Property |Meaning |Values + +|`javax.persistence.schema-generation.database.action` +|Specifies the action to be taken by the persistence provider with regard +to the database artifacts. +|`none`, `create`, `drop-and-create`, `drop` + +|`javax.persistence.schema-generation.create-source` +`javax.persistence.schema-generation.drop-source` +|Specifies whether the creation or deletion of database artifacts is to +occur on the basis of the object/relational mapping metadata, DDL +script, or a combination of the two. +|`metadata`, `script`, `metadata-then-script`, `script-then-metadata` + +|`javax.persistence.schema-generation.create-script-source` +`javax.persistence.schema-generation.drop-script-source` +|Specifies a `java.IO.Reader` configured for reading of the SQL script or a +string designating a file URL for the SQL script to create or delete +database artifacts. +| + +|`javax.persistence.sql-load-script-source` +|Specifies a `java.IO.Reader` configured for reading of the SQL load script +for database initialization or a string designating a file URL for the +script. +| +|=== + +Refer to the http://jcp.org/en/jsr/detail?id=338[JPA 2.1 Specification] +for a complete understanding of these properties. + +In the application, the scripts are bundled in the WAR file in +`META-INF' directory. As the location of these scripts is specified as a +URL, the scripts may be loaded from outside the WAR file as well. + +Feel free to open `create.sql', `drop.sql' and `load.sql' and read +through the SQL scripts. The database schema is shown. + +.Database schema +image::3.5-schema.png[] + +This folder also contains `sales.csv' which carries some comma-separated +data, and is used later in the application. +-- + +. *JPA entities, Stateless EJBs, and REST endpoints*: Expand `Source +Packages'. The package `org.javaee7.movieplex7.entities` contains the +JPA entities corresponding to the database table definitions. Each JPA +entity has several convenient `@NamedQuery` defined and uses Bean +Validation constraints to enforce validation. ++ +-- +The package `org.javaee7.movieplex7.rest` contains stateless EJBs +corresponding to different JPA entities. + +Each EJB has methods to perform CRUD operations on the JPA entity and +convenience query methods. Each EJB is also EL-injectable (@Named) and +published as a REST endpoint (@Path). The AplicationConfig class defines +the base path of REST endpoint. The path for the REST endpoint is the +same as the JPA entity class name. + +The mapping between JPA entity classes, EJB classes, and the URI of the +corresponding REST endpoint is shown. + +[cols="1m,1m,1e"] +|=== +|JPA Entity Class |EJB Class |RESTful Path + +|Movie +|MovieFacadeREST +|/webresources/movie + +|Sales +|SalesFacadeREST +|/webresources/sales + +|ShowTiming +|ShowTimingFacadeREST +|/webresources/showtiming + +|Theater +|TheaterFacadeREST +|/webresources/theater + +|Timeslot +|TimeslotFacadeREST +|/webresources/timeslot +|=== + +Feel free to browse through the code. +-- + +. *JSF pages*: `WEB-INF/template.xhtml' defines the template of the +web page and has a header, left navigation bar, and a main content +section. `index.xhtml' uses this template and the EJBs to display the +number of movies and theaters. ++ +-- +Java EE 7 enables CDI discovery of beans by default. No `beans.xml' is +required in `WEB-INF'. This allows all beans with bean defining +annotation, i.e. either a bean with an explicit CDI scope or EJBs to be +available for injection. + +Note, `template.xhtml' is in `WEB-INF' folder as it allows the template +to be accessible from the pages bundled with the application only. If it +were bundled with rest of the pages then it would be accessible outside +the application and thus allowing other external pages to use it as +well. +-- + +. *Run the sample*: Right-click on the project and select `Run'. +This will download all the maven dependencies on your machine, build a +WAR file, deploy on +ifdef::server-glassfish[] +GlassFish 4 +endif::server-glassfish[] +ifdef::server-wildfly[] +WildFly 8 +endif::server-wildfly[] +, and show the URL +http://localhost:8080/movieplex7[localhost:8080/movieplex7] in the +default browser configured in NetBeans. Note that this could take a +while if you have never built a Maven application on your machine. ++ +-- +TIP: The project will show red squiggly lines in the source code indicating +that the classes cannot be resolved. This is expected before the +dependencies are downloaded. However these references will be resolved +correctly after the dependencies are downloaded during project building. + +During the first run, the IDE will ask you to select a deployment server. +ifdef::server-wildfly[] +Choose the configured WildFly server and click on `OK'. + +.WildFly deployment server +image::3.6-wildfly-server.png[] +endif::server-wildfly[] +ifdef::server-glassfish[] +Choose the configured GlassFish server and click on `OK'. + +.GlassFish server +image::3.6-glassfish-server.png[] +endif::server-glassfish[] + +The output looks like as shown. + +.Application main page +image::3.8-first-page.png[] +-- diff --git a/docs/chapters/websocket.adoc b/docs/chapters/websocket.adoc new file mode 100644 index 0000000..12f1b02 --- /dev/null +++ b/docs/chapters/websocket.adoc @@ -0,0 +1,308 @@ +:imagesdir: ../images +:experimental: + +[[websocket]] +== Chat Room (Java API for WebSocket) + +*Purpose*: Build a chat room for viewers. In doing so several new +features of Java API for WebSocket 1.0 will be introduced and +demonstrated by using them in the application. + +*Estimated Time*: 30-45 mins + +WebSocket provide a full-duplex and bi-directional communication +protocol over a single TCP connection. WebSocket is a combination of +http://tools.ietf.org/html/rfc6455[IETF RFC 6455] +http://tools.ietf.org/html/rfc6455[Protocol] and +http://www.w3.org/TR/websockets/[W3C JavaScript WebSocket API] (a +Candidate Recommendation as of this writing). The protocol defines an +opening handshake and data transfer. The API enables Web pages to use +the WebSocket protocol for two-way communication with the remote host. + +http://jcp.org/en/jsr/detail?id=356[JSR 356] defines a standard API for +creating WebSocket applications in the Java EE 7 Platform. The JSR +provides support for: + +* Create WebSocket endpoint using annotations and interface +* Initiating and intercepting WebSocket events +* Creation and consumption of WebSocket text and binary messages +* Configuration and management of WebSocket sessions +* Integration with Java EE security model + +This section will build a chat room for movie viewers. + +. Right-click on `Source Packages' , select `New', `Java Class'. +Give the class name as `ChatServer', package as +`org.javaee7.movieplex7.chat', and click on `Finish'. ++ +. Change the class such that it looks like: ++ +[source,java] +---- +@ServerEndpoint("/websocket") +public class ChatServer { + private static final Set peers = + Collections.synchronizedSet(new HashSet()); + + @OnOpen + public void onOpen(Session peer) { + peers.add(peer); + } + + @OnClose + public void onClose(Session peer) { + peers.remove(peer); + } + + @OnMessage + public void message(String message, Session client) + throws IOException, EncodeException { + for (Session peer : peers) { + peer.getBasicRemote().sendText(message); + } + } +} +---- ++ +In this code: ++ +.. `@ServerEndpoint` decorates the class to be a WebSocket endpoint. The +value defines the URI where this endpoint is published. +.. `@OnOpen` and `@OnClose` decorate the methods that must be called when +WebSocket session is opened or closed. The peer parameter defines the +client requesting connection initiation and termination. +.. `@OnMessage` decorates the message that receives the incoming WebSocket +message. The first parameter, message, is the payload of the message. +The second parameter, `client`, defines the other end of the WebSocket +connection. The method implementation transmits the received text message to +all clients connected to this endpoint. ++ +Resolve the imports by right-clicking in the editor and selecting `Fix +Imports' or (kbd:[Command+Shift+I] shortcut on OSX or kbd:[Ctrl+Shift+I] on +Windows). ++ +WARNING: Make sure to pick `java.websocket.Session` for resolving imports. This is not the default option shown by NetBeans. ++ +image::4.2-imports.png[title="javax.websocket.Session import"] ++ +Right-click again in the editor pane and select `Format' to format your +code. ++ +. In `Web Pages', select `New', `Folder', give the folder name as +`chat' and click on `Finish'. ++ +. Right-click on the newly created folder, select `New', `Other', +`Java Server Faces', `Facelets Template Client', give the File Name as +`chatroom'. Click on `Browse' next to `Template:', expand `Web Pages', +`WEB-INF', select `template.xhtml', and click on `Select File'. Click on +`Finish'. ++ +image::4.4-template.png[title="Choose template"] ++ +In this file, remove sections where name attribute value is +`top' and `left'. These sections are inherited from the template. ++ +Replace `` section with `content' name such that it looks like: ++ +[source,xml] + + +
+ + + + + + + + +
+ Chat Log
+ +
+ Users
+ +
+ + +

+ +

+
+
+ +
+
++ +The code builds an HTML form that has two textareas – one to display the +chat log and the other to display the list of users currently logged. A +single text box is used to take the user name or the chat message. +Clicking on `Join' button takes the value as user name and clicking on +`Send' takes the value as chat message. ++ +JavaScript methods are invoked +when these buttons are clicked and these are explained in the next +section. The chat messages are sent and received as WebSocket payloads. +There is an explicit button to disconnect the WebSocket connection. +`output` div is the placeholder for status messages. The WebSocket +initialization occurs in `websocket.js' included at the bottom of the +fragment. ++ +. Right-click on `chat' in `Web Pages', select `New', `Other', `Web' +categories, `JavaScript File' file type. Click on `Next'. ++ +Give the name as `websocket' and click on `Finish'. ++ +. Edit the contents of `websocket.js' such that it looks like: ++ +[source,javascript] +---- +var wsUri = 'ws://' + document.location.host + + document.location.pathname.substr(0, + document.location.pathname.indexOf("/faces")) + + '/websocket'; +console.log(wsUri); + +var websocket = new WebSocket(wsUri); +var textField = document.getElementById("textField"); +var users = document.getElementById("users"); +var chatlog = document.getElementById("chatlog"); +var username; + +websocket.onopen = function(evt) { onOpen(evt); }; +websocket.onmessage = function(evt) { onMessage(evt); }; +websocket.onerror = function(evt) { onError(evt); }; +websocket.onclose = function(evt) { onClose(evt); }; + +var output = document.getElementById("output"); + +function join() { + username = textField.value; + websocket.send(username + " joined"); +} + +function send_message() { + websocket.send(username + ": " + textField.value); +} + +function onOpen() { + writeToScreen("CONNECTED"); +} + +function onClose() { + writeToScreen("DISCONNECTED"); +} + +function onMessage(evt) { + writeToScreen("RECEIVED: " + evt.data); + if (evt.data.indexOf("joined") !== -1) { + users.innerHTML += evt.data.substring(0, evt.data.indexOf(" joined")) + "\n"; + } else { + chatlog.innerHTML += evt.data + "\n"; + } +} + +function onError(evt) { + writeToScreen('ERROR: ' + evt.data); +} + +function disconnect() { + websocket.close(); +} + +function writeToScreen(message) { + var pre = document.createElement("p"); + pre.style.wordWrap = "break-word"; + pre.innerHTML = message; + output.appendChild(pre); +} +---- ++ +The WebSocket endpoint URI is calculated by using standard JavaScript +variables and appending the URI specified in the `ChatServer` class. +WebSocket is initialized by calling new `WebSocket(...)`. Event handlers are +registered for lifecycle events using `onXXX` messages. The listeners +registered in this script are explained in the table. ++ +[cols="1,3" options="header"] +|=== +| Listeners | Called When + +| `onOpen(evt)` | WebSocket connection is initiated + +| `onMessage(evt)` | WebSocket message is received + +| `onError(evt)` | Error occurs during the communication + +| `onClose(evt)` | WebSocket connection is terminated +|=== ++ +Any relevant data is passed along as parameter to the function. Each +method prints the status on the browser using `writeToScreen` utility +method. The join method sends a message to the endpoint +that a particular user has joined. The endpoint then broadcasts the +message to all the listening clients. The `send_message` method appends +the logged in user name and the value of the text field and broadcasts +to all the clients similarly. The `onMessage` method updates the list of +logged in users as well. ++ +. Edit `WEB-INF/template.xhtml' and change: ++ +[source,xml] +Item 2 ++ +to ++ +[source,xml] + + Chat Room + ++ +The `outputLink` tag renders an HTML anchor tag with an `href` attribute. +`${facesContext.externalContext.requestContextPath}` provides the request +URI that identifies the web application context for this request. This +allows the links in the left navigation bar to be fully-qualified URLs. ++ +. Run the project by right clicking on the project and selecting +`Run'. The browser shows +http://localhost:8080/movieplex7[localhost:8080/movieplex7]. ++ +image::4.6-chatroom.png[title="Chatroom link on main page"] ++ +Click on `Chat Room' to see the output. ++ +The `CONNECTED' status message is shown and indicates that the WebSocket +connection with the endpoint is established. ++ +image::4.8-chatroom.png[title="Chatroom output"] ++ +Please make sure your browser supports WebSocket in order for this page +to show up successfully. Chrome 14.0+, Firefox 11.0+, Safari 6.0+, and +IE 10.0+ are the browsers that support WebSocket. A complete list of +supported browsers is available at +http://caniuse.com/websockets[caniuse.com/websockets]. ++ +Open the URI http://localhost:8080/movieplex7[localhost:8080/movieplex7] +in another browser window. Enter `Duke' in the text box in the first +browser and click `Join'. ++ +image::4.8-chatroom-joined.png[title="Chatroom with single user"] ++ +Notice that the user list and the status message in both the browsers +gets updated. Enter `James' in the text box of the second browser and +click on `Join'. Once again the user list and the status message in both +the browsers is updated. Now you can type any messages in any of the +browser and click on `Send' to send the message. ++ +The output from two different browsers after the initial greeting looks +like as shown. ++ +image::4.8-chatroom-two-browsers.png[title="Chatroom with two users"] ++ +Here it shows output from Chrome on the top and Firefox on the bottom. ++ +Chrome Developer Tools or Firebug in Firefox can be used to monitor +WebSocket traffic. + diff --git a/docs/convert.sh b/docs/convert.sh new file mode 100755 index 0000000..ce469af --- /dev/null +++ b/docs/convert.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +# Run using: +# +# ./convert.sh +# +# or +# +# ./convert.sh html,pdf +# +# ...where the first argument is a comma-delimited list of formats + +# Program paths +ASCIIDOCTOR=asciidoctor +FOPUB=~/tools/asciidoctor-fopub/fopub +ASCIIDOCTOR_PDF=~/tools/asciidoctor-pdf/bin/asciidoctor-pdf +#FOPUB=~/projects/asciidoctor/fopub/fopub +#ASCIIDOCTOR_PDF=~/projects/asciidoctor/asciidoctor-pdf/bin/asciidoctor-pdf + +# File names +MASTER_ADOC=javaee7-hol.adoc +MASTER_DOCBOOK=${MASTER_ADOC/.adoc/.xml} + +# Command options +SHARED_OPTIONS='-a numbered -a experimental -a source-highlighter=coderay -a imagesdir=images' + +# Formats +if [ ! -z $1 ]; then + read -a FORMATS <<<$(IFS=','; echo $1) +else + FORMATS=(html +docbook +fopdf) +fi + +for f in ${FORMATS[*]}; do + if [ $f == 'html' ]; then + echo "Converting to HTML ..." + $ASCIIDOCTOR -v $SHARED_OPTIONS $MASTER_ADOC + elif [ $f == 'docbook' ]; then + echo "Converting to DocBook ..." + $ASCIIDOCTOR -b docbook $SHARED_OPTIONS $MASTER_ADOC + elif [ $f == 'fopdf' ]; then + echo "Converting to FO-PDF ..." + $FOPUB $MASTER_DOCBOOK + elif [ $f == 'pdf' ]; then + echo "Converting to PDF ..." + $ASCIIDOCTOR_PDF $SHARED_OPTIONS $MASTER_ADOC + fi +done + +exit 0 diff --git a/docs/images/1.1-netbeans-download.png b/docs/images/1.1-netbeans-download.png new file mode 100644 index 0000000..6d15885 Binary files /dev/null and b/docs/images/1.1-netbeans-download.png differ diff --git a/docs/images/11-wildfly-server-log.png b/docs/images/11-wildfly-server-log.png new file mode 100644 index 0000000..6846731 Binary files /dev/null and b/docs/images/11-wildfly-server-log.png differ diff --git a/docs/images/16-netbeans-add-dev-update-center.png b/docs/images/16-netbeans-add-dev-update-center.png new file mode 100644 index 0000000..7eefcc2 Binary files /dev/null and b/docs/images/16-netbeans-add-dev-update-center.png differ diff --git a/docs/images/16-netbeans-add-instance-wildfly.png b/docs/images/16-netbeans-add-instance-wildfly.png new file mode 100644 index 0000000..514866f Binary files /dev/null and b/docs/images/16-netbeans-add-instance-wildfly.png differ diff --git a/docs/images/16-netbeans-available-plugins-wildfly.png b/docs/images/16-netbeans-available-plugins-wildfly.png new file mode 100644 index 0000000..060cc7b Binary files /dev/null and b/docs/images/16-netbeans-available-plugins-wildfly.png differ diff --git a/docs/images/16-netbeans-wildfly-full-platform.png b/docs/images/16-netbeans-wildfly-full-platform.png new file mode 100644 index 0000000..e67562d Binary files /dev/null and b/docs/images/16-netbeans-wildfly-full-platform.png differ diff --git a/docs/images/16-netbeans-wildfly-server.png b/docs/images/16-netbeans-wildfly-server.png new file mode 100644 index 0000000..497f37d Binary files /dev/null and b/docs/images/16-netbeans-wildfly-server.png differ diff --git a/docs/images/2.0-problem-statement.png b/docs/images/2.0-problem-statement.png new file mode 100644 index 0000000..bb7f307 Binary files /dev/null and b/docs/images/2.0-problem-statement.png differ diff --git a/docs/images/2.0-technologies.png b/docs/images/2.0-technologies.png new file mode 100644 index 0000000..ebf0f96 Binary files /dev/null and b/docs/images/2.0-technologies.png differ diff --git a/docs/images/2.1-fix-imports.png b/docs/images/2.1-fix-imports.png new file mode 100644 index 0000000..2f5397d Binary files /dev/null and b/docs/images/2.1-fix-imports.png differ diff --git a/docs/images/2.1-format.png b/docs/images/2.1-format.png new file mode 100644 index 0000000..1fc6364 Binary files /dev/null and b/docs/images/2.1-format.png differ diff --git a/docs/images/2.1-server-endpoint.png b/docs/images/2.1-server-endpoint.png new file mode 100644 index 0000000..4d92c91 Binary files /dev/null and b/docs/images/2.1-server-endpoint.png differ diff --git a/docs/images/3.2-persistence-xml.png b/docs/images/3.2-persistence-xml.png new file mode 100644 index 0000000..a2d1972 Binary files /dev/null and b/docs/images/3.2-persistence-xml.png differ diff --git a/docs/images/3.2-project-structure.png b/docs/images/3.2-project-structure.png new file mode 100644 index 0000000..7e49a92 Binary files /dev/null and b/docs/images/3.2-project-structure.png differ diff --git a/docs/images/3.4-missing-server.png b/docs/images/3.4-missing-server.png new file mode 100644 index 0000000..f96bfa3 Binary files /dev/null and b/docs/images/3.4-missing-server.png differ diff --git a/docs/images/3.5-schema.png b/docs/images/3.5-schema.png new file mode 100644 index 0000000..d308d3a Binary files /dev/null and b/docs/images/3.5-schema.png differ diff --git a/docs/images/3.6-glassfish-server.png b/docs/images/3.6-glassfish-server.png new file mode 100644 index 0000000..465f63f Binary files /dev/null and b/docs/images/3.6-glassfish-server.png differ diff --git a/docs/images/3.6-wildfly-server.png b/docs/images/3.6-wildfly-server.png new file mode 100644 index 0000000..9dfcedf Binary files /dev/null and b/docs/images/3.6-wildfly-server.png differ diff --git a/docs/images/3.8-first-page.png b/docs/images/3.8-first-page.png new file mode 100644 index 0000000..11f8485 Binary files /dev/null and b/docs/images/3.8-first-page.png differ diff --git a/docs/images/4.2-imports.png b/docs/images/4.2-imports.png new file mode 100644 index 0000000..26da576 Binary files /dev/null and b/docs/images/4.2-imports.png differ diff --git a/docs/images/4.4-template.png b/docs/images/4.4-template.png new file mode 100644 index 0000000..a5c8b7b Binary files /dev/null and b/docs/images/4.4-template.png differ diff --git a/docs/images/4.6-chatroom.png b/docs/images/4.6-chatroom.png new file mode 100644 index 0000000..b4817a3 Binary files /dev/null and b/docs/images/4.6-chatroom.png differ diff --git a/docs/images/4.8-chatroom-joined.png b/docs/images/4.8-chatroom-joined.png new file mode 100644 index 0000000..7ac7e5d Binary files /dev/null and b/docs/images/4.8-chatroom-joined.png differ diff --git a/docs/images/4.8-chatroom-two-browsers.png b/docs/images/4.8-chatroom-two-browsers.png new file mode 100644 index 0000000..31ef745 Binary files /dev/null and b/docs/images/4.8-chatroom-two-browsers.png differ diff --git a/docs/images/4.8-chatroom.png b/docs/images/4.8-chatroom.png new file mode 100644 index 0000000..579e8f2 Binary files /dev/null and b/docs/images/4.8-chatroom.png differ diff --git a/docs/images/5.0-batch-intro.png b/docs/images/5.0-batch-intro.png new file mode 100644 index 0000000..44638de Binary files /dev/null and b/docs/images/5.0-batch-intro.png differ diff --git a/docs/images/5.10-imports.png b/docs/images/5.10-imports.png new file mode 100644 index 0000000..2f447a9 Binary files /dev/null and b/docs/images/5.10-imports.png differ diff --git a/docs/images/5.14-sales.png b/docs/images/5.14-sales.png new file mode 100644 index 0000000..0fb1d96 Binary files /dev/null and b/docs/images/5.14-sales.png differ diff --git a/docs/images/5.15-sales.png b/docs/images/5.15-sales.png new file mode 100644 index 0000000..f74ae57 Binary files /dev/null and b/docs/images/5.15-sales.png differ diff --git a/docs/images/5.16-sales-output.png b/docs/images/5.16-sales-output.png new file mode 100644 index 0000000..4487df1 Binary files /dev/null and b/docs/images/5.16-sales-output.png differ diff --git a/docs/images/6.12-output.png b/docs/images/6.12-output.png new file mode 100644 index 0000000..8465916 Binary files /dev/null and b/docs/images/6.12-output.png differ diff --git a/docs/images/6.14-output.png b/docs/images/6.14-output.png new file mode 100644 index 0000000..2cd85fc Binary files /dev/null and b/docs/images/6.14-output.png differ diff --git a/docs/images/6.2-imports.png b/docs/images/6.2-imports.png new file mode 100644 index 0000000..adbf0cc Binary files /dev/null and b/docs/images/6.2-imports.png differ diff --git a/docs/images/6.6-imports.png b/docs/images/6.6-imports.png new file mode 100644 index 0000000..a9820c0 Binary files /dev/null and b/docs/images/6.6-imports.png differ diff --git a/docs/images/6.8-output.png b/docs/images/6.8-output.png new file mode 100644 index 0000000..2cc8d38 Binary files /dev/null and b/docs/images/6.8-output.png differ diff --git a/docs/images/7.11-imports.png b/docs/images/7.11-imports.png new file mode 100644 index 0000000..352b9b9 Binary files /dev/null and b/docs/images/7.11-imports.png differ diff --git a/docs/images/7.14-imports.png b/docs/images/7.14-imports.png new file mode 100644 index 0000000..807d980 Binary files /dev/null and b/docs/images/7.14-imports.png differ diff --git a/docs/images/7.15-output.png b/docs/images/7.15-output.png new file mode 100644 index 0000000..9bef70e Binary files /dev/null and b/docs/images/7.15-output.png differ diff --git a/docs/images/7.16-output.png b/docs/images/7.16-output.png new file mode 100644 index 0000000..1118c95 Binary files /dev/null and b/docs/images/7.16-output.png differ diff --git a/docs/images/7.16-output2.png b/docs/images/7.16-output2.png new file mode 100644 index 0000000..31966f9 Binary files /dev/null and b/docs/images/7.16-output2.png differ diff --git a/docs/images/7.2-imports.png b/docs/images/7.2-imports.png new file mode 100644 index 0000000..2279197 Binary files /dev/null and b/docs/images/7.2-imports.png differ diff --git a/docs/images/7.3-implements.png b/docs/images/7.3-implements.png new file mode 100644 index 0000000..3c18095 Binary files /dev/null and b/docs/images/7.3-implements.png differ diff --git a/docs/images/7.6-imports.png b/docs/images/7.6-imports.png new file mode 100644 index 0000000..4293c1d Binary files /dev/null and b/docs/images/7.6-imports.png differ diff --git a/docs/images/7.7-implements.png b/docs/images/7.7-implements.png new file mode 100644 index 0000000..3bdb2bc Binary files /dev/null and b/docs/images/7.7-implements.png differ diff --git a/docs/images/8.10-output.png b/docs/images/8.10-output.png new file mode 100644 index 0000000..fd43ec1 Binary files /dev/null and b/docs/images/8.10-output.png differ diff --git a/docs/images/8.10-output2.png b/docs/images/8.10-output2.png new file mode 100644 index 0000000..19cc72d Binary files /dev/null and b/docs/images/8.10-output2.png differ diff --git a/docs/images/8.10-output3.png b/docs/images/8.10-output3.png new file mode 100644 index 0000000..91f99c5 Binary files /dev/null and b/docs/images/8.10-output3.png differ diff --git a/docs/images/8.10-output4.png b/docs/images/8.10-output4.png new file mode 100644 index 0000000..76967ba Binary files /dev/null and b/docs/images/8.10-output4.png differ diff --git a/docs/images/8.10-output5.png b/docs/images/8.10-output5.png new file mode 100644 index 0000000..be2be9a Binary files /dev/null and b/docs/images/8.10-output5.png differ diff --git a/docs/images/8.10-output6.png b/docs/images/8.10-output6.png new file mode 100644 index 0000000..cffa7f0 Binary files /dev/null and b/docs/images/8.10-output6.png differ diff --git a/docs/images/8.10-output7.png b/docs/images/8.10-output7.png new file mode 100644 index 0000000..9e17b8c Binary files /dev/null and b/docs/images/8.10-output7.png differ diff --git a/docs/images/8.2-imports.png b/docs/images/8.2-imports.png new file mode 100644 index 0000000..3c088bc Binary files /dev/null and b/docs/images/8.2-imports.png differ diff --git a/docs/images/9.11-output.png b/docs/images/9.11-output.png new file mode 100644 index 0000000..6dbcc7c Binary files /dev/null and b/docs/images/9.11-output.png differ diff --git a/docs/images/9.11-output2.png b/docs/images/9.11-output2.png new file mode 100644 index 0000000..1a163ca Binary files /dev/null and b/docs/images/9.11-output2.png differ diff --git a/docs/images/9.11-output3.png b/docs/images/9.11-output3.png new file mode 100644 index 0000000..ec6a48a Binary files /dev/null and b/docs/images/9.11-output3.png differ diff --git a/docs/images/9.11-output4.png b/docs/images/9.11-output4.png new file mode 100644 index 0000000..a43de29 Binary files /dev/null and b/docs/images/9.11-output4.png differ diff --git a/docs/images/9.11-output5.png b/docs/images/9.11-output5.png new file mode 100644 index 0000000..4384843 Binary files /dev/null and b/docs/images/9.11-output5.png differ diff --git a/docs/images/9.3-imports.png b/docs/images/9.3-imports.png new file mode 100644 index 0000000..6cab382 Binary files /dev/null and b/docs/images/9.3-imports.png differ diff --git a/docs/images/i13-back-icon.png b/docs/images/i13-back-icon.png new file mode 100755 index 0000000..0c05e47 Binary files /dev/null and b/docs/images/i13-back-icon.png differ diff --git a/docs/images/i13-ellipsis-button.png b/docs/images/i13-ellipsis-button.png new file mode 100755 index 0000000..f182c44 Binary files /dev/null and b/docs/images/i13-ellipsis-button.png differ diff --git a/docs/images/i13-exclamation-mark-icon.png b/docs/images/i13-exclamation-mark-icon.png new file mode 100755 index 0000000..19b168b Binary files /dev/null and b/docs/images/i13-exclamation-mark-icon.png differ diff --git a/docs/images/i13-glassfish-defined.png b/docs/images/i13-glassfish-defined.png new file mode 100755 index 0000000..7aa34c9 Binary files /dev/null and b/docs/images/i13-glassfish-defined.png differ diff --git a/docs/images/i13-glassfish-fix-deployment.png b/docs/images/i13-glassfish-fix-deployment.png new file mode 100755 index 0000000..e17e261 Binary files /dev/null and b/docs/images/i13-glassfish-fix-deployment.png differ diff --git a/docs/images/i13-glassfish-fix-domain.png b/docs/images/i13-glassfish-fix-domain.png new file mode 100755 index 0000000..e7d0cc4 Binary files /dev/null and b/docs/images/i13-glassfish-fix-domain.png differ diff --git a/docs/images/i13-glassfish-fix-url.png b/docs/images/i13-glassfish-fix-url.png new file mode 100755 index 0000000..c35c0a5 Binary files /dev/null and b/docs/images/i13-glassfish-fix-url.png differ diff --git a/docs/images/i13-glassfish-home-directory.png b/docs/images/i13-glassfish-home-directory.png new file mode 100755 index 0000000..28a3490 Binary files /dev/null and b/docs/images/i13-glassfish-home-directory.png differ diff --git a/docs/images/i13-glassfish-server-dialog-final.png b/docs/images/i13-glassfish-server-dialog-final.png new file mode 100755 index 0000000..e275ed2 Binary files /dev/null and b/docs/images/i13-glassfish-server-dialog-final.png differ diff --git a/docs/images/i13-glassfish-server-dialog-initial.png b/docs/images/i13-glassfish-server-dialog-initial.png new file mode 100755 index 0000000..593b531 Binary files /dev/null and b/docs/images/i13-glassfish-server-dialog-initial.png differ diff --git a/docs/images/i13-glassfish-start-database.png b/docs/images/i13-glassfish-start-database.png new file mode 100755 index 0000000..8fa91cb Binary files /dev/null and b/docs/images/i13-glassfish-start-database.png differ diff --git a/docs/images/i13-initial-project-structure.png b/docs/images/i13-initial-project-structure.png new file mode 100755 index 0000000..ef05105 Binary files /dev/null and b/docs/images/i13-initial-project-structure.png differ diff --git a/docs/images/i13-jboss-defined.png b/docs/images/i13-jboss-defined.png new file mode 100755 index 0000000..dde050e Binary files /dev/null and b/docs/images/i13-jboss-defined.png differ diff --git a/docs/images/i13-jboss-fix-deployment.png b/docs/images/i13-jboss-fix-deployment.png new file mode 100755 index 0000000..780be81 Binary files /dev/null and b/docs/images/i13-jboss-fix-deployment.png differ diff --git a/docs/images/i13-jboss-fix-extension.png b/docs/images/i13-jboss-fix-extension.png new file mode 100755 index 0000000..6b25387 Binary files /dev/null and b/docs/images/i13-jboss-fix-extension.png differ diff --git a/docs/images/i13-jboss-home-directory.png b/docs/images/i13-jboss-home-directory.png new file mode 100755 index 0000000..d3ad87f Binary files /dev/null and b/docs/images/i13-jboss-home-directory.png differ diff --git a/docs/images/i13-jboss-invalid-extension.png b/docs/images/i13-jboss-invalid-extension.png new file mode 100755 index 0000000..e2b96c2 Binary files /dev/null and b/docs/images/i13-jboss-invalid-extension.png differ diff --git a/docs/images/i13-jboss-server-dialog-final.png b/docs/images/i13-jboss-server-dialog-final.png new file mode 100755 index 0000000..f5f3056 Binary files /dev/null and b/docs/images/i13-jboss-server-dialog-final.png differ diff --git a/docs/images/i13-jboss-server-dialog-initial.png b/docs/images/i13-jboss-server-dialog-initial.png new file mode 100755 index 0000000..efdb007 Binary files /dev/null and b/docs/images/i13-jboss-server-dialog-initial.png differ diff --git a/docs/images/i13-jboss-url-fixed.png b/docs/images/i13-jboss-url-fixed.png new file mode 100755 index 0000000..b7dcb50 Binary files /dev/null and b/docs/images/i13-jboss-url-fixed.png differ diff --git a/docs/images/i13-jdk-defined.png b/docs/images/i13-jdk-defined.png new file mode 100755 index 0000000..97e8ce5 Binary files /dev/null and b/docs/images/i13-jdk-defined.png differ diff --git a/docs/images/i13-jdk-home.png b/docs/images/i13-jdk-home.png new file mode 100755 index 0000000..a2dc333 Binary files /dev/null and b/docs/images/i13-jdk-home.png differ diff --git a/docs/images/i13-jpa-detected-event-log.png b/docs/images/i13-jpa-detected-event-log.png new file mode 100755 index 0000000..54f2b7c Binary files /dev/null and b/docs/images/i13-jpa-detected-event-log.png differ diff --git a/docs/images/i13-jpa-detected-status-bar.png b/docs/images/i13-jpa-detected-status-bar.png new file mode 100755 index 0000000..25fd3dd Binary files /dev/null and b/docs/images/i13-jpa-detected-status-bar.png differ diff --git a/docs/images/i13-jpa-detected.png b/docs/images/i13-jpa-detected.png new file mode 100755 index 0000000..a59818e Binary files /dev/null and b/docs/images/i13-jpa-detected.png differ diff --git a/docs/images/i13-open-project.png b/docs/images/i13-open-project.png new file mode 100755 index 0000000..4eefa9a Binary files /dev/null and b/docs/images/i13-open-project.png differ diff --git a/docs/images/i13-open-terminal.png b/docs/images/i13-open-terminal.png new file mode 100755 index 0000000..22fb319 Binary files /dev/null and b/docs/images/i13-open-terminal.png differ diff --git a/docs/images/i13-plus-glassfish.png b/docs/images/i13-plus-glassfish.png new file mode 100755 index 0000000..6bbad3e Binary files /dev/null and b/docs/images/i13-plus-glassfish.png differ diff --git a/docs/images/i13-plus-icon.png b/docs/images/i13-plus-icon.png new file mode 100755 index 0000000..b2fac58 Binary files /dev/null and b/docs/images/i13-plus-icon.png differ diff --git a/docs/images/i13-plus-jboss.png b/docs/images/i13-plus-jboss.png new file mode 100755 index 0000000..9f9bfc6 Binary files /dev/null and b/docs/images/i13-plus-jboss.png differ diff --git a/docs/images/i13-plus-jdk.png b/docs/images/i13-plus-jdk.png new file mode 100755 index 0000000..3bef0be Binary files /dev/null and b/docs/images/i13-plus-jdk.png differ diff --git a/docs/images/i13-project-sdk.png b/docs/images/i13-project-sdk.png new file mode 100755 index 0000000..377da32 Binary files /dev/null and b/docs/images/i13-project-sdk.png differ diff --git a/docs/images/i13-run-configs-plus-glassfish.png b/docs/images/i13-run-configs-plus-glassfish.png new file mode 100755 index 0000000..a2c49b0 Binary files /dev/null and b/docs/images/i13-run-configs-plus-glassfish.png differ diff --git a/docs/images/i13-run-configs-plus-jboss.png b/docs/images/i13-run-configs-plus-jboss.png new file mode 100755 index 0000000..380fd23 Binary files /dev/null and b/docs/images/i13-run-configs-plus-jboss.png differ diff --git a/docs/images/i13-run-edit-configurations.png b/docs/images/i13-run-edit-configurations.png new file mode 100755 index 0000000..8c489da Binary files /dev/null and b/docs/images/i13-run-edit-configurations.png differ diff --git a/docs/images/i13-run-glassfish.png b/docs/images/i13-run-glassfish.png new file mode 100755 index 0000000..ecda42d Binary files /dev/null and b/docs/images/i13-run-glassfish.png differ diff --git a/docs/images/i13-run-icon.png b/docs/images/i13-run-icon.png new file mode 100755 index 0000000..c1a405b Binary files /dev/null and b/docs/images/i13-run-icon.png differ diff --git a/docs/images/i13-run-tool-window-glassfish.png b/docs/images/i13-run-tool-window-glassfish.png new file mode 100755 index 0000000..3cb828a Binary files /dev/null and b/docs/images/i13-run-tool-window-glassfish.png differ diff --git a/docs/images/i13-run-tool-window-wildfly.png b/docs/images/i13-run-tool-window-wildfly.png new file mode 100755 index 0000000..458d2ee Binary files /dev/null and b/docs/images/i13-run-tool-window-wildfly.png differ diff --git a/docs/images/i13-run-wildfly.png b/docs/images/i13-run-wildfly.png new file mode 100755 index 0000000..4066961 Binary files /dev/null and b/docs/images/i13-run-wildfly.png differ diff --git a/docs/images/i13-select-pom.png b/docs/images/i13-select-pom.png new file mode 100755 index 0000000..8827d2b Binary files /dev/null and b/docs/images/i13-select-pom.png differ diff --git a/docs/images/i13-setup-frameworks-jpa.png b/docs/images/i13-setup-frameworks-jpa.png new file mode 100755 index 0000000..3500e00 Binary files /dev/null and b/docs/images/i13-setup-frameworks-jpa.png differ diff --git a/docs/images/i13-show-tool-windows-icon.png b/docs/images/i13-show-tool-windows-icon.png new file mode 100755 index 0000000..ddfd7a6 Binary files /dev/null and b/docs/images/i13-show-tool-windows-icon.png differ diff --git a/docs/images/i13-starting-page-in-browser.png b/docs/images/i13-starting-page-in-browser.png new file mode 100755 index 0000000..cfc6bee Binary files /dev/null and b/docs/images/i13-starting-page-in-browser.png differ diff --git a/docs/images/i13-welcome-configure.png b/docs/images/i13-welcome-configure.png new file mode 100755 index 0000000..a08a02e Binary files /dev/null and b/docs/images/i13-welcome-configure.png differ diff --git a/docs/images/idea-add-glassfish-server-configuration.png b/docs/images/idea-add-glassfish-server-configuration.png new file mode 100644 index 0000000..e713f03 Binary files /dev/null and b/docs/images/idea-add-glassfish-server-configuration.png differ diff --git a/docs/images/idea-configure-jpa-dialogbox.png b/docs/images/idea-configure-jpa-dialogbox.png new file mode 100644 index 0000000..a2b46bd Binary files /dev/null and b/docs/images/idea-configure-jpa-dialogbox.png differ diff --git a/docs/images/idea-configure-jpa.png b/docs/images/idea-configure-jpa.png new file mode 100644 index 0000000..9d9c884 Binary files /dev/null and b/docs/images/idea-configure-jpa.png differ diff --git a/docs/images/idea-convertproject.png b/docs/images/idea-convertproject.png new file mode 100644 index 0000000..5cab240 Binary files /dev/null and b/docs/images/idea-convertproject.png differ diff --git a/docs/images/idea-edit-glassfish-server-configuration-DeploymentTab.png b/docs/images/idea-edit-glassfish-server-configuration-DeploymentTab.png new file mode 100644 index 0000000..d19d24f Binary files /dev/null and b/docs/images/idea-edit-glassfish-server-configuration-DeploymentTab.png differ diff --git a/docs/images/idea-edit-glassfish-server-configuration-servertab.png b/docs/images/idea-edit-glassfish-server-configuration-servertab.png new file mode 100644 index 0000000..a7f2493 Binary files /dev/null and b/docs/images/idea-edit-glassfish-server-configuration-servertab.png differ diff --git a/docs/images/idea-mavenprojects-run-package-command.png b/docs/images/idea-mavenprojects-run-package-command.png new file mode 100644 index 0000000..92ede12 Binary files /dev/null and b/docs/images/idea-mavenprojects-run-package-command.png differ diff --git a/docs/images/idea-open-mavenprojects-pane.png b/docs/images/idea-open-mavenprojects-pane.png new file mode 100644 index 0000000..907d777 Binary files /dev/null and b/docs/images/idea-open-mavenprojects-pane.png differ diff --git a/docs/images/netbeans-addserver.png b/docs/images/netbeans-addserver.png new file mode 100644 index 0000000..67f9a91 Binary files /dev/null and b/docs/images/netbeans-addserver.png differ diff --git a/docs/images/netbeans-view-log.png b/docs/images/netbeans-view-log.png new file mode 100644 index 0000000..ed64dd4 Binary files /dev/null and b/docs/images/netbeans-view-log.png differ diff --git a/docs/javaee7-hol.adoc b/docs/javaee7-hol.adoc new file mode 100644 index 0000000..bda142c --- /dev/null +++ b/docs/javaee7-hol.adoc @@ -0,0 +1,41 @@ += Java EE 7 Hands-on Lab +Arun Gupta +v2.1, Jun 25, 2014 +:doctype: book +:toc: +:toclevels: 3 +:ide-netbeans: true +:server-wildfly: true + +include::chapters/introduction.adoc[] + +include::chapters/statement.adoc[] + +include::chapters/walkthrough.adoc[] + +include::chapters/websocket.adoc[] + +include::chapters/batch.adoc[] + +include::chapters/jaxrs.adoc[] + +include::chapters/json.adoc[] + +include::chapters/jms.adoc[] + +include::chapters/jsf.adoc[] + +include::chapters/conclusion.adoc[] + +include::chapters/troubleshooting.adoc[] + +include::chapters/acks.adoc[] + +include::chapters/solutions.adoc[] + +include::chapters/todo.adoc[] + +include::chapters/revision.adoc[] + +include::chapters/appendix.adoc[] + diff --git a/docs/javaee7-hol.html b/docs/javaee7-hol.html new file mode 100644 index 0000000..883fe06 --- /dev/null +++ b/docs/javaee7-hol.html @@ -0,0 +1,4820 @@ + + + + + + +Java EE 7 Hands-on Lab + + + + + +
+
+

1. Introduction

+
+
+

The Java EE 7 platform continues the ease of development push that +characterized prior releases by bringing further simplification to +enterprise development. It adds new and important APIs such as the REST +client API in JAX-RS 2.0 and the long awaited Batch Processing API. Java +Message Service 2.0 has undergone an extreme makeover to align with the +improvements in the Java language. There are plenty of improvements to +several other components. Newer web standards like HTML 5, WebSocket, +and JSON processing are embraced to build modern web applications.

+
+
+

This hands-on lab will build a typical 3-tier end-to-end application +using the following Java EE 7 technologies:

+
+
+
    +
  • +

    Java API for WebSocket 1.0 (JSR 356)

    +
  • +
  • +

    Batch Applications for the Java Platform 1.0 (JSR 352)

    +
  • +
  • +

    Java API for JSON Processing 1.0 (JSR 353)

    +
  • +
  • +

    Java API for RESTful Web Services 2.0 (JSR 339)

    +
  • +
  • +

    Java Message Service 2.0 (JSR 343)

    +
  • +
  • +

    Java Persistence API 2.1 (JSR 338)

    +
  • +
  • +

    JavaServer Faces 2.2 (JSR 344)

    +
  • +
  • +

    Contexts and Dependency Injection 1.1 (JSR 346)

    +
  • +
  • +

    Bean Validation 1.1 (JSR 349)

    +
  • +
  • +

    Java Transaction API 1.2 (JSR 907)

    +
  • +
+
+
+

Together these APIs will allow you to be more productive by simplifying enterprise development.

+
+
+

The latest version of this document can be downloaded from javaee7-hol.html. Please file issues or send pull requests for any updates.

+
+
+

1.1. Software Requirement

+
+

The following software needs to be downloaded and installed:

+
+
+ +
+
+
+
+
+

2. Problem Statement

+
+
+

This hands-on lab builds a typical 3-tier Java EE 7 Web application that +allows customers to view the show timings for a movie in a 7-theater +Cineplex and make reservations. Users can add new movies and delete +existing movies. Customers can discuss the movie in a chat room. Total +sales from each showing are calculated at the end of the day. Customers +also accrue points for watching movies.

+
+
+
+2.0 problem statement +
+
Figure 2. Architecture diagram
+
+
+

This figure shows the key components of the application. The User +Interface initiates all the flows in the application. Show Booking, +Add/Delete Movie and Ticket Sales interact with the database; Movie +Points may interact with the database, however, this is out of scope for +this application; and Chat Room does not interact with the database.

+
+
+

The different functions of the application, as detailed above, utilize +various Java technologies and web standards in their implementation. The +following figure shows how Java EE technologies are used in different +flows.

+
+
+
+2.0 technologies +
+
Figure 3. Technologies used in the application
+
+
+

The table below details the components and the selected technology used +in its’ implementation.

+
+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FlowDescription

User Interface

Written entirely in JavaServer Faces (JSF)

Chat Room

Utilizes client-side JavaScript and JSON to communicate with a WebSocket endpoint

Ticket Sales

Uses Batch Applications for the Java Platform to calculate the total +sales and persist to the database.

Add/Delete Movie

Implemented using RESTful Web Services. JSON is used as on-the-wire data format

Movie Points

Uses Java Message Service (JMS) to update and obtain loyalty reward +points; an optional implementation using database technology may be +performed

Show Booking

Uses lightweight Enterprise JavaBeans to communicate with the database +using Java Persistence API

+ +
+

This document is not a comprehensive tutorial of Java EE. The attendees +are expected to know the basic Java EE concepts such as EJB, JPA, +JAX-RS, and CDI. The Java +EE 7 Tutorial is a good place to learn all these concepts. However +enough explanation is provided in this guide to get you started with the +application.

+
+
+ + + + + +
+
Warning
+
+This is a sample application and the code may not be +following the best practices to prevent SQL injection, cross-side +scripting attacks, escaping parameters, and other similar features +expected of a robust enterprise application. This is intentional such as +to stay focused on explaining the technology. It is highly recommended +to make sure that the code copied from this sample application is +updated to meet those requirements. +
+
+
+

2.1. Lab Flow

+
+

The attendees will start with an existing maven application and by +following the instructions and guidance provided by this lab they will:

+
+
+
    +
  • +

    Read existing source code to gain an understanding of the structure of +the application and use of the selected platform technologies.

    +
  • +
  • +

    Add new and update existing code with provided fragments in order to +demonstrate usage of different technology stacks in the Java EE 7 +platform.

    +
  • +
+
+
+

While you are copy/pasting the code from this document into NetBeans, +here are couple of tips that will be really useful and make your +experience enjoyable!

+
+
+
+
Source Code Formatting
+
+

NetBeans provides capability to neatly format the source code +following conventions. This can be done for any type of source code, +whether its XML or Java or something else. It is highly recommended to +use this functionality after the code is copy/pasted from this document +to the editor. This keeps the code legible.

+
+
+
+

This functionality can be accessed by right-clicking in the editor pane +and selecting “Format” as shown.

+
+
+
+2.1 format +
+
Figure 4. Format code in NetBeans
+
+
+

This functionality is also accessible using the following keyboard +shortcuts:

+
+ ++++ + + + + + + + + + + + + + + + + + + + + +
ShortcutOperating System

Ctrl+Shift+F

OSX

Alt+Shift+F

Windows

Alt+Shift+F

Linux

+ +
+
+
+
Automatic Imports
+
+

Copy/pasting the Java code from this document in NetBeans editor does +not auto-import the classes. This is required to be done manually in +order for the classes to compile. This can be fixed for each missing +import statement by clicking on the yellow bulb shown in the side bar.

+
+
+
+
+2.1 server endpoint +
+
Figure 5. ServerEndpoint import
+
+
+

Alternatively all the imports can be resolved by right-clicking on the +editor pane and selecting “Fix Imports” as shown.

+
+
+
+2.1 fix imports +
+
Figure 6. Fix Imports in NetBeans
+
+
+

This functionality is also accessible using the following keyboard +shortcuts:

+
+ ++++ + + + + + + + + + + + + + + + + + + + + +
ShortcutOperating System

Command+Shift+I

OSX

Ctrl+Shift+I

Windows

Ctrl+Shift+I

Linux

+ +
+

The defaults may work in most of the cases. Choices are shown in case a +class is available to import from multiple packages. If multiple +packages are available then specific packages to import from are clearly +marked in the document.

+
+
+
+
+
+
+
+
+

2.2. Estimated Time

+
+

Following the complete instructions in this document can take any where +from two to four hours. The wide time range accommodates for learning +the new technologies, finding your way in NetBeans, copy/pasting the +code, and debugging the errors.

+
+
+

The recommended flow is where you follow through the instructions in all +sections in the listed sequence. Alternatively, you may like to cover +section Walk-through of Sample Application through Show Booking (JavaServer Faces) in an order of your choice, based upon your +interest and preference of the technology. However section View and Delete Movie (Java API for RESTful Web Services) is a +pre-requisite for Add Movie (Java API for JSON Processing).

+
+
+

Here is an approximate time estimate for each section:

+
+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Section TitleEstimated Time

Walk-through of Sample Application

15 - 30 mins

Chat Room (Java API for WebSocket)

30 - 45 mins

Ticket Sales (Batch Applications for the Java Platform)

30 - 45 mins

View and Delete Movie (Java API for RESTful Web Services)

30 - 45 mins

Add Movie (Java API for JSON Processing)

30 - 45 mins

Movie Points (Java Message Service)

30 - 45 mins

Show Booking (JavaServer Faces)

30 - 45 mins

+ +
+

The listed time for each section is only an estimate and by no means +restrict you within that. These sections have been completed in much +shorter time, and you can do it too!

+
+
+ + + + + +
+
Tip
+
+The listed time for each section also allows you to create a custom +version of the lab depending upon your target audience and available +time. +
+
+
+
+
+
+

3. Walk-through of Sample Application

+
+
+

Purpose: This section will download the sample application to be used +in this hands-on lab. A walk-through of the application will be +performed to provide an understanding of the application architecture.

+
+
+

Estimated Time: 15-30 mins

+
+
+
    +
  1. +

    Download the sample application from +movieplex7-starting-template.zip +and unzip. This will create a ‘movieplex7’ directory and unzips all the +content there.

    +
  2. +
  3. +

    In NetBeans IDE, select ‘File’, ‘Open Project’, select the +unzipped directory, and click on ‘Open Project’. The project structure +is shown.

    +
    +
    +3.2 project structure +
    +
    Figure 7. Project structure in NetBeans
    +
    +
  4. +
  5. +

    Maven Coordinates: Expand ‘Project Files’ and double click on +‘pom.xml’. In the ‘pom.xml’, the Java EE 7 API is specified as a +<dependency>:

    +
    +
    +
    +
    +
    <dependencies>
    +    <dependency>
    +        <groupId>javax</groupId>
    +        <artifactId>javaee-api</artifactId>
    +        <version>7.0</version>
    +        <scope>provided</scope>
    +    </dependency>
    +</dependencies>
    +
    +
    +
    +

    This will ensure that Java EE 7 APIs are retrieved from the central +Maven repository.

    +
    +
    + + + + + +
    +
    Note
    +
    +
    +

    The Java EE 6 platform introduced the notion of ‘profiles’. A profile is +a configuration of the Java EE platform targeted at a specific class of +applications. All Java EE profiles share a set of common features, such +as naming and resource injection, packaging rules, security +requirements, etc. A profile may contain a proper subset or superset of +the technologies contained in the platform.

    +
    +
    +

    The Java EE Web Profile is a profile of the Java EE Platform +specifically targeted at modern web applications. The complete set of +specifications defined in the Web Profile is defined in the Java EE 7 +Web Profile Specification.

    +
    +
    +
    +
    +

    WildFly can be started in Full Platform or Web Profile.

    +
    +
    +
    +
  6. +
  7. +

    Default Data Source: Expand ‘Other Sources’, +‘src/main/resources’, ‘META-INF’, and double-click on ‘persistence.xml’. +By default, NetBeans opens the file in Design View. Click on ‘Source’ tab +to view the XML source.

    +
    +
    +
    +
    +3.2 persistence xml +
    +
    Figure 8. persistence.xml
    +
    +
    +

    It looks like:

    +
    +
    +
    +
    <?xml version="1.0" encoding="UTF-8"?>
    +<persistence
    +    version="2.1"
    +    xmlns="http://xmlns.jcp.org/xml/ns/persistence"
    +    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    +    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
    +    http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
    +    <persistence-unit name="movieplex7PU" transaction-type="JTA">
    +        <!--
    +            <jta-data-source>java:comp/DefaultDataSource</jta-data-source>
    +        -->
    +        <properties>
    +            <property
    +                name="javax.persistence.schema-generation.database.action"
    +                value="drop-and-create"/>
    +            <property
    +                name="javax.persistence.schema-generation.create-source"
    +                value="script"/>
    +            <property
    +                name="javax.persistence.schema-generation.drop-source"
    +                value="script"/>
    +            <property
    +                name="javax.persistence.schema-generation.drop-script-source"
    +                value="META-INF/drop.sql"/>
    +            <property
    +                name="javax.persistence.sql-load-script-source"
    +                value="META-INF/load.sql"/>
    +            <property
    +                name="eclipselink.deploy-on-startup"
    +                value="true"/>
    +            <property
    +                name="eclipselink.logging.exceptions"
    +                value="false"/>
    +        </properties>
    +    </persistence-unit>
    +</persistence>
    +
    +
    +
    +

    Notice <jta-data-source> is commented out, i.e. no data source element +is specified. This element identifies the JDBC resource to connect to in +the runtime environment of the underlying application server.

    +
    +
    +

    The Java EE 7 platform defines a new default data source that must be +provided by the runtime. This pre-configured data source is accessible +under the JNDI name

    +
    +
    +
    +
    java:comp/DefaultDataSource
    +
    +
    +
    +

    The JPA 2.1 specification says if neither jta-data-source nor +non-jta-data-source elements are specified, the deployer must specify a +JTA data source or the default JTA data source must be provided by the +container.

    +
    +
    +

    For WildFly 8, the default data source is bound to the JDBC resource what name.

    +
    +
    +

    Clicking back and forth between ‘Design’ and ‘Source’ view may prompt +the error shown below:

    +
    +
    +
    +3.4 missing server +
    +
    Figure 9. Missing server error from persistence.xml
    +
    +
    +

    This will get resolved when we run the application. Click on ‘OK’ to +dismiss the dialog.

    +
    +
    +
    +
  8. +
  9. +

    Schema Generation: JPA 2.1 defines a new set of +javax.persistence.schema-generation.* properties that can be used to +generate database artifacts like tables, indexes, and constraints in a +database schema. This helps in prototyping of your application where the +required artifacts are generated either prior to application deployment +or as part of EntityManagerFactory creation. This feature will allow +your JPA domain object model to be directly generated in a database. The +generated schema may need to be tuned for actual production environment.

    +
    +
    +
    +

    The “persistence.xml” in the application has the following +javax.persistence.schema-generation.* properties. Their meaning and +possible values are explained:

    +
    + +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    PropertyMeaningValues

    javax.persistence.schema-generation.database.action

    Specifies the action to be taken by the persistence provider with regard +to the database artifacts.

    none, create, drop-and-create, drop

    javax.persistence.schema-generation.create-source +javax.persistence.schema-generation.drop-source

    Specifies whether the creation or deletion of database artifacts is to +occur on the basis of the object/relational mapping metadata, DDL +script, or a combination of the two.

    metadata, script, metadata-then-script, script-then-metadata

    javax.persistence.schema-generation.create-script-source +javax.persistence.schema-generation.drop-script-source

    Specifies a java.IO.Reader configured for reading of the SQL script or a +string designating a file URL for the SQL script to create or delete +database artifacts.

    javax.persistence.sql-load-script-source

    Specifies a java.IO.Reader configured for reading of the SQL load script +for database initialization or a string designating a file URL for the +script.

    + +
    +

    Refer to the JPA 2.1 Specification +for a complete understanding of these properties.

    +
    +
    +

    In the application, the scripts are bundled in the WAR file in +‘META-INF’ directory. As the location of these scripts is specified as a +URL, the scripts may be loaded from outside the WAR file as well.

    +
    +
    +

    Feel free to open ‘create.sql’, ‘drop.sql’ and ‘load.sql’ and read +through the SQL scripts. The database schema is shown.

    +
    +
    +
    +3.5 schema +
    +
    Figure 10. Database schema
    +
    +
    +

    This folder also contains ‘sales.csv’ which carries some comma-separated +data, and is used later in the application.

    +
    +
    +
    +
  10. +
  11. +

    JPA entities, Stateless EJBs, and REST endpoints: Expand Source +Packages'. The package `org.javaee7.movieplex7.entities contains the +JPA entities corresponding to the database table definitions. Each JPA +entity has several convenient @NamedQuery defined and uses Bean +Validation constraints to enforce validation.

    +
    +
    +
    +

    The package org.javaee7.movieplex7.rest contains stateless EJBs +corresponding to different JPA entities.

    +
    +
    +

    Each EJB has methods to perform CRUD operations on the JPA entity and +convenience query methods. Each EJB is also EL-injectable (@Named) and +published as a REST endpoint (@Path). The AplicationConfig class defines +the base path of REST endpoint. The path for the REST endpoint is the +same as the JPA entity class name.

    +
    +
    +

    The mapping between JPA entity classes, EJB classes, and the URI of the +corresponding REST endpoint is shown.

    +
    + +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    JPA Entity ClassEJB ClassRESTful Path

    Movie

    MovieFacadeREST

    /webresources/movie

    Sales

    SalesFacadeREST

    /webresources/sales

    ShowTiming

    ShowTimingFacadeREST

    /webresources/showtiming

    Theater

    TheaterFacadeREST

    /webresources/theater

    Timeslot

    TimeslotFacadeREST

    /webresources/timeslot

    + +
    +

    Feel free to browse through the code.

    +
    +
    +
    +
  12. +
  13. +

    JSF pages: ‘WEB-INF/template.xhtml’ defines the template of the +web page and has a header, left navigation bar, and a main content +section. ‘index.xhtml’ uses this template and the EJBs to display the +number of movies and theaters.

    +
    +
    +
    +

    Java EE 7 enables CDI discovery of beans by default. No ‘beans.xml’ is +required in ‘WEB-INF’. This allows all beans with bean defining +annotation, i.e. either a bean with an explicit CDI scope or EJBs to be +available for injection.

    +
    +
    +

    Note, ‘template.xhtml’ is in ‘WEB-INF’ folder as it allows the template +to be accessible from the pages bundled with the application only. If it +were bundled with rest of the pages then it would be accessible outside +the application and thus allowing other external pages to use it as +well.

    +
    +
    +
    +
  14. +
  15. +

    Run the sample: Right-click on the project and select ‘Run’. +This will download all the maven dependencies on your machine, build a +WAR file, deploy on +WildFly 8 +, and show the URL +localhost:8080/movieplex7 in the +default browser configured in NetBeans. Note that this could take a +while if you have never built a Maven application on your machine.

    +
    +
    +
    + + + + + +
    +
    Tip
    +
    +The project will show red squiggly lines in the source code indicating +that the classes cannot be resolved. This is expected before the +dependencies are downloaded. However these references will be resolved +correctly after the dependencies are downloaded during project building. +
    +
    +
    +

    During the first run, the IDE will ask you to select a deployment server. +Choose the configured WildFly server and click on ‘OK’.

    +
    +
    +
    +3.6 wildfly server +
    +
    Figure 11. WildFly deployment server
    +
    +
    +

    The output looks like as shown.

    +
    +
    +
    +3.8 first page +
    +
    Figure 12. Application main page
    +
    +
    +
    +
  16. +
+
+
+
+
+

4. Chat Room (Java API for WebSocket)

+
+
+

Purpose: Build a chat room for viewers. In doing so several new +features of Java API for WebSocket 1.0 will be introduced and +demonstrated by using them in the application.

+
+
+

Estimated Time: 30-45 mins

+
+
+

WebSocket provide a full-duplex and bi-directional communication +protocol over a single TCP connection. WebSocket is a combination of +IETF RFC 6455 +Protocol and +W3C JavaScript WebSocket API (a +Candidate Recommendation as of this writing). The protocol defines an +opening handshake and data transfer. The API enables Web pages to use +the WebSocket protocol for two-way communication with the remote host.

+
+
+

JSR 356 defines a standard API for +creating WebSocket applications in the Java EE 7 Platform. The JSR +provides support for:

+
+
+
    +
  • +

    Create WebSocket endpoint using annotations and interface

    +
  • +
  • +

    Initiating and intercepting WebSocket events

    +
  • +
  • +

    Creation and consumption of WebSocket text and binary messages

    +
  • +
  • +

    Configuration and management of WebSocket sessions

    +
  • +
  • +

    Integration with Java EE security model

    +
  • +
+
+
+

This section will build a chat room for movie viewers.

+
+
+
    +
  1. +

    Right-click on ‘Source Packages’ , select ‘New’, ‘Java Class’. +Give the class name as ‘ChatServer’, package as +‘org.javaee7.movieplex7.chat’, and click on ‘Finish’.

    +
  2. +
  3. +

    Change the class such that it looks like:

    +
    +
    +
    @ServerEndpoint("/websocket")
    +public class ChatServer {
    +    private static final Set<Session> peers =
    +               Collections.synchronizedSet(new HashSet<Session>());
    +
    +    @OnOpen
    +    public void onOpen(Session peer) {
    +        peers.add(peer);
    +    }
    +
    +    @OnClose
    +    public void onClose(Session peer) {
    +        peers.remove(peer);
    +    }
    +
    +    @OnMessage
    +    public void message(String message, Session client)
    +           throws IOException, EncodeException {
    +        for (Session peer : peers) {
    +            peer.getBasicRemote().sendText(message);
    +        }
    +    }
    +}
    +
    +
    +
    +

    In this code:

    +
    +
    +
      +
    1. +

      @ServerEndpoint decorates the class to be a WebSocket endpoint. The +value defines the URI where this endpoint is published.

      +
    2. +
    3. +

      @OnOpen and @OnClose decorate the methods that must be called when +WebSocket session is opened or closed. The peer parameter defines the +client requesting connection initiation and termination.

      +
    4. +
    5. +

      @OnMessage decorates the message that receives the incoming WebSocket +message. The first parameter, message, is the payload of the message. +The second parameter, client, defines the other end of the WebSocket +connection. The method implementation transmits the received text message to +all clients connected to this endpoint.

      +
      +

      Resolve the imports by right-clicking in the editor and selecting ‘Fix +Imports’ or (Command+Shift+I shortcut on OSX or Ctrl+Shift+I on +Windows).

      +
      +
      + + + + + +
      +
      Warning
      +
      +Make sure to pick java.websocket.Session for resolving imports. This is not the default option shown by NetBeans. +
      +
      +
      +
      +4.2 imports +
      +
      Figure 13. javax.websocket.Session import
      +
      +
      +

      Right-click again in the editor pane and select ‘Format’ to format your +code.

      +
      +
    6. +
    +
    +
  4. +
  5. +

    In ‘Web Pages’, select ‘New’, ‘Folder’, give the folder name as +‘chat’ and click on ‘Finish’.

    +
  6. +
  7. +

    Right-click on the newly created folder, select ‘New’, ‘Other’, +‘Java Server Faces’, ‘Facelets Template Client’, give the File Name as +‘chatroom’. Click on ‘Browse’ next to ‘Template:’, expand ‘Web Pages’, +‘WEB-INF’, select ‘template.xhtml’, and click on ‘Select File’. Click on +‘Finish’.

    +
    +
    +4.4 template +
    +
    Figure 14. Choose template
    +
    +
    +

    In this file, remove <ui:define> sections where name attribute value is +‘top’ and ‘left’. These sections are inherited from the template.

    +
    +
    +

    Replace <ui:define> section with ‘content’ name such that it looks like:

    +
    +
    +
    +
    <ui:composition template="../WEB-INF/template.xhtml">
    +    <ui:define name="content">
    +        <form action="">
    +            <table>
    +                <tr>
    +                    <td>
    +                        Chat Log<br/>
    +                        <textarea readonly="true" rows="6" cols="50" id="chatlog"></textarea>
    +                    </td>
    +                    <td>
    +                        Users<br/>
    +                        <textarea readonly="true" rows="6" cols="20" id="users"></textarea>
    +                    </td>
    +                </tr>
    +                <tr>
    +                    <td colspan="2">
    +                        <input id="textField" name="name" value="Duke" type="text"/>
    +                        <input onclick="join();" value="Join" type="button"/>
    +                        <input onclick="send_message();" value="Send" type="button"/><p/>
    +                        <input onclick="disconnect();" value="Disconnect" type="button"/>
    +                    </td>
    +                </tr>
    +            </table>
    +        </form>
    +        <div id="output"></div>
    +            <script language="javascript" type="text/javascript"
    +                src="${facesContext.externalContext.requestContextPath}/chat/websocket.js"></script>
    +    </ui:define>
    +</ui:composition>
    +
    +
    +
    +

    The code builds an HTML form that has two textareas – one to display the +chat log and the other to display the list of users currently logged. A +single text box is used to take the user name or the chat message. +Clicking on ‘Join’ button takes the value as user name and clicking on +‘Send’ takes the value as chat message.

    +
    +
    +

    JavaScript methods are invoked +when these buttons are clicked and these are explained in the next +section. The chat messages are sent and received as WebSocket payloads. +There is an explicit button to disconnect the WebSocket connection. +output div is the placeholder for status messages. The WebSocket +initialization occurs in ‘websocket.js’ included at the bottom of the +fragment.

    +
    +
  8. +
  9. +

    Right-click on ‘chat’ in ‘Web Pages’, select ‘New’, ‘Other’, ‘Web’ +categories, ‘JavaScript File’ file type. Click on ‘Next’.

    +
    +

    Give the name as ‘websocket’ and click on ‘Finish’.

    +
    +
  10. +
  11. +

    Edit the contents of ‘websocket.js’ such that it looks like:

    +
    +
    +
    var wsUri = 'ws://' + document.location.host
    +            + document.location.pathname.substr(0,
    +              document.location.pathname.indexOf("/faces")) +
    +              '/websocket';
    +console.log(wsUri);
    +
    +var websocket = new WebSocket(wsUri);
    +var textField = document.getElementById("textField");
    +var users = document.getElementById("users");
    +var chatlog = document.getElementById("chatlog");
    +var username;
    +
    +websocket.onopen = function(evt) { onOpen(evt); };
    +websocket.onmessage = function(evt) { onMessage(evt); };
    +websocket.onerror = function(evt) { onError(evt); };
    +websocket.onclose = function(evt) { onClose(evt); };
    +
    +var output = document.getElementById("output");
    +
    +function join() {
    +    username = textField.value;
    +    websocket.send(username + " joined");
    +}
    +
    +function send_message() {
    +    websocket.send(username + ": " + textField.value);
    +}
    +
    +function onOpen() {
    +    writeToScreen("CONNECTED");
    +}
    +
    +function onClose() {
    +    writeToScreen("DISCONNECTED");
    +}
    +
    +function onMessage(evt) {
    +    writeToScreen("RECEIVED: " + evt.data);
    +    if (evt.data.indexOf("joined") !== -1) {
    +        users.innerHTML += evt.data.substring(0, evt.data.indexOf(" joined")) + "\n";
    +    } else {
    +        chatlog.innerHTML += evt.data + "\n";
    +    }
    +}
    +
    +function onError(evt) {
    +    writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
    +}
    +
    +function disconnect() {
    +    websocket.close();
    +}
    +
    +function writeToScreen(message) {
    +    var pre = document.createElement("p");
    +    pre.style.wordWrap = "break-word";
    +    pre.innerHTML = message;
    +    output.appendChild(pre);
    +}
    +
    +
    +
    +

    The WebSocket endpoint URI is calculated by using standard JavaScript +variables and appending the URI specified in the ChatServer class. +WebSocket is initialized by calling new WebSocket(...). Event handlers are +registered for lifecycle events using onXXX messages. The listeners +registered in this script are explained in the table.

    +
    + ++++ + + + + + + + + + + + + + + + + + + + + + + + + +
    ListenersCalled When

    onOpen(evt)

    WebSocket connection is initiated

    onMessage(evt)

    WebSocket message is received

    onError(evt)

    Error occurs during the communication

    onClose(evt)

    WebSocket connection is terminated

    + +
    +

    Any relevant data is passed along as parameter to the function. Each +method prints the status on the browser using writeToScreen utility +method. The join method sends a message to the endpoint +that a particular user has joined. The endpoint then broadcasts the +message to all the listening clients. The send_message method appends +the logged in user name and the value of the text field and broadcasts +to all the clients similarly. The onMessage method updates the list of +logged in users as well.

    +
    +
  12. +
  13. +

    Edit ‘WEB-INF/template.xhtml’ and change:

    +
    +
    +
    <h:outputLink value="item2.xhtml">Item 2</h:outputLink>
    +
    +
    +
    +

    to

    +
    +
    +
    +
    <h:outputLink
    +    value="${facesContext.externalContext.requestContextPath}/faces/chat/chatroom.xhtml">
    +    Chat Room
    +</h:outputLink>
    +
    +
    +
    +

    The outputLink tag renders an HTML anchor tag with an href attribute. +${facesContext.externalContext.requestContextPath} provides the request +URI that identifies the web application context for this request. This +allows the links in the left navigation bar to be fully-qualified URLs.

    +
    +
  14. +
  15. +

    Run the project by right clicking on the project and selecting +‘Run’. The browser shows +localhost:8080/movieplex7.

    +
    +
    +4.6 chatroom +
    +
    Figure 15. Chatroom link on main page
    +
    +
    +

    Click on ‘Chat Room’ to see the output.

    +
    +
    +

    The ‘CONNECTED’ status message is shown and indicates that the WebSocket +connection with the endpoint is established.

    +
    +
    +
    +4.8 chatroom +
    +
    Figure 16. Chatroom output
    +
    +
    +

    Please make sure your browser supports WebSocket in order for this page +to show up successfully. Chrome 14.0+, Firefox 11.0+, Safari 6.0+, and +IE 10.0+ are the browsers that support WebSocket. A complete list of +supported browsers is available at +caniuse.com/websockets.

    +
    +
    +

    Open the URI localhost:8080/movieplex7 +in another browser window. Enter ‘Duke’ in the text box in the first +browser and click ‘Join’.

    +
    +
    +
    +4.8 chatroom joined +
    +
    Figure 17. Chatroom with single user
    +
    +
    +

    Notice that the user list and the status message in both the browsers +gets updated. Enter ‘James’ in the text box of the second browser and +click on ‘Join’. Once again the user list and the status message in both +the browsers is updated. Now you can type any messages in any of the +browser and click on ‘Send’ to send the message.

    +
    +
    +

    The output from two different browsers after the initial greeting looks +like as shown.

    +
    +
    +
    +4.8 chatroom two browsers +
    +
    Figure 18. Chatroom with two users
    +
    +
    +

    Here it shows output from Chrome on the top and Firefox on the bottom.

    +
    +
    +

    Chrome Developer Tools or Firebug in Firefox can be used to monitor +WebSocket traffic.

    +
    +
  16. +
+
+
+
+
+

5. Ticket Sales (Batch Applications for the Java Platform)

+
+
+

Purpose: Read the total sales for each show and populate the database. +In doing so several new features of Java API for Batch Processing 1.0 +will be introduced and demonstrated by using them in the application.

+
+
+

Estimated Time: 30-45 mins

+
+
+

Batch Processing is execution of series of ‘jobs’ that is suitable for +non-interactive, bulk-oriented and long-running tasks. Batch +Applications for the Java Platform (JSR 352) will define a programming +model for batch applications and a runtime for scheduling and executing +jobs.

+
+
+
+5.0 batch intro +
+
Figure 19. Introduction to Batch
+
+
+

The core concepts of Batch Processing are:

+
+
+
    +
  • +

    Job is an instance that encapsulates an entire batch process. A +job is typically put together using a Job Specification Language and +consists of multiple steps. The Job Specification Language for JSR 352 +is implemented with XML and is referred as ‘Job XML’.

    +
  • +
  • +

    Step is a domain object that encapsulates an independent, +sequential phase of a job. A step contains all of the information +necessary to define and control the actual batch processing.

    +
  • +
  • +

    JobOperator provides an interface to manage all aspects of job +processing, including operational commands, such as start, restart, and +stop, as well as job repository commands, such as retrieval of job and +step executions.

    +
  • +
  • +

    JobRepository holds information about jobs current running and jobs +that run in the past. JobOperator provides access to this repository.

    +
  • +
  • +

    Reader-Processor-Writer pattern is the primary pattern and is called +as Chunk-oriented Processing. In this, ItemReader reads one item +at a time, ItemProcessor processes the item based upon the business +logic, such as calculate account balance and hands it +to ItemWriter for aggregation. Once the chunk numbers of items are +aggregated, they are written out, and the transaction is committed.

    +
  • +
+
+
+

This section will read the cumulative sales for each show from a CSV +file and populate them in a database.

+
+
+
    +
  1. +

    Right-click on Source Packages, select ‘New’, ‘Java Package’, +specify the value as ‘org.javaee7.movieplex7.batch’, and click on +‘Finish’.

    +
  2. +
  3. +

    Right-click on newly created package, select ‘New’, ‘Java Class’, +specify the name as ‘SalesReader’. Make this class extend from +‘AbstractItemReader’ by changing the class definition and add:

    +
    +
    +
    extends AbstractItemReader
    +
    +
    +
    +

    AbstractItemReader is an abstract class that implements ItemReader +interface. The ItemReader interface defines methods that read a stream +of items for chunk processing. This reader implementation returns a +String item type as indicated in the class definition.

    +
    +
    +

    Add @Named as a class-level annotations and it allows the bean to be +injected in Job XML. Add @Dependent as another class-level annotation to +mark this bean as a bean defining annotation so that this bean is +available for injection.

    +
    +
    +

    Resolve the imports.

    +
    +
  4. +
  5. +

    Override open() method to initialize the reader by adding the following code:

    +
    +
    +
    private BufferedReader reader;
    +
    +public void open(Serializable checkpoint) throws Exception {
    +    reader = new BufferedReader(
    +        new InputStreamReader(
    +        Thread.currentThread()
    +            .getContextClassLoader()
    +            .getResourceAsStream("META-INF/sales.csv")));
    +}
    +
    +
    +
    +

    This method initializes a BufferedReader from ‘META-INF/sales.csv’ that +is bundled with the application.

    +
    +
    +

    Sampling of the first few lines from ‘sales.csv’ is shown below:

    +
    +
    +
    +
    1,500.00
    +2,660.00
    +3,80.00
    +4,470.00
    +5,1100.x0
    +
    +
    +
    +

    Each line has a show identifier comma separated by the total sales for +that show. Note that the last line (5th record in the sample) has an +intentional typo. In addition, 17th record also has an additional +typo. The lab will use these lines to demonstrate how to handle parsing +errors.

    +
    +
  6. +
  7. +

    Override the following method from the abstract class:

    +
    +
    +
    @Override
    +public String readItem() {
    +    String string = null;
    +    try {
    +        string = reader.readLine();
    +    } catch (IOException ex) {
    +        ex.printStackTrace();
    +    }
    +    return string;
    +}
    +
    +
    +
    +

    The readItem method returns the next item from the stream. It returns +null to indicate end of stream. Note end of stream indicates end of chunk, +so the current chunk will be committed and the step will end.

    +
    +
    +

    Resolve the imports.

    +
    +
  8. +
  9. +

    Right-click on ‘org.javaee7.movieplex7.batch’ package, select +‘New’, ‘Java Class’, specify the name as ‘SalesProcessor’. Change the +class definition and add:

    +
    +
    +
    implements ItemProcessor
    +
    +
    +
    +

    ItemProcessor is an interface that defines a method that is used to +operate on an input item and produce an output item. This processor +accepts a String input item from the reader, SalesReader in our case, +and returns a Sales instance to the writer (coming shortly). Sales is +the pre-packaged JPA entity with the application starter source code.

    +
    +
    +

    Add @Named and @Dependent as class-level annotations so that it allows +the bean to be injected in Job XML.

    +
    +
    +

    Resolve the imports.

    +
    +
  10. +
  11. +

    Add implementation of the abstract method from the interface as:

    +
    +
    +
    @Override
    +public Sales processItem(Object s) {
    +    Sales sales = new Sales();
    +    StringTokenizer tokens = new StringTokenizer((String)s, ",");
    +    sales.setId(Integer.parseInt(tokens.nextToken()));
    +    sales.setAmount(Float.parseFloat(tokens.nextToken()));
    +
    +    return sales;
    +}
    +
    +
    +
    +

    This method takes a String parameter coming from the SalesReader, parses +the value, populates them in the Sales instance, and returns it. This is +then aggregated with the writer.

    +
    +
    +

    The method can return null indicating that the item should not be +aggregated. For example, the parsing errors can be handled within the +method and return null if the values are not correct. However this +method is implemented where any parsing errors are thrown as exception. +Job XML can be instructed to skip these exceptions and thus that +particular record is skipped from aggregation as well (shown later).

    +
    +
    +

    Resolve the imports.

    +
    +
  12. +
  13. +

    Right-click on org.javaee7.movieplex7.batch package, select +‘New’, ‘Java Class’, specify the name as ‘SalesWriter’. Change the +class definition and add:

    +
    +
    +
    extends AbstractItemWriter
    +
    +
    +
    +

    AbstractItemWriter is an abstract class that implements ItemWriter +interface. The ItemWriter interface defines methods that write to a +stream of items for chunk processing. This writer writes a list of Sales +items.

    +
    +
    +

    Add @Named and @Dependent as class-level annotations so that it allows +the bean to be injected in Job XML.

    +
    +
    +

    Resolve the imports.

    +
    +
  14. +
  15. +

    Inject EntityManager as:

    +
    +
    +
    @PersistenceContext EntityManager em;
    +
    +
    +
    +

    Override writeItems method from the abstract class by adding the following code:

    +
    +
    +
    +
    @Override
    +@Transactional
    +public void writeItems(List list) {
    +    for (Sales s : (List<Sales>)list) {
    +        em.persist(s);
    +    }
    +}
    +
    +
    +
    +

    Batch runtime aggregates the list of Sales instances returned from the +SalesProessor and makes it available as List in this method. This method +iterates over the list and persist each item in the database.

    +
    +
    +

    The method also specifies @Transactional as a method level annotation. +This is a new annotation introduced by JTA 1.2 that provides the ability +to control transaction boundaries on CDI managed beans. This provides +the semantics of EJB transaction attributes in CDI beans without +dependencies such as RMI. This support is implemented via an +implementation of a CDI interceptor that conducts the necessary +suspending, resuming, etc. 

    +
    +
    +

    In this case, a transaction is automatically started before the method +is called, committed if no checked exceptions are thrown, and rolled +back if runtime exceptions are thrown. This behavior can be overridden +using rollbackOn and dontRollbackOn attributes of the annotation.

    +
    +
    + + + + + +
    +
    Note
    +
    +
    +

    Each chunk is processed within a container-managed transaction already. +There is really no need for @Transactional on writeItems method but +shows a usage for the annotation.

    +
    +
    +
    +
    +

    Resolve the imports.

    +
    +
  16. +
  17. +

    Create Job XML that defines the job, step, and chunk.

    +
    +

    In ‘Files’ tab, expand the project → ‘src’ → ‘main’ → ‘resources’, +right-click on ‘META-INF’, select ‘New’, ‘Folder’, specify +the name as ‘batch-jobs’, and click on ‘Finish’.

    +
    +
    +

    Right-click on the newly created folder, select ‘New’, ‘Other’, select +‘XML’, ‘XML Document’, click on ‘Next >’, give the name as ‘eod-sales’, +click on ‘Next’, take the default, and click on ‘Finish’.

    +
    +
    +

    Replace contents of the file with the following:

    +
    +
    +
    +
    <job id="endOfDaySales"
    +    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    +    version="1.0">
    +    <step id="populateSales">
    +        <chunk item-count="3" skip-limit="5">
    +            <reader ref="salesReader"/>
    +            <processor ref="salesProcessor"/>
    +            <writer ref="salesWriter"/>
    +            <skippable-exception-classes>
    +                <include class="java.lang.NumberFormatException"/>
    +            </skippable-exception-classes>
    +        </chunk>
    +    </step>
    +</job>
    +
    +
    +
    +

    This code shows that the job has one step of chunk type. The <reader>, +<processor>, and <writer> elements define the CDI bean name of the +implementations of ItemReader, ItemProcessor, and ItemWriter interfaces. +The item-count attribute defines that 3 items are +read/processed/aggregated and then given to the writer. The entire +reader/processor/writer cycle is executed within a transaction.

    +
    +
    +

    The <skippable-exception-classes> element specifies a set of exceptions to +be skipped by chunk processing.

    +
    +
    +

    CSV file used for this lab has intentionally introduced couple of typos +that would generate NumberFormatException. Specifying this element +allows skipping the exception, ignore that particular element, and +continue processing. If this element is not specified then the batch +processing will halt. The skip-limit attribute specifies the number of +exceptions a step will skip.

    +
    +
  18. +
  19. +

    Lets invoke the batch job.

    +
    +

    In ‘Projects’ tab, right-click on ‘org.javaee7.movieplex7.batch’ package, select ‘New’, +‘Java Class’. Enter the name as ‘SalesBean’ and click on ‘Finish’ +button.

    +
    +
    +

    Add the following code to the bean:

    +
    +
    +
    +
    public void runJob() {
    +    try {
    +        JobOperator jo = BatchRuntime.getJobOperator();
    +        long jobId = jo.start("eod-sales", new Properties());
    +        System.out.println("Started job: with id: " + jobId);
    +    } catch (JobStartException ex) {
    +        ex.printStackTrace();
    +    }
    +}
    +
    +
    +
    +

    This method uses BatchRuntime to get an instance of JobOperator, which +is then used to start the job. JobOperator is the interface for +operating on batch jobs. It can be used to start, stop, and restart +jobs. It can additionally inspect job history, to discover what jobs are +currently running and what jobs have previously run.

    +
    +
    +

    Add @Named and @RequestScoped as class-level annotations. This allows +the bean to be injectable in an EL expression.

    +
    +
    +

    Resolve the imports.

    +
    +
    +
    +5.10 imports +
    +
    Figure 20. RequestScoped import
    +
    +
  20. +
  21. +

    Inject EntityManagerFactory in the class as:

    +
    +
    +
    @PersistenceUnit EntityManagerFactory emf;
    +
    +
    +
    +

    and add the following method:

    +
    +
    +
    +
    public List<Sales> getSalesData() {
    +    return emf.
    +        createEntityManager().
    +        createNamedQuery("Sales.findAll", Sales.class).
    +        getResultList();
    +}
    +
    +
    +
    +

    This method uses a pre-defined @NamedQuery to query the database and +return all the rows from the table.

    +
    +
    +

    Resolve the imports.

    +
    +
  22. +
  23. +

    Right-click on ‘Web Pages’, select ‘New’, ‘Folder’, specify the +name as ‘batch’, and click on ‘Finish’.

    +
    +

    Right-click on the newly created folder, select ‘New’, ‘Other’, +‘JavaServer Faces’, ‘Facelets Template Client’, and click on ‘Next >’.

    +
    +
    +

    Give the File Name as ‘sales’. Click on ‘Browse’ next to ‘Template:’, +expand ‘Web Pages’, ‘WEB-INF’, select ‘template.xhtml’, and click on +‘Select File’. Click on ‘Finish’.

    +
    +
    +

    In this file, remove <ui:define> sections where name attribute value is +‘top’ and ‘left’. These sections are inherited from the template.

    +
    +
    +

    Replace <ui:define> section with ‘content’ name such that it looks like:

    +
    +
    +
    +
    <ui:composition template="../WEB-INF/template.xhtml">
    +    <ui:define name="content">
    +        <h1>Movie Sales</h1>
    +        <h:form>
    +            <h:dataTable value="#{salesBean.salesData}" var="s" border="1">
    +                <h:column>
    +                    <f:facet name="header">
    +                        <h:outputText value="Show ID" />
    +                    </f:facet>
    +                    #{s.id}
    +                </h:column>
    +                <h:column>
    +                    <f:facet name="header">
    +                        <h:outputText value="Sales" />
    +                    </f:facet>
    +                    #{s.amount}
    +                </h:column>
    +            </h:dataTable>
    +            <h:commandButton
    +                value="Run Job"
    +                action="sales"
    +                actionListener="#{salesBean.runJob()}"/>
    +            <h:commandButton
    +                value="Refresh"
    +                action="sales" />
    +        </h:form>
    +    </ui:define>
    +</ui:composition>
    +
    +
    +
    +

    This code displays the show identifier and sales from that show in a +table by invoking SalesBean.getSalesData(). First command button allows +invoking the job that processes the CSV file and populates the database. +The second command button refreshes the page.

    +
    +
    +

    Right-click on the yellow bulb to fix namespace prefix/URI mapping for h:. This +needs to be repeated for f: prefix.

    +
    +
  24. +
  25. +

    Add the following code in template.xhtml along with other <outputLink>s:

    +
    +
    +
    <p/><h:outputLink
    +    value="${facesContext.externalContext.requestContextPath}/faces/batch/sales.xhtml">
    +    Sales
    +    </h:outputLink>
    +
    +
    +
  26. +
  27. +

    Run the project to see the output as shown.

    +
    +
    +5.14 sales +
    +
    Figure 21. Sales link on main page
    +
    +
    +

    Notice, a new ‘Sales’ entry is displayed in the left navigation bar.

    +
    +
  28. +
  29. +

    Click on ‘Sales’ to see the output as shown.

    +
    +
    +5.15 sales +
    +
    Figure 22. Movie Sales page
    +
    +
    +

    The empty table indicates that there is no sales data in the database.

    +
    +
  30. +
  31. +

    Click on ‘Run Job’ button to initiate data processing of CSV +file. Look for ‘Waiting for localhost’ in the browser status bar, +wait for a couple of seconds for the processing to finish, and then +click on ‘Refresh’ button to see the updated output as shown.

    +
    +
    +5.16 sales output +
    +
    Figure 23. Movie Sales output page
    +
    +
    +

    Now the table is populated with the sales data.

    +
    +
    +

    Note that record 5 is missing from the table, as this records did not +have correct numeric entries for the sales total. The Job XML for the +application explicitly mentioned to skip such errors.

    +
    +
  32. +
+
+
+
+
+

6. View and Delete Movie (Java API for RESTful Web Services)

+
+
+

Purpose: View, and delete a movie. In doing so several new features of +JAX-RS 2 will be introduced and demonstrated by using them in the +application.

+
+
+

Estimated Time: 30-45 mins

+
+
+

JAX-RS 2 defines a standard API to create, publish, and invoke a REST +endpoint. JAX-RS 2 adds several new features to the API:

+
+
+
    +
  • +

    Client API that can be used to access Web resources and provides +integration with JAX-RS Providers. Without this API, the users need to +use a low-level HttpUrlConnection to access the REST endpoint.

    +
  • +
  • +

    Asynchronous processing capabilities in Client and Server that enables +more scalable applications.

    +
  • +
  • +

    Message Filters and Entity Interceptors as well-defined extension +points to extend the capabilities of an implementation.

    +
  • +
  • +

    Validation constraints can be specified to validate the parameters and +return type.

    +
  • +
+
+
+

This section will provide the ability to view all the movies, details of +a selected movie, and delete an existing movie using the JAX-RS Client +API.

+
+
+
    +
  1. +

    Right-click on ‘Source Packages’, select ‘New’, ‘Java Class’. +Give the class name as ‘MovieClientBean’, package as +‘org.javaee7.movieplex7.client’, and click on ‘Finish’.

    +
    +

    This bean will be used to invoke the REST endpoint.

    +
    +
  2. +
  3. +

    Add @Named and @RequestScoped class-level annotations. This allows +the class to be injected in an EL expression and also defines the bean +to be automatically activated and passivated with the request.

    +
    +

    Resolve the imports.

    +
    +
    + + + + + +
    +
    Warning
    +
    +Make sure to pick javax.enterprise.context.RequestScoped class. +
    +
    +
    +
    +6.2 imports +
    +
    Figure 24. RequestScoped import
    +
    +
  4. +
  5. +

    Add the following code to the class:

    +
    +
    +
    Client client;
    +WebTarget target;
    +
    +@Inject HttpServletRequest httpServletRequest;
    +
    +@PostConstruct
    +public void init() {
    +    client = ClientBuilder.newClient();
    +    target = client
    +                .target("http://" +
    +                httpServletRequest.getLocalName() +
    +                ":" +
    +                httpServletRequest.getLocalPort() +
    +                "/" +
    +                httpServletRequest.getContextPath() +
    +                "/webresources/movie/");
    +
    +}
    +
    +@PreDestroy
    +public void destroy() {
    +    client.close();
    +}
    +
    +
    +
    +

    ClientBuilder is the main entry point to the Client API. It uses a +fluent builder API to invoke REST endpoints. A new Client instance is +created using the default client builder implementation provided by the +JAX-RS implementation provider. Client are heavy-weight objects that +manage the client-side communication infrastructure. It is highly +recommended to create only required number of instances of Client and +close it appropriately.

    +
    +
    +

    In this case, Client instance is created and destroyed in the lifecycle +callback methods. The endpoint URI is set on this instance by calling +the target method. Note that the endpoint address is dynamically created +by injecting an instance of HttpServletRequest. This is a new feature +added in CDI 1.1

    +
    +
  6. +
  7. +

    Add the following method to the class:

    +
    +
    +
    public Movie[] getMovies() {
    +    return target
    +        .request()
    +        .get(Movie[].class);
    +}
    +
    +
    +
    +

    A request is prepared by calling the request method. HTTP GET method is +invoked by calling get method. The response type is specified in the +last method call and so return value is of the type Movie[].

    +
    +
  8. +
  9. +

    Right-click on ‘Web Pages’, select ‘New’, ‘Folder’, specify the +name as ‘client’, and click on ‘Finish’.

    +
    +

    Right-click on the newly created folder, select ‘New’, ‘Other’, +‘JavaServer Faces’, ‘Facelets Template Client’, and click on ‘Next >’.

    +
    +
    +

    Give the File Name as ‘movies’. Click on ‘Browse’ next to ‘Template:’, +expand ‘Web Pages’, ‘WEB-INF’, select ‘template.xhtml’, and click on +‘Select File’. Click on ‘Finish’.

    +
    +
  10. +
  11. +

    In this file, remove <ui:define> sections where name attribute value is +‘top’ and ‘left’. These sections are inherited from the template.

    +
    +

    Replace <ui:define> section with ‘content’ name such that it looks like:

    +
    +
    +
    +
    <ui:composition template="../WEB-INF/template.xhtml">
    +    <ui:define name="content">
    +        <h:form prependId="false">
    +            <h:selectOneRadio value="#{movieBackingBean.movieId}" layout="pageDirection">
    +                <c:forEach items="#{movieClientBean.movies}" var="m">
    +                    <f:selectItem itemValue="#{m.id}" itemLabel="#{m.name}"/>
    +                </c:forEach>
    +            </h:selectOneRadio>
    +            <h:commandButton value="Details" action="movie" />
    +        </h:form>
    +    </ui:define>
    +</ui:composition>
    +
    +
    +
    +

    This code fragment invokes getMovies method from MovieClientBean, +iterates over the response in a for loop, and display the name of each +movie with a radio button. The selected radio button value is bound to +the EL expression #{movieBackingBean.movieId}.

    +
    +
    +

    The code also has a button with ‘Details’ label and looks for +‘movie.xhtml’ in the same directory. We will create this file later.

    +
    +
    +

    Click on the yellow bulb in the left bar to resolve the namespace +prefix-to-URI resolution. This needs to be completed for h:, c:, +and f: prefixes.

    +
    +
    +
    +6.6 imports +
    +
    Figure 25. Namespace prefix imports
    +
    +
  12. +
  13. +

    Right-click on ‘org.javaee7.movieplex7.client’ package, select +‘New’, ‘Java Class’, specify the value as ‘MovieBackingBean’ and click +on ‘Finish’.

    +
    +

    Add the following field:

    +
    +
    +
    +
    int movieId;
    +
    +
    +
    +

    Add getters/setters by right-clicking on the editor pane and selecting +‘Insert Code’ (Ctrl+I shortcut on OSX). Select the field and click on +‘Generate’.

    +
    +
    +

    Add @Named and @SessionScoped class-level annotations and implements +Serializable.

    +
    +
    +

    Resolve the imports.

    +
    +
    + + + + + +
    +
    Warning
    +
    +Make sure to import javax.enterprise.context.SessionScoped. +
    +
    +
  14. +
  15. +

    In ‘template.xhtml’, add the following code along with other <outputLink>s:

    +
    +
    +
    <p/><h:outputLink
    +        value="${facesContext.externalContext.requestContextPath}/faces/client/movies.xhtml">
    +        Movies
    +    </h:outputLink>
    +
    +
    +
    +

    Running the project (Fn+F6 shortcut on OSX) and clicking on ‘Movies’ +in the left navigation bar shows the output as shown.

    +
    +
    +
    +6.8 output +
    +
    Figure 26. List of movies output page
    +
    +
    +

    The list of all the movies with a radio button next to them is +displayed.

    +
    +
  16. +
  17. +

    In MovieClientBean, inject MovieBackingBean to read the value +of selected movie from the page. Add the following code:

    +
    +
    +
    @Inject
    +MovieBackingBean bean;
    +
    +
    +
  18. +
  19. +

    In MovieClientBean, add the following method:

    +
    +
    +
    public Movie getMovie() {
    +    Movie m = target
    +        .path("{movie}")
    +        .resolveTemplate("movie", bean.getMovieId())
    +        .request()
    +        .get(Movie.class);
    +    return m;
    +}
    +
    +
    +
    +

    This code reuses the Client and WebTarget instances created in +@PostConstruct. It also adds a variable part to the URI of the REST +endpoint, defined using {movie}, and binds it to a concrete value using +resolveTemplate method. The return type is specified as a parameter to +the get method.

    +
    +
  20. +
  21. +

    Right-click on ‘client’ folder, select ‘New’, ‘Facelets Template +Client’, give the File Name as ‘movie’. Click on ‘Browse’ next to +‘Template:’, expand ‘Web Pages’, ‘WEB-INF’, select ‘template.xhtml’, and +click on ‘Select File’. Click on ‘Finish’.

    +
  22. +
  23. +

    In this file, remove <ui:define> sections where name attribute value is +‘top’ and ‘left’. These sections are inherited from the template.

    +
    +

    Replace <ui:define> with ‘content’ name such that it looks like:

    +
    +
    +
    +
    <ui:define name="content">
    +    <h1>Movie Details</h1>
    +    <h:form>
    +        <table cellpadding="5" cellspacing="5">
    +            <tr>
    +                <th align="left">Movie Id:</th>
    +                <td>#{movieClientBean.movie.id}</td>
    +            </tr>
    +            <tr>
    +                <th align="left">Movie Name:</th>
    +                <td>#{movieClientBean.movie.name}</td>
    +            </tr>
    +            <tr>
    +                <th align="left">Movie Actors:</th>
    +                <td>#{movieClientBean.movie.actors}</td>
    +            </tr>
    +        </table>
    +        <h:commandButton value="Back" action="movies" />
    +    </h:form>
    +</ui:define>
    +
    +
    +
    +

    Click on the yellow-bulb to resolve the namespace prefix-URI mapping for +h:.

    +
    +
    +

    The output values are displayed by calling the getMovie method and +using the id, name, and actors property values.

    +
    +
  24. +
  25. +

    Run the project, select ‘Movies’ in the left navigation bar, +select a radio button next to any movie, and click on details to see the +output as shown.

    +
    +
    +6.12 output +
    +
    Figure 27. Movie Details page
    +
    +
    +

    Click on the ‘Back’ button to select another movie.

    +
    +
  26. +
  27. +

    Add the ability to delete a movie. In ‘movies.xhtml’, add the +following code with other <commandButton>.

    +
    +
    +
    <h:commandButton
    +    value="Delete"
    +    action="movies"
    +    actionListener="#{movieClientBean.deleteMovie()}"/>
    +
    +
    +
    +

    This button displays a label ‘Delete’, invokes the method deleteMovie +from ‘MovieClientBean’, and then renders ‘movies.xhtml’.

    +
    +
  28. +
  29. +

    Add the following code to ‘MovieClientBean’:

    +
    +
    +
    public void deleteMovie() {
    +    target
    +        .path("{movieId}")
    +        .resolveTemplate("movieId", bean.getMovieId())
    +        .request()
    +        .delete();
    +}
    +
    +
    +
    +

    This code again reuses the Client and WebTarget instances created in +@PostConstruct. It also adds a variable part to the URI of the REST +endpoint, defined using {movieId}, and binds it to a concrete value +using resolveTemplate method. The URI of the resource to be deleted is +prepared and then delete method is called to delete the resource.

    +
    +
    +

    Make sure to resolve the imports.

    +
    +
    +

    Running the project shows the output shown.

    +
    +
    +
    +6.14 output +
    +
    Figure 28. Delete button
    +
    +
    +

    Select a movie and click on Delete button. This deletes the movie from +the database and refreshes list on the page. Note that a redeploy of the +project will delete all the movies anyway and add them all back.

    +
    +
  30. +
+
+
+
+
+

7. Add Movie (Java API for JSON Processing)

+
+
+

Purpose: Add a new movie. In doing so several new features of the Java +API for JSON Processing 1.0 will be introduced and demonstrated by using +them in the application.

+
+
+

Estimated Time: 30-45 mins

+
+
+

Java API for JSON Processing provides a standard API to parse and +generate JSON so that the applications can rely upon a portable API. +This API will provide:

+
+
+
    +
  • +

    Produce/Consume JSON in a streaming fashion (similar to StAX API for XML)

    +
  • +
  • +

    Build a Java Object Model for JSON (similar to DOM API for XML)

    +
  • +
+
+
+

This section will define a JAX-RS Entity Providers that will allow +reading and writing JSON for a Movie POJO. The JAX-RS Client API will +request this JSON representation.

+
+
+

JAX-RS Entity Providers supply mapping services between on-the-wire +representations and their associated Java types.  Several standard Java +types such as String, byte[], javax.xml.bind.JAXBElement, +java.io.InputStream, java.io.File, and others have a pre-defined mapping +and is required by the specification. Applications may provide their own +mapping to custom types using MessageBodyReader and MessageBodyWriter +interfaces.

+
+
+

This section will provide the ability to add a new movie to the +application. Typically, this functionality will be available after +proper authentication and authorization.

+
+
+
    +
  1. +

    Right-click on Source Packages, select ‘New’, ‘Java Class’, +specify the name as ‘MovieReader’, package as ‘org.javaee7.movieplex7.json’ +and click on ‘Finish’. Add the following class-level annotations:

    +
  2. +
  3. +

    Right-click on newly created package, select ‘New’, ‘Java Class’, +specify the name as ‘MovieReader’, and click on ‘Finish’. Add the +following class-level annotations:

    +
    +
    +
    @Provider
    +@Consumes(MediaType.APPLICATION_JSON)
    +
    +
    +
    +

    @Provider allows this implementation to be discovered by the JAX-RS +runtime during the provider scanning phase. @Consumes indicates that +this implementation will consume a JSON representation of the resource.

    +
    +
    +

    Make sure to resolve imports from the appropriate package as shown.

    +
    +
    +
    +7.2 imports +
    +
    Figure 29. Provider import
    +
    +
  4. +
  5. +

    Make the class implements MessageBodyReader<Movie>.

    +
    +
    +7.3 implements +
    +
    Figure 30. Implement abstract methods for MessageBodyReader
    +
    +
    +

    Click on the hint (shown as yellow bulb) on the class definition and +select ‘Implement all abstract methods’.

    +
    +
  6. +
  7. +

    Change implementation of the isReadable method as:

    +
    +
    +
    return Movie.class.isAssignableFrom(type);
    +
    +
    +
    +

    This method ascertains if the MessageBodyReader can produce an instance +of a particular type.

    +
    +
  8. +
  9. +

    Replace the readFrom method with:

    +
    +
    +
    @Override
    +public Movie readFrom(
    +    Class<Movie> type,
    +    Type type1,
    +    Annotation[] antns,
    +    MediaType mt,
    +    MultivaluedMap<String, String> mm,
    +    InputStream in)
    +      throws IOException, WebApplicationException {
    +
    +    Movie movie = new Movie();
    +    JsonParser parser = Json.createParser(in);
    +    while (parser.hasNext()) {
    +        switch (parser.next()) {
    +            case KEY_NAME:
    +                String key = parser.getString();
    +                parser.next();
    +                switch (key) {
    +                    case "id":
    +                        movie.setId(parser.getInt());
    +                        break;
    +                    case "name":
    +                        movie.setName(parser.getString());
    +                        break;
    +                    case "actors":
    +                        movie.setActors(parser.getString());
    +                        break;
    +                    default:
    +                        break;
    +                }
    +                break;
    +            default:
    +                break;
    +        }
    +    }
    +    return movie;
    +}
    +
    +
    +
    +

    This code reads a type from the input stream in. JsonParser, a streaming +parser, is created from the input stream. Key values are read from the +parser and a Movie instance is populated and returned.

    +
    +
    +

    Resolve the imports.

    +
    +
  10. +
  11. +

    Right-click on ‘org.javaee7.movieplex7.json’ package, select ‘New’, ‘Java Class’, +specify the name as ‘MovieWriter’, and click on ‘Finish’. Add the +following class-level annotations:

    +
    +
    +
    @Provider
    +@Produces(MediaType.APPLICATION_JSON)
    +
    +
    +
    +

    @Provider allows this implementation to be discovered by the JAX-RS +runtime during the provider scanning phase. @Produces indicates that +this implementation will produce a JSON representation of the resource.

    +
    +
    +

    Resolve the imports as shown.

    +
    +
    +
    +7.6 imports +
    +
    Figure 31. Provider import
    +
    +
  12. +
  13. +

    Make this class implement MessageBodyWriter interface by adding the following code:

    +
    +
    +
    implements MessageBodyWriter<Movie>
    +
    +
    +
    +

    Resolve the imports.

    +
    +
    +

    The IDE provide a hint to implement abstract methods as:

    +
    +
    +
    +7.7 implements +
    +
    Figure 32. Implement abstract methods for MessageBodyWriter
    +
    +
    +

    Click on the hint (show as yellow bulb) on the class definition and +select ‘Implement all abstract methods’.

    +
    +
  14. +
  15. +

    Change implementation of the isWritable method to:

    +
    +
    +
    return Movie.class.isAssignableFrom(type);
    +
    +
    +
    +

    This method ascertains if the MessageBodyWriter supports a particular +type.

    +
    +
  16. +
  17. +

    Add implementation of the getSize method as:

    +
    +
    +
    return -1;
    +
    +
    +
    +

    Originally, this method was called to ascertain the length in bytes of +the serialized form of t. In JAX-RS 2.0, this method is deprecated and +the value returned by the method is ignored by a JAX-RS runtime. All +MessageBodyWriter implementations are advised to return -1.

    +
    +
  18. +
  19. +

    Change implementation of the writeTo method to:

    +
    +
    +
    JsonGenerator gen = Json.createGenerator(entityStream);
    +gen.writeStartObject()
    +    .write("id", t.getId())
    +    .write("name", t.getName())
    +    .write("actors", t.getActors())
    +    .writeEnd();
    +    gen.flush();
    +
    +
    +
    +

    This method writes a type to an HTTP message. JsonGenerator writes JSON +data to an output stream in a streaming way. Overloaded write methods +are used to write different data types to the stream.

    +
    +
    +

    Resolve the imports.

    +
    +
  20. +
  21. +

    In ‘Web Pages’, right-click on ‘client’ folder, select ‘New’, +‘Facelets Template Client’. Give the File Name as ‘addmovie’. +Click on ‘Browse’ next to ‘Template:’, expand ‘Web Pages’, +‘WEB-INF’, select ‘template.xhtml’, and click on ‘Select File’. +Click on ‘Finish’.

    +
  22. +
  23. +

    In this file, remove <ui:define> sections where name attribute value is +‘top’ and ‘left’. These sections are inherited from the template.

    +
    +

    Replace <ui:define> section with ‘content’ name such that it looks like:

    +
    +
    +
    +
    <ui:composition template="../WEB-INF/template.xhtml">
    +    <ui:define name="content">
    +    <h1>Add a New Movie</h1>
    +    <h:form>
    +        <table cellpadding="5" cellspacing="5">
    +            <tr>
    +                <th align="left">Movie Id:</th>
    +                <td><h:inputText value="#{movieBackingBean.movieId}"/></td>
    +            </tr>
    +            <tr>
    +                <th align="left">Movie Name:</th>
    +                <td><h:inputText value="#{movieBackingBean.movieName}"/> </td>
    +            </tr>
    +            <tr>
    +                <th align="left">Movie Actors:</th>
    +                <td><h:inputText value="#{movieBackingBean.actors}"/></td>
    +            </tr>
    +        </table>
    +        <h:commandButton
    +            value="Add"
    +            action="movies"
    +            actionListener="#{movieClientBean.addMovie()}"/>
    +    </h:form>
    +    </ui:define>
    +</ui:composition>
    +
    +
    +
    +

    This code creates a form to accept input of id, name, and actors of a +movie. These values are bound to fields in MovieBackingBean. The click +of command button invokes the addMovie method from MovieClientBean and +then renders ‘movies.xhtml’.

    +
    +
    +

    Click on the hint (show as yellow bulb) to resolve the namespace +prefix/URI mapping as shown.

    +
    +
    +
    +7.11 imports +
    +
    Figure 33. Namespace prefix mapping imports
    +
    +
  24. +
  25. +

    Add movieName and actors field to MovieBackingBean as:

    +
    +
    +
    String movieName;
    +String actors;
    +
    +
    +
    +

    Generate getters and setters by clicking on the menu item ‘Source’ and +then ‘Insert Code’.

    +
    +
  26. +
  27. +

    Add the following code to ‘movies.xhtml’

    +
    +
    +
    <h:commandButton value="New Movie" action="addmovie" />
    +
    +
    +
    +

    along with rest of the <commandButton>s.

    +
    +
  28. +
  29. +

    Add the following method in MovieClientBean:

    +
    +
    +
    public void addMovie() {
    +    Movie m = new Movie();
    +    m.setId(bean.getMovieId());
    +    m.setName(bean.getMovieName());
    +    m.setActors(bean.getActors());
    +    target
    +        .register(MovieWriter.class)
    +        .request()
    +        .post(Entity.entity(m, MediaType.APPLICATION_JSON));
    +}
    +
    +
    +
    +

    This method creates a new Movie instance, populates it with the values +from the backing bean, and POSTs the bean to the REST endpoint. The +register method registers a MovieWriter that provides conversion from +the POJO to JSON. Media type of application/json is specified using MediaType.APPLICATION_JSON.

    +
    +
    +

    Resolve the imports as shown

    +
    +
    +
    +7.14 imports +
    +
    Figure 34. Entity import
    +
    +
  30. +
  31. +

    Run the project to see the updated main page as:

    +
    +
    +7.15 output +
    +
    Figure 35. New Movie button
    +
    +
    +

    A new movie can be added by clicking on ‘New Movie’ button.

    +
    +
  32. +
  33. +

    Enter the details as shown:

    +
    +
    +7.16 output +
    +
    Figure 36. Add a New Movie page
    +
    +
    +

    Click on ‘Add’ button. The ‘Movie Id’ value has to be greater than 20 +otherwise the primary key constraint will be violated. The table +definition may be updated to generate the primary key based upon a +sequence; however this is not done in the application.

    +
    +
    +

    The updated page looks like as shown

    +
    +
    +
    +7.16 output2 +
    +
    Figure 37. Newly added movie
    +
    +
    +

    Note that the newly added movie is now displayed.

    +
    +
  34. +
+
+
+
+
+

8. Movie Points (Java Message Service)

+
+
+

Purpose: Customers accrue points for watching a movie.

+
+
+

Estimated Time: 30-45 mins

+
+
+

Java Message Service 2.0 allows sending and receiving messages between +distributed systems. JMS 2 introduced several improvements over the +previous version such as:

+
+
+
    +
  • +

    New JMSContext interface

    +
  • +
  • +

    AutoCloseable JMSContext, Connection, and Session

    +
  • +
  • +

    Use of runtime exceptions

    +
  • +
  • +

    Method chaining on JMSProducer

    +
  • +
  • +

    Simplified message sending

    +
  • +
+
+
+

This section will provide a page to simulate submission of movie points +accrued by a customer. These points are submitted to a JMS queue that is +then read synchronously by another bean. JMS queue for further +processing, possibly storing in the database using JPA.

+
+
+
    +
  1. +

    Right-click on Source Packages, select ‘New’, ‘Java Class’, +specify the name as ‘SendPointsBean’, package as ‘org.javaee7.movieplex7.points’, +and click on ‘Finish’.

    +
    +

    Add the following class-level annotations:

    +
    +
    +
    +
    @Named
    +@RequestScoped
    +
    +
    +
    +

    This makes the bean to be EL-injectable and automatically activated and +passivated with the request.

    +
    +
    +

    Resolve the imports.

    +
    +
    +
    +8.2 imports +
    +
    Figure 38. RequestScoped import
    +
    +
  2. +
  3. +

    A message to a JMS Queue is sent after the customer has bought the +tickets. Another bean will then retrieve this message and update the +points for that customer. This allows the two systems, one generating +the data about tickets purchased and the other about crediting the +account with the points, completely decoupled.

    +
    +

    This lab will mimic the sending and consuming of a message by an +explicit call to the bean from a JSF page.

    +
    +
    +

    Add the following field to the class:

    +
    +
    +
    +
    @NotNull
    +@Pattern(regexp = "^\\d{2},\\d{2}",
    +         message = "Message format must be 2 digits, comma, 2 digits, e.g.12,12")
    +private String message;
    +
    +
    +
    +

    This field contains the message sent to the queue. This field’s value is +bound to an inputText in a JSF page (created later). Constraints have +been specified on this bean that enable validation of data on form +submit. It requires the data to consists of two numerical digits, followed +by a comma, and then two more numerical digits. If the message does not +meet the validation criteria then the error message to be displayed is +specified using message attribute.

    +
    +
    +

    This could be thought as conveying the customer identifier and the +points accrued by that customer.

    +
    +
    +

    Generate getter/setters for this field. Right-click in the editor pane, +select ‘Insert Code’ (Ctrl+I shortcut on OSX), select ‘Getter and +Setter’, select the field, and click on ‘Generate’.

    +
    +
  4. +
  5. +

    Add the following code to the class:

    +
    +
    +
    @Inject
    +JMSContext context;
    +
    +@Resource(lookup = "java:global/jms/pointsQueue")
    +Queue pointsQueue;
    +
    +public void sendMessage() {
    +    System.out.println("Sending message: " + message);
    +    context.createProducer().send(pointsQueue, message);
    +}
    +
    +
    +
    +

    The Java EE Platform requires a pre-configured JMS connection factory +under the JNDI name java:comp/DefaultJMSConnectionFactory. If no +connection factory is specified then the pre-configured connection +factory is used. In a Java EE environment, where CDI is enabled by +default anyway, a container-managed JMSContext can be injected as:

    +
    +
    +
    +
    @Inject
    +JMSContext context;
    +
    +
    +
    +

    This code uses the default factory to inject an instance of +container-managed JMSContext.

    +
    +
    +

    JMSContext is a new interface introduced in JMS 2. This combines in a +single object the functionality of two separate objects from the JMS 1.1 +API: a Connection and a Session.

    +
    +
    +

    When an application needs to send messages it use the createProducer +method to create a JMSProducer that provides methods to configure and +send messages. Messages may be sent either synchronously or +asynchronously.

    +
    +
    +

    When an application needs to receive messages it uses one of several +createConsumer or createDurableConsumer methods to create a JMSConsumer. +A JMSConsumer provides methods to receive messages either synchronously +or asynchronously.

    +
    +
    +

    All messages are then sent to a Queue instance (created later) +identified by java:global/jms/pointsQueue JNDI name. The actual message +is obtained from the value entered in the JSF page and bound to the +message field.

    +
    +
    +

    Resolve the imports.

    +
    +
    + + + + + +
    +
    Warning
    +
    +Make sure Queue class is imported from javax.jms.Queue instead of the +default java.util.Queue. +
    +
    +
    +

    Click on ‘OK’.

    +
    +
  6. +
  7. +

    Right-click on ‘org.javaee7.movieplex7.points’ package, select +‘New’, ‘Java Class’, specify the name as ‘ReceivePointsBean’.

    +
    +

    Add the following class-level annotations:

    +
    +
    +
    +
    @JMSDestinationDefinition(name = "java:global/jms/pointsQueue",
    +interfaceName = "javax.jms.Queue")
    +@Named
    +@RequestScoped
    +
    +
    +
    +

    This allows the bean to refered from an EL expression. It also activates +and passivates the bean with the request.

    +
    +
    +

    JMSDestinationDefinition is a new annotation introduced in JMS 2. It is +used by the application to provision the required resources and allow an +application to be deployed into a Java EE environment with minimal +administrative configuration. This code will create Queue with the JNDI +name java:global/jms/pointsQueue.

    +
    +
  8. +
  9. +

    Add the following code to the class:

    +
    +
    +
    @Inject
    +JMSContext context;
    +
    +@Resource(lookup="java:global/jms/pointsQueue")
    +Queue pointsQueue;
    +
    +public String receiveMessage() {
    +    try (JMSConsumer consumer = context.createConsumer(pointsQueue)) {
    +        String message = consumer.receiveBody(String.class);
    +        System.out.println("Received message: " + message);
    +        return message;
    +    }
    +}
    +
    +
    +
    +

    This code creates JMSConsumer in a try-with-resources block +which is then used to synchronously receive a message. Note that JMSConsumer +is created as an auto-managed resource and so is closed automatically after +receiving each message. Alternatively asynchronous message delivery can also be setup +using Message Driven Beans. However that is not covered in this lab.

    +
    +
  10. +
  11. +

    Add the following method to the class:

    +
    +
    +
    public int getQueueSize() {
    +    int count = 0;
    +    try {
    +        QueueBrowser browser = context.createBrowser(pointsQueue);
    +        Enumeration elems = browser.getEnumeration();
    +        while (elems.hasMoreElements()) {
    +            elems.nextElement();
    +            count++;
    +        }
    +    } catch (JMSException ex) {
    +        ex.printStackTrace();
    +    }
    +    return count;
    +}
    +
    +
    +
    +

    This code creates a QueueBrowser to look at the messages on a queue +without removing them. It calculates and returns the total number of +messages in the queue.

    +
    +
    +

    Make sure to resolve the import from javax.jms.Queue, take all other +defaults.

    +
    +
  12. +
  13. +

    Right-click on ‘Web Pages’, select ‘New’, ‘Folder’, specify the +name as ‘points’, and click on ‘Finish’.

    +
    +

    In ‘Web Pages’, right-click on newly created folder, select ‘Facelets +Template Client’, give the File Name as ‘points’. Click on ‘Browse’ +next to ‘Template:’, expand ‘Web Pages’, ‘WEB-INF’, select +‘template.xhtml’, and click on ‘Select File’. Click on ‘Finish’.

    +
    +
  14. +
  15. +

    In this file, remove <ui:define> sections where name attribute value is +‘top’ and ‘left’. These sections are inherited from the template.

    +
    +

    Replace the <ui:define> section with ‘content’ name such that it looks like:

    +
    +
    +
    +
    <ui:composition template="../WEB-INF/template.xhtml">
    +    <ui:define name="content">
    +        <h1>Points</h1>
    +        <h:form>
    +        Queue size:
    +            <h:outputText value="#{receivePointsBean.queueSize}"/><p/>
    +            <h:inputText value="#{sendPointsBean.message}"/>
    +            <h:commandButton
    +                value="Send Message"
    +                action="points"
    +                actionListener="#{sendPointsBean.sendMessage()}"/>
    +        </h:form>
    +        <h:form>
    +            <h:commandButton
    +                value="Receive Message"
    +                action="points"
    +                actionListener="#{receivePointsBean.receiveMessage()}"/>
    +        </h:form>
    +    </ui:define>
    +</ui:composition>
    +
    +
    +
    +

    Click on the yellow bulb to resolve namespace prefix/URI mapping for h: +prefix.

    +
    +
    +

    This page displays the number of messages in the current queue. It +provides a text box for entering the message that can be sent to the +queue. The first command button invokes sendMessage method from +SendPointsBean and refreshes the page. Updated queue count, incremented +by 1 in this case, is displayed. The second command button invokes +receiveMessage method from ReceivePointsBean and refreshes the page. The +queue count is updated again, decremented by 1 in this case.

    +
    +
    +

    If the message does not meet the validation criteria then the error +message is displayed on the screen.

    +
    +
  16. +
  17. +

    Add the following code in ‘template.xhtml’ along with other +<outputLink>s:

    +
    +
    +
    <p/><h:outputLink
    +        value="${facesContext.externalContext.requestContextPath}/faces/points/points.xhtml">
    +        Points
    +    </h:outputLink>
    +
    +
    +
  18. +
  19. +

    Run the project. The update page looks like as shown:

    +
    +
    +8.10 output +
    +
    Figure 39. Points link on main page
    +
    +
    +

    Click on ‘Points’ to see the output as:

    +
    +
    +
    +8.10 output2 +
    +
    Figure 40. Points output page
    +
    +
    +

    The output shows that the queue has 0 messages. Enter a message ‘1212’ +in the text box and click on ‘Send Message’ to see the output as shown.

    +
    +
    +
    +8.10 output3 +
    +
    Figure 41. Validation message on Points page
    +
    +
    +

    This message is not meeting the validation criteria and so the error +message is displayed.

    +
    +
    +

    Enter a message as ‘12,12’ in the text box and click on ‘Send Message’ +button to see the output as:

    +
    +
    +
    +8.10 output4 +
    +
    Figure 42. Correct input for Points page - Send Message (queue size=1)
    +
    +
    +

    The updated count now shows that there is 1 message in the queue. Click +on ‘Receive Message’ button to see output as:

    +
    +
    +
    +8.10 output5 +
    +
    Figure 43. Correct input for Points page - Receive Message (queue size=0)
    +
    +
    +

    The updated count now shows that the message has been consumed and the +queue has 0 messages.

    +
    +
    +

    Click on ‘Send Message’ 4 times to see the output as:

    +
    +
    +
    +8.10 output6 +
    +
    Figure 44. Correct input for Points page - Send Message (queue size=4)
    +
    +
    +

    The updated count now shows that the queue has 4 messages. Click on +‘Receive Message’ 2 times to see the output as:

    +
    +
    +
    +8.10 output7 +
    +
    Figure 45. Correct input for Points page - Receive Message (queue size=2)
    +
    +
    +

    The count is once again updated to reflect the 2 consumed and 2 +remaining messages in the queue.

    +
    +
  20. +
+
+
+
+
+

9. Show Booking (JavaServer Faces)

+
+
+

Purpose: Build pages that allow a user to book a particular movie show +in a theater. In doing so a new feature of JavaServer Faces 2.2 will be +introduced and demonstrated by using in the application.

+
+
+

Estimated Time: 30-45 mins

+
+
+

JavaServer Faces 2.2 introduces a new feature called Faces Flow that +provides an encapsulation of related views/pages with application +defined entry and exit points. Faces Flow borrows core concepts from ADF +TaskFlow, Spring Web Flow, and Apache MyFaces CODI.

+
+
+

It introduces @FlowScoped CDI annotation for flow-local storage and +@FlowDefinition to define the flow using CDI producer methods. There are +clearly defined entry and exit points with well-defined parameters. This +allows the flow to be packaged together as a JAR or ZIP file and be +reused. The application thus becomes a collection of flows and non-flow +pages. Usually the objects in a flow are designed to allow the user to +accomplish a task that requires input over a number of different views.

+
+
+

This application will build a flow that allows the user to make a movie +reservation. The flow will contain four pages:

+
+
+
    +
  • +

    Display the list of movies

    +
  • +
  • +

    Display the list of available show timings

    +
  • +
  • +

    Confirm the choices

    +
  • +
  • +

    Make the reservation and show the ticket

    +
  • +
+
+
+

Lets build the application.

+
+
+
    +
  1. +

    Items in a flow are logically related to each other and so it is +required to keep them together in a directory. In NetBeans, right-click +on the ‘Web Pages’, select ‘New’, ‘Folder’, specify the folder name +‘booking’, and click on ‘Finish’.

    +
  2. +
  3. +

    Right-click on the newly created folder, select ‘New’, ‘Facelets +Template Client’, give the File Name as ‘booking’. Click on ‘Browse’ +next to ‘Template:’, expand ‘Web Pages’, ‘WEB-INF’, select +‘template.xhtml’, and click on ‘Select File’. Click on ‘Finish’.

    +
  4. +
  5. +

    ‘booking.xhtml’ is the entry point to the flow (more on this later).

    +
    +

    In this file, remove <ui:define> sections with ‘top’ and ‘left’ name +attributes. These sections are inherited from the template.

    +
    +
    +

    Replace <ui:define> section with ‘content’ name such that it looks like:

    +
    +
    +
    +
    <ui:composition template="../WEB-INF/template.xhtml">
    +    <ui:define name="content">
    +        <h2>Pick a movie</h2>
    +        <h:form prependId="false">
    +            <h:selectOneRadio
    +                value="#{booking.movieId}"
    +                layout="pageDirection"
    +                required="true">
    +                <f:selectItems
    +                    value="#{movieFacadeREST.all}"
    +                    var="m"
    +                    itemValue="#{m.id}"
    +                    itemLabel="#{m.name}"/>
    +            </h:selectOneRadio>
    +            <h:commandButton id="shows" value="Pick a time" action="showtimes" />
    +        </h:form>
    +    </ui:define>
    +</ui:composition>
    +
    +
    +
    +

    The code builds an HTML form that displays the list of movies as radio +button choices. The chosen movie is bound to #{booking.movieId} which +will be defined as a flow-scoped bean. The value of action attribute on +commandButton refers to the next view in the flow, i.e. +‘showtimes.xhtml’ in the same directory in our case.

    +
    +
    +

    Click on the yellow bulb as shown and click on the suggestion to +add namespace prefix/URI mapping for h:. Repeat the same for f: prefix as well.

    +
    +
    +
    +9.3 imports +
    +
    Figure 46. Namespace prefix mapping imports
    +
    +
  6. +
  7. +

    Right-click on ‘Source Packages’, select ‘New’, ‘Java Class’. +Specify the class name as ‘Booking’ and the package name as +‘org.javaee7.movieplex7.booking’.

    +
    +

    Add @Named class-level annotation to make the class EL-injectable.

    +
    +
    +

    Add @FlowScoped("booking") to define the scope of bean as the flow. The bean is automatically activated and passivated as the flow is entered or exited.

    +
    +
    +

    Add implements Serializable to the class as beans with @FlowScoped annotation need to be passivation capable, and thus serializable.

    +
    +
    +

    Add the following field:

    +
    +
    +
    +
    int movieId;
    +
    +
    +
    +

    and generate getters/setters by going to ‘Source’, ‘Insert Code’, +selecting ‘Getter and Setter’, and select the field.

    +
    +
    +

    Inject EntityManager in this class by adding the following code:

    +
    +
    +
    +
    @PersistenceContext
    +EntityManager em;
    +
    +
    +
    +

    Add the following convenience method:

    +
    +
    +
    +
    public String getMovieName() {
    +    try {
    +        return em.createNamedQuery("Movie.findById", Movie.class)
    +                  .setParameter("id", movieId)
    +                  .getSingleResult()
    +                  .getName();
    +    } catch (NoResultException e) {
    +        return "";
    +    }
    +}
    +
    +
    +
    +

    This method will return the movie name based upon the selected movie.

    +
    +
    +

    Alternatively, movie id and name may be passed from the selected radio +button and parsed in the backing bean. This will reduce an extra trip to +the database.

    +
    +
    +

    Resolve the imports.

    +
    +
  8. +
  9. +

    Create ‘showtimes.xhtml’ in the ‘booking’ folder following the +steps used to create ‘booking.xhtml’.

    +
    +

    In this file, remove <ui:define> sections with ‘top’ and ‘left’ name +attributes. These sections are inherited from the template.

    +
    +
    +

    Replace <ui:define> section with ‘content’ name such that it looks like:

    +
    +
    +
    +
    <ui:composition template="../WEB-INF/template.xhtml">
    +    <ui:define name="content">
    +        <h2>Show Timings for <font color="red">#{booking.movieName}</font></h2>
    +        <h:form>
    +            <h:selectOneRadio value="#{booking.startTime}" layout="pageDirection" required="true">
    +                <c:forEach items="#{timeslotFacadeREST.all}" var="s">
    +                    <f:selectItem itemValue="#{s.id},#{s.startTime}" itemLabel="#{s.startTime}"/>
    +                </c:forEach>
    +            </h:selectOneRadio>
    +            <h:commandButton value="Confirm" action="confirm" />
    +            <h:commandButton id="back" value="Back" action="booking" immediate="true"/>
    +        </h:form>
    +    </ui:define>
    +</ui:composition>
    +
    +
    +
    +

    This code builds an HTML form that displays the chosen movie name and +all the show times. #{timeslotFacadeREST.all} returns the list of all +the movies and iterates over them using a c:forEach loop. The id and +start time of the selected show are bound to #{booking.startTime}. +Command button with value ‘Back’ allows going back to the previous page and +the other command button with value ‘Confirm’ takes to the next view in the +flow, ‘confirm.xhtml’ in our case.

    +
    +
    +

    Typically a user will expect the show times only for the selected movie +but all the show times are shown here. This allows us to demonstrate +going back and forth within a flow if an incorrect show time for a movie +is chosen. A different query may be written that displays only the shows +available for this movie; however this is not part of the application.

    +
    +
    +

    Right-click on the yellow bulb to fix namespace prefix/URI mapping for +h:. This needs to be repeated for c: and f: prefix as well.

    +
    +
  10. +
  11. +

    Add the following fields to the Booking class:

    +
    +
    +
    String startTime;
    +int startTimeId;
    +
    +
    +
    +

    And the following methods:

    +
    +
    +
    +
    public String getStartTime() {
    +    return startTime;
    +}
    +
    +public void setStartTime(String startTime) {
    +    StringTokenizer tokens = new StringTokenizer(startTime, ",");
    +    startTimeId = Integer.parseInt(tokens.nextToken());
    +    this.startTime = tokens.nextToken();
    +}
    +
    +public int getStartTimeId() {
    +    return startTimeId;
    +}
    +
    +
    +
    +

    These methods will parse the values received from the form. Also add the +following method:

    +
    +
    +
    +
    public String getTheater() {
    +    // for a movie and show
    +    try {
    +
    +        // Always return the first theater
    +        List<ShowTiming> list =
    +            em.createNamedQuery("ShowTiming.findByMovieAndTimingId",
    +                ShowTiming.class)
    +                .setParameter("movieId", movieId)
    +                .setParameter("timingId", startTimeId)
    +                .getResultList();
    +
    +        if (list.isEmpty())
    +            return "none";
    +
    +        return list
    +                .get(0)
    +                .getTheaterId()
    +                .getId()
    +                .toString();
    +    } catch (NoResultException e) {
    +        return "none";
    +    }
    +}
    +
    +
    +
    +

    This method will find the first theater available for the chosen movie +and show the timing.

    +
    +
    +

    Additionally a list of theaters offering that movie may be shown in a +separate page.

    +
    +
    +

    Resolve the imports.

    +
    +
  12. +
  13. +

    Create ‘confirm.xhtml’ page in the ‘booking’ folder by following +the steps used to create ‘booking.xhtml’.

    +
    +

    In this file, remove <ui:define> sections wht ‘top’ and ‘left’ name +attributes. These sections are inherited from the template.

    +
    +
    +

    Replace ‘<ui:define>’ section with ‘content’ name such that it looks like:

    +
    +
    +
    +
    <ui:composition template="../WEB-INF/template.xhtml">
    +    <ui:define name="content">
    +        <c:choose>
    +            <c:when test="#{booking.theater == 'none'}">
    +                <h2>No theater found, choose a different time</h2>
    +                <h:form>
    +                    Movie name: #{booking.movieName}<p/>
    +                    Starts at: #{booking.startTime}<p/>
    +                    <h:commandButton id="back" value="Back" action="showtimes"/>
    +                </h:form>
    +            </c:when>
    +            <c:otherwise>
    +                <h2>Confirm ?</h2>
    +                <h:form>
    +                    Movie name: #{booking.movieName}<p/>
    +                    Starts at: #{booking.startTime}<p/>
    +                    Theater: #{booking.theater}<p/>
    +                    <h:commandButton id="next" value="Book" action="print"/>
    +                    <h:commandButton id="back" value="Back" action="showtimes"/>
    +                </h:form>
    +            </c:otherwise>
    +        </c:choose>
    +    </ui:define>
    +</ui:composition>
    +
    +
    +
    +

    The code displays the selected movie, show timing, and theater if +available. The reservation can proceed if all three are available. +‘print.xhtml’ is the last page that shows the confirmed reservation +and is shown when ‘Book’ commandButton is clicked.

    +
    +
    +

    actionListener can be added to commandButton to invoke the business +logic for making the reservation. Additional pages may be added to take +the credit card details and email address.

    +
    +
    +

    Right-click on the yellow bulb to fix namespace prefix/URI mapping for ‘c:’. +This needs to be repeated for ‘h:’ prefix as well.

    +
    +
  14. +
  15. +

    Create ‘print.xhtml’ page in the ‘booking’ folder by following the +steps used to create ‘booking.xhtml’.

    +
    +

    In this file, remove <ui:define> sections wht ‘top’ and ‘left’ name +attributes. These sections are inherited from the template.

    +
    +
    +

    Replace <ui:define> section with ‘content’ name such that it looks like:

    +
    +
    +
    +
    <ui:composition template="../WEB-INF/template.xhtml">
    +    <ui:define name="content">
    +        <h2>Reservation Confirmed</h2>
    +        <h:form>
    +            Movie name: #{booking.movieName}<p/>
    +            Starts at: #{booking.startTime}<p/>
    +            Theater: #{booking.theater}<p/>
    +            <h:commandButton id="home" value="home" action="goHome" /><p/>
    +        </h:form>
    +    </ui:define>
    +</ui:composition>
    +
    +
    +
    +

    This code displays the movie name, show timings, and the selected +theater.

    +
    +
    +

    Right-click on the yellow bulb to fix namespace prefix/URI mapping for ‘h:’.

    +
    +
    +

    The commandButton initiates exit from the flow. The action attribute +defines a navigation rule that will be defined in the next step.

    +
    +
  16. +
  17. +

    ‘booking.xhtml’, ‘showtimes.xhtml’, ‘confirm.xhtml’, and +‘print.xhtml’ are all in the same directory. Now the runtime needs to be +informed that the views in this directory are to be treated as view +nodes in a flow. This can be done declaratively by adding ‘booking/booking-flow.xml’ +or programmatically by having a class with a method with the following annotations:

    +
    +
    +
    @Produces @FlowDefinition
    +
    +
    +
    +

    This lab takes the declarative approach.

    +
    +
    +

    Right-click on ‘Web Pages/booking’ folder, select ‘New’, ‘Other’, ‘XML’, +‘XML Document’, give the name as ‘booking-flow’, click on ‘Next>’, take +the default of ‘Well-formed Document’, and click on ‘Finish’.

    +
    +
    +

    Replace the generated code with the following:

    +
    +
    +
    +
    <faces-config
    +    version="2.2"
    +    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    +    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    +    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
    +        http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
    +    <flow-definition id="booking">
    +        <flow-return id="goHome">
    +        <from-outcome>/index</from-outcome>
    +        </flow-return>
    +    </flow-definition>
    +</faces-config>
    +
    +
    +
    +

    This defines the flow graph. It uses the parent element used in +a standard faces-config.xml but defines a <flow-definition> inside it.

    +
    +
    +

    <flow-return> defines a return node in a flow graph. <from-outcome> +contains the node value, or an EL expression that defines the node, to +return to. In this case, the navigation returns to the home page.

    +
    +
  18. +
  19. +

    Finally, invoke the flow by editing ‘WEB-INF/template.xhtml’ and +changing:

    +
    +
    +
    <h:commandLink action="item1">Item 1</h:commandLink>
    +
    +
    +
    +

    to

    +
    +
    +
    +
    <h:commandLink action="booking">Book a movie</h:commandLink>
    +
    +
    +
    +

    commandLink renders an HTML anchor tag that behaves like a form submit +button. The action attribute points to the directory where all views for +the flow are stored. This directory already contains ‘booking-flow.xml’ +which defines the flow of the pages.

    +
    +
  20. +
  21. +

    Run the project by right clicking on the project and selecting +‘Run’. The browser shows the updated output.

    +
    +
    +9.11 output +
    +
    Figure 47. Book a movie link on main page
    +
    +
    +

    Click on ‘Book a movie’ to see the page as shown.

    +
    +
    +
    +9.11 output2 +
    +
    Figure 48. Book a movie page
    +
    +
    +

    Select a movie, say ‘The Shiningr and click on `Pick a time’ to see the +page output as shown.

    +
    +
    +
    +9.11 output3 +
    +
    Figure 49. Show Timings page
    +
    +
    +

    Pick a time slot, say ‘04:00’, click on ‘Confirm’ to see the output as shown.

    +
    +
    +
    +9.11 output4 +
    +
    Figure 50. Confirm? page
    +
    +
    +

    Click on ‘Book’ to confirm and see the output as:

    +
    +
    +
    +9.11 output5 +
    +
    Figure 51. Reservation Confirmed page
    +
    +
    +

    Feel free to enter other combinations, go back and forth in the flow and +notice how the values in the bean are preserved.

    +
    +
    +

    Click on ‘home’ takes to the main application page.

    +
    +
  22. +
+
+
+
+
+

10. Conclusion

+
+
+

This hands-on lab built a trivial 3-tier web application using Java EE 7 +and demonstrated the following features of the platform:

+
+
+
    +
  1. +

    Java EE 7 Platform

    +
    +
      +
    1. +

      Maven coordinates

      +
    2. +
    3. +

      Default DataSource

      +
    4. +
    5. +

      Default JMSConnectionFactory

      +
    6. +
    +
    +
  2. +
  3. +

    Java API for WebSocket 1.0

    +
    +
      +
    1. +

      Annotated server endpoint

      +
    2. +
    3. +

      JavaScript client

      +
    4. +
    +
    +
  4. +
  5. +

    Batch Applications for the Java Platform 1.0

    +
    +
      +
    1. +

      Chunk-style processing

      +
    2. +
    3. +

      Exception handling

      +
    4. +
    +
    +
  6. +
  7. +

    Java API for JSON Processing 1.0

    +
    +
      +
    1. +

      Streaming API for generating JSON

      +
    2. +
    3. +

      Streaming API for consuming JSON

      +
    4. +
    +
    +
  8. +
  9. +

    Java API for RESTful Web Services 2.0

    +
    +
      +
    1. +

      Client API

      +
    2. +
    3. +

      Custom Entity Providers

      +
    4. +
    +
    +
  10. +
  11. +

    Java Message Service 2.0

    +
    +
      +
    1. +

      Default ConnectionFactory

      +
    2. +
    3. +

      Injecting JMSContext

      +
    4. +
    5. +

      Synchronous message send and receive

      +
    6. +
    +
    +
  12. +
  13. +

    Contexts and Dependency Injection 1.1

    +
    +
      +
    1. +

      Automatic discovery of beans

      +
    2. +
    3. +

      Injection of beans

      +
    4. +
    +
    +
  14. +
  15. +

    JavaServer Faces 2.2

    +
    +
      +
    1. +

      Faces Flow

      +
    2. +
    +
    +
  16. +
  17. +

    Bean Validation 1.1

    +
    +
      +
    1. +

      Integration with JavaServer Faces

      +
    2. +
    +
    +
  18. +
  19. +

    Java Transaction API 1.2

    +
    +
      +
    1. +

      @Transactional

      +
    2. +
    +
    +
  20. +
  21. +

    Java Persistence API 2.1

    +
    +
      +
    1. +

      Schema generation properties

      +
    2. +
    +
    +
  22. +
+
+
+

Hopefully this has raised your interest enough in trying out Java EE 7 applications using +WildFly 8.

+
+
+

Send us feedback or file issues at http://github.com/javaee-samples/javaee7-hol.

+
+
+
+
+

11. Troubleshooting

+
+
+
    +
  1. +

    How can I start/stop/restart the application server from within the IDE ?

    +

    In the ‘Services’ tab, right-click on +‘WildFly 8’. +All the commands to start, stop, and restart are available from the pop-up menu.

    +
  2. +
  3. +

    I accidentally closed the output log window. How do I bring it back ?

    +

    In “Services” tab of NetBeans, expand ‘Servers’, choose the application server +node, and select +‘View Server Log’.

    +
    +
    +11 wildfly server log +
    +
    Figure 52. View WildFly server log in NetBeans
    +
    +
    +

    In addition, the web-based administration console can be seen by clicking on +‘View Admin Console’.

    +
    +
  4. +
+
+
+
+
+

12. Acknowledgements

+
+
+

The following Java EE community members graciously reviewed and contributed to this hands-on lab:

+
+
+ +
+
+

Thank you very much for providing the valuable feedback!

+
+
+
+
+

13. Completed Solutions

+
+
+

The completed solution for this lab can be downloaded from javaee7-hol.

+
+
+
+
+

14. TODO

+
+
+
    +
  1. +

    Add the following use cases:

    +
    +
      +
    1. +

      Concurrency Utilities for Java EE

      +
    2. +
    3. +

      WebSocket Java Client

      +
    4. +
    +
    +
  2. +
  3. +

    Disable errors in persistence.xml

    +
  4. +
  5. +

    Add icons for Fix Imports, Format, Fix namespaces, Run the Project.

    +
  6. +
  7. +

    Change logging to use java.util.Logging.

    +
  8. +
+
+
+
+
+

15. Revision History

+
+
+
    +
  1. +

    Cleaned up NetBeans instructions and some other typos from DevoxxUK (Jun 25, 2014)

    +
  2. +
  3. +

    Added IntelliJ IDEA specific instructions (Jan 22, 2014)

    +
  4. +
  5. +

    Added macros to generate WildFly and GlassFish-server specific instructions. Also enabled IntelliJ and Eclipse specific macros (Jan 10, 2014)

    +
  6. +
  7. +

    Moving the source document from Pages to AsciiDoc (Dec 3, 2013)

    +
  8. +
+
+
+
+
+

16. Appendix A: Appendix

+
+
+

16.1. Configure WildFly 8 in NetBeans

+
+

16.1.1. Install WildFly plugin

+
+
    +
  1. +

    In NetBeans, click on ‘Tools’, ‘Plugins’, ‘Available Plugins’, type “wildfly” in ‘Search:’ box, and select the plugin by clicking on the checkbox in ‘Install’ column.

    +
    +
    +16 netbeans available plugins wildfly +
    +
    Figure 53. Available Plugins in NetBeans
    +
    +
    +

    The exact plugin version and the date may be different.

    +
    +
  2. +
  3. +

    Click the Install button, then Next >, accept the license agreement by clicking on the checkbox, then click the Install button to install the plugin. Click the Finish button to restart the IDE and complete installation.

    +
  4. +
+
+
+
+

16.1.2. Configure WildFly 8

+
+
    +
  1. +

    In NetBeans, click on ‘Services’ tab.

    +
  2. +
  3. +

    Right-click on Servers, choose ‘Add Server…’ in the pop-up menu.

    +
    +
    +netbeans addserver +
    +
    Figure 54. Add Server in NetBeans
    +
    +
  4. +
  5. +

    Select ‘WildFly Application Server’ in the Add Server Instance wizard, set the +name to ‘WildFly 8’ and click Next >.

    +
    +
    +16 netbeans add instance wildfly +
    +
    Figure 55. Add WildFly instance to NetBeans
    +
    +
  6. +
  7. +

    Click on Browse… for ‘Server Location’ and select the directory that got created +when WildFly archive was unzipped. Click on Browse… for ‘Server Configuration’ and +select the ‘standalone/configuration/standalone-full.xml’ file in the unzipped WildFly +archive.

    +
    +
    +16 netbeans wildfly full platform +
    +
    Figure 56. Configure WildFly full instance in NetBeans
    +
    +
    +

    Click on Next and then Finish. The ‘Services’ should show the WildFly instance.

    +
    +
    +
    +16 netbeans wildfly server +
    +
    Figure 57. WildFly instance in NetBeans Services tab
    +
    +
  8. +
+
+
+
+
+

16.2. Prepare IntelliJ IDEA for working with WildFly 8

+
+

To be able to perform the exercises discussed in this tutorial, you need the Ultimate Edition of IntelliJ IDEA. Keep that in mind when downloading IntelliJ IDEA from http://www.jetbrains.com/idea/download/.

+
+
+

When the appropriate edition of IntelliJ IDEA is installed, you can start preparing the IDE for the exercises:

+
+ +
+

16.2.1. Specify the JDK

+
+

First of all, you should specify the JDK that you are going to use. In IntelliJ IDEA, this is done in the Project Structure dialog:

+
+
+
    +
  1. +

    Start IntelliJ IDEA. If, as a result, a project opens, close the project (File ▸ Close Project).

    +
  2. +
  3. +

    On the Welcome screen, under Quick Start, click Configure.

    +
    +
    +i13 welcome configure +
    +
    Figure 58. Welcome to IntelliJ IDEA
    +
    +
  4. +
  5. +

    Under Configure, click Project Defaults, and then, under Project Defaults, click Project Structure.

    +
  6. +
  7. +

    In the left-hand pane of the Project Structure dialog, under Platform Settings, select SDKs. Click i13-plus-icon and select JDK.

    +
    +
    +i13 plus jdk +
    +
    Figure 59. Add JDK in IntelliJ IDEA
    +
    +
  8. +
  9. +

    In the Select Home Directory for JDK dialog, select the folder in which the JDK that you are going to use is installed, and click OK.

    +
    +
    +i13 jdk home +
    +
    Figure 60. JDK home in IntelliJ IDEA
    +
    +
  10. +
  11. +

    In the Project Structure dialog, click Apply.

    +
    +
    +i13 jdk defined +
    +
    Figure 61. JDK defined in IntelliJ IDEA
    +
    +
    +

    Now, let’s make the JDK that we have specified the default SDK.

    +
    +
  12. +
  13. +

    In the left-hand pane, under Project Settings, select Project. In the right-hand part of the dialog, under Project SDK, select the JDK from the list.

    +
    +
    +i13 project sdk +
    +
    Figure 62. Project SDK in IntelliJ IDEA
    +
    +
  14. +
  15. +

    Click OK.

    +
  16. +
+
+
+
+

16.2.2. Define WildFly

+
+

Defining an application server in IntelliJ IDEA, normally, is just telling the IDE where the server is installed. The servers are defined in the Settings dialog. (On OSX, this dialog is called Preferences.)

+
+
+
    +
  1. +

    On the Welcome screen, to the left of Project Defaults, click Back i13-back-icon.

    +
  2. +
  3. +

    Under Configure, click Settings.

    +
  4. +
  5. +

    In the left-hand pane of the Settings (Preferences) dialog, under IDE Settings, select Application Servers. On the Application Servers page, click i13-plus-icon and select JBoss Server. (WildFly is a server from the "JBoss family".)

    +
    +
    +i13 plus jboss +
    +
    Figure 63. Add WildFly in IntelliJ IDEA
    +
    +
  6. +
  7. +

    In the JBoss Server dialog, click i13-ellipsis-button to the right of the JBoss Home field.

    +
    +
    +i13 jboss server dialog initial +
    +
    Figure 64. WildFly server dialog in IntelliJ IDEA
    +
    +
  8. +
  9. +

    In the JBoss Home Directory dialog, select the folder in which you have the WildFly server installed, and click OK.

    +
    +
    +i13 jboss home directory +
    +
    Figure 65. WildFly home in IntelliJ IDEA
    +
    +
  10. +
  11. +

    Click OK in the JBoss Server dialog.

    +
    +
    +i13 jboss server dialog final +
    +
    Figure 66. WildFly final dialog in IntelliJ IDEA
    +
    +
  12. +
  13. +

    In the Settings (Preferences) dialog, click OK.

    +
    +
    +i13 jboss defined +
    +
    Figure 67. WildFly defined in IntelliJ IDEA
    +
    +
  14. +
+
+
+
+

16.2.3. Create a project

+
+

The sample application is supplied as a Maven project with an associated pom.xml file that contains all the necessary project definitions. The corresponding IntelliJ IDEA project in such a case can be created by simply "opening" the pom.xml file. (Obviously, this isn’t the only way to create projects in IDEA. You can create projects for existing collections of source files, import Eclipse and Flash Builder projects, and Gradle build scripts. Finally, you can create projects from scratch.)

+
+
+
    +
  1. +

    On the Welcome screen, to the left of Configure, click Back i13-back-icon.

    +
  2. +
  3. +

    Under Quick Start, click Open Project.

    +
    +
    +i13 open project +
    +
    Figure 68. Open project in IntelliJ IDEA
    +
    +
  4. +
  5. +

    In the Open Project dialog, select the pom.xml file associated with the sample application, and click OK.

    +
    +
    +i13 select pom +
    +
    Figure 69. Select pom in IntelliJ IDEA
    +
    +
    +

    Wait while IntelliJ IDEA is processing pom.xml and creating the project. When this process is complete, the following message is shown:

    +
    +
    +
    +i13 jpa detected +
    +
    Figure 70. Configure JPA in IntelliJ IDEA
    +
    +
  6. +
  7. +

    Click Configure in the message box. (If by now the message has disappeared, click i13-exclamation-mark-icon on the Status bar.

    +
    +
    +i13 jpa detected status bar +
    +
    Figure 71. JPA detected in status bar in IntelliJ IDEA
    +
    +
    +

    The Event Log tool window will open. Click Configure in this window.)

    +
    +
    +
    +i13 jpa detected event log +
    +
    Figure 72. JPA detected event log in IntelliJ IDEA
    +
    +
  8. +
  9. +

    In the Setup Frameworks dialog, just click OK. (By doing so you confirm that the file persistence.xml found in the project belongs to the JPA framework.)

    +
    +
    +i13 setup frameworks jpa +
    +
    Figure 73. Setup frameworks in IntelliJ IDEA
    +
    +
    +

    Now, as an intermediate check, make sure that the project structure looks something similar to this:

    +
    +
    +
    +i13 initial project structure +
    +
    Figure 74. Project structure in IntelliJ IDEA
    +
    +
  10. +
+
+
+
+

16.2.4. Create a run/debug configuration

+
+

Applications in IntelliJ IDEA are run and debugged according to what is called run/debug configurations. Now we are going to create the configuration for running and debugging the sample application in the context of WildFly.

+
+
+
    +
  1. +

    In the main menu, select Run ▸ Edit Configurations….

    +
    +
    +i13 run edit configurations +
    +
    Figure 75. Edit configurations in IntelliJ IDEA
    +
    +
  2. +
  3. +

    In the Run/Debug Configurations dialog, click i13-plus-icon, select JBoss Server, and then select Local.

    +
    +
    +i13 run configs plus jboss +
    +
    Figure 76. WildFly configuration in IntelliJ IDEA
    +
    +
    +

    As a result, the run/debug configuration for the WildFly server is created and its settings are shown in the right-hand part of the dialog.

    +
    +
  4. +
  5. +

    Change the name of the run/debug configuration to WildFly8 (optional).

    +
  6. +
  7. +

    In the lower part of the dialog, within the line Warning: No artifacts marked for deployment, click Fix and select movieplex7:war exploded. (Artifacts in IntelliJ IDEA are deployment-ready project outputs and also the configurations according to which such outputs are produced. In our case, there are two configurations for the sample application (movieplex7:war and movieplex7:war exploded). Both configurations represent a format suitable for deployment onto a Java EE 7-enabled application server. movieplex7:war corresponds to a Web archive (WAR). movieplex7:war exploded corresponds to the sample application directory structure (a decompressed archive). The second of the formats is more suitable at the development stage because manipulations with it are faster.)

    +
    +
    +i13 jboss fix deployment +
    +
    Figure 77. Fixing deployment warning in IntelliJ IDEA
    +
    +
  8. +
  9. +

    Within the line Error: Artifact 'movieplex7: exploded' has invalid extension, click Fix.

    +
    +
    +i13 jboss invalid extension +
    +
    Figure 78. Invalid extension error message in IntelliJ IDEA
    +
    +
  10. +
  11. +

    In the Project Structure dialog, add .war at the end of the output directory path, and click OK. (For the servers of the JBoss family, the application root directory has to have .war at the end.)

    +
    +
    +i13 jboss fix extension +
    +
    Figure 79. Extension error fix in IntelliJ IDEA
    +
    +
  12. +
  13. +

    In the Run/Debug Configurations dialog, switch to the Server tab. In the field for the application starting page URL, replace http://localhost:8080/movieplex7-1/ with http://localhost:8080/movieplex7-1.0-SNAPSHOT/ and click OK.

    +
    +
    +i13 jboss url fixed +
    +
    Figure 80. Fixing application URL in IntelliJ IDEA
    +
    +
  14. +
+
+
+

The Application Servers tool window opens in the lower part of the workspace. Shown in this window are the server run/debug configuration and the associated deployment artifact. Now you are ready to run the application.

+
+
+
+

16.2.5. Run the application

+
+

In the Application Servers tool window, select the server run/debug configuration (WildFly8 [local]) and click Run i13-run-icon.

+
+
+
+i13 run wildfly +
+
Figure 81. Run WildFly in IntelliJ IDEA
+
+
+

IntelliJ IDEA compiles the code, builds the artifact, starts WildFly and deploys the artifact to the server. You can monitor this process in the Run tool window that opens in the lower part of the workspace.

+
+
+
+i13 run tool window wildfly +
+
Figure 82. Run tool window in IntelliJ IDEA
+
+
+

Finally, your default Web browser opens and the starting page of the application is shown.

+
+
+
+i13 starting page in browser +
+
Figure 83. Starting page in browser from IntelliJ IDEA
+
+
+

At this step IntelliJ IDEA is fully prepared for your development work, and you can continue with your exercises.

+
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/docs/javaee7-hol.pages b/docs/javaee7-hol.pages deleted file mode 100644 index 58c9413..0000000 Binary files a/docs/javaee7-hol.pages and /dev/null differ diff --git a/docs/javaee7-hol.pdf b/docs/javaee7-hol.pdf index 357bac3..73810e5 100644 Binary files a/docs/javaee7-hol.pdf and b/docs/javaee7-hol.pdf differ diff --git a/docs/javaee7-hol.xml b/docs/javaee7-hol.xml new file mode 100644 index 0000000..bba9492 --- /dev/null +++ b/docs/javaee7-hol.xml @@ -0,0 +1,3954 @@ + + + + + + +Java EE 7 Hands-on Lab +Jun 25, 2014 + +Arun + +Gupta +https://twitter.com/arungupta[@arungupta] + + +AG + + +2.1 +Jun 25, 2014 +AG + + + + + + + + +Introduction +The Java EE 7 platform continues the ease of development push that +characterized prior releases by bringing further simplification to +enterprise development. It adds new and important APIs such as the REST +client API in JAX-RS 2.0 and the long awaited Batch Processing API. Java +Message Service 2.0 has undergone an extreme makeover to align with the +improvements in the Java language. There are plenty of improvements to +several other components. Newer web standards like HTML 5, WebSocket, +and JSON processing are embraced to build modern web applications. +This hands-on lab will build a typical 3-tier end-to-end application +using the following Java EE 7 technologies: + + +Java API for WebSocket 1.0 (JSR 356) + + +Batch Applications for the Java Platform 1.0 (JSR 352) + + +Java API for JSON Processing 1.0 (JSR 353) + + +Java API for RESTful Web Services 2.0 (JSR 339) + + +Java Message Service 2.0 (JSR 343) + + +Java Persistence API 2.1 (JSR 338) + + +JavaServer Faces 2.2 (JSR 344) + + +Contexts and Dependency Injection 1.1 (JSR 346) + + +Bean Validation 1.1 (JSR 349) + + +Java Transaction API 1.2 (JSR 907) + + + +Together these APIs will allow you to be more productive by simplifying enterprise development. +The latest version of this document can be downloaded from javaee7-hol.html. Please file issues or send pull requests for any updates. +
+Software Requirement +The following software needs to be downloaded and installed: + + +JDK 7 from +http://www.oracle.com/technetwork/java/javase/downloads/index.html. + + +Application Server: This lab can use WildFly 8 or GlassFish 4 as the application server. This document provides instructions for +WildFly 8. + + +IDE: NetBeans 8.0+, JBoss Developer Studio (Eclipse-based), or IntelliJ IDEA 13 can be used. This document provides instructions for +NetBeans 8. +Download “All” or “Java EE” version from +http://netbeans.org/downloads/. A +snapshot of the downloads page is shown and highlights the exact +‘Download’ button to be clicked. +
+NetBeans download + + + + + 1.1 netbeans download + +
+ +WildFly 8 needs to be downloaded from wildfly.org and configured in NetBeans IDE following the instructions in . + + explains how to configure WildFly in IntelliJ IDEA. + + +
+
+ +
+
+ +Problem Statement +This hands-on lab builds a typical 3-tier Java EE 7 Web application that +allows customers to view the show timings for a movie in a 7-theater +Cineplex and make reservations. Users can add new movies and delete +existing movies. Customers can discuss the movie in a chat room. Total +sales from each showing are calculated at the end of the day. Customers +also accrue points for watching movies. +
+Architecture diagram + + + + + 2.0 problem statement + +
+ +This figure shows the key components of the application. The User +Interface initiates all the flows in the application. Show Booking, +Add/Delete Movie and Ticket Sales interact with the database; Movie +Points may interact with the database, however, this is out of scope for +this application; and Chat Room does not interact with the database. +The different functions of the application, as detailed above, utilize +various Java technologies and web standards in their implementation. The +following figure shows how Java EE technologies are used in different +flows. +
+Technologies used in the application + + + + + 2.0 technologies + +
+ +The table below details the components and the selected technology used +in its’ implementation. + + + + + + + + + + + + + + Flow + + Description + + + + + + + + + + User Interface + + Written entirely in JavaServer Faces (JSF) + + + + + + Chat Room + + Utilizes client-side JavaScript and JSON to communicate with a WebSocket endpoint + + + + + + Ticket Sales + + Uses Batch Applications for the Java Platform to calculate the total +sales and persist to the database. + + + + + + Add/Delete Movie + + Implemented using RESTful Web Services. JSON is used as on-the-wire data format + + + + + + Movie Points + + Uses Java Message Service (JMS) to update and obtain loyalty reward +points; an optional implementation using database technology may be +performed + + + + + + Show Booking + + Uses lightweight Enterprise JavaBeans to communicate with the database +using Java Persistence API + + + + + + + + +This document is not a comprehensive tutorial of Java EE. The attendees +are expected to know the basic Java EE concepts such as EJB, JPA, +JAX-RS, and CDI. The Java +EE 7 Tutorial is a good place to learn all these concepts. However +enough explanation is provided in this guide to get you started with the +application. + +This is a sample application and the code may not be +following the best practices to prevent SQL injection, cross-side +scripting attacks, escaping parameters, and other similar features +expected of a robust enterprise application. This is intentional such as +to stay focused on explaining the technology. It is highly recommended +to make sure that the code copied from this sample application is +updated to meet those requirements. + + +
+Lab Flow +The attendees will start with an existing maven application and by +following the instructions and guidance provided by this lab they will: + + +Read existing source code to gain an understanding of the structure of +the application and use of the selected platform technologies. + + +Add new and update existing code with provided fragments in order to +demonstrate usage of different technology stacks in the Java EE 7 +platform. + + + +While you are copy/pasting the code from this document into NetBeans, +here are couple of tips that will be really useful and make your +experience enjoyable! + + +Source Code Formatting + +NetBeans provides capability to neatly format the source code +following conventions. This can be done for any type of source code, +whether its XML or Java or something else. It is highly recommended to +use this functionality after the code is copy/pasted from this document +to the editor. This keeps the code legible. +This functionality can be accessed by right-clicking in the editor pane +and selecting “Format” as shown. +
+Format code in NetBeans + + + + + 2.1 format + +
+ +This functionality is also accessible using the following keyboard +shortcuts: + + + + + + + + + + + + + + + + + + Shortcut + + Operating System + + + + + + + + + + CtrlShiftF + + OSX + + + + + + AltShiftF + + Windows + + + + + + AltShiftF + + Linux + + + + + + + + +
+
+ +Automatic Imports + +Copy/pasting the Java code from this document in NetBeans editor does +not auto-import the classes. This is required to be done manually in +order for the classes to compile. This can be fixed for each missing +import statement by clicking on the yellow bulb shown in the side bar. +
+ServerEndpoint import + + + + + 2.1 server endpoint + +
+ +Alternatively all the imports can be resolved by right-clicking on the +editor pane and selecting “Fix Imports” as shown. +
+Fix Imports in NetBeans + + + + + 2.1 fix imports + +
+ +This functionality is also accessible using the following keyboard +shortcuts: + + + + + + + + + + + + + + + + + + Shortcut + + Operating System + + + + + + + + + + CommandShiftI + + OSX + + + + + + CtrlShiftI + + Windows + + + + + + CtrlShiftI + + Linux + + + + + + + + +The defaults may work in most of the cases. Choices are shown in case a +class is available to import from multiple packages. If multiple +packages are available then specific packages to import from are clearly +marked in the document. +
+
+
+ +
+
+Estimated Time +Following the complete instructions in this document can take any where +from two to four hours. The wide time range accommodates for learning +the new technologies, finding your way in NetBeans, copy/pasting the +code, and debugging the errors. +The recommended flow is where you follow through the instructions in all +sections in the listed sequence. Alternatively, you may like to cover +section through in an order of your choice, based upon your +interest and preference of the technology. However section is a +pre-requisite for . +Here is an approximate time estimate for each section: + + + + + + + + + + + + + + Section Title + + Estimated Time + + + + + + + + + + + + 15 - 30 mins + + + + + + + + 30 - 45 mins + + + + + + + + 30 - 45 mins + + + + + + + + 30 - 45 mins + + + + + + + + 30 - 45 mins + + + + + + + + 30 - 45 mins + + + + + + + + 30 - 45 mins + + + + + + + + +The listed time for each section is only an estimate and by no means +restrict you within that. These sections have been completed in much +shorter time, and you can do it too! + +The listed time for each section also allows you to create a custom +version of the lab depending upon your target audience and available +time. + + +
+
+ +Walk-through of Sample Application +Purpose: This section will download the sample application to be used +in this hands-on lab. A walk-through of the application will be +performed to provide an understanding of the application architecture. +Estimated Time: 15-30 mins + + +Download the sample application from +movieplex7-starting-template.zip +and unzip. This will create a ‘movieplex7’ directory and unzips all the +content there. + + +In NetBeans IDE, select ‘File’, ‘Open Project’, select the +unzipped directory, and click on ‘Open Project’. The project structure +is shown. +
+Project structure in NetBeans + + + + + 3.2 project structure + +
+ +
+ +Maven Coordinates: Expand ‘Project Files’ and double click on +‘pom.xml’. In the ‘pom.xml’, the Java EE 7 API is specified as a +<dependency>: +<dependencies> + <dependency> + <groupId>javax</groupId> + <artifactId>javaee-api</artifactId> + <version>7.0</version> + <scope>provided</scope> + </dependency> +</dependencies> + +This will ensure that Java EE 7 APIs are retrieved from the central +Maven repository. + +The Java EE 6 platform introduced the notion of ‘profiles’. A profile is +a configuration of the Java EE platform targeted at a specific class of +applications. All Java EE profiles share a set of common features, such +as naming and resource injection, packaging rules, security +requirements, etc. A profile may contain a proper subset or superset of +the technologies contained in the platform. +The Java EE Web Profile is a profile of the Java EE Platform +specifically targeted at modern web applications. The complete set of +specifications defined in the Web Profile is defined in the Java EE 7 +Web Profile Specification. + + +WildFly can be started in Full Platform or Web Profile. + + +Default Data Source: Expand ‘Other Sources’, +‘src/main/resources’, ‘META-INF’, and double-click on ‘persistence.xml’. +By default, NetBeans opens the file in Design View. Click on ‘Source’ tab +to view the XML source. +
+persistence.xml + + + + + 3.2 persistence xml + +
+ +It looks like: +<?xml version="1.0" encoding="UTF-8"?> +<persistence + version="2.1" + xmlns="http://xmlns.jcp.org/xml/ns/persistence" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence + http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"> + <persistence-unit name="movieplex7PU" transaction-type="JTA"> + <!-- + <jta-data-source>java:comp/DefaultDataSource</jta-data-source> + --> + <properties> + <property + name="javax.persistence.schema-generation.database.action" + value="drop-and-create"/> + <property + name="javax.persistence.schema-generation.create-source" + value="script"/> + <property + name="javax.persistence.schema-generation.drop-source" + value="script"/> + <property + name="javax.persistence.schema-generation.drop-script-source" + value="META-INF/drop.sql"/> + <property + name="javax.persistence.sql-load-script-source" + value="META-INF/load.sql"/> + <property + name="eclipselink.deploy-on-startup" + value="true"/> + <property + name="eclipselink.logging.exceptions" + value="false"/> + </properties> + </persistence-unit> +</persistence> + +Notice <jta-data-source> is commented out, i.e. no data source element +is specified. This element identifies the JDBC resource to connect to in +the runtime environment of the underlying application server. +The Java EE 7 platform defines a new default data source that must be +provided by the runtime. This pre-configured data source is accessible +under the JNDI name +java:comp/DefaultDataSource + +The JPA 2.1 specification says if neither jta-data-source nor +non-jta-data-source elements are specified, the deployer must specify a +JTA data source or the default JTA data source must be provided by the +container. +For WildFly 8, the default data source is bound to the JDBC resource what name. +Clicking back and forth between ‘Design’ and ‘Source’ view may prompt +the error shown below: +
+Missing server error from persistence.xml + + + + + 3.4 missing server + +
+ +This will get resolved when we run the application. Click on ‘OK’ to +dismiss the dialog. +
+ +Schema Generation: JPA 2.1 defines a new set of +javax.persistence.schema-generation.* properties that can be used to +generate database artifacts like tables, indexes, and constraints in a +database schema. This helps in prototyping of your application where the +required artifacts are generated either prior to application deployment +or as part of EntityManagerFactory creation. This feature will allow +your JPA domain object model to be directly generated in a database. The +generated schema may need to be tuned for actual production environment. +The “persistence.xml” in the application has the following +javax.persistence.schema-generation.* properties. Their meaning and +possible values are explained: + + + + + + + + + + + + + + + + Property + + Meaning + + Values + + + + + + + + + + javax.persistence.schema-generation.database.action + + Specifies the action to be taken by the persistence provider with regard +to the database artifacts. + + none, create, drop-and-create, drop + + + + + + javax.persistence.schema-generation.create-source +javax.persistence.schema-generation.drop-source + + Specifies whether the creation or deletion of database artifacts is to +occur on the basis of the object/relational mapping metadata, DDL +script, or a combination of the two. + + metadata, script, metadata-then-script, script-then-metadata + + + + + + javax.persistence.schema-generation.create-script-source +javax.persistence.schema-generation.drop-script-source + + Specifies a java.IO.Reader configured for reading of the SQL script or a +string designating a file URL for the SQL script to create or delete +database artifacts. + + + + + + + + javax.persistence.sql-load-script-source + + Specifies a java.IO.Reader configured for reading of the SQL load script +for database initialization or a string designating a file URL for the +script. + + + + + + + + + + +Refer to the JPA 2.1 Specification +for a complete understanding of these properties. +In the application, the scripts are bundled in the WAR file in +‘META-INF’ directory. As the location of these scripts is specified as a +URL, the scripts may be loaded from outside the WAR file as well. +Feel free to open ‘create.sql’, ‘drop.sql’ and ‘load.sql’ and read +through the SQL scripts. The database schema is shown. +
+Database schema + + + + + 3.5 schema + +
+ +This folder also contains ‘sales.csv’ which carries some comma-separated +data, and is used later in the application. +
+ +JPA entities, Stateless EJBs, and REST endpoints: Expand Source +Packages'. The package `org.javaee7.movieplex7.entities contains the +JPA entities corresponding to the database table definitions. Each JPA +entity has several convenient @NamedQuery defined and uses Bean +Validation constraints to enforce validation. +The package org.javaee7.movieplex7.rest contains stateless EJBs +corresponding to different JPA entities. +Each EJB has methods to perform CRUD operations on the JPA entity and +convenience query methods. Each EJB is also EL-injectable (@Named) and +published as a REST endpoint (@Path). The AplicationConfig class defines +the base path of REST endpoint. The path for the REST endpoint is the +same as the JPA entity class name. +The mapping between JPA entity classes, EJB classes, and the URI of the +corresponding REST endpoint is shown. + + + + + + + + + + + + + + + + JPA Entity Class + + EJB Class + + RESTful Path + + + + + + + + + + Movie + + MovieFacadeREST + + /webresources/movie + + + + + + Sales + + SalesFacadeREST + + /webresources/sales + + + + + + ShowTiming + + ShowTimingFacadeREST + + /webresources/showtiming + + + + + + Theater + + TheaterFacadeREST + + /webresources/theater + + + + + + Timeslot + + TimeslotFacadeREST + + /webresources/timeslot + + + + + + + + +Feel free to browse through the code. + + +JSF pages: ‘WEB-INF/template.xhtml’ defines the template of the +web page and has a header, left navigation bar, and a main content +section. ‘index.xhtml’ uses this template and the EJBs to display the +number of movies and theaters. +Java EE 7 enables CDI discovery of beans by default. No ‘beans.xml’ is +required in ‘WEB-INF’. This allows all beans with bean defining +annotation, i.e. either a bean with an explicit CDI scope or EJBs to be +available for injection. +Note, ‘template.xhtml’ is in ‘WEB-INF’ folder as it allows the template +to be accessible from the pages bundled with the application only. If it +were bundled with rest of the pages then it would be accessible outside +the application and thus allowing other external pages to use it as +well. + + +Run the sample: Right-click on the project and select ‘Run’. +This will download all the maven dependencies on your machine, build a +WAR file, deploy on +WildFly 8 +, and show the URL +localhost:8080/movieplex7 in the +default browser configured in NetBeans. Note that this could take a +while if you have never built a Maven application on your machine. + +The project will show red squiggly lines in the source code indicating +that the classes cannot be resolved. This is expected before the +dependencies are downloaded. However these references will be resolved +correctly after the dependencies are downloaded during project building. + + +During the first run, the IDE will ask you to select a deployment server. +Choose the configured WildFly server and click on ‘OK’. +
+WildFly deployment server + + + + + 3.6 wildfly server + +
+ +The output looks like as shown. +
+Application main page + + + + + 3.8 first page + +
+ +
+
+ +
+ +Chat Room (Java API for WebSocket) +Purpose: Build a chat room for viewers. In doing so several new +features of Java API for WebSocket 1.0 will be introduced and +demonstrated by using them in the application. +Estimated Time: 30-45 mins +WebSocket provide a full-duplex and bi-directional communication +protocol over a single TCP connection. WebSocket is a combination of +IETF RFC 6455 +Protocol and +W3C JavaScript WebSocket API (a +Candidate Recommendation as of this writing). The protocol defines an +opening handshake and data transfer. The API enables Web pages to use +the WebSocket protocol for two-way communication with the remote host. +JSR 356 defines a standard API for +creating WebSocket applications in the Java EE 7 Platform. The JSR +provides support for: + + +Create WebSocket endpoint using annotations and interface + + +Initiating and intercepting WebSocket events + + +Creation and consumption of WebSocket text and binary messages + + +Configuration and management of WebSocket sessions + + +Integration with Java EE security model + + + +This section will build a chat room for movie viewers. + + +Right-click on ‘Source Packages’ , select ‘New’, ‘Java Class’. +Give the class name as ‘ChatServer’, package as +‘org.javaee7.movieplex7.chat’, and click on ‘Finish’. + + +Change the class such that it looks like: +@ServerEndpoint("/websocket") +public class ChatServer { + private static final Set<Session> peers = + Collections.synchronizedSet(new HashSet<Session>()); + + @OnOpen + public void onOpen(Session peer) { + peers.add(peer); + } + + @OnClose + public void onClose(Session peer) { + peers.remove(peer); + } + + @OnMessage + public void message(String message, Session client) + throws IOException, EncodeException { + for (Session peer : peers) { + peer.getBasicRemote().sendText(message); + } + } +} + +In this code: + + +@ServerEndpoint decorates the class to be a WebSocket endpoint. The +value defines the URI where this endpoint is published. + + +@OnOpen and @OnClose decorate the methods that must be called when +WebSocket session is opened or closed. The peer parameter defines the +client requesting connection initiation and termination. + + +@OnMessage decorates the message that receives the incoming WebSocket +message. The first parameter, message, is the payload of the message. +The second parameter, client, defines the other end of the WebSocket +connection. The method implementation transmits the received text message to +all clients connected to this endpoint. +Resolve the imports by right-clicking in the editor and selecting ‘Fix +Imports’ or (CommandShiftI shortcut on OSX or CtrlShiftI on +Windows). + +Make sure to pick java.websocket.Session for resolving imports. This is not the default option shown by NetBeans. + + +
+javax.websocket.Session import + + + + + 4.2 imports + +
+ +Right-click again in the editor pane and select ‘Format’ to format your +code. +
+
+ +
+ +In ‘Web Pages’, select ‘New’, ‘Folder’, give the folder name as +‘chat’ and click on ‘Finish’. + + +Right-click on the newly created folder, select ‘New’, ‘Other’, +‘Java Server Faces’, ‘Facelets Template Client’, give the File Name as +‘chatroom’. Click on ‘Browse’ next to ‘Template:’, expand ‘Web Pages’, +‘WEB-INF’, select ‘template.xhtml’, and click on ‘Select File’. Click on +‘Finish’. +
+Choose template + + + + + 4.4 template + +
+ +In this file, remove <ui:define> sections where name attribute value is +‘top’ and ‘left’. These sections are inherited from the template. +Replace <ui:define> section with ‘content’ name such that it looks like: +<ui:composition template="../WEB-INF/template.xhtml"> + <ui:define name="content"> + <form action=""> + <table> + <tr> + <td> + Chat Log<br/> + <textarea readonly="true" rows="6" cols="50" id="chatlog"></textarea> + </td> + <td> + Users<br/> + <textarea readonly="true" rows="6" cols="20" id="users"></textarea> + </td> + </tr> + <tr> + <td colspan="2"> + <input id="textField" name="name" value="Duke" type="text"/> + <input onclick="join();" value="Join" type="button"/> + <input onclick="send_message();" value="Send" type="button"/><p/> + <input onclick="disconnect();" value="Disconnect" type="button"/> + </td> + </tr> + </table> + </form> + <div id="output"></div> + <script language="javascript" type="text/javascript" + src="${facesContext.externalContext.requestContextPath}/chat/websocket.js"></script> + </ui:define> +</ui:composition> + +The code builds an HTML form that has two textareas – one to display the +chat log and the other to display the list of users currently logged. A +single text box is used to take the user name or the chat message. +Clicking on ‘Join’ button takes the value as user name and clicking on +‘Send’ takes the value as chat message. +JavaScript methods are invoked +when these buttons are clicked and these are explained in the next +section. The chat messages are sent and received as WebSocket payloads. +There is an explicit button to disconnect the WebSocket connection. +output div is the placeholder for status messages. The WebSocket +initialization occurs in ‘websocket.js’ included at the bottom of the +fragment. +
+ +Right-click on ‘chat’ in ‘Web Pages’, select ‘New’, ‘Other’, ‘Web’ +categories, ‘JavaScript File’ file type. Click on ‘Next’. +Give the name as ‘websocket’ and click on ‘Finish’. + + +Edit the contents of ‘websocket.js’ such that it looks like: +var wsUri = 'ws://' + document.location.host + + document.location.pathname.substr(0, + document.location.pathname.indexOf("/faces")) + + '/websocket'; +console.log(wsUri); + +var websocket = new WebSocket(wsUri); +var textField = document.getElementById("textField"); +var users = document.getElementById("users"); +var chatlog = document.getElementById("chatlog"); +var username; + +websocket.onopen = function(evt) { onOpen(evt); }; +websocket.onmessage = function(evt) { onMessage(evt); }; +websocket.onerror = function(evt) { onError(evt); }; +websocket.onclose = function(evt) { onClose(evt); }; + +var output = document.getElementById("output"); + +function join() { + username = textField.value; + websocket.send(username + " joined"); +} + +function send_message() { + websocket.send(username + ": " + textField.value); +} + +function onOpen() { + writeToScreen("CONNECTED"); +} + +function onClose() { + writeToScreen("DISCONNECTED"); +} + +function onMessage(evt) { + writeToScreen("RECEIVED: " + evt.data); + if (evt.data.indexOf("joined") !== -1) { + users.innerHTML += evt.data.substring(0, evt.data.indexOf(" joined")) + "\n"; + } else { + chatlog.innerHTML += evt.data + "\n"; + } +} + +function onError(evt) { + writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data); +} + +function disconnect() { + websocket.close(); +} + +function writeToScreen(message) { + var pre = document.createElement("p"); + pre.style.wordWrap = "break-word"; + pre.innerHTML = message; + output.appendChild(pre); +} + +The WebSocket endpoint URI is calculated by using standard JavaScript +variables and appending the URI specified in the ChatServer class. +WebSocket is initialized by calling new WebSocket(...). Event handlers are +registered for lifecycle events using onXXX messages. The listeners +registered in this script are explained in the table. + + + + + + + + + + + + + + Listeners + + Called When + + + + + + + + + + onOpen(evt) + + WebSocket connection is initiated + + + + + + onMessage(evt) + + WebSocket message is received + + + + + + onError(evt) + + Error occurs during the communication + + + + + + onClose(evt) + + WebSocket connection is terminated + + + + + + + + +Any relevant data is passed along as parameter to the function. Each +method prints the status on the browser using writeToScreen utility +method. The join method sends a message to the endpoint +that a particular user has joined. The endpoint then broadcasts the +message to all the listening clients. The send_message method appends +the logged in user name and the value of the text field and broadcasts +to all the clients similarly. The onMessage method updates the list of +logged in users as well. + + +Edit ‘WEB-INF/template.xhtml’ and change: +<h:outputLink value="item2.xhtml">Item 2</h:outputLink> + +to +<h:outputLink + value="${facesContext.externalContext.requestContextPath}/faces/chat/chatroom.xhtml"> + Chat Room +</h:outputLink> + +The outputLink tag renders an HTML anchor tag with an href attribute. +${facesContext.externalContext.requestContextPath} provides the request +URI that identifies the web application context for this request. This +allows the links in the left navigation bar to be fully-qualified URLs. + + +Run the project by right clicking on the project and selecting +‘Run’. The browser shows +localhost:8080/movieplex7. +
+Chatroom link on main page + + + + + 4.6 chatroom + +
+ +Click on ‘Chat Room’ to see the output. +The ‘CONNECTED’ status message is shown and indicates that the WebSocket +connection with the endpoint is established. +
+Chatroom output + + + + + 4.8 chatroom + +
+ +Please make sure your browser supports WebSocket in order for this page +to show up successfully. Chrome 14.0+, Firefox 11.0+, Safari 6.0+, and +IE 10.0+ are the browsers that support WebSocket. A complete list of +supported browsers is available at +caniuse.com/websockets. +Open the URI localhost:8080/movieplex7 +in another browser window. Enter ‘Duke’ in the text box in the first +browser and click ‘Join’. +
+Chatroom with single user + + + + + 4.8 chatroom joined + +
+ +Notice that the user list and the status message in both the browsers +gets updated. Enter ‘James’ in the text box of the second browser and +click on ‘Join’. Once again the user list and the status message in both +the browsers is updated. Now you can type any messages in any of the +browser and click on ‘Send’ to send the message. +The output from two different browsers after the initial greeting looks +like as shown. +
+Chatroom with two users + + + + + 4.8 chatroom two browsers + +
+ +Here it shows output from Chrome on the top and Firefox on the bottom. +Chrome Developer Tools or Firebug in Firefox can be used to monitor +WebSocket traffic. +
+
+ +
+ +Ticket Sales (Batch Applications for the Java Platform) +Purpose: Read the total sales for each show and populate the database. +In doing so several new features of Java API for Batch Processing 1.0 +will be introduced and demonstrated by using them in the application. +Estimated Time: 30-45 mins +Batch Processing is execution of series of ‘jobs’ that is suitable for +non-interactive, bulk-oriented and long-running tasks. Batch +Applications for the Java Platform (JSR 352) will define a programming +model for batch applications and a runtime for scheduling and executing +jobs. +
+Introduction to Batch + + + + + 5.0 batch intro + +
+ +The core concepts of Batch Processing are: + + +Job is an instance that encapsulates an entire batch process. A +job is typically put together using a Job Specification Language and +consists of multiple steps. The Job Specification Language for JSR 352 +is implemented with XML and is referred as ‘Job XML’. + + +Step is a domain object that encapsulates an independent, +sequential phase of a job. A step contains all of the information +necessary to define and control the actual batch processing. + + +JobOperator provides an interface to manage all aspects of job +processing, including operational commands, such as start, restart, and +stop, as well as job repository commands, such as retrieval of job and +step executions. + + +JobRepository holds information about jobs current running and jobs +that run in the past. JobOperator provides access to this repository. + + +Reader-Processor-Writer pattern is the primary pattern and is called +as Chunk-oriented Processing. In this, ItemReader reads one item +at a time, ItemProcessor processes the item based upon the business +logic, such as calculate account balance and hands it +to ItemWriter for aggregation. Once the chunk numbers of items are +aggregated, they are written out, and the transaction is committed. + + + +This section will read the cumulative sales for each show from a CSV +file and populate them in a database. + + +Right-click on Source Packages, select ‘New’, ‘Java Package’, +specify the value as ‘org.javaee7.movieplex7.batch’, and click on +‘Finish’. + + +Right-click on newly created package, select ‘New’, ‘Java Class’, +specify the name as ‘SalesReader’. Make this class extend from +‘AbstractItemReader’ by changing the class definition and add: +extends AbstractItemReader + +AbstractItemReader is an abstract class that implements ItemReader +interface. The ItemReader interface defines methods that read a stream +of items for chunk processing. This reader implementation returns a +String item type as indicated in the class definition. +Add @Named as a class-level annotations and it allows the bean to be +injected in Job XML. Add @Dependent as another class-level annotation to +mark this bean as a bean defining annotation so that this bean is +available for injection. +Resolve the imports. + + +Override open() method to initialize the reader by adding the following code: +private BufferedReader reader; + +public void open(Serializable checkpoint) throws Exception { + reader = new BufferedReader( + new InputStreamReader( + Thread.currentThread() + .getContextClassLoader() + .getResourceAsStream("META-INF/sales.csv"))); +} + +This method initializes a BufferedReader from ‘META-INF/sales.csv’ that +is bundled with the application. +Sampling of the first few lines from ‘sales.csv’ is shown below: +1,500.00 +2,660.00 +3,80.00 +4,470.00 +5,1100.x0 + +Each line has a show identifier comma separated by the total sales for +that show. Note that the last line (5th record in the sample) has an +intentional typo. In addition, 17th record also has an additional +typo. The lab will use these lines to demonstrate how to handle parsing +errors. + + +Override the following method from the abstract class: +@Override +public String readItem() { + String string = null; + try { + string = reader.readLine(); + } catch (IOException ex) { + ex.printStackTrace(); + } + return string; +} + +The readItem method returns the next item from the stream. It returns +null to indicate end of stream. Note end of stream indicates end of chunk, +so the current chunk will be committed and the step will end. +Resolve the imports. + + +Right-click on ‘org.javaee7.movieplex7.batch’ package, select +‘New’, ‘Java Class’, specify the name as ‘SalesProcessor’. Change the +class definition and add: +implements ItemProcessor + +ItemProcessor is an interface that defines a method that is used to +operate on an input item and produce an output item. This processor +accepts a String input item from the reader, SalesReader in our case, +and returns a Sales instance to the writer (coming shortly). Sales is +the pre-packaged JPA entity with the application starter source code. +Add @Named and @Dependent as class-level annotations so that it allows +the bean to be injected in Job XML. +Resolve the imports. + + +Add implementation of the abstract method from the interface as: +@Override +public Sales processItem(Object s) { + Sales sales = new Sales(); + StringTokenizer tokens = new StringTokenizer((String)s, ","); + sales.setId(Integer.parseInt(tokens.nextToken())); + sales.setAmount(Float.parseFloat(tokens.nextToken())); + + return sales; +} + +This method takes a String parameter coming from the SalesReader, parses +the value, populates them in the Sales instance, and returns it. This is +then aggregated with the writer. +The method can return null indicating that the item should not be +aggregated. For example, the parsing errors can be handled within the +method and return null if the values are not correct. However this +method is implemented where any parsing errors are thrown as exception. +Job XML can be instructed to skip these exceptions and thus that +particular record is skipped from aggregation as well (shown later). +Resolve the imports. + + +Right-click on org.javaee7.movieplex7.batch package, select +‘New’, ‘Java Class’, specify the name as ‘SalesWriter’. Change the +class definition and add: +extends AbstractItemWriter + +AbstractItemWriter is an abstract class that implements ItemWriter +interface. The ItemWriter interface defines methods that write to a +stream of items for chunk processing. This writer writes a list of Sales +items. +Add @Named and @Dependent as class-level annotations so that it allows +the bean to be injected in Job XML. +Resolve the imports. + + +Inject EntityManager as: +@PersistenceContext EntityManager em; + +Override writeItems method from the abstract class by adding the following code: +@Override +@Transactional +public void writeItems(List list) { + for (Sales s : (List<Sales>)list) { + em.persist(s); + } +} + +Batch runtime aggregates the list of Sales instances returned from the +SalesProessor and makes it available as List in this method. This method +iterates over the list and persist each item in the database. +The method also specifies @Transactional as a method level annotation. +This is a new annotation introduced by JTA 1.2 that provides the ability +to control transaction boundaries on CDI managed beans. This provides +the semantics of EJB transaction attributes in CDI beans without +dependencies such as RMI. This support is implemented via an +implementation of a CDI interceptor that conducts the necessary +suspending, resuming, etc.  +In this case, a transaction is automatically started before the method +is called, committed if no checked exceptions are thrown, and rolled +back if runtime exceptions are thrown. This behavior can be overridden +using rollbackOn and dontRollbackOn attributes of the annotation. + +Each chunk is processed within a container-managed transaction already. +There is really no need for @Transactional on writeItems method but +shows a usage for the annotation. + + +Resolve the imports. + + +Create Job XML that defines the job, step, and chunk. +In ‘Files’ tab, expand the project → ‘src’ → ‘main’ → ‘resources’, +right-click on ‘META-INF’, select ‘New’, ‘Folder’, specify +the name as ‘batch-jobs’, and click on ‘Finish’. +Right-click on the newly created folder, select ‘New’, ‘Other’, select +‘XML’, ‘XML Document’, click on ‘Next >’, give the name as ‘eod-sales’, +click on ‘Next’, take the default, and click on ‘Finish’. +Replace contents of the file with the following: +<job id="endOfDaySales" + xmlns="http://xmlns.jcp.org/xml/ns/javaee" + version="1.0"> + <step id="populateSales"> + <chunk item-count="3" skip-limit="5"> + <reader ref="salesReader"/> + <processor ref="salesProcessor"/> + <writer ref="salesWriter"/> + <skippable-exception-classes> + <include class="java.lang.NumberFormatException"/> + </skippable-exception-classes> + </chunk> + </step> +</job> + +This code shows that the job has one step of chunk type. The <reader>, +<processor>, and <writer> elements define the CDI bean name of the +implementations of ItemReader, ItemProcessor, and ItemWriter interfaces. +The item-count attribute defines that 3 items are +read/processed/aggregated and then given to the writer. The entire +reader/processor/writer cycle is executed within a transaction. +The <skippable-exception-classes> element specifies a set of exceptions to +be skipped by chunk processing. +CSV file used for this lab has intentionally introduced couple of typos +that would generate NumberFormatException. Specifying this element +allows skipping the exception, ignore that particular element, and +continue processing. If this element is not specified then the batch +processing will halt. The skip-limit attribute specifies the number of +exceptions a step will skip. + + +Lets invoke the batch job. +In ‘Projects’ tab, right-click on ‘org.javaee7.movieplex7.batch’ package, select ‘New’, +‘Java Class’. Enter the name as ‘SalesBean’ and click on ‘Finish’ +button. +Add the following code to the bean: +public void runJob() { + try { + JobOperator jo = BatchRuntime.getJobOperator(); + long jobId = jo.start("eod-sales", new Properties()); + System.out.println("Started job: with id: " + jobId); + } catch (JobStartException ex) { + ex.printStackTrace(); + } +} + +This method uses BatchRuntime to get an instance of JobOperator, which +is then used to start the job. JobOperator is the interface for +operating on batch jobs. It can be used to start, stop, and restart +jobs. It can additionally inspect job history, to discover what jobs are +currently running and what jobs have previously run. +Add @Named and @RequestScoped as class-level annotations. This allows +the bean to be injectable in an EL expression. +Resolve the imports. +
+RequestScoped import + + + + + 5.10 imports + +
+ +
+ +Inject EntityManagerFactory in the class as: +@PersistenceUnit EntityManagerFactory emf; + +and add the following method: +public List<Sales> getSalesData() { + return emf. + createEntityManager(). + createNamedQuery("Sales.findAll", Sales.class). + getResultList(); +} + +This method uses a pre-defined @NamedQuery to query the database and +return all the rows from the table. +Resolve the imports. + + +Right-click on ‘Web Pages’, select ‘New’, ‘Folder’, specify the +name as ‘batch’, and click on ‘Finish’. +Right-click on the newly created folder, select ‘New’, ‘Other’, +‘JavaServer Faces’, ‘Facelets Template Client’, and click on ‘Next >’. +Give the File Name as ‘sales’. Click on ‘Browse’ next to ‘Template:’, +expand ‘Web Pages’, ‘WEB-INF’, select ‘template.xhtml’, and click on +‘Select File’. Click on ‘Finish’. +In this file, remove <ui:define> sections where name attribute value is +‘top’ and ‘left’. These sections are inherited from the template. +Replace <ui:define> section with ‘content’ name such that it looks like: +<ui:composition template="../WEB-INF/template.xhtml"> + <ui:define name="content"> + <h1>Movie Sales</h1> + <h:form> + <h:dataTable value="#{salesBean.salesData}" var="s" border="1"> + <h:column> + <f:facet name="header"> + <h:outputText value="Show ID" /> + </f:facet> + #{s.id} + </h:column> + <h:column> + <f:facet name="header"> + <h:outputText value="Sales" /> + </f:facet> + #{s.amount} + </h:column> + </h:dataTable> + <h:commandButton + value="Run Job" + action="sales" + actionListener="#{salesBean.runJob()}"/> + <h:commandButton + value="Refresh" + action="sales" /> + </h:form> + </ui:define> +</ui:composition> + +This code displays the show identifier and sales from that show in a +table by invoking SalesBean.getSalesData(). First command button allows +invoking the job that processes the CSV file and populates the database. +The second command button refreshes the page. +Right-click on the yellow bulb to fix namespace prefix/URI mapping for h:. This +needs to be repeated for f: prefix. + + +Add the following code in template.xhtml along with other <outputLink>s: +<p/><h:outputLink + value="${facesContext.externalContext.requestContextPath}/faces/batch/sales.xhtml"> + Sales + </h:outputLink> + + + +Run the project to see the output as shown. +
+Sales link on main page + + + + + 5.14 sales + +
+ +Notice, a new ‘Sales’ entry is displayed in the left navigation bar. +
+ +Click on ‘Sales’ to see the output as shown. +
+Movie Sales page + + + + + 5.15 sales + +
+ +The empty table indicates that there is no sales data in the database. +
+ +Click on ‘Run Job’ button to initiate data processing of CSV +file. Look for ‘Waiting for localhost’ in the browser status bar, +wait for a couple of seconds for the processing to finish, and then +click on ‘Refresh’ button to see the updated output as shown. +
+Movie Sales output page + + + + + 5.16 sales output + +
+ +Now the table is populated with the sales data. +Note that record 5 is missing from the table, as this records did not +have correct numeric entries for the sales total. The Job XML for the +application explicitly mentioned to skip such errors. +
+
+ +
+ +View and Delete Movie (Java API for RESTful Web Services) +Purpose: View, and delete a movie. In doing so several new features of +JAX-RS 2 will be introduced and demonstrated by using them in the +application. +Estimated Time: 30-45 mins +JAX-RS 2 defines a standard API to create, publish, and invoke a REST +endpoint. JAX-RS 2 adds several new features to the API: + + +Client API that can be used to access Web resources and provides +integration with JAX-RS Providers. Without this API, the users need to +use a low-level HttpUrlConnection to access the REST endpoint. + + +Asynchronous processing capabilities in Client and Server that enables +more scalable applications. + + +Message Filters and Entity Interceptors as well-defined extension +points to extend the capabilities of an implementation. + + +Validation constraints can be specified to validate the parameters and +return type. + + + +This section will provide the ability to view all the movies, details of +a selected movie, and delete an existing movie using the JAX-RS Client +API. + + +Right-click on ‘Source Packages’, select ‘New’, ‘Java Class’. +Give the class name as ‘MovieClientBean’, package as +‘org.javaee7.movieplex7.client’, and click on ‘Finish’. +This bean will be used to invoke the REST endpoint. + + +Add @Named and @RequestScoped class-level annotations. This allows +the class to be injected in an EL expression and also defines the bean +to be automatically activated and passivated with the request. +Resolve the imports. + +Make sure to pick javax.enterprise.context.RequestScoped class. + + +
+RequestScoped import + + + + + 6.2 imports + +
+ +
+ +Add the following code to the class: +Client client; +WebTarget target; + +@Inject HttpServletRequest httpServletRequest; + +@PostConstruct +public void init() { + client = ClientBuilder.newClient(); + target = client + .target("http://" + + httpServletRequest.getLocalName() + + ":" + + httpServletRequest.getLocalPort() + + "/" + + httpServletRequest.getContextPath() + + "/webresources/movie/"); + +} + +@PreDestroy +public void destroy() { + client.close(); +} + +ClientBuilder is the main entry point to the Client API. It uses a +fluent builder API to invoke REST endpoints. A new Client instance is +created using the default client builder implementation provided by the +JAX-RS implementation provider. Client are heavy-weight objects that +manage the client-side communication infrastructure. It is highly +recommended to create only required number of instances of Client and +close it appropriately. +In this case, Client instance is created and destroyed in the lifecycle +callback methods. The endpoint URI is set on this instance by calling +the target method. Note that the endpoint address is dynamically created +by injecting an instance of HttpServletRequest. This is a new feature +added in CDI 1.1 + + +Add the following method to the class: +public Movie[] getMovies() { + return target + .request() + .get(Movie[].class); +} + +A request is prepared by calling the request method. HTTP GET method is +invoked by calling get method. The response type is specified in the +last method call and so return value is of the type Movie[]. + + +Right-click on ‘Web Pages’, select ‘New’, ‘Folder’, specify the +name as ‘client’, and click on ‘Finish’. +Right-click on the newly created folder, select ‘New’, ‘Other’, +‘JavaServer Faces’, ‘Facelets Template Client’, and click on ‘Next >’. +Give the File Name as ‘movies’. Click on ‘Browse’ next to ‘Template:’, +expand ‘Web Pages’, ‘WEB-INF’, select ‘template.xhtml’, and click on +‘Select File’. Click on ‘Finish’. + + +In this file, remove <ui:define> sections where name attribute value is +‘top’ and ‘left’. These sections are inherited from the template. +Replace <ui:define> section with ‘content’ name such that it looks like: +<ui:composition template="../WEB-INF/template.xhtml"> + <ui:define name="content"> + <h:form prependId="false"> + <h:selectOneRadio value="#{movieBackingBean.movieId}" layout="pageDirection"> + <c:forEach items="#{movieClientBean.movies}" var="m"> + <f:selectItem itemValue="#{m.id}" itemLabel="#{m.name}"/> + </c:forEach> + </h:selectOneRadio> + <h:commandButton value="Details" action="movie" /> + </h:form> + </ui:define> +</ui:composition> + +This code fragment invokes getMovies method from MovieClientBean, +iterates over the response in a for loop, and display the name of each +movie with a radio button. The selected radio button value is bound to +the EL expression #{movieBackingBean.movieId}. +The code also has a button with ‘Details’ label and looks for +‘movie.xhtml’ in the same directory. We will create this file later. +Click on the yellow bulb in the left bar to resolve the namespace +prefix-to-URI resolution. This needs to be completed for h:, c:, +and f: prefixes. +
+Namespace prefix imports + + + + + 6.6 imports + +
+ +
+ +Right-click on ‘org.javaee7.movieplex7.client’ package, select +‘New’, ‘Java Class’, specify the value as ‘MovieBackingBean’ and click +on ‘Finish’. +Add the following field: +int movieId; + +Add getters/setters by right-clicking on the editor pane and selecting +‘Insert Code’ (CtrlI shortcut on OSX). Select the field and click on +‘Generate’. +Add @Named and @SessionScoped class-level annotations and implements +Serializable. +Resolve the imports. + +Make sure to import javax.enterprise.context.SessionScoped. + + + + +In ‘template.xhtml’, add the following code along with other <outputLink>s: +<p/><h:outputLink + value="${facesContext.externalContext.requestContextPath}/faces/client/movies.xhtml"> + Movies + </h:outputLink> + +Running the project (FnF6 shortcut on OSX) and clicking on ‘Movies’ +in the left navigation bar shows the output as shown. +
+List of movies output page + + + + + 6.8 output + +
+ +The list of all the movies with a radio button next to them is +displayed. +
+ +In MovieClientBean, inject MovieBackingBean to read the value +of selected movie from the page. Add the following code: +@Inject +MovieBackingBean bean; + + + +In MovieClientBean, add the following method: +public Movie getMovie() { + Movie m = target + .path("{movie}") + .resolveTemplate("movie", bean.getMovieId()) + .request() + .get(Movie.class); + return m; +} + +This code reuses the Client and WebTarget instances created in +@PostConstruct. It also adds a variable part to the URI of the REST +endpoint, defined using {movie}, and binds it to a concrete value using +resolveTemplate method. The return type is specified as a parameter to +the get method. + + +Right-click on ‘client’ folder, select ‘New’, ‘Facelets Template +Client’, give the File Name as ‘movie’. Click on ‘Browse’ next to +‘Template:’, expand ‘Web Pages’, ‘WEB-INF’, select ‘template.xhtml’, and +click on ‘Select File’. Click on ‘Finish’. + + +In this file, remove <ui:define> sections where name attribute value is +‘top’ and ‘left’. These sections are inherited from the template. +Replace <ui:define> with ‘content’ name such that it looks like: +<ui:define name="content"> + <h1>Movie Details</h1> + <h:form> + <table cellpadding="5" cellspacing="5"> + <tr> + <th align="left">Movie Id:</th> + <td>#{movieClientBean.movie.id}</td> + </tr> + <tr> + <th align="left">Movie Name:</th> + <td>#{movieClientBean.movie.name}</td> + </tr> + <tr> + <th align="left">Movie Actors:</th> + <td>#{movieClientBean.movie.actors}</td> + </tr> + </table> + <h:commandButton value="Back" action="movies" /> + </h:form> +</ui:define> + +Click on the yellow-bulb to resolve the namespace prefix-URI mapping for +h:. +The output values are displayed by calling the getMovie method and +using the id, name, and actors property values. + + +Run the project, select ‘Movies’ in the left navigation bar, +select a radio button next to any movie, and click on details to see the +output as shown. +
+Movie Details page + + + + + 6.12 output + +
+ +Click on the ‘Back’ button to select another movie. +
+ +Add the ability to delete a movie. In ‘movies.xhtml’, add the +following code with other <commandButton>. +<h:commandButton + value="Delete" + action="movies" + actionListener="#{movieClientBean.deleteMovie()}"/> + +This button displays a label ‘Delete’, invokes the method deleteMovie +from ‘MovieClientBean’, and then renders ‘movies.xhtml’. + + +Add the following code to ‘MovieClientBean’: +public void deleteMovie() { + target + .path("{movieId}") + .resolveTemplate("movieId", bean.getMovieId()) + .request() + .delete(); +} + +This code again reuses the Client and WebTarget instances created in +@PostConstruct. It also adds a variable part to the URI of the REST +endpoint, defined using {movieId}, and binds it to a concrete value +using resolveTemplate method. The URI of the resource to be deleted is +prepared and then delete method is called to delete the resource. +Make sure to resolve the imports. +Running the project shows the output shown. +
+Delete button + + + + + 6.14 output + +
+ +Select a movie and click on Delete button. This deletes the movie from +the database and refreshes list on the page. Note that a redeploy of the +project will delete all the movies anyway and add them all back. +
+
+ +
+ +Add Movie (Java API for JSON Processing) +Purpose: Add a new movie. In doing so several new features of the Java +API for JSON Processing 1.0 will be introduced and demonstrated by using +them in the application. +Estimated Time: 30-45 mins +Java API for JSON Processing provides a standard API to parse and +generate JSON so that the applications can rely upon a portable API. +This API will provide: + + +Produce/Consume JSON in a streaming fashion (similar to StAX API for XML) + + +Build a Java Object Model for JSON (similar to DOM API for XML) + + + +This section will define a JAX-RS Entity Providers that will allow +reading and writing JSON for a Movie POJO. The JAX-RS Client API will +request this JSON representation. +JAX-RS Entity Providers supply mapping services between on-the-wire +representations and their associated Java types.  Several standard Java +types such as String, byte[], javax.xml.bind.JAXBElement, +java.io.InputStream, java.io.File, and others have a pre-defined mapping +and is required by the specification. Applications may provide their own +mapping to custom types using MessageBodyReader and MessageBodyWriter +interfaces. +This section will provide the ability to add a new movie to the +application. Typically, this functionality will be available after +proper authentication and authorization. + + +Right-click on Source Packages, select ‘New’, ‘Java Class’, +specify the name as ‘MovieReader’, package as ‘org.javaee7.movieplex7.json’ +and click on ‘Finish’. Add the following class-level annotations: + + +Right-click on newly created package, select ‘New’, ‘Java Class’, +specify the name as ‘MovieReader’, and click on ‘Finish’. Add the +following class-level annotations: +@Provider +@Consumes(MediaType.APPLICATION_JSON) + +@Provider allows this implementation to be discovered by the JAX-RS +runtime during the provider scanning phase. @Consumes indicates that +this implementation will consume a JSON representation of the resource. +Make sure to resolve imports from the appropriate package as shown. +
+Provider import + + + + + 7.2 imports + +
+ +
+ +Make the class implements MessageBodyReader<Movie>. +
+Implement abstract methods for MessageBodyReader + + + + + 7.3 implements + +
+ +Click on the hint (shown as yellow bulb) on the class definition and +select ‘Implement all abstract methods’. +
+ +Change implementation of the isReadable method as: +return Movie.class.isAssignableFrom(type); + +This method ascertains if the MessageBodyReader can produce an instance +of a particular type. + + +Replace the readFrom method with: +@Override +public Movie readFrom( + Class<Movie> type, + Type type1, + Annotation[] antns, + MediaType mt, + MultivaluedMap<String, String> mm, + InputStream in) + throws IOException, WebApplicationException { + + Movie movie = new Movie(); + JsonParser parser = Json.createParser(in); + while (parser.hasNext()) { + switch (parser.next()) { + case KEY_NAME: + String key = parser.getString(); + parser.next(); + switch (key) { + case "id": + movie.setId(parser.getInt()); + break; + case "name": + movie.setName(parser.getString()); + break; + case "actors": + movie.setActors(parser.getString()); + break; + default: + break; + } + break; + default: + break; + } + } + return movie; +} + +This code reads a type from the input stream in. JsonParser, a streaming +parser, is created from the input stream. Key values are read from the +parser and a Movie instance is populated and returned. +Resolve the imports. + + +Right-click on ‘org.javaee7.movieplex7.json’ package, select ‘New’, ‘Java Class’, +specify the name as ‘MovieWriter’, and click on ‘Finish’. Add the +following class-level annotations: +@Provider +@Produces(MediaType.APPLICATION_JSON) + +@Provider allows this implementation to be discovered by the JAX-RS +runtime during the provider scanning phase. @Produces indicates that +this implementation will produce a JSON representation of the resource. +Resolve the imports as shown. +
+Provider import + + + + + 7.6 imports + +
+ +
+ +Make this class implement MessageBodyWriter interface by adding the following code: +implements MessageBodyWriter<Movie> + +Resolve the imports. +The IDE provide a hint to implement abstract methods as: +
+Implement abstract methods for MessageBodyWriter + + + + + 7.7 implements + +
+ +Click on the hint (show as yellow bulb) on the class definition and +select ‘Implement all abstract methods’. +
+ +Change implementation of the isWritable method to: +return Movie.class.isAssignableFrom(type); + +This method ascertains if the MessageBodyWriter supports a particular +type. + + +Add implementation of the getSize method as: +return -1; + +Originally, this method was called to ascertain the length in bytes of +the serialized form of t. In JAX-RS 2.0, this method is deprecated and +the value returned by the method is ignored by a JAX-RS runtime. All +MessageBodyWriter implementations are advised to return -1. + + +Change implementation of the writeTo method to: +JsonGenerator gen = Json.createGenerator(entityStream); +gen.writeStartObject() + .write("id", t.getId()) + .write("name", t.getName()) + .write("actors", t.getActors()) + .writeEnd(); + gen.flush(); + +This method writes a type to an HTTP message. JsonGenerator writes JSON +data to an output stream in a streaming way. Overloaded write methods +are used to write different data types to the stream. +Resolve the imports. + + +In ‘Web Pages’, right-click on ‘client’ folder, select ‘New’, +‘Facelets Template Client’. Give the File Name as ‘addmovie’. +Click on ‘Browse’ next to ‘Template:’, expand ‘Web Pages’, +‘WEB-INF’, select ‘template.xhtml’, and click on ‘Select File’. +Click on ‘Finish’. + + +In this file, remove <ui:define> sections where name attribute value is +‘top’ and ‘left’. These sections are inherited from the template. +Replace <ui:define> section with ‘content’ name such that it looks like: +<ui:composition template="../WEB-INF/template.xhtml"> + <ui:define name="content"> + <h1>Add a New Movie</h1> + <h:form> + <table cellpadding="5" cellspacing="5"> + <tr> + <th align="left">Movie Id:</th> + <td><h:inputText value="#{movieBackingBean.movieId}"/></td> + </tr> + <tr> + <th align="left">Movie Name:</th> + <td><h:inputText value="#{movieBackingBean.movieName}"/> </td> + </tr> + <tr> + <th align="left">Movie Actors:</th> + <td><h:inputText value="#{movieBackingBean.actors}"/></td> + </tr> + </table> + <h:commandButton + value="Add" + action="movies" + actionListener="#{movieClientBean.addMovie()}"/> + </h:form> + </ui:define> +</ui:composition> + +This code creates a form to accept input of id, name, and actors of a +movie. These values are bound to fields in MovieBackingBean. The click +of command button invokes the addMovie method from MovieClientBean and +then renders ‘movies.xhtml’. +Click on the hint (show as yellow bulb) to resolve the namespace +prefix/URI mapping as shown. +
+Namespace prefix mapping imports + + + + + 7.11 imports + +
+ +
+ +Add movieName and actors field to MovieBackingBean as: +String movieName; +String actors; + +Generate getters and setters by clicking on the menu item ‘Source’ and +then ‘Insert Code’. + + +Add the following code to ‘movies.xhtml’ +<h:commandButton value="New Movie" action="addmovie" /> + +along with rest of the <commandButton>s. + + +Add the following method in MovieClientBean: +public void addMovie() { + Movie m = new Movie(); + m.setId(bean.getMovieId()); + m.setName(bean.getMovieName()); + m.setActors(bean.getActors()); + target + .register(MovieWriter.class) + .request() + .post(Entity.entity(m, MediaType.APPLICATION_JSON)); +} + +This method creates a new Movie instance, populates it with the values +from the backing bean, and POSTs the bean to the REST endpoint. The +register method registers a MovieWriter that provides conversion from +the POJO to JSON. Media type of application/json is specified using MediaType.APPLICATION_JSON. +Resolve the imports as shown +
+Entity import + + + + + 7.14 imports + +
+ +
+ +Run the project to see the updated main page as: +
+New Movie button + + + + + 7.15 output + +
+ +A new movie can be added by clicking on ‘New Movie’ button. +
+ +Enter the details as shown: +
+Add a New Movie page + + + + + 7.16 output + +
+ +Click on ‘Add’ button. The ‘Movie Id’ value has to be greater than 20 +otherwise the primary key constraint will be violated. The table +definition may be updated to generate the primary key based upon a +sequence; however this is not done in the application. +The updated page looks like as shown +
+Newly added movie + + + + + 7.16 output2 + +
+ +Note that the newly added movie is now displayed. +
+
+ +
+ +Movie Points (Java Message Service) +Purpose: Customers accrue points for watching a movie. +Estimated Time: 30-45 mins +Java Message Service 2.0 allows sending and receiving messages between +distributed systems. JMS 2 introduced several improvements over the +previous version such as: + + +New JMSContext interface + + +AutoCloseable JMSContext, Connection, and Session + + +Use of runtime exceptions + + +Method chaining on JMSProducer + + +Simplified message sending + + + +This section will provide a page to simulate submission of movie points +accrued by a customer. These points are submitted to a JMS queue that is +then read synchronously by another bean. JMS queue for further +processing, possibly storing in the database using JPA. + + +Right-click on Source Packages, select ‘New’, ‘Java Class’, +specify the name as ‘SendPointsBean’, package as ‘org.javaee7.movieplex7.points’, +and click on ‘Finish’. +Add the following class-level annotations: +@Named +@RequestScoped + +This makes the bean to be EL-injectable and automatically activated and +passivated with the request. +Resolve the imports. +
+RequestScoped import + + + + + 8.2 imports + +
+ +
+ +A message to a JMS Queue is sent after the customer has bought the +tickets. Another bean will then retrieve this message and update the +points for that customer. This allows the two systems, one generating +the data about tickets purchased and the other about crediting the +account with the points, completely decoupled. +This lab will mimic the sending and consuming of a message by an +explicit call to the bean from a JSF page. +Add the following field to the class: +@NotNull +@Pattern(regexp = "^\\d{2},\\d{2}", + message = "Message format must be 2 digits, comma, 2 digits, e.g.12,12") +private String message; + +This field contains the message sent to the queue. This field’s value is +bound to an inputText in a JSF page (created later). Constraints have +been specified on this bean that enable validation of data on form +submit. It requires the data to consists of two numerical digits, followed +by a comma, and then two more numerical digits. If the message does not +meet the validation criteria then the error message to be displayed is +specified using message attribute. +This could be thought as conveying the customer identifier and the +points accrued by that customer. +Generate getter/setters for this field. Right-click in the editor pane, +select ‘Insert Code’ (CtrlI shortcut on OSX), select ‘Getter and +Setter’, select the field, and click on ‘Generate’. + + +Add the following code to the class: +@Inject +JMSContext context; + +@Resource(lookup = "java:global/jms/pointsQueue") +Queue pointsQueue; + +public void sendMessage() { + System.out.println("Sending message: " + message); + context.createProducer().send(pointsQueue, message); +} + +The Java EE Platform requires a pre-configured JMS connection factory +under the JNDI name java:comp/DefaultJMSConnectionFactory. If no +connection factory is specified then the pre-configured connection +factory is used. In a Java EE environment, where CDI is enabled by +default anyway, a container-managed JMSContext can be injected as: +@Inject +JMSContext context; + +This code uses the default factory to inject an instance of +container-managed JMSContext. +JMSContext is a new interface introduced in JMS 2. This combines in a +single object the functionality of two separate objects from the JMS 1.1 +API: a Connection and a Session. +When an application needs to send messages it use the createProducer +method to create a JMSProducer that provides methods to configure and +send messages. Messages may be sent either synchronously or +asynchronously. +When an application needs to receive messages it uses one of several +createConsumer or createDurableConsumer methods to create a JMSConsumer. +A JMSConsumer provides methods to receive messages either synchronously +or asynchronously. +All messages are then sent to a Queue instance (created later) +identified by java:global/jms/pointsQueue JNDI name. The actual message +is obtained from the value entered in the JSF page and bound to the +message field. +Resolve the imports. + +Make sure Queue class is imported from javax.jms.Queue instead of the +default java.util.Queue. + + +Click on ‘OK’. + + +Right-click on ‘org.javaee7.movieplex7.points’ package, select +‘New’, ‘Java Class’, specify the name as ‘ReceivePointsBean’. +Add the following class-level annotations: +@JMSDestinationDefinition(name = "java:global/jms/pointsQueue", +interfaceName = "javax.jms.Queue") +@Named +@RequestScoped + +This allows the bean to refered from an EL expression. It also activates +and passivates the bean with the request. +JMSDestinationDefinition is a new annotation introduced in JMS 2. It is +used by the application to provision the required resources and allow an +application to be deployed into a Java EE environment with minimal +administrative configuration. This code will create Queue with the JNDI +name java:global/jms/pointsQueue. + + +Add the following code to the class: +@Inject +JMSContext context; + +@Resource(lookup="java:global/jms/pointsQueue") +Queue pointsQueue; + +public String receiveMessage() { + try (JMSConsumer consumer = context.createConsumer(pointsQueue)) { + String message = consumer.receiveBody(String.class); + System.out.println("Received message: " + message); + return message; + } +} + +This code creates JMSConsumer in a try-with-resources block +which is then used to synchronously receive a message. Note that JMSConsumer +is created as an auto-managed resource and so is closed automatically after +receiving each message. Alternatively asynchronous message delivery can also be setup +using Message Driven Beans. However that is not covered in this lab. + + +Add the following method to the class: +public int getQueueSize() { + int count = 0; + try { + QueueBrowser browser = context.createBrowser(pointsQueue); + Enumeration elems = browser.getEnumeration(); + while (elems.hasMoreElements()) { + elems.nextElement(); + count++; + } + } catch (JMSException ex) { + ex.printStackTrace(); + } + return count; +} + +This code creates a QueueBrowser to look at the messages on a queue +without removing them. It calculates and returns the total number of +messages in the queue. +Make sure to resolve the import from javax.jms.Queue, take all other +defaults. + + +Right-click on ‘Web Pages’, select ‘New’, ‘Folder’, specify the +name as ‘points’, and click on ‘Finish’. +In ‘Web Pages’, right-click on newly created folder, select ‘Facelets +Template Client’, give the File Name as ‘points’. Click on ‘Browse’ +next to ‘Template:’, expand ‘Web Pages’, ‘WEB-INF’, select +‘template.xhtml’, and click on ‘Select File’. Click on ‘Finish’. + + +In this file, remove <ui:define> sections where name attribute value is +‘top’ and ‘left’. These sections are inherited from the template. +Replace the <ui:define> section with ‘content’ name such that it looks like: +<ui:composition template="../WEB-INF/template.xhtml"> + <ui:define name="content"> + <h1>Points</h1> + <h:form> + Queue size: + <h:outputText value="#{receivePointsBean.queueSize}"/><p/> + <h:inputText value="#{sendPointsBean.message}"/> + <h:commandButton + value="Send Message" + action="points" + actionListener="#{sendPointsBean.sendMessage()}"/> + </h:form> + <h:form> + <h:commandButton + value="Receive Message" + action="points" + actionListener="#{receivePointsBean.receiveMessage()}"/> + </h:form> + </ui:define> +</ui:composition> + +Click on the yellow bulb to resolve namespace prefix/URI mapping for h: +prefix. +This page displays the number of messages in the current queue. It +provides a text box for entering the message that can be sent to the +queue. The first command button invokes sendMessage method from +SendPointsBean and refreshes the page. Updated queue count, incremented +by 1 in this case, is displayed. The second command button invokes +receiveMessage method from ReceivePointsBean and refreshes the page. The +queue count is updated again, decremented by 1 in this case. +If the message does not meet the validation criteria then the error +message is displayed on the screen. + + +Add the following code in ‘template.xhtml’ along with other +<outputLink>s: +<p/><h:outputLink + value="${facesContext.externalContext.requestContextPath}/faces/points/points.xhtml"> + Points + </h:outputLink> + + + +Run the project. The update page looks like as shown: +
+Points link on main page + + + + + 8.10 output + +
+ +Click on ‘Points’ to see the output as: +
+Points output page + + + + + 8.10 output2 + +
+ +The output shows that the queue has 0 messages. Enter a message ‘1212’ +in the text box and click on ‘Send Message’ to see the output as shown. +
+Validation message on Points page + + + + + 8.10 output3 + +
+ +This message is not meeting the validation criteria and so the error +message is displayed. +Enter a message as ‘12,12’ in the text box and click on ‘Send Message’ +button to see the output as: +
+Correct input for Points page - Send Message (queue size=1) + + + + + 8.10 output4 + +
+ +The updated count now shows that there is 1 message in the queue. Click +on ‘Receive Message’ button to see output as: +
+Correct input for Points page - Receive Message (queue size=0) + + + + + 8.10 output5 + +
+ +The updated count now shows that the message has been consumed and the +queue has 0 messages. +Click on ‘Send Message’ 4 times to see the output as: +
+Correct input for Points page - Send Message (queue size=4) + + + + + 8.10 output6 + +
+ +The updated count now shows that the queue has 4 messages. Click on +‘Receive Message’ 2 times to see the output as: +
+Correct input for Points page - Receive Message (queue size=2) + + + + + 8.10 output7 + +
+ +The count is once again updated to reflect the 2 consumed and 2 +remaining messages in the queue. +
+
+ +
+ +Show Booking (JavaServer Faces) +Purpose: Build pages that allow a user to book a particular movie show +in a theater. In doing so a new feature of JavaServer Faces 2.2 will be +introduced and demonstrated by using in the application. +Estimated Time: 30-45 mins +JavaServer Faces 2.2 introduces a new feature called Faces Flow that +provides an encapsulation of related views/pages with application +defined entry and exit points. Faces Flow borrows core concepts from ADF +TaskFlow, Spring Web Flow, and Apache MyFaces CODI. +It introduces @FlowScoped CDI annotation for flow-local storage and +@FlowDefinition to define the flow using CDI producer methods. There are +clearly defined entry and exit points with well-defined parameters. This +allows the flow to be packaged together as a JAR or ZIP file and be +reused. The application thus becomes a collection of flows and non-flow +pages. Usually the objects in a flow are designed to allow the user to +accomplish a task that requires input over a number of different views. +This application will build a flow that allows the user to make a movie +reservation. The flow will contain four pages: + + +Display the list of movies + + +Display the list of available show timings + + +Confirm the choices + + +Make the reservation and show the ticket + + + +Lets build the application. + + +Items in a flow are logically related to each other and so it is +required to keep them together in a directory. In NetBeans, right-click +on the ‘Web Pages’, select ‘New’, ‘Folder’, specify the folder name +‘booking’, and click on ‘Finish’. + + +Right-click on the newly created folder, select ‘New’, ‘Facelets +Template Client’, give the File Name as ‘booking’. Click on ‘Browse’ +next to ‘Template:’, expand ‘Web Pages’, ‘WEB-INF’, select +‘template.xhtml’, and click on ‘Select File’. Click on ‘Finish’. + + +‘booking.xhtml’ is the entry point to the flow (more on this later). +In this file, remove <ui:define> sections with ‘top’ and ‘left’ name +attributes. These sections are inherited from the template. +Replace <ui:define> section with ‘content’ name such that it looks like: +<ui:composition template="../WEB-INF/template.xhtml"> + <ui:define name="content"> + <h2>Pick a movie</h2> + <h:form prependId="false"> + <h:selectOneRadio + value="#{booking.movieId}" + layout="pageDirection" + required="true"> + <f:selectItems + value="#{movieFacadeREST.all}" + var="m" + itemValue="#{m.id}" + itemLabel="#{m.name}"/> + </h:selectOneRadio> + <h:commandButton id="shows" value="Pick a time" action="showtimes" /> + </h:form> + </ui:define> +</ui:composition> + +The code builds an HTML form that displays the list of movies as radio +button choices. The chosen movie is bound to #{booking.movieId} which +will be defined as a flow-scoped bean. The value of action attribute on +commandButton refers to the next view in the flow, i.e. +‘showtimes.xhtml’ in the same directory in our case. +Click on the yellow bulb as shown and click on the suggestion to +add namespace prefix/URI mapping for h:. Repeat the same for f: prefix as well. +
+Namespace prefix mapping imports + + + + + 9.3 imports + +
+ +
+ +Right-click on ‘Source Packages’, select ‘New’, ‘Java Class’. +Specify the class name as ‘Booking’ and the package name as +‘org.javaee7.movieplex7.booking’. +Add @Named class-level annotation to make the class EL-injectable. +Add @FlowScoped("booking") to define the scope of bean as the flow. The bean is automatically activated and passivated as the flow is entered or exited. +Add implements Serializable to the class as beans with @FlowScoped annotation need to be passivation capable, and thus serializable. +Add the following field: +int movieId; + +and generate getters/setters by going to ‘Source’, ‘Insert Code’, +selecting ‘Getter and Setter’, and select the field. +Inject EntityManager in this class by adding the following code: +@PersistenceContext +EntityManager em; + +Add the following convenience method: +public String getMovieName() { + try { + return em.createNamedQuery("Movie.findById", Movie.class) + .setParameter("id", movieId) + .getSingleResult() + .getName(); + } catch (NoResultException e) { + return ""; + } +} + +This method will return the movie name based upon the selected movie. +Alternatively, movie id and name may be passed from the selected radio +button and parsed in the backing bean. This will reduce an extra trip to +the database. +Resolve the imports. + + +Create ‘showtimes.xhtml’ in the ‘booking’ folder following the +steps used to create ‘booking.xhtml’. +In this file, remove <ui:define> sections with ‘top’ and ‘left’ name +attributes. These sections are inherited from the template. +Replace <ui:define> section with ‘content’ name such that it looks like: +<ui:composition template="../WEB-INF/template.xhtml"> + <ui:define name="content"> + <h2>Show Timings for <font color="red">#{booking.movieName}</font></h2> + <h:form> + <h:selectOneRadio value="#{booking.startTime}" layout="pageDirection" required="true"> + <c:forEach items="#{timeslotFacadeREST.all}" var="s"> + <f:selectItem itemValue="#{s.id},#{s.startTime}" itemLabel="#{s.startTime}"/> + </c:forEach> + </h:selectOneRadio> + <h:commandButton value="Confirm" action="confirm" /> + <h:commandButton id="back" value="Back" action="booking" immediate="true"/> + </h:form> + </ui:define> +</ui:composition> + +This code builds an HTML form that displays the chosen movie name and +all the show times. #{timeslotFacadeREST.all} returns the list of all +the movies and iterates over them using a c:forEach loop. The id and +start time of the selected show are bound to #{booking.startTime}. +Command button with value ‘Back’ allows going back to the previous page and +the other command button with value ‘Confirm’ takes to the next view in the +flow, ‘confirm.xhtml’ in our case. +Typically a user will expect the show times only for the selected movie +but all the show times are shown here. This allows us to demonstrate +going back and forth within a flow if an incorrect show time for a movie +is chosen. A different query may be written that displays only the shows +available for this movie; however this is not part of the application. +Right-click on the yellow bulb to fix namespace prefix/URI mapping for +h:. This needs to be repeated for c: and f: prefix as well. + + +Add the following fields to the Booking class: +String startTime; +int startTimeId; + +And the following methods: +public String getStartTime() { + return startTime; +} + +public void setStartTime(String startTime) { + StringTokenizer tokens = new StringTokenizer(startTime, ","); + startTimeId = Integer.parseInt(tokens.nextToken()); + this.startTime = tokens.nextToken(); +} + +public int getStartTimeId() { + return startTimeId; +} + +These methods will parse the values received from the form. Also add the +following method: +public String getTheater() { + // for a movie and show + try { + + // Always return the first theater + List<ShowTiming> list = + em.createNamedQuery("ShowTiming.findByMovieAndTimingId", + ShowTiming.class) + .setParameter("movieId", movieId) + .setParameter("timingId", startTimeId) + .getResultList(); + + if (list.isEmpty()) + return "none"; + + return list + .get(0) + .getTheaterId() + .getId() + .toString(); + } catch (NoResultException e) { + return "none"; + } +} + +This method will find the first theater available for the chosen movie +and show the timing. +Additionally a list of theaters offering that movie may be shown in a +separate page. +Resolve the imports. + + +Create ‘confirm.xhtml’ page in the ‘booking’ folder by following +the steps used to create ‘booking.xhtml’. +In this file, remove <ui:define> sections wht ‘top’ and ‘left’ name +attributes. These sections are inherited from the template. +Replace ‘<ui:define>’ section with ‘content’ name such that it looks like: +<ui:composition template="../WEB-INF/template.xhtml"> + <ui:define name="content"> + <c:choose> + <c:when test="#{booking.theater == 'none'}"> + <h2>No theater found, choose a different time</h2> + <h:form> + Movie name: #{booking.movieName}<p/> + Starts at: #{booking.startTime}<p/> + <h:commandButton id="back" value="Back" action="showtimes"/> + </h:form> + </c:when> + <c:otherwise> + <h2>Confirm ?</h2> + <h:form> + Movie name: #{booking.movieName}<p/> + Starts at: #{booking.startTime}<p/> + Theater: #{booking.theater}<p/> + <h:commandButton id="next" value="Book" action="print"/> + <h:commandButton id="back" value="Back" action="showtimes"/> + </h:form> + </c:otherwise> + </c:choose> + </ui:define> +</ui:composition> + +The code displays the selected movie, show timing, and theater if +available. The reservation can proceed if all three are available. +‘print.xhtml’ is the last page that shows the confirmed reservation +and is shown when ‘Book’ commandButton is clicked. +actionListener can be added to commandButton to invoke the business +logic for making the reservation. Additional pages may be added to take +the credit card details and email address. +Right-click on the yellow bulb to fix namespace prefix/URI mapping for ‘c:’. +This needs to be repeated for ‘h:’ prefix as well. + + +Create ‘print.xhtml’ page in the ‘booking’ folder by following the +steps used to create ‘booking.xhtml’. +In this file, remove <ui:define> sections wht ‘top’ and ‘left’ name +attributes. These sections are inherited from the template. +Replace <ui:define> section with ‘content’ name such that it looks like: +<ui:composition template="../WEB-INF/template.xhtml"> + <ui:define name="content"> + <h2>Reservation Confirmed</h2> + <h:form> + Movie name: #{booking.movieName}<p/> + Starts at: #{booking.startTime}<p/> + Theater: #{booking.theater}<p/> + <h:commandButton id="home" value="home" action="goHome" /><p/> + </h:form> + </ui:define> +</ui:composition> + +This code displays the movie name, show timings, and the selected +theater. +Right-click on the yellow bulb to fix namespace prefix/URI mapping for ‘h:’. +The commandButton initiates exit from the flow. The action attribute +defines a navigation rule that will be defined in the next step. + + +‘booking.xhtml’, ‘showtimes.xhtml’, ‘confirm.xhtml’, and +‘print.xhtml’ are all in the same directory. Now the runtime needs to be +informed that the views in this directory are to be treated as view +nodes in a flow. This can be done declaratively by adding ‘booking/booking-flow.xml’ +or programmatically by having a class with a method with the following annotations: +@Produces @FlowDefinition + +This lab takes the declarative approach. +Right-click on ‘Web Pages/booking’ folder, select ‘New’, ‘Other’, ‘XML’, +‘XML Document’, give the name as ‘booking-flow’, click on ‘Next>’, take +the default of ‘Well-formed Document’, and click on ‘Finish’. +Replace the generated code with the following: +<faces-config + version="2.2" + xmlns="http://xmlns.jcp.org/xml/ns/javaee" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee + http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"> + <flow-definition id="booking"> + <flow-return id="goHome"> + <from-outcome>/index</from-outcome> + </flow-return> + </flow-definition> +</faces-config> + +This defines the flow graph. It uses the parent element used in +a standard faces-config.xml but defines a <flow-definition> inside it. +<flow-return> defines a return node in a flow graph. <from-outcome> +contains the node value, or an EL expression that defines the node, to +return to. In this case, the navigation returns to the home page. + + +Finally, invoke the flow by editing ‘WEB-INF/template.xhtml’ and +changing: +<h:commandLink action="item1">Item 1</h:commandLink> + +to +<h:commandLink action="booking">Book a movie</h:commandLink> + +commandLink renders an HTML anchor tag that behaves like a form submit +button. The action attribute points to the directory where all views for +the flow are stored. This directory already contains ‘booking-flow.xml’ +which defines the flow of the pages. + + +Run the project by right clicking on the project and selecting +‘Run’. The browser shows the updated output. +
+Book a movie link on main page + + + + + 9.11 output + +
+ +Click on ‘Book a movie’ to see the page as shown. +
+Book a movie page + + + + + 9.11 output2 + +
+ +Select a movie, say ‘The Shiningr and click on `Pick a time’ to see the +page output as shown. +
+Show Timings page + + + + + 9.11 output3 + +
+ +Pick a time slot, say ‘04:00’, click on ‘Confirm’ to see the output as shown. +
+Confirm? page + + + + + 9.11 output4 + +
+ +Click on ‘Book’ to confirm and see the output as: +
+Reservation Confirmed page + + + + + 9.11 output5 + +
+ +Feel free to enter other combinations, go back and forth in the flow and +notice how the values in the bean are preserved. +Click on ‘home’ takes to the main application page. +
+
+ +
+ +Conclusion +This hands-on lab built a trivial 3-tier web application using Java EE 7 +and demonstrated the following features of the platform: + + +Java EE 7 Platform + + +Maven coordinates + + +Default DataSource + + +Default JMSConnectionFactory + + + + + +Java API for WebSocket 1.0 + + +Annotated server endpoint + + +JavaScript client + + + + + +Batch Applications for the Java Platform 1.0 + + +Chunk-style processing + + +Exception handling + + + + + +Java API for JSON Processing 1.0 + + +Streaming API for generating JSON + + +Streaming API for consuming JSON + + + + + +Java API for RESTful Web Services 2.0 + + +Client API + + +Custom Entity Providers + + + + + +Java Message Service 2.0 + + +Default ConnectionFactory + + +Injecting JMSContext + + +Synchronous message send and receive + + + + + +Contexts and Dependency Injection 1.1 + + +Automatic discovery of beans + + +Injection of beans + + + + + +JavaServer Faces 2.2 + + +Faces Flow + + + + + +Bean Validation 1.1 + + +Integration with JavaServer Faces + + + + + +Java Transaction API 1.2 + + +@Transactional + + + + + +Java Persistence API 2.1 + + +Schema generation properties + + + + + + +Hopefully this has raised your interest enough in trying out Java EE 7 applications using +WildFly 8. +Send us feedback or file issues at http://github.com/javaee-samples/javaee7-hol. + + +Troubleshooting + + + +How can I start/stop/restart the application server from within the IDE ? + + +In the ‘Services’ tab, right-click on +‘WildFly 8’. +All the commands to start, stop, and restart are available from the pop-up menu. + + + + +I accidentally closed the output log window. How do I bring it back ? + + +In “Services” tab of NetBeans, expand ‘Servers’, choose the application server +node, and select +‘View Server Log’. +
+View WildFly server log in NetBeans + + + + + 11 wildfly server log + +
+ +In addition, the web-based administration console can be seen by clicking on +‘View Admin Console’. +
+
+
+ +
+ +Acknowledgements +The following Java EE community members graciously reviewed and contributed to this hands-on lab: + + +Antonio Goncalves (@agoncal) + + +Markus Eisele (@myfear) + + +Craig Sharpe (@dapugs) + + +Marcus Vinicius Margarites (@mvfm) + + +David Delabasse (@delabasse) + + +John Clingan (@jclingan) + + +Reza Rahman (@reza_rahman) + + +Marian Muller (@mullermarian) + + +Jason Porter (@lightguardjp) + + +Dan Allen (@mojavelinux) + + +Andrey Cheptsov (@andrey_cheptsov) + + + +Thank you very much for providing the valuable feedback! + + +Completed Solutions +The completed solution for this lab can be downloaded from javaee7-hol. + + +TODO + + +Add the following use cases: + + +Concurrency Utilities for Java EE + + +WebSocket Java Client + + + + + +Disable errors in persistence.xml + + +Add icons for Fix Imports, Format, Fix namespaces, Run the Project. + + +Change logging to use java.util.Logging. + + + + + +Revision History + + +Cleaned up NetBeans instructions and some other typos from DevoxxUK (Jun 25, 2014) + + +Added IntelliJ IDEA specific instructions (Jan 22, 2014) + + +Added macros to generate WildFly and GlassFish-server specific instructions. Also enabled IntelliJ and Eclipse specific macros (Jan 10, 2014) + + +Moving the source document from Pages to AsciiDoc (Dec 3, 2013) + + + + + +Appendix +
+Configure WildFly 8 in NetBeans +
+Install WildFly plugin + + +In NetBeans, click on ‘Tools’, ‘Plugins’, ‘Available Plugins’, type “wildfly” in ‘Search:’ box, and select the plugin by clicking on the checkbox in ‘Install’ column. +
+Available Plugins in NetBeans + + + + + 16 netbeans available plugins wildfly + +
+ +The exact plugin version and the date may be different. +
+ +Click the Install button, then Next >, accept the license agreement by clicking on the checkbox, then click the Install button to install the plugin. Click the Finish button to restart the IDE and complete installation. + +
+ +
+
+Configure WildFly 8 + + +In NetBeans, click on ‘Services’ tab. + + +Right-click on Servers, choose ‘Add Server…’ in the pop-up menu. +
+Add Server in NetBeans + + + + + netbeans addserver + +
+ +
+ +Select ‘WildFly Application Server’ in the Add Server Instance wizard, set the +name to ‘WildFly 8’ and click Next >. +
+Add WildFly instance to NetBeans + + + + + 16 netbeans add instance wildfly + +
+ +
+ +Click on Browse… for ‘Server Location’ and select the directory that got created +when WildFly archive was unzipped. Click on Browse… for ‘Server Configuration’ and +select the ‘standalone/configuration/standalone-full.xml’ file in the unzipped WildFly +archive. +
+Configure WildFly full instance in NetBeans + + + + + 16 netbeans wildfly full platform + +
+ +Click on Next and then Finish. The ‘Services’ should show the WildFly instance. +
+WildFly instance in NetBeans Services tab + + + + + 16 netbeans wildfly server + +
+ +
+
+ +
+
+
+Prepare IntelliJ IDEA for working with WildFly 8 +To be able to perform the exercises discussed in this tutorial, you need the Ultimate Edition of IntelliJ IDEA. Keep that in mind when downloading IntelliJ IDEA from http://www.jetbrains.com/idea/download/. +When the appropriate edition of IntelliJ IDEA is installed, you can start preparing the IDE for the exercises: + + + + + + + + + + + + + + + + + + +
+Specify the JDK +First of all, you should specify the JDK that you are going to use. In IntelliJ IDEA, this is done in the Project Structure dialog: + + +Start IntelliJ IDEA. If, as a result, a project opens, close the project (File Close Project). + + +On the Welcome screen, under Quick Start, click Configure. +
+Welcome to IntelliJ IDEA + + + + + i13 welcome configure + +
+ +
+ +Under Configure, click Project Defaults, and then, under Project Defaults, click Project Structure. + + +In the left-hand pane of the Project Structure dialog, under Platform Settings, select SDKs. Click + + + + i13-plus-icon + and select JDK. +
+Add JDK in IntelliJ IDEA + + + + + i13 plus jdk + +
+ +
+ +In the Select Home Directory for JDK dialog, select the folder in which the JDK that you are going to use is installed, and click OK. +
+JDK home in IntelliJ IDEA + + + + + i13 jdk home + +
+ +
+ +In the Project Structure dialog, click Apply. +
+JDK defined in IntelliJ IDEA + + + + + i13 jdk defined + +
+ +Now, let’s make the JDK that we have specified the default SDK. +
+ +In the left-hand pane, under Project Settings, select Project. In the right-hand part of the dialog, under Project SDK, select the JDK from the list. +
+Project SDK in IntelliJ IDEA + + + + + i13 project sdk + +
+ +
+ +Click OK. + +
+ +
+
+Define WildFly +Defining an application server in IntelliJ IDEA, normally, is just telling the IDE where the server is installed. The servers are defined in the Settings dialog. (On OSX, this dialog is called Preferences.) + + +On the Welcome screen, to the left of Project Defaults, click Back + + + + i13-back-icon +. + + +Under Configure, click Settings. + + +In the left-hand pane of the Settings (Preferences) dialog, under IDE Settings, select Application Servers. On the Application Servers page, click + + + + i13-plus-icon + and select JBoss Server. (WildFly is a server from the "JBoss family".) +
+Add WildFly in IntelliJ IDEA + + + + + i13 plus jboss + +
+ +
+ +In the JBoss Server dialog, click + + + + i13-ellipsis-button + to the right of the JBoss Home field. +
+WildFly server dialog in IntelliJ IDEA + + + + + i13 jboss server dialog initial + +
+ +
+ +In the JBoss Home Directory dialog, select the folder in which you have the WildFly server installed, and click OK. +
+WildFly home in IntelliJ IDEA + + + + + i13 jboss home directory + +
+ +
+ +Click OK in the JBoss Server dialog. +
+WildFly final dialog in IntelliJ IDEA + + + + + i13 jboss server dialog final + +
+ +
+ +In the Settings (Preferences) dialog, click OK. +
+WildFly defined in IntelliJ IDEA + + + + + i13 jboss defined + +
+ +
+
+ +
+
+Create a project +The sample application is supplied as a Maven project with an associated pom.xml file that contains all the necessary project definitions. The corresponding IntelliJ IDEA project in such a case can be created by simply "opening" the pom.xml file. (Obviously, this isn’t the only way to create projects in IDEA. You can create projects for existing collections of source files, import Eclipse and Flash Builder projects, and Gradle build scripts. Finally, you can create projects from scratch.) + + +On the Welcome screen, to the left of Configure, click Back + + + + i13-back-icon +. + + +Under Quick Start, click Open Project. +
+Open project in IntelliJ IDEA + + + + + i13 open project + +
+ +
+ +In the Open Project dialog, select the pom.xml file associated with the sample application, and click OK. +
+Select pom in IntelliJ IDEA + + + + + i13 select pom + +
+ +Wait while IntelliJ IDEA is processing pom.xml and creating the project. When this process is complete, the following message is shown: +
+Configure JPA in IntelliJ IDEA + + + + + i13 jpa detected + +
+ +
+ +Click Configure in the message box. (If by now the message has disappeared, click + + + + i13-exclamation-mark-icon + on the Status bar. +
+JPA detected in status bar in IntelliJ IDEA + + + + + i13 jpa detected status bar + +
+ +The Event Log tool window will open. Click Configure in this window.) +
+JPA detected event log in IntelliJ IDEA + + + + + i13 jpa detected event log + +
+ +
+ +In the Setup Frameworks dialog, just click OK. (By doing so you confirm that the file persistence.xml found in the project belongs to the JPA framework.) +
+Setup frameworks in IntelliJ IDEA + + + + + i13 setup frameworks jpa + +
+ +Now, as an intermediate check, make sure that the project structure looks something similar to this: +
+Project structure in IntelliJ IDEA + + + + + i13 initial project structure + +
+ +
+
+ +
+
+Create a run/debug configuration +Applications in IntelliJ IDEA are run and debugged according to what is called run/debug configurations. Now we are going to create the configuration for running and debugging the sample application in the context of WildFly. + + +In the main menu, select Run Edit Configurations…. +
+Edit configurations in IntelliJ IDEA + + + + + i13 run edit configurations + +
+ +
+ +In the Run/Debug Configurations dialog, click + + + + i13-plus-icon +, select JBoss Server, and then select Local. +
+WildFly configuration in IntelliJ IDEA + + + + + i13 run configs plus jboss + +
+ +As a result, the run/debug configuration for the WildFly server is created and its settings are shown in the right-hand part of the dialog. +
+ +Change the name of the run/debug configuration to WildFly8 (optional). + + +In the lower part of the dialog, within the line Warning: No artifacts marked for deployment, click Fix and select movieplex7:war exploded. (Artifacts in IntelliJ IDEA are deployment-ready project outputs and also the configurations according to which such outputs are produced. In our case, there are two configurations for the sample application (movieplex7:war and movieplex7:war exploded). Both configurations represent a format suitable for deployment onto a Java EE 7-enabled application server. movieplex7:war corresponds to a Web archive (WAR). movieplex7:war exploded corresponds to the sample application directory structure (a decompressed archive). The second of the formats is more suitable at the development stage because manipulations with it are faster.) +
+Fixing deployment warning in IntelliJ IDEA + + + + + i13 jboss fix deployment + +
+ +
+ +Within the line Error: Artifact 'movieplex7: exploded' has invalid extension, click Fix. +
+Invalid extension error message in IntelliJ IDEA + + + + + i13 jboss invalid extension + +
+ +
+ +In the Project Structure dialog, add .war at the end of the output directory path, and click OK. (For the servers of the JBoss family, the application root directory has to have .war at the end.) +
+Extension error fix in IntelliJ IDEA + + + + + i13 jboss fix extension + +
+ +
+ +In the Run/Debug Configurations dialog, switch to the Server tab. In the field for the application starting page URL, replace http://localhost:8080/movieplex7-1/ with http://localhost:8080/movieplex7-1.0-SNAPSHOT/ and click OK. +
+Fixing application URL in IntelliJ IDEA + + + + + i13 jboss url fixed + +
+ +
+
+ +The Application Servers tool window opens in the lower part of the workspace. Shown in this window are the server run/debug configuration and the associated deployment artifact. Now you are ready to run the application. +
+
+Run the application +In the Application Servers tool window, select the server run/debug configuration (WildFly8 [local]) and click Run + + + + i13-run-icon +. +
+Run WildFly in IntelliJ IDEA + + + + + i13 run wildfly + +
+ +IntelliJ IDEA compiles the code, builds the artifact, starts WildFly and deploys the artifact to the server. You can monitor this process in the Run tool window that opens in the lower part of the workspace. +
+Run tool window in IntelliJ IDEA + + + + + i13 run tool window wildfly + +
+ +Finally, your default Web browser opens and the starting page of the application is shown. +
+Starting page in browser from IntelliJ IDEA + + + + + i13 starting page in browser + +
+ +At this step IntelliJ IDEA is fully prepared for your development work, and you can continue with your exercises. +
+
+
+
\ No newline at end of file diff --git a/solution/movieplex7-1.0-SNAPSHOT.war b/solution/movieplex7-1.0-SNAPSHOT.war new file mode 100644 index 0000000..6ca561e Binary files /dev/null and b/solution/movieplex7-1.0-SNAPSHOT.war differ diff --git a/solution/movieplex7-solution.zip b/solution/movieplex7-solution.zip index 550bd55..0b33108 100644 Binary files a/solution/movieplex7-solution.zip and b/solution/movieplex7-solution.zip differ diff --git a/solution/movieplex7/nb-configuration.xml b/solution/movieplex7/nb-configuration.xml deleted file mode 100644 index 4da1f6c..0000000 --- a/solution/movieplex7/nb-configuration.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - ide - - diff --git a/solution/movieplex7/pom.xml b/solution/movieplex7/pom.xml index 1140a79..ba0863e 100644 --- a/solution/movieplex7/pom.xml +++ b/solution/movieplex7/pom.xml @@ -37,6 +37,11 @@ false + + org.wildfly.plugins + wildfly-maven-plugin + 1.0.2.Final + diff --git a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/batch/SalesBean.java b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/batch/SalesBean.java similarity index 97% rename from solution/movieplex7/src/main/java/org/glassfish/movieplex7/batch/SalesBean.java rename to solution/movieplex7/src/main/java/org/javaee7/movieplex7/batch/SalesBean.java index d726f91..62bfc0f 100644 --- a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/batch/SalesBean.java +++ b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/batch/SalesBean.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.batch; +package org.javaee7.movieplex7.batch; import java.util.List; import java.util.Properties; @@ -48,7 +48,7 @@ import javax.inject.Named; import javax.persistence.EntityManagerFactory; import javax.persistence.PersistenceUnit; -import org.glassfish.movieplex7.entities.Sales; +import org.javaee7.movieplex7.entities.Sales; /** diff --git a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/batch/SalesProcessor.java b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/batch/SalesProcessor.java similarity index 96% rename from solution/movieplex7/src/main/java/org/glassfish/movieplex7/batch/SalesProcessor.java rename to solution/movieplex7/src/main/java/org/javaee7/movieplex7/batch/SalesProcessor.java index 6bd73d3..f9a06db 100644 --- a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/batch/SalesProcessor.java +++ b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/batch/SalesProcessor.java @@ -37,13 +37,13 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.batch; +package org.javaee7.movieplex7.batch; import java.util.StringTokenizer; import javax.batch.api.chunk.ItemProcessor; import javax.enterprise.context.Dependent; import javax.inject.Named; -import org.glassfish.movieplex7.entities.Sales; +import org.javaee7.movieplex7.entities.Sales; /** * @author Arun Gupta diff --git a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/batch/SalesReader.java b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/batch/SalesReader.java similarity index 98% rename from solution/movieplex7/src/main/java/org/glassfish/movieplex7/batch/SalesReader.java rename to solution/movieplex7/src/main/java/org/javaee7/movieplex7/batch/SalesReader.java index c054491..29e69c1 100644 --- a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/batch/SalesReader.java +++ b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/batch/SalesReader.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.batch; +package org.javaee7.movieplex7.batch; import java.io.BufferedReader; import java.io.IOException; diff --git a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/batch/SalesWriter.java b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/batch/SalesWriter.java similarity index 96% rename from solution/movieplex7/src/main/java/org/glassfish/movieplex7/batch/SalesWriter.java rename to solution/movieplex7/src/main/java/org/javaee7/movieplex7/batch/SalesWriter.java index 5e9a026..c6e47dc 100644 --- a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/batch/SalesWriter.java +++ b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/batch/SalesWriter.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.batch; +package org.javaee7.movieplex7.batch; import java.util.List; import javax.batch.api.chunk.AbstractItemWriter; @@ -46,7 +46,7 @@ import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.transaction.Transactional; -import org.glassfish.movieplex7.entities.Sales; +import org.javaee7.movieplex7.entities.Sales; /** * @author Arun Gupta diff --git a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/booking/Booking.java b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/booking/Booking.java similarity index 94% rename from solution/movieplex7/src/main/java/org/glassfish/movieplex7/booking/Booking.java rename to solution/movieplex7/src/main/java/org/javaee7/movieplex7/booking/Booking.java index 9b5ff3a..7d8d145 100644 --- a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/booking/Booking.java +++ b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/booking/Booking.java @@ -37,8 +37,9 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.booking; +package org.javaee7.movieplex7.booking; +import java.io.Serializable; import java.util.List; import java.util.StringTokenizer; import javax.faces.flow.FlowScoped; @@ -46,15 +47,15 @@ import javax.persistence.EntityManager; import javax.persistence.NoResultException; import javax.persistence.PersistenceContext; -import org.glassfish.movieplex7.entities.Movie; -import org.glassfish.movieplex7.entities.ShowTiming; +import org.javaee7.movieplex7.entities.Movie; +import org.javaee7.movieplex7.entities.ShowTiming; /** * @author Arun Gupta */ @Named @FlowScoped("booking") -public class Booking { +public class Booking implements Serializable { int movieId; String startTime; diff --git a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/chat/ChatServer.java b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/chat/ChatServer.java similarity index 96% rename from solution/movieplex7/src/main/java/org/glassfish/movieplex7/chat/ChatServer.java rename to solution/movieplex7/src/main/java/org/javaee7/movieplex7/chat/ChatServer.java index 495d2f7..f72de09 100644 --- a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/chat/ChatServer.java +++ b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/chat/ChatServer.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.chat; +package org.javaee7.movieplex7.chat; import java.io.IOException; import java.util.Collections; @@ -71,7 +71,7 @@ public void onClose(Session peer) { @OnMessage public void message(String message, Session client) throws IOException, EncodeException { for (Session peer : peers) { - peer.getBasicRemote().sendObject(message); + peer.getBasicRemote().sendText(message); } } } diff --git a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/client/MovieBackingBean.java b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/client/MovieBackingBean.java similarity index 98% rename from solution/movieplex7/src/main/java/org/glassfish/movieplex7/client/MovieBackingBean.java rename to solution/movieplex7/src/main/java/org/javaee7/movieplex7/client/MovieBackingBean.java index 7b0f223..3cf783a 100644 --- a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/client/MovieBackingBean.java +++ b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/client/MovieBackingBean.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.client; +package org.javaee7.movieplex7.client; import java.io.Serializable; import javax.enterprise.context.SessionScoped; diff --git a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/client/MovieClientBean.java b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/client/MovieClientBean.java similarity index 88% rename from solution/movieplex7/src/main/java/org/glassfish/movieplex7/client/MovieClientBean.java rename to solution/movieplex7/src/main/java/org/javaee7/movieplex7/client/MovieClientBean.java index 1bae185..3a154e4 100644 --- a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/client/MovieClientBean.java +++ b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/client/MovieClientBean.java @@ -37,20 +37,21 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.client; +package org.javaee7.movieplex7.client; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.inject.Named; +import javax.servlet.http.HttpServletRequest; import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.Entity; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.MediaType; -import org.glassfish.movieplex7.entities.Movie; -import org.glassfish.movieplex7.json.MovieWriter; +import org.javaee7.movieplex7.entities.Movie; +import org.javaee7.movieplex7.json.MovieWriter; /** * @author Arun Gupta @@ -64,12 +65,20 @@ public class MovieClientBean { Client client; WebTarget target; + + @Inject HttpServletRequest httpServletRequest; @PostConstruct public void init() { client = ClientBuilder.newClient(); target = client - .target("http://localhost:8080/movieplex7/webresources/movie/"); + .target("http://" + + httpServletRequest.getLocalName() + + ":" + + httpServletRequest.getLocalPort() + + "/" + + httpServletRequest.getContextPath() + + "/webresources/movie/"); } @PreDestroy diff --git a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/entities/Movie.java b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/entities/Movie.java similarity index 99% rename from solution/movieplex7/src/main/java/org/glassfish/movieplex7/entities/Movie.java rename to solution/movieplex7/src/main/java/org/javaee7/movieplex7/entities/Movie.java index f964290..644ad5e 100644 --- a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/entities/Movie.java +++ b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/entities/Movie.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.entities; +package org.javaee7.movieplex7.entities; import java.io.Serializable; import java.util.Collection; diff --git a/starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/entities/Sales.java b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/entities/Sales.java similarity index 99% rename from starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/entities/Sales.java rename to solution/movieplex7/src/main/java/org/javaee7/movieplex7/entities/Sales.java index e5955f9..f726c0c 100644 --- a/starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/entities/Sales.java +++ b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/entities/Sales.java @@ -38,7 +38,7 @@ * holder. */ -package org.glassfish.movieplex7.entities; +package org.javaee7.movieplex7.entities; import java.io.Serializable; import javax.persistence.Basic; diff --git a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/entities/ShowTiming.java b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/entities/ShowTiming.java similarity index 99% rename from solution/movieplex7/src/main/java/org/glassfish/movieplex7/entities/ShowTiming.java rename to solution/movieplex7/src/main/java/org/javaee7/movieplex7/entities/ShowTiming.java index 59eb80d..3511554 100644 --- a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/entities/ShowTiming.java +++ b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/entities/ShowTiming.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.entities; +package org.javaee7.movieplex7.entities; import java.io.Serializable; import javax.persistence.Column; diff --git a/starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/entities/Theater.java b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/entities/Theater.java similarity index 99% rename from starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/entities/Theater.java rename to solution/movieplex7/src/main/java/org/javaee7/movieplex7/entities/Theater.java index b4b7b9d..20aea55 100644 --- a/starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/entities/Theater.java +++ b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/entities/Theater.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.entities; +package org.javaee7.movieplex7.entities; import java.io.Serializable; import java.util.Collection; diff --git a/starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/entities/Timeslot.java b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/entities/Timeslot.java similarity index 99% rename from starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/entities/Timeslot.java rename to solution/movieplex7/src/main/java/org/javaee7/movieplex7/entities/Timeslot.java index f323cd1..f2b9055 100644 --- a/starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/entities/Timeslot.java +++ b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/entities/Timeslot.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.entities; +package org.javaee7.movieplex7.entities; import java.io.Serializable; import java.util.Collection; diff --git a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/json/MovieReader.java b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/json/MovieReader.java similarity index 97% rename from solution/movieplex7/src/main/java/org/glassfish/movieplex7/json/MovieReader.java rename to solution/movieplex7/src/main/java/org/javaee7/movieplex7/json/MovieReader.java index 47ed374..8c080a2 100644 --- a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/json/MovieReader.java +++ b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/json/MovieReader.java @@ -38,7 +38,7 @@ * holder. */ -package org.glassfish.movieplex7.json; +package org.javaee7.movieplex7.json; import java.io.IOException; import java.io.InputStream; @@ -53,7 +53,7 @@ import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.MessageBodyReader; import javax.ws.rs.ext.Provider; -import org.glassfish.movieplex7.entities.Movie; +import org.javaee7.movieplex7.entities.Movie; /** * @author Arun Gupta diff --git a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/json/MovieWriter.java b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/json/MovieWriter.java similarity index 97% rename from solution/movieplex7/src/main/java/org/glassfish/movieplex7/json/MovieWriter.java rename to solution/movieplex7/src/main/java/org/javaee7/movieplex7/json/MovieWriter.java index c262702..243b0af 100644 --- a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/json/MovieWriter.java +++ b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/json/MovieWriter.java @@ -38,7 +38,7 @@ * holder. */ -package org.glassfish.movieplex7.json; +package org.javaee7.movieplex7.json; import java.io.IOException; import java.io.OutputStream; @@ -52,7 +52,7 @@ import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.MessageBodyWriter; import javax.ws.rs.ext.Provider; -import org.glassfish.movieplex7.entities.Movie; +import org.javaee7.movieplex7.entities.Movie; /** * @author Arun Gupta diff --git a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/points/ReceivePointsBean.java b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/points/ReceivePointsBean.java similarity index 91% rename from solution/movieplex7/src/main/java/org/glassfish/movieplex7/points/ReceivePointsBean.java rename to solution/movieplex7/src/main/java/org/javaee7/movieplex7/points/ReceivePointsBean.java index 03f5507..b30d0af 100644 --- a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/points/ReceivePointsBean.java +++ b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/points/ReceivePointsBean.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.points; +package org.javaee7.movieplex7.points; import java.util.Enumeration; import java.util.logging.Level; @@ -46,6 +46,7 @@ import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.inject.Named; +import javax.jms.JMSConsumer; import javax.jms.JMSContext; import javax.jms.JMSDestinationDefinition; import javax.jms.JMSException; @@ -69,9 +70,11 @@ public class ReceivePointsBean { Queue pointsQueue; public String receiveMessage() { - String message = context.createConsumer(pointsQueue).receiveBody(String.class); - System.out.println("Received message: " + message); - return message; + try (JMSConsumer consumer = context.createConsumer(pointsQueue)) { + String message = consumer.receiveBody(String.class); + System.out.println("Received message: " + message); + return message; + } } public int getQueueSize() { diff --git a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/points/SendPointsBean.java b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/points/SendPointsBean.java similarity index 98% rename from solution/movieplex7/src/main/java/org/glassfish/movieplex7/points/SendPointsBean.java rename to solution/movieplex7/src/main/java/org/javaee7/movieplex7/points/SendPointsBean.java index 697d041..31e8938 100644 --- a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/points/SendPointsBean.java +++ b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/points/SendPointsBean.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.points; +package org.javaee7.movieplex7.points; import javax.annotation.Resource; import javax.enterprise.context.RequestScoped; @@ -48,7 +48,6 @@ import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; - /** * @author Arun Gupta */ diff --git a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/rest/AbstractFacade.java b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/rest/AbstractFacade.java similarity index 98% rename from solution/movieplex7/src/main/java/org/glassfish/movieplex7/rest/AbstractFacade.java rename to solution/movieplex7/src/main/java/org/javaee7/movieplex7/rest/AbstractFacade.java index 7fd3fe2..a02f72c 100644 --- a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/rest/AbstractFacade.java +++ b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/rest/AbstractFacade.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.rest; +package org.javaee7.movieplex7.rest; import java.util.List; import javax.persistence.EntityManager; diff --git a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/rest/ApplicationConfig.java b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/rest/ApplicationConfig.java similarity index 83% rename from solution/movieplex7/src/main/java/org/glassfish/movieplex7/rest/ApplicationConfig.java rename to solution/movieplex7/src/main/java/org/javaee7/movieplex7/rest/ApplicationConfig.java index 76ce6aa..c9ab7a0 100644 --- a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/rest/ApplicationConfig.java +++ b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/rest/ApplicationConfig.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.rest; +package org.javaee7.movieplex7.rest; import java.util.Set; import javax.ws.rs.core.Application; @@ -61,12 +61,12 @@ public Set> getClasses() { * given list with all resources defined in the project. */ private void addRestResourceClasses(Set> resources) { - resources.add(org.glassfish.movieplex7.json.MovieReader.class); - resources.add(org.glassfish.movieplex7.json.MovieWriter.class); - resources.add(org.glassfish.movieplex7.rest.MovieFacadeREST.class); - resources.add(org.glassfish.movieplex7.rest.SalesFacadeREST.class); - resources.add(org.glassfish.movieplex7.rest.ShowTimingFacadeREST.class); - resources.add(org.glassfish.movieplex7.rest.TheaterFacadeREST.class); - resources.add(org.glassfish.movieplex7.rest.TimeslotFacadeREST.class); + resources.add(org.javaee7.movieplex7.json.MovieReader.class); + resources.add(org.javaee7.movieplex7.json.MovieWriter.class); + resources.add(org.javaee7.movieplex7.rest.MovieFacadeREST.class); + resources.add(org.javaee7.movieplex7.rest.SalesFacadeREST.class); + resources.add(org.javaee7.movieplex7.rest.ShowTimingFacadeREST.class); + resources.add(org.javaee7.movieplex7.rest.TheaterFacadeREST.class); + resources.add(org.javaee7.movieplex7.rest.TimeslotFacadeREST.class); } } diff --git a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/rest/MovieFacadeREST.java b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/rest/MovieFacadeREST.java similarity index 97% rename from solution/movieplex7/src/main/java/org/glassfish/movieplex7/rest/MovieFacadeREST.java rename to solution/movieplex7/src/main/java/org/javaee7/movieplex7/rest/MovieFacadeREST.java index 7538e4a..8d069c5 100644 --- a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/rest/MovieFacadeREST.java +++ b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/rest/MovieFacadeREST.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.rest; +package org.javaee7.movieplex7.rest; import java.util.List; import javax.ejb.Stateless; @@ -52,7 +52,7 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; -import org.glassfish.movieplex7.entities.Movie; +import org.javaee7.movieplex7.entities.Movie; /** * @author Arun Gupta diff --git a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/rest/SalesFacadeREST.java b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/rest/SalesFacadeREST.java similarity index 96% rename from solution/movieplex7/src/main/java/org/glassfish/movieplex7/rest/SalesFacadeREST.java rename to solution/movieplex7/src/main/java/org/javaee7/movieplex7/rest/SalesFacadeREST.java index ca66efb..0e66155 100644 --- a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/rest/SalesFacadeREST.java +++ b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/rest/SalesFacadeREST.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.rest; +package org.javaee7.movieplex7.rest; import java.util.List; import javax.ejb.Stateless; @@ -52,8 +52,8 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; -import org.glassfish.movieplex7.entities.Sales; -import org.glassfish.movieplex7.entities.Timeslot; +import org.javaee7.movieplex7.entities.Sales; +import org.javaee7.movieplex7.entities.Timeslot; /** * @author Arun Gupta diff --git a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/rest/ShowTimingFacadeREST.java b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/rest/ShowTimingFacadeREST.java similarity index 97% rename from solution/movieplex7/src/main/java/org/glassfish/movieplex7/rest/ShowTimingFacadeREST.java rename to solution/movieplex7/src/main/java/org/javaee7/movieplex7/rest/ShowTimingFacadeREST.java index 4f7b01a..5ed9c4b 100644 --- a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/rest/ShowTimingFacadeREST.java +++ b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/rest/ShowTimingFacadeREST.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.rest; +package org.javaee7.movieplex7.rest; import java.util.List; import javax.ejb.Stateless; @@ -52,7 +52,7 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; -import org.glassfish.movieplex7.entities.ShowTiming; +import org.javaee7.movieplex7.entities.ShowTiming; /** * @author Arun Gupta diff --git a/starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/rest/TheaterFacadeREST.java b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/rest/TheaterFacadeREST.java similarity index 97% rename from starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/rest/TheaterFacadeREST.java rename to solution/movieplex7/src/main/java/org/javaee7/movieplex7/rest/TheaterFacadeREST.java index 0972708..bdcad4a 100644 --- a/starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/rest/TheaterFacadeREST.java +++ b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/rest/TheaterFacadeREST.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.rest; +package org.javaee7.movieplex7.rest; import java.util.List; import javax.ejb.Stateless; @@ -52,7 +52,7 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; -import org.glassfish.movieplex7.entities.Theater; +import org.javaee7.movieplex7.entities.Theater; /** * @author Arun Gupta diff --git a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/rest/TimeslotFacadeREST.java b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/rest/TimeslotFacadeREST.java similarity index 97% rename from solution/movieplex7/src/main/java/org/glassfish/movieplex7/rest/TimeslotFacadeREST.java rename to solution/movieplex7/src/main/java/org/javaee7/movieplex7/rest/TimeslotFacadeREST.java index f024a99..d284715 100644 --- a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/rest/TimeslotFacadeREST.java +++ b/solution/movieplex7/src/main/java/org/javaee7/movieplex7/rest/TimeslotFacadeREST.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.rest; +package org.javaee7.movieplex7.rest; import java.util.List; import javax.ejb.Stateless; @@ -52,7 +52,7 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; -import org.glassfish.movieplex7.entities.Timeslot; +import org.javaee7.movieplex7.entities.Timeslot; /** * @author Arun Gupta diff --git a/solution/movieplex7/src/main/webapp/WEB-INF/faces-config.xml b/solution/movieplex7/src/main/webapp/WEB-INF/faces-config.xml new file mode 100644 index 0000000..fffe7ec --- /dev/null +++ b/solution/movieplex7/src/main/webapp/WEB-INF/faces-config.xml @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/starting-template/movieplex7-starting-template.zip b/starting-template/movieplex7-starting-template.zip index 7a00281..52a5133 100644 Binary files a/starting-template/movieplex7-starting-template.zip and b/starting-template/movieplex7-starting-template.zip differ diff --git a/starting-template/movieplex7/nb-configuration.xml b/starting-template/movieplex7/nb-configuration.xml deleted file mode 100644 index 4da1f6c..0000000 --- a/starting-template/movieplex7/nb-configuration.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - ide - - diff --git a/starting-template/movieplex7/pom.xml b/starting-template/movieplex7/pom.xml index f22bc89..286607b 100644 --- a/starting-template/movieplex7/pom.xml +++ b/starting-template/movieplex7/pom.xml @@ -2,7 +2,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - org.glassfish + org.javaee7 movieplex7 1.0-SNAPSHOT war @@ -37,6 +37,11 @@ false + + org.wildfly.plugins + wildfly-maven-plugin + 1.0.2.Final + diff --git a/starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/entities/Movie.java b/starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/entities/Movie.java similarity index 99% rename from starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/entities/Movie.java rename to starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/entities/Movie.java index f964290..644ad5e 100644 --- a/starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/entities/Movie.java +++ b/starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/entities/Movie.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.entities; +package org.javaee7.movieplex7.entities; import java.io.Serializable; import java.util.Collection; diff --git a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/entities/Sales.java b/starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/entities/Sales.java similarity index 99% rename from solution/movieplex7/src/main/java/org/glassfish/movieplex7/entities/Sales.java rename to starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/entities/Sales.java index e5955f9..f726c0c 100644 --- a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/entities/Sales.java +++ b/starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/entities/Sales.java @@ -38,7 +38,7 @@ * holder. */ -package org.glassfish.movieplex7.entities; +package org.javaee7.movieplex7.entities; import java.io.Serializable; import javax.persistence.Basic; diff --git a/starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/entities/ShowTiming.java b/starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/entities/ShowTiming.java similarity index 99% rename from starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/entities/ShowTiming.java rename to starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/entities/ShowTiming.java index bee2992..3714acb 100644 --- a/starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/entities/ShowTiming.java +++ b/starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/entities/ShowTiming.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.entities; +package org.javaee7.movieplex7.entities; import java.io.Serializable; import javax.persistence.Entity; diff --git a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/entities/Theater.java b/starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/entities/Theater.java similarity index 99% rename from solution/movieplex7/src/main/java/org/glassfish/movieplex7/entities/Theater.java rename to starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/entities/Theater.java index b4b7b9d..20aea55 100644 --- a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/entities/Theater.java +++ b/starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/entities/Theater.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.entities; +package org.javaee7.movieplex7.entities; import java.io.Serializable; import java.util.Collection; diff --git a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/entities/Timeslot.java b/starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/entities/Timeslot.java similarity index 99% rename from solution/movieplex7/src/main/java/org/glassfish/movieplex7/entities/Timeslot.java rename to starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/entities/Timeslot.java index f323cd1..f2b9055 100644 --- a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/entities/Timeslot.java +++ b/starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/entities/Timeslot.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.entities; +package org.javaee7.movieplex7.entities; import java.io.Serializable; import java.util.Collection; diff --git a/starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/rest/AbstractFacade.java b/starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/rest/AbstractFacade.java similarity index 98% rename from starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/rest/AbstractFacade.java rename to starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/rest/AbstractFacade.java index ba7ac5b..5d2345a 100644 --- a/starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/rest/AbstractFacade.java +++ b/starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/rest/AbstractFacade.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.rest; +package org.javaee7.movieplex7.rest; import java.util.List; import javax.persistence.EntityManager; diff --git a/starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/rest/ApplicationConfig.java b/starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/rest/ApplicationConfig.java similarity index 98% rename from starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/rest/ApplicationConfig.java rename to starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/rest/ApplicationConfig.java index 99381e5..96f83d0 100644 --- a/starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/rest/ApplicationConfig.java +++ b/starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/rest/ApplicationConfig.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.rest; +package org.javaee7.movieplex7.rest; import javax.ws.rs.core.Application; diff --git a/starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/rest/MovieFacadeREST.java b/starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/rest/MovieFacadeREST.java similarity index 97% rename from starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/rest/MovieFacadeREST.java rename to starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/rest/MovieFacadeREST.java index 7538e4a..8d069c5 100644 --- a/starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/rest/MovieFacadeREST.java +++ b/starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/rest/MovieFacadeREST.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.rest; +package org.javaee7.movieplex7.rest; import java.util.List; import javax.ejb.Stateless; @@ -52,7 +52,7 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; -import org.glassfish.movieplex7.entities.Movie; +import org.javaee7.movieplex7.entities.Movie; /** * @author Arun Gupta diff --git a/starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/rest/ShowTimingFacadeREST.java b/starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/rest/ShowTimingFacadeREST.java similarity index 97% rename from starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/rest/ShowTimingFacadeREST.java rename to starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/rest/ShowTimingFacadeREST.java index 48719ed..51bcd26 100644 --- a/starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/rest/ShowTimingFacadeREST.java +++ b/starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/rest/ShowTimingFacadeREST.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.rest; +package org.javaee7.movieplex7.rest; import java.util.List; import javax.ejb.Stateless; @@ -52,7 +52,7 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; -import org.glassfish.movieplex7.entities.ShowTiming; +import org.javaee7.movieplex7.entities.ShowTiming; /** * @author Arun Gupta diff --git a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/rest/TheaterFacadeREST.java b/starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/rest/TheaterFacadeREST.java similarity index 97% rename from solution/movieplex7/src/main/java/org/glassfish/movieplex7/rest/TheaterFacadeREST.java rename to starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/rest/TheaterFacadeREST.java index 0972708..bdcad4a 100644 --- a/solution/movieplex7/src/main/java/org/glassfish/movieplex7/rest/TheaterFacadeREST.java +++ b/starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/rest/TheaterFacadeREST.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.rest; +package org.javaee7.movieplex7.rest; import java.util.List; import javax.ejb.Stateless; @@ -52,7 +52,7 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; -import org.glassfish.movieplex7.entities.Theater; +import org.javaee7.movieplex7.entities.Theater; /** * @author Arun Gupta diff --git a/starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/rest/TimeslotFacadeREST.java b/starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/rest/TimeslotFacadeREST.java similarity index 97% rename from starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/rest/TimeslotFacadeREST.java rename to starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/rest/TimeslotFacadeREST.java index 83e1711..083cfec 100644 --- a/starting-template/movieplex7/src/main/java/org/glassfish/movieplex7/rest/TimeslotFacadeREST.java +++ b/starting-template/movieplex7/src/main/java/org/javaee7/movieplex7/rest/TimeslotFacadeREST.java @@ -37,7 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.movieplex7.rest; +package org.javaee7.movieplex7.rest; import java.util.List; import javax.ejb.Stateless; @@ -52,7 +52,7 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; -import org.glassfish.movieplex7.entities.Timeslot; +import org.javaee7.movieplex7.entities.Timeslot; /** * @author Arun Gupta diff --git a/starting-template/movieplex7/src/main/webapp/WEB-INF/web.xml b/starting-template/movieplex7/src/main/webapp/WEB-INF/web.xml index cc2927d..7412f94 100644 --- a/starting-template/movieplex7/src/main/webapp/WEB-INF/web.xml +++ b/starting-template/movieplex7/src/main/webapp/WEB-INF/web.xml @@ -40,7 +40,7 @@ * holder. */ --> - + javax.faces.PROJECT_STAGE Development