Enterprise Java Development@TOPIC@

Enterprise Java (JavaEE) Course Notes

Revision: v2019-08-08

Built on: 2019-08-22 07:12 EST

Abstract

This book contains course notes covering Enterprise Computing with Java. This comprehensive course explores a variety of modern Java frameworks and technologies that can be used for developing mission-critical complex enterprise applications. The emphasis is on use of the latest Java EE platform, its set of underlying specifications, designing and developing server-side application components. Students will learn thru having hands-on experience in building multi-tier distributed enterprise applications, comparing and using variety of Java EE design patterns, rich set of server-side components and technologies, and web-enabled by modern design practices and communication protocols. Students will learn to:

  • Implement a data access tier to a relational database using Java Persistence API (JPA), Java Database Connectivity (JDBC) API, and variety of data modeling and access patterns.

  • Implement synchronous and asynchronous server-side business logic using stateless and stateful session EJBs, message-driven EJBs and the EJB Timer service.

  • Integrate server-side logic with the web-tier components using legacy server-side and more modern RESTful API approaches that include JSON and XML.

Other critical Java EE infrastructure services will be discussed, including the Java Naming and Directory Interface (JNDI), the Java Message Service (JMS), the Java Transaction (JTA) API, and Java EE security. Using modern development tools, commercial persistence providers, and application servers, students will design and implement several significant programming projects using the above-mentioned technologies and deploy them to a Java EE environment that they will manage.

The course is being actively updated to JavaEE 8 (JPA 2.2, EJB 3.2, JAX-RS 2.1, and JMS 2.0) but a few order APIs and legacy examples remain. The development environment used in class is based on the Wildfly application server and is JavaSE 8 and JavaEE 7-compliant with the ability to preview some JavaEE 8-specific features.


I. Enterprise Computing with Java (605.784.31) Course Syllabus
1. Course Description
1.1. Meeting Times/Location
1.2. Description
1.3. Student Background
1.4. Student Commitment
1.5. Course Text(s)
1.6. Required Software
1.7. References
1.8. Grading
1.9. Grading Policy
1.10. Academic Integrity
1.11. Instructor Availability
2. Syllabus
3. Course Projects
3.1. Project 0: Sanity Check
3.2. Project 1: Data Access Tier and Business Logic
3.3. Project 2: N-Tier Application
3.4. Project 3: Secure and Asynchronous Application
3.5. General Requirements
3.6. Submission Guidelines
II. Java Enterprise Edition (JavaEE) Intro
Purpose
1. Goals
2. Objectives
4. Java Technology Levels
4.1. Java Standard Edition(tm) (JavaSE or JSE)
4.2. Java Micro Edition(tm) (JavaME)
4.3. Java Enterprise Edition(tm) (JavaEE, JEE, EE4J, or Jakarta EE)
5. Why Enterprise?
6. JavaEE Architecture
6.1. Profiles
6.1.1. Web Profile
6.1.2. Micro Profile (coming?)
6.2. Application Components
6.2.1. Application Clients
6.2.2. Applets
6.2.3. Servlets/JSPs/JSF
6.2.4. Enterprise JavaBeans(tm) (EJB)
6.3. Containers
6.3.1. Servers
6.4. Resource Adapters
6.5. Databases
7. JavaEE Specifications
8. JavaEE Version Highlights
9. Summary
A. Sources
III. Development Environment Overview
Purpose
1. Goals
2. Objectives
10. Maven Basics
10.1. Directory Structure
10.1.1. Source Tree
10.1.2. Build Tree
10.2. Maven POM
10.2.1. Packaging
10.2.2. Project Identity
10.2.3. Maven Repositories
10.2.4. Properties
10.2.5. Dependencies
10.2.6. Build Definition
10.3. Maven Commands
11. Maven Profiles
11.1. Profile Definition
11.2. Activation
11.2.1. Explicit Activation/Deactivation
11.2.2. Activation/Deactivation By Rule
12. Maven Modules
12.1. POM Inheritance
12.2. Submodules
13. Dependency/Plugin Management
13.1. Dependency Management
13.2. Plugin Management
14. Maven Eclipse Integration
14.1. Project Import
14.2. Project Update
14.3. Project Refresh
15. Eclipse
15.1. Project Organization
15.2. Project Testing
15.2.1. Maven test from Eclipse
15.2.2. Raw JUnit within Eclipse
15.3. Program Debugging
15.3.1. Debugging within Eclipse
15.3.2. Debugging Maven test from Eclipse
IV. eSport Semester Projects
V. Business Logic
Purpose
1. Goals
2. Objectives
30. Session Facade Pattern
30.1. Context
30.2. Problem
30.3. Forces
30.4. Solution
30.5. Consequences
31. Using Business Logic for Requirements
31.1. Business Interfaces Capture Functional Requirements
31.2. Business Objects Capture Data Requirements
31.3. Test Cases as Executable Requirements
31.4. Sample Project Layout
VI. Data Access Objects (DAOs)
Purpose
1. Goals
2. Objectives
32. Data Access Object (DAO) Pattern
32.1. Context
32.2. Problem
32.3. Forces
32.4. Solution
32.5. DAO Implementation Structure
32.6. Consequences
33. DAO Interface
33.1. DAO Interface
33.2. DAO Exceptions
33.3. DAO Implementation
33.4. DAO Test
34. Data Transfer Object (DTO) Pattern
34.1. Context
34.2. Problem
34.3. Forces
34.4. Solution
34.5. Consequences
35. RDBMS Schema
35.1. RDBMS
35.1.1. Background
35.1.2. Tables/Columns
35.1.3. Constraints
35.1.4. Relationships
35.1.5. Indexes
35.2. Data Definition Language (DDL)
35.2.1. Create Table
35.2.2. Drop Table
35.2.3. Create Foreign Key Constraint
35.2.4. Drop Foreign Key Constraint
35.2.5. Create Index
35.2.6. Drop Index
35.3. DDL Files in Maven Module
36. SQL Basics
36.1. Create/INSERT
36.2. Read/SELECT
36.3. Update/UPDATE
36.4. Delete/DELETE
37. Working with Native SQL
37.1. Java Database Connectivity (JDBC)
37.2. Java Persistence API (JPA) Native SQL
VII. Java Persistence API
Purpose
1. Goals
2. Objectives
38. JPA Overview
38.1. Background
38.2. EntityManager
38.3. Entity
38.4. JPA Example
38.5. Entity States
38.5.1. Managed
38.5.2. Detached
38.6. Persistence Context
38.7. Persistence Unit
38.8. Example Layout
38.9. Persistence.xml
38.9.1. Application Example
38.9.2. Server Example
38.9.3. Optional hibernate.properties
38.9.4. Sample orm.xml
38.9.5. persistence.xml Elements
38.9.6. Entity Discovery
38.10. Schema Generation
38.10.1. Hibernate DDL Generation Example
38.10.2. javax.persistence Schema Generation
38.11. Basic Testing Usage Steps
38.12. Entity Manager Methods
38.12.1. Basic CRUD Operations
38.12.2. Membership Operations
38.12.3. State Synchronization Operations
38.12.4. Locking Operations
38.12.5. Query Operations
38.12.6. Other Operations
39. Entity Manager CRUD Methods
39.1. persist()
39.2. find()
39.3. getReference()
39.4. merge()
39.5. remove()
40. Entity Manager Membership Methods
40.1. contains()
40.2. clear()
40.3. detach()
41. Entity Manager State Synchronization Methods
41.1. flush()
41.2. FlushMode
41.3. refresh()
42. Entity Manager Locking Methods
42.1. Primary Lock Types
42.2. LockModeType
42.3. lock()
42.4. find(lock)
42.5. refresh(lock)
42.6. getLockMode()
43. Entity Manager Query Methods
43.1. JPA Queries
43.2. Native Queries
43.3. Criteria Queries
44. Other Entity Manager Methods
44.1. isOpen()
44.2. close()
44.3. getTransaction()
44.4. joinTransaction()
44.5. unwrap()
44.6. getDelegate()
44.7. getMetaModel()
44.8. getEntityManagerFactory()
44.9. setProperties()/getProperties()
45. JPA Maven Environment
45.1. JPA Maven Dependencies
45.1.1. JPA API classes
45.1.2. JPA Provider classes
45.1.3. Database
45.2. Supplying Runtime Properties
45.2.1. Turn on Resource Filtering in pom.xml
45.2.2. Use ${variable} References in Resource Files
45.2.3. Define Property Values in Parent pom.xml
45.2.4. Run with Filtered Values
VIII. Java Persistence API: Entities
Purpose
1. Goals
2. Objectives
46. Identifying Entity Classes
46.1. Entities
46.2. Entity Name
46.3. Field/Property Access
46.3.1. Access Types
46.3.2. Defining Access using @Annotations
46.3.3. Defining Access using orm.xml
46.4. Approaches
46.4.1. Java First
46.4.2. Schema First
47. Mapping Entity Classes
47.1. Core Mappings
47.2. More Detailed Property Mappings
47.3. Entity Mapping Example
47.3.1. Example Schema
47.3.2. Mapping Entity Class with Annotations
47.3.3. Mapping Entity Class with orm.xml
47.4. More on Precision/Scale
47.5. Transient Properties
47.6. Lob Properties
47.7. Temporal and Enumerated Properties
47.7.1. Temporals
47.7.2. Enums
47.7.3. Temporal/Enum Example
48. Primary Keys
48.1. Generated Simple Primary Keys
48.1.1. GenerationType.AUTO
48.1.2. GenerationType.IDENTITY
48.1.3. GenerationType.SEQUENCE
48.1.4. GenerationType.TABLE
49. Composite Primary Keys
49.1. Example Composite Primary Key Class
49.2. Composite @IdClass
49.3. Composite @EmbeddedId
49.4. Composite Overrides
49.5. Composite Primary Key Summary
50. Multi-table Mapping
50.1. Multi-table Example
50.2. Summary
51. Embedded Object
51.1. Embedded Object Example
51.2. Summary
IX. Validation API
Purpose
52. Core Validation API
52.1. Validation Goals
52.2. Constraints
52.2.1. Annotating Class
52.2.2. Validating Instances
52.3. Constraint Groups
52.3.1. Uses
52.3.2. Defining Groups
52.3.3. Groups Applied to Classes
52.3.4. Validating with Groups
52.4. Custom Validators
52.4.1. Annotation
52.4.2. Validator
52.4.3. Annotating Class
52.4.4. Example Usage
52.5. Composite Constraints
52.5.1. Multiple Constraints
52.5.2. Replace With Composite
52.5.3. Composite Annotation
52.5.4. Composed Validations Individually Reported
52.5.5. @ReportAsSingleViolation
52.6. Group Sequences
52.6.1. Validation Groups and Sequence
52.6.2. Assign Constraints from Sequence
52.6.3. Sample Execution
52.6.4. Optionally Assign Sequence to be Default for Class
52.7. Validating Types
52.7.1. Annotation
52.7.2. Validator
52.7.3. Validator
52.8. Cascade Validation
52.8.1. Trigger Validation Cascade with @Valid
52.8.2. Validation
52.9. XML Descriptor
52.9.1. POJO Bean Class
52.9.2. META-INF/validation.xml
52.9.3. Constraint Mapping
52.10. Summary
53. JPA Persistence Lifecycle
53.1. Callbacks
53.2. Listeners
53.3. Summary
54. Validation API/JPA Integration
54.1. Entity Class
54.2. Persistence Unit
54.3. Validation
54.4. Validation with JPA
54.5. Summary
55. Validation API Build/Maven Aspects
55.1. Adding POM Dependencies
X. Java Persistence API: Relations
Purpose
1. Goals
2. Objectives
56. One-to-One Relationships
56.1. One-to-One: Uni-directional
56.2. @OneToOne Annotation
56.3. @JoinColumn Annotation
56.4. @JoinColumns Annotation
56.5. One-to-One: Bi-directional
56.6. Bi-directional Relationships and Ownership
56.7. Other Topics
56.8. Summary
57. One-to-Many Relationships
57.1. One-to-Many: Uni-directional
57.2. @OneToMany Annotation
57.3. One-to-Many: Bi-directional
57.4. @ManyToOne Annotation
57.5. One-to-Many: Element Collection
57.6. @ElementCollection Annotation
57.7. Other Topics
57.8. Summary
58. Relationship Capabilities
58.1. Fetching
58.1.1. fetch=LAZY
58.1.2. fetch=EAGER
58.2. Cascades
58.3. Orphan Removal
58.4. Summary
59. Object Collections
59.1. Object Identity
59.1.1. Instance Id
59.1.2. Primary Key Id
59.1.3. Switching Ids
59.1.4. Business Id
59.2. Collection Types
59.3. Summary
60. Foreign Keys
60.1. Primary Key Join
60.1.1. @PrimaryKeyJoin Annotation
60.2. Using Composite Primary Key Property as Foreign Key
60.2.1. Using @IdClass Composite Primary Key Property as Foreign Key
60.2.2. Using @EmbeddedId Composite Primary Key Property as Foreign Key
60.3. Using Foreign Key in Composite Primary Key
60.3.1. Using Foreign Key in @IdClass Composite Primary Key
60.3.2. Using Foreign Key in @EmbeddedId Composite Primary Key (@MapsId)
60.4. Join Tables
60.4.1. @JoinTable Annotation
60.5. Summary
61. Many-to-One Relationships
61.1. Many-to-One: Uni-directional
61.2. Summary
62. Many-to-Many Relationships
62.1. Many-to-Many: Uni-directional
62.2. Many-to-Many: Bi-directional
62.3. Summary
XI. Java Persistence API: Inheritance
Purpose
1. Goals
2. Objectives
63. Inheritance Strategy: Single Table
63.1. Single Table Strategy Overview
63.2. Single Table Example Database Schema
63.3. Single Table Example Java Mapping
63.4. Single Table Example Usage
63.5. @DiscriminatorColumn Annotation
63.6. Summary
64. Inheritance Strategy:Table per Concrete Class
64.1. Table per Concrete Class Strategy Overview
64.2. Table per Concrete Class Example Database Schema
64.3. Table per Concrete Class Example Java Mapping
64.4. Table per Concrete Class Example Usage
64.5. Summary
65. Inheritance Strategy:Join
65.1. Join Strategy Overview
65.2. Join Example Database Schema
65.3. Join Example Java Mapping
65.4. Join Example Usage (Persist)
65.5. Summary
66. Inheritance Strategy:Non-Entity
66.1. Non-Entity Strategy Overview
66.2. Non-Entity Example Database Schema
66.3. Non-Entity Example Java Mapping
66.4. Non-Entity Example Usage (Persist)
66.5. Summary
67. Mixed Inheritance Strategies
67.1. Which Strategy Gets Used for Subclass?
67.2. Generated Database Schema
67.3. Summary
XII. JPA Queries
Purpose
1. Goals
2. Objectives
XIII. JPA Tuning
Topics
76. SQL Tuning
76.1. Reasons for Inefficient SQL Performance
76.2. Execution Plan
76.3. Diagnostic Tools
76.3.1. Client/DAO Result
76.3.2. EXPLAIN PLAN
76.3.3. AUTOTRACE
76.3.4. Display Cursor Execution Plan within V$PLAN
76.4. Summary
77. Example Domain Model: Movies
77.1. Class Model
77.2. Database Schema
77.3. Database Size
77.4. Prepare DB Between Tests
78. Table Access
78.1. Full Table Scan
78.1.1. Full Table Scan: Unconstrained Access
78.1.2. Full Table Scan: Using Where (without Index)
78.1.3. RowId Scan: Using Where (with Index)
78.1.4. Full Table Scan: Invalidating Index using Function applied to Row Column
78.1.5. RowId Scan: Using Index with Function applied to Row Column
78.1.6. Full Table Scan: Invalidating Index by using Leading Wildcards
78.2. Order By
78.2.1. Order By using Sort
78.2.2. Order By using Index
78.2.3. Order By using Index DESC
78.2.4. Order By using Reverse Index DESC
78.3. Summary
79. Indexes
79.1. Index Range Scan
79.2. Unique Index Scan
79.3. Composite Index
79.3.1. Query Parts
79.3.2. First Term Indexed
79.3.3. First and Second Term Indexed (using Composite Index)
79.4. Index Fast Full Scan (with Composite Index)
79.4.1. Query Parts
79.4.2. Option: Use Range Scan and RowId Access
79.4.3. Option: Use Range Scan Alone with Composite Index
79.4.4. Option: Fast Full Scan
79.5. Summary
80. Joins
80.1. Foreign Keys
80.1.1. Query Parts
80.1.2. No Indexes
80.1.3. Perform Query with Support for Foreign Key Index
80.1.4. Foreign Key and Where Columns Indexed
80.1.5. Foreign Key, Where, and Join Columns Indexed
80.2. Join Types
80.2.1. Nested Loop Join
80.2.2. Hash Join
80.2.3. Sort Merge Join
80.3. Summary
81. JPA
81.1. Lazy and Eager Fetching
81.1.1. Get Parent
81.1.2. Get Parent and Children
81.2. Obtaining Instance Counts
81.2.1. Query Parts
81.2.2. Collection Size in DAO from Relation
81.2.3. Row Count in DAO from Query
81.2.4. Row Count in DB using Count() Query
81.2.5. Row Count in DB using Count Query without JOIN
81.3. Query Loops
81.3.1. Query Parts
81.3.2. Query Loops in DAO
81.3.3. Query Loops using DB Subquery
81.4. Paging
81.4.1. Query Parts
81.4.2. Paging within DAO
81.4.3. Paging within DB
81.5. Summary
82. H2 Execution Plans
82.1. Column Index
82.2. Summary
83. JPA/SQL Tuning Summary
83.1. Other Topics
XIV. Enterprise JavaBeans (EJB) Overview
Purpose
1. Goals
2. Objectives
84. Why Enterprise?
84.1. Related Patterns
84.1.1. Pattern: Session Facade
84.2. Pattern: Remote Facade
84.2.1. Context
84.2.2. Problem
84.2.3. Forces
84.2.4. Solution
84.2.5. Consequences
84.3. Pattern: Data Transfer Object (DTO)
84.3.1. Context
84.3.2. Problem
84.3.3. Forces
84.3.4. Solution
84.3.5. Consequences
85. Overview of EJB Styles
85.1. EJB Uses
85.2. EJB Granularity
85.3. EJB Bean Types
85.3.1. Entities (formerly Entity EJB)
85.3.2. Stateless Session EJB
85.3.3. Stateful Session EJB
85.3.4. Singleton Session EJB
85.3.5. Message Driven EJB (MDB)
85.4. EJB Interface Styles
85.4.1. Business Interface
85.4.2. Remote Interface
85.4.3. Local Interface
85.4.4. No Interface EJB
85.4.5. Other Interface Types
85.5. EJB Deployments
85.5.1. EJB Module
85.5.2. Naked EJB Deployment
85.5.3. EJB EAR Deployment
85.5.4. EJB WAR ("flexible") Deployment
85.5.5. EAR Deployment Class Loaders
B. Sources
XV. Basic Session EJB
Purpose
1. Goals
2. Objectives
86. Basic Session EJB POJO Aspects
86.1. POJO Class
86.1.1. Bean Class
86.1.2. Business Method
86.1.3. Interface
86.1.4. Serializable DTO
86.1.5. Business Exceptions
86.2. POJO Unit Test
86.2.1. JUnit Test Case
86.2.2. JUnit Test Method(s)
86.3. POJO Module
86.3.1. Built POJO Archive
86.3.2. Source POJO Module
86.4. Unit Test Execution
86.5. Maven Aspects: POJO
86.6. Summary
87. Basic Session EJB Component
87.1. Making POJO an EJB
87.1.1. Annotate Bean class with @javax.ejb.Stateless
87.1.2. Annotate Interface with @javax.ejb.Remote
87.1.3. Optionally Annotate Empty Interface Extending POJO Business Interface
87.1.4. @PostConstruct/@PreDestory
87.2. EJB Module
87.2.1. Source EJB Module
87.3. Naked EJB-based Deployment
87.4. Maven Aspects: EJB
87.5. JBoss Session EJB Pooling
87.6. Summary
88. EAR Deployment
88.1. EAR Artifact
88.1.1. EAR META-INF/application.xml
88.2. EAR-based EJB Deployment
88.3. Maven Aspects: EAR
88.3.1. EAR Module Build
88.3.2. EAR Module Cleanup
88.3.3. IT Test Module
88.4. Summary
89. WAR Deployment
89.1. WAR Artifact
89.1.1. WEB-INF/jboss-web.xml
89.2. WAR-based EJB Deployment
89.3. Maven Aspects: WAR
89.3.1. WAR Module Build
89.3.2. IT Test Module
89.4. Summary
90. IT Testing
90.1. EJB Access
90.1.1. JNDI
90.1.2. JBoss Remoting
90.1.3. EJB Client
90.1.4. Blending JBoss Remoting and EJB Client
90.2. Integration Test Session EJB
90.2.1. Integration Test (IT) JUnit Class
90.2.2. @Test Methods
90.3. IT Module
90.4. IT Test Execution
90.5. Summary
XVI. EJB Development Scenarios
Purpose
1. Goals
2. Objectives
91. Application Server
91.1. Server Configuration
91.1.1. Logger
91.2. Standalone Server
91.2.1. Standalone Server Start
91.2.2. Standalone Server Stop
91.3. Embedded Server
91.3.1. Embedded Server Setup
91.3.2. Embedded Server Start
91.3.3. Embedded Server Stop
91.4. Summary
92. Build Commands
92.1. Example Source Tree
92.2. Unit Tests
92.2.1. Resources Plugin
92.2.2. Surefire Plugin
92.3. Integration (IT) Tests
92.3.1. Cargo Plugin
92.3.2. Failsafe Plugin
92.4. Summary
93. IT Testing with IDE
93.1. Run IT Tests Against Server
93.2. Debug Code Running on Server
93.3. Summary
XVII. Configuring EJBs using ENC and JNDI
Topics
94. Global JNDI and the Enterprise Naming Context (ENC)
94.1. Java Naming and Directory Interface
94.2. Global JNDI Naming Context
94.2.1. Remote JNDI Naming Context
94.2.2. Local JNDI Naming Context
94.3. Enterprise Naming Context (ENC)
94.4. Summary
95. Injecting Specific EJB Resources without ENC
95.1. Injecting EJB SessionContext
95.2. Injecting JDBC Resources
95.3. Injecting JPA Resources
95.4. Injecting EJB Resources
95.5. Summary
XVIII. Context and Dependency Injection (CDI) Configuration
Purpose
1. Goals
2. Objectives
96. Context and Dependency Injection (CDI) JSR-299
96.1. History
96.2. Injection Concept
96.2.1. Beans
96.2.2. Injection
96.2.3. Ambiguity Error
96.2.4. Qualifiers and Qualified Injection
96.2.5. Qualifier Annotation
96.3. CDI Activation
96.3.1. beans.xml
96.4. Summary
97. CDI Injection
97.1. Producer Field
97.2. Producer Method
97.3. Other Producers
97.3.1. Example JNDI Resource Lookup Producer
97.3.2. Example String Resource Producer
97.4. @Any Instance
97.5. Bean Types
97.6. Named Beans
97.7. Summary
XIX. JPA Server-Side Deployment
Purpose
98. Server-side Resources
98.1. SQL DataSource (defined in Server - standalone.xml)
98.2. Server-side Persistence Units
98.2.1. transaction-type=JTA (default)
98.2.2. transaction-type=RESOURCE_LOCAL
98.3. persistence.xml Placement
98.3.1. EJB persistence.xml Placement
98.3.2. WAR persistence.xml Placement
98.4. Reference External @Entities
98.4.1. Reference External @Entities: EAR Deploy
98.4.2. Reference External @Entities: WAR Deploy
98.5. Summary
99. Persistence Unit/Context Injection
99.1. @PersistenceContext Injection
99.2. @PersistenceUnit Injection
99.3. Context and Dependency Injection (CDI)
99.4. Summary
100. Managed Entities and Remote Interfaces
100.1. Problem: Provider Proxy Classes Marshaled to Client
100.1.1. Potential Solution: Add JPA Provider Classes to Client Classpath
100.1.2. More Scenario Details
100.1.3. Candidate Solution: Cleansed DTOs
100.2. Problem: Lazy Load Exception
100.2.1. Lazy Load Scenario Details
100.2.2. Candidate Solution: Load thru "Touching" Object Tree in Remote Facade
100.2.3. Candidate Solution: Load thru Fetching Object Tree in Query
100.2.4. Candidate Solution: Abstract Remote Interface View with DTO
100.3. Summary
101. Persistence Context Propagation
101.1. Stateless Persistence Context Interaction
101.1.1. Stateless EJB Example Check-in
101.1.2. Stateless EJB Example Client Check-in
101.1.3. EJB Gets Available Rooms from DB
101.1.4. EJB Gets Specific Room
101.1.5. EJB Adds Guest
101.1.6. EJB associates Guest with Room
101.2. Stateful Facade Persistence Context Interaction
101.2.1. Example Stateful Reservation EJB Caches Guest Requests for Client
101.2.2. Example Stateful Reservation EJB Acting on Cached State
101.2.3. Stateful EJB Example Client Check-in
101.2.4. Stateful EJB Persists Guests Prior to Active JTA Transaction
101.2.5. Stateless EJB Populates Propagated Persistence Context with Rooms
101.2.6. Stateful EJB Method Activates Transaction and flush()es Guests in EntityManager Cache
101.2.7. Stateful EJB uses pre-loaded Rooms and Guests without accessing DB (until association)
101.3. Transaction Rollbacks
101.3.1. Stateless Transaction Rollback
101.3.2. Stateful Transaction Rollback
101.4. Pessamistic Locking
101.5. Summary
XX. EJB Transactions and Concurrency
Purpose
1. Goals
2. Objectives
102. Transactions
102.1. Transaction Examples
102.2. Transaction ACID Properties
102.3. EJB Transaction Scenarios
102.3.1. Scenario: Multiple DB Updates
102.3.2. Scenario: Update Multiple Databases
102.3.3. Scenario: Coordinate JMS with Database Transaction
102.4. Transaction Terms
102.5. JTA and JTS Specifications
102.5.1. Java Transaction API (JTA)
102.5.2. Java Transaction API (JTA) and Java Transaction Service (JTS)
102.6. EJB Transactions
102.6.1. Container-Managed Transactions (default)
102.6.2. Bean-Managed Transactions
102.7. Summary
103. Container-Managed Transactions
103.1. Transaction Scope
103.1.1. Transaction Not Supported
103.1.2. Transaction Required
103.1.3. Transaction Supports
103.1.4. Transaction Requires New
103.1.5. Transaction Mandatory
103.1.6. Transaction Never
103.1.7. All Transaction Scopes
103.2. @Asynchronous Methods and Transactions
103.3. EJB Lifecycle Methods
103.4. Callback Methods
103.4.1. Message Driven Callback
103.4.2. Timer Callbacks @Asynchronous
103.5. EJB Control of Container-Managed Transactions
103.5.1. EJBContext
103.6. Exceptions
103.6.1. Checked/Application Exceptions
103.6.2. Checked/Application Exception Rollback
103.7. Persistence Context Propagation
103.8. Summary
104. Bean-Managed Transactions
104.1. UserTransaction
104.2. Message-Driven Callbacks and Bean-Managed Transactions
104.3. Summary
105. Stateful Session Synchronization
105.1. Stateful Session Synchronization Events
105.2. Additional Stateful Rules
105.3. Summary
XXI. Web-enabled EJBs
Purpose
1. Goals
2. Objectives
106. REST-like Concepts
106.1. REST
106.2. "REST-like"
106.3. HTTP Protocol embraced
106.4. Resource
106.5. Uniform Resource Indentifiers (URIs)
106.6. Methods
106.6.1. Method Safety
106.6.2. Idempotent
106.7. Response Codes
106.8. Links
106.9. Summary
107. JAX-RS Basics
107.1. JAX-RS Client Basics
107.2. JAX-RS Server Basics
107.3. JAX-RS Maven Aspects
107.4. Summary
108. JAX-RS Resource/EJB Integration
108.1. EJB Injection
108.2. Candidate EJB/Business Tier Exceptions for Web API Status
108.2.1. Client Error
108.2.2. Service Error
108.3. Resource/EJB method
108.4. Summary
109. JAX-RS Content
109.1. JSON Content
109.1.1. JSON-B JSON Marshaling/Demarshaling
109.1.2. Jackson JSON Marshaling/Demarshaling
109.2. XML Content
109.2.1. Common JAXB Annotations
109.2.2. JAXB Maven Aspects
109.3. Content Handling
109.3.1. Client Marshal Request Content
109.3.2. API Receive Request Content
109.3.3. API Send Response Content
109.3.4. Demarshal Response Content
109.4. JAX-RS Client Maven Aspects
109.5. Summary
110. Resource Examples
110.1. JAX-RS Resource Class
110.2. JAX-RS GET Resource Collection
110.2.1. Server-side GET Resource Collection
110.2.2. Client-side GET Resource Collection
110.3. JAX-RS Resource POST Method
110.3.1. Server-side POST Resource Collection
110.3.2. Client-side POST Resource Collection
110.4. JAX-RS GET Resource Single Method
110.4.1. Server-side GET Single Resource
110.4.2. Client-side GET Single Resource
110.5. JAX-RS PUT (Nested) Resource Method
110.5.1. Server-Side JAX-RS PUT Resource Method
110.5.2. Client-side JAX-RS PUT Resource Method
110.6. JAX-RS DELETE Resource Method
110.6.1. Server-side JAX-RS DELETE Resource Method
110.6.2. Client-side JAX-RS DELETE Resource Method
110.7. Summary
XXII. JavaEE Security
Purpose
1. Goals
2. Objectives
111. JavaEE Security Access Control Points
112. EJB Security
112.1. Declarative EJB Access Control
112.2. Programmatic Security
112.3. Optional Role Mapping: ejb-jar.xml
112.4. EJB Security Setup: META-INF/jboss-ejb3.xml
112.5. Summary
113. JBoss/Wildfly Security
113.1. Security Realm
113.2. Security Realm References
113.3. Security Domain
113.3.1. "other" Security Domain
113.4. Wildfly Built-in Authentication and Authorization
113.5. Summary
114. EJB Security RMI Client
114.1. JBoss Remoting
114.1.1. jndi.properties
114.1.2. JBoss Remoting JNDI Name
114.1.3. JBoss Remoting Authentication with JNDI InitialContext
114.1.4. Example Changing Users with JBoss Remoting
114.1.5. Optional Fixed Credentials
114.2. EJBClient
114.2.1. jndi.properties
114.2.2. EJBClient JNDI Name
114.2.3. EJBClient Authentication with JNDI InitialContext
114.2.4. Example Changing Users with EJBClient
114.2.5. Optional Fixed Credentials
114.3. Security Sanity Check
114.3.1. whoAmI
114.3.2. isCallerInRole
114.3.3. Client Issues Security Query Calls
114.3.4. Example Access Violation
114.3.5. Example Access Granted
114.4. Summary
115. run-as
115.1. Default security-identity: use-caller-identity
115.2. Run-as security-identity: role-name
115.3. Run-as principal: identity
115.4. Invoking Protected EJB thru Run-as Proxy
115.5. Summary
116. JAX-RS Resource Security
116.1. Web Security Setup
116.1.1. Assign WAR security-domain: jboss-web.xml
116.1.2. Assign WAR auth-method: web.xml
116.2. JAX-RS Resource Class
116.2.1. JAX-RS Debug Methods
116.3. JAX-RS Client Authentication
116.3.1. Authorization Header
116.3.2. JAX-RS Client Authorization Filter
116.3.3. JAX-RS Client Authorization Filter Registration
116.3.4. Protect BASIC Credentials with HTTPS
116.4. Declarative Access Control
116.4.1. Two intermediate contexts defined to access Nested Pinger Resource
116.4.2. Same Nested Pinger Resource Exposed
116.4.3. Declarative Access Control Constraints
116.4.4. Nested Resource Called from Two URIs
116.5. Summary
117. Web Tier Access Control
117.1. Authentication
117.2. Security Constraints (web.xml)
117.3. FORM-based Login
117.4. BASIC Authentication
117.5. Summary
XXIII. JavaEE Interceptors
Purpose
1. Goals
2. Objectives
118. EJB Interceptors
118.1. Business Method Callbacks
118.1.1. Sample Business Method being Intercepted
118.1.2. Sample Business Method Interceptor
118.1.3. Sample Output
118.2. InvocationContext
118.2.1. InvocationContext Interface
118.3. Lifecycle Callbacks
118.3.1. Lifecycle Callbacks
118.4. Timer Callbacks
118.4.1. Timer Interceptor
118.5. Activating Interceptors
118.5.1. EJB Descriptor (ejb-jar.xml) Activation
118.5.2. @Interceptor(s) Annotation Activation
118.6. Summary
119. CDI Interceptors
119.1. @InterceptorBinding Annotation
119.2. Interceptor Class
119.3. CDI Bean Class
119.4. CDI Descriptor (beans.xml) Activation
119.5. Summary
120. Interceptor Example: Input Normalization and Validation
120.1. EJB Method Creates Contact
120.2. Non-normalized Caller Data
120.3. Partial Solution: Validation API
120.4. PrePersistCheck
120.5. PostNormalizedCheck
120.6. PreNormalizedCheck
120.7. Validator Interceptors
120.8. Normalization Interceptor
120.9. Bean Class
120.10. CDI Activation (bean.xml)
120.11. Example Output
120.12. Summary
XXIV. Java Messaging Service (JMS) 2.0 API
Purpose
1. Goals
2. Objectives
121. Messaging
121.1. MOM Architecture: Direct Connections
121.2. MOM Architecture: Unicast Connections
121.3. MOM Architecture: Broker-based
121.4. MOM Architecture: Multi-Broker
121.5. Messaging Key Concepts
121.6. Message Queuing
121.7. Publish/Subscribe
121.8. Request/Reply
121.9. Messaging and Transactions
121.10. Messaging Summary
122. JMS
122.1. JMS Background
122.2. Not Specified by JMS
122.3. JMS History
122.4. JMS Demonstrations
122.4.1. JMS Topic Demonstration: JMS Notifier
122.4.2. JMS Queue Demonstration: JMS Scheduler
122.5. Chapter Summary
123. JMS API
123.1. JMS API Classes
123.2. ConnectionFactory
123.3. JMSContext
123.4. Destinations
123.4.1. JNDI Lookup
123.4.2. Injected Destination
123.5. Send Message
123.6. Receive Message
123.6.1. Poll from JMSConsumer
123.6.2. JMSConsumer Listener
123.6.3. Message Driven Bean (MDB)
123.7. Message Acknowledgement
123.7.1. Message Acknowledgement: AUTO_ACKNOWLEDGE
123.7.2. Message Acknowledgement: DUPS_OK_ACKNOWLEDGE
123.7.3. Message Acknowledgement: CLIENT_ACKNOWLEDGE
123.7.4. Message Acknowledgement: SESSION_TRANSACTED
123.8. JMS API Summary
124. JMS Maven Aspects
124.1. JMS API Maven Aspects
124.2. JMS Testing Maven Aspects
C. Sources
XXV. Asynchronous Methods
Purpose
1. Goals
2. Objectives
125. Asynchronous Overview
125.1. Asynchronous Scenario
125.2. Potential Asynchronous Solution: Manually Spawn Thread
125.2.1. Threads Solution Analysis
125.3. Potential Asynchronous Solution: JMS
125.3.1. JMS Solution Details
125.3.2. JMS Solution Analysis
125.4. Potential Asynchronous Solution: Asynchronous Methods
125.4.1. Asynchronous Method Solution Details
125.4.2. Asynchronous Method Solution Analysis
125.5. Asynchronous Summary
126. Asynchronous Methods Example
126.1. Synchronous Example
126.1.1. Synchronous Example: Client
126.1.2. Synchronous Example: Worker
126.1.3. Synchronous Example: Results
126.2.
126.2.1. Asynchronous Example: Client
126.2.2. Asynchronous Example: Worker
126.2.3. Asynchronous Example: Results
126.3. Asynchronous Example Summary
D. Sources
XXVI. EJB Timers
Purpose
1. Goals
2. Objectives
127. EJB Timers Overview
127.1. EJB Timer History
127.2. EJB Timer Service
127.3. Single Action EJB Timers
127.4. Interval EJB Timers
127.5. Calendar EJB Timers
127.6. EJB Timer Summary
128. EJB Timers Example
128.1. Declarative Calendar
128.2. Programmatic Calendar
128.3. Chapter Summary
E. Sources

This comprehensive course explores a variety of modern Java frameworks and technologies that can be used for developing mission-critical complex enterprise applications. The emphasis is on use of the latest Java EE platform, its set of underlying specifications, designing and developing server-side application components. Students will learn thru having hands-on experience in building multi-tier distributed enterprise applications, comparing and using variety of Java EE design patterns, rich set of server-side components and technologies, and web-enabled by modern design practices and communication protocols. Students will learn to:

Other critical Java SE/EE infrastructure services will be discussed, including the Java Database Connectivity (JDBC) API, Java Naming and Directory Interface (JNDI), the Java Message Service (JMS), the Java Transaction (JTA) API, and Java EE security. Using modern development tools, commercial persistence providers, and application servers, students will design and implement several significant programming projects using the above-mentioned technologies and deploy them to a Java EE environment that they will manage.

The course is being actively updated to JavaEE 8 (JPA 2.2, EJB 3.2, JAX-RS 2.1, and JMS 2.0) and JDK 11 but a few order APIs and legacy examples remain for demonstration purposes. The development environment used in class is based on the Wildfly application server and is JDK 11 and JavaEE 8-compliant.

I am available at least 20min before class, breaks, and most times after class for extra discussion. I provide detailed answers to project and technical questions through the course newsgroup. You can get individual, non-technical questions answered via e-mail. It is very common for me to ask for a copy of your broken project so that I can provide more analysis and precise feedback. This is commonly transmitted either as an attachment to the e-mail or a link to an archive in GoogleDocs. Students needing further assistance are also welcome make other arrangements during the week or schedule a web meeting using Zoom Conferencing.




  • Projects must build using Maven 3. Ant will be used late in the semester to wrap any JavaSE commands with classpaths

  • All projects must be portable to build and deploy within grader and intructor environments. You may submit partial projects early to get portability feedback (not early content grading feedback).

  • Test Cases must be written using JUnit or another Java-based unit test framework that will run within a Maven surefire and failsafe environments.

  • Projects must be supplied with a README that points out how project meets requirements.

You should test your application prior to submission by

You will e-mail the projects to the grader and me with the following subject line

Your submission will include source zip (link to cloud storage is acceptable) and README (could be in source zip). You may also include pre-built artifacts if it does not add a huge size burden. The easiest way to do this is to zip up the project from the root after completing a build.

If you need to make a correction, the correction should have the following e-mail subject. The body should describe what you wish to revise.

Submission e-mails (mail to all):

  • Jim - jim.stafford@jhu.edu


Table 7.1. JavaEE 8 Specifications [2] [3]

Enterprise Application TechnologiesVersionWeb ProfileCourse Relevance
Enterprise JavaBeans (EJB)3.2EJB-litePrimary
Java Persistence API (JPA)2.2YY
Context and Dependency Injection (CDI)2.0YY
Dependency Injection for Java (JSR-330)1.0YY
Common Annotations for the Java Platform1.3YY
Java Transaction API (JTA)1.2YY
Java Message Service API (JMS)2.0 Y
Bean Validation2.0YY
Interceptors1.2YY
Managed Beans1.0Y 
JavaEE Connector Architecture1.7  
JavaMail1.6  
Batch Applications for the Java Platform1.0  
Concurrency Utilities for Java EE1.0  
Web Service TechnologiesVersionWeb ProfileCourse Relevance
Java API for RESTful Services (JAX-RS)2.1YY
Java Architecture for XML Binding (JAXB)2.2 Y
JSON-B1.0 Y
JSON-P1.1Y(maybe)
Java API for XML-based Web Services (JAX-WS)2.2  
Web Services Metadata2.1  
Java API for XML-based RPC (JAX-RPC)1.1  
Java API for XML Registries (JAXR)1.0  
Web Services for JavaEE1.4  
Web Socket1.1  
Web Related TechnologiesVersionWeb ProfileCourse Relevance
Servlet4.0YMinor
JavaServer Pages (JSP)2.3YMinor
JavaServer Faces (JSF)2.3Y 
Expression Language3.0Y 
JSTL1.2Y 
Management TechnologiesVersionWeb ProfileCourse Relevance
Java Authentication Service Provider Interface for Containers (JASPIC)1.1  
Java Authorization Service Provider Contract for Containers (JACC)1.5  
Java EE Management1.1  
Java EE Deployment1.2  
Debugging Support for Other Languages1.0Y 

[4]

1.2

"CORBA Integration"; Dec 1999

  • RMI over IIOP

1.3

"Local Interface EJBs"; Sep 2001

  • Connector API

  • EJB (2.x) local interfaces and new CMP model

1.4

"Web Service Enabled"; Nov 2003

  • SOAP Web Services

  • Deployment, Management, JAAS

5

"Ease of Development"; May 2006

  • Annotations

  • Better Defaults

  • EJB and Servlet Dependency Injection

  • JPA, StAX, JAX-WS

6

"More Ease of Development"; Dec 2009

  • Profiles; making some components optional

  • Flexible deployments; no EJB.jar or EAR requirement

  • No interface EJBs

  • Component and Dependency Injection

7

"Offloading Additional Common Infrastructure Tasks and More Ease of Development"; June 2013

  • Additional Web standards; Web Sockets and JSON

  • HTTP Client API within JAX-RS

  • Batch API

  • Concurrency Utilities

  • More consistency between CDI and component models

  • More inherent integrations with Validation API

  • Simplified JMS API

  • JPA Schema generation

8

"Focus on modern web applications";August 2017

  • Expanding support of JSON with JSON-B

  • HTTP/2 protocol

  • JAX-RS server-sent events

  • API for identity stores

  • ...

|-- pom.xml
|-- src
|   |-- main
|   `-- test
`-- target
    |-- classes
    |-- ex1-1.0-SNAPSHOT.jar
    |-- surefire-reports
    `-- test-classes
target
|-- classes
|   `-- myorg
|       `-- mypackage
|           `-- ex1
|               `-- App.class
|-- ex1-1.0-SNAPSHOT.jar
|-- log4j-out.txt
|-- surefire-reports
|   |-- myorg.mypackage.ex1.AppTest.txt
|   `-- TEST-myorg.mypackage.ex1.AppTest.xml
`-- test-classes
    |-- log4j.xml
    `-- myorg
        `-- mypackage
            `-- ex1
                `-- AppTest.class
classes

An exploded directory tree for built artifacts to be placed into the module archive

test-classes

An exploded directory tree for built artifacts used for test

*-reports

Results of unit and integration testing

{artifactId}-{version}.jar

Results of unit and integration testing

log4j-out.txt

An example output file of the build/tests written to the target directory


<?xml version="1.0"?>
<project 
    xmlns="http://maven.apache.org/POM/4.0.0" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>myorg.myproject</groupId>
    <artifactId>ex1</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>My First Simple Project</name>
        ...
    <properties>
        ...
    </properties>

    <dependencies>
        ...
    <dependencies>

    <build>
        <plugins>
            ...
        </plugins>
    </build>
</project>

  1. Maven will first look for a dependency artifact in the local repository

  2. Maven will then look for a dependency artifact in a remote repository like ibiblio

  3. Maven will search all known repositories until the artifact is found or all repositories have been checked

  4. Maven will install module artifacts into the local repository during the install phase of the build

    $ mvn install
        ...
    [INFO] --- maven-install-plugin:2.3.1:install (default-install) @ ex1 ---
    [INFO] Installing /home/jcstaff/proj/ejava-javaee/solutions/ex1/target/ex1-1.0-SNAPSHOT.jar to 
        /home/jcstaff/.m2/repository/myorg/myproject/ex1/1.0-SNAPSHOT/ex1-1.0-SNAPSHOT.jar
    [INFO] Installing /home/jcstaff/proj/ejava-javaee/solutions/ex1/pom.xml to 
        /home/jcstaff/.m2/repository/myorg/myproject/ex1/1.0-SNAPSHOT/ex1-1.0-SNAPSHOT.pom
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
                    

Use -U to force an update check

Maven will limit its search of a remote repository to expressed time limits. If it attempts and fails to locate an artifact from a remote repository it will not re-attempt to query that repository until the time limit expires. Sometimes the failure has nothing to do with the repository not having the artifact (e.g., wireless network down) and when you clear the error you can force Maven to retry before the timeout period expires by adding a -U to the command line.

Use the -o offline flag to eliminate repository checks

Many times Maven will be too aggressive searching for artifacts you already know are not of value to the current build. You can tell Maven to work in an offline mode to bypass the work involved. This is helpful when you are disconnected from the network but will fail if you are truly missing a required artifact. You can activate/deactivate the offline more consistently using an element in the settings.xml


<offline>false</offline>

Use dependency:go-offline prior to network/repo outage

Maven will lazily download required artifacts on-demand as different plugins are executed. If you know you will be separated from the network or a key repository will have an upcoming outage -- you can prepare your local environment by having Maven eagerly resolve and download all dependencies using the dependency:go-offline goal.


$ mvn dependency:go-offline
[INFO] Scanning for projects...

<dependencies>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
...
</dependencies>
GAV

Must provide its groupId, artifactId, and version for each dependency

scope

Defines when the dependency applies (default=compile)

type

Defines the artifact type of ther module (default=jar)

Value directly relates to the project packaging element of the dependency

|-- ejbsessionBankImpl
|       |-- ejbsessionBankImpl-3.0.2013.2-SNAPSHOT.jar
|-- ejbsessionBankEJB
|       |-- ejbsessionBankEJB-3.0.2013.2-SNAPSHOT.jar
|-- ejbsessionBankWAR
|       |-- ejbsessionBankWAR-3.0.2013.2-SNAPSHOT.war
|-- ejbsessionBankEAR
|       |-- ejbsessionBankEAR-3.0.2013.2-SNAPSHOT.ear
classifier

Defines a special type of artifact/custom from a module

`-- target
...
    |-- txHotelDAO-3.0.2013.2-SNAPSHOT.jar
    |-- txHotelDAO-3.0.2013.2-SNAPSHOT-sources.jar
    `-- txHotelDAO-3.0.2013.2-SNAPSHOT-tests.jar

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.7.0</version>
            <configuration>
                <source>${java.source.version}</source>
                <target>${java.target.version}</target>
            </configuration>
        </plugin>
         <!-- surefire.argLine is set in debugger profile -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.22</version>
            <configuration>
                <argLine>${surefire.argLine}</argLine>
            </configuration>
        </plugin>
        ...
  • New plugins can be added, but will need to be wired into a specific lifecycle phase of the build.

    
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-failsafe-plugin</artifactId>
        <version>${maven-failsafe-plugin.version}</version>
        <configuration>
            <argLine>${surefire.argLine}</argLine>
        </configuration>
        <executions>
            <execution> <!-- run the tests here -->
               <id>integration-tests</id>
                   <phase>integration-test</phase>
               <goals>
                   <goal>integration-test</goal>
               </goals>
            </execution>
            <execution> <!--  delay failures to after undeploy -->
                <id>verify</id>
                <phase>verify</phase>
                <goals>
                    <goal>verify</goal>
                </goals>
            </execution>
        </executions>
    </plugin>

    The above example shows the failsafe plugin being configured to share some properties with surefire and have specific goals wired into the integration-test and verify lifecycle phases.

$ mvn (phase) (plugin:goal) -D(system-property) -P(profile)
        
  • phase

    clean

    Remove contents of target tree

    test

    Build the src/main and src/test tree and execute the unit tests as defined by the surefire plugin

    pre-integration

    Perform unit tests and perform any steps required to ready for integration test

    integration-test

    Perform integration tests as defined by the failsafe plugin

    post-integration-test

    Tear down anything required to perform integration tests

    verify

    Evaluate results of integration tests

    install

    Install module artifacts into local repository

  • (plugin:goal)

    • Manually trigger a plugin goal without a specific build lifecycle

    • ex. jetty:run, dependency:tree, or dependency:help

  • -D(system-property)

    • Manually define a system property key or key and value to be used as a property in the build

    • Must configure surefire/failsafe to promote these as properties Junit -- these are system properties of the maven build and not of the JVM tasks kicked off by the maven build.

  • -P(profile)

    • Manually trigger build configurations latched by a profile

    • e.g., which DB driver to use

  • -P!(profile)

    • Manually turn off a profile

    • e.g., turn off a profile activated by a rule or settings.xml

    Use -P\!(profile) with bash shell

    The bash shell requires the bang ("!") character to be escaped. Use -P\!(profile) when using the bash shell.

  • Provide groups of conditional configuration

  • Adds flexibility to build

  • Flexibility can sometimes add confusion for both developers and build tools -- be careful

  • Maven modules boundaries are highly influenced by the re-usable artifacts that must be produced

    • If you have many .jars, you likely have many maven modules

    • If you have few but large .jars, you likely have few but large maven modules

    • If you require different types of artifacts, you likely have different maven modules

  • Maven can be used in multi-module configuration

  • Many maven modules share the same configuration needs -- could lead to duplication

  • POM Inheritance helps mitigate duplication issues

  • Defines what is possible without actively imposing the dependency or plugin defined

  • Placed in a parent of multi-module project


Set program breakpoints where problems have been identified

Table of Contents

I. Project 1
16. eSport Project 1 Description
16.1. Purpose
16.1.1. Goals
16.1.2. Objectives
16.2. Business Description
16.3. Technical Overview
16.4. Assembly Overview
17. Project 1 Technical Details
17.1. Database Schema
17.1.1. eLeague Candidate Database Schema
17.1.2. eClub Candidate Database Schema
17.2. Business Objects
17.2.1. eLeague Candidate Business Objects
17.2.2. eClub Candidate Business Objects
17.3. Validation API
17.4. eLeague Ingest
17.5. Data Access Objects (DAOs)
17.5.1. eLeague DAOs
17.5.2. eClub DAOs
17.6. SQL Tuning (Indexes)
17.7. Business Logic
17.7.1. eLeague Business Logic
17.7.2. eClub Business Logic
18. Project 1 Getting Started
19. Project 1 Testing
20. Project 1 Grading
II. Project 2
21. eSport Project 2 Description
21.1. Purpose
21.1.1. Goals
21.1.2. Objectives
21.2. Technical Overview
22. Project 2 Technical Details
22.1. Data Tier and Business Logic Support
22.2. Module Layout
22.2.1. eLeague Modules
22.2.2. eClub Module(s)
22.3. EJB Tier
22.3.1. EJB Base Tier
22.3.2. EJB Business
22.3.3. eClub EJB Tier
22.3.4. Remote Interfaces
22.3.5. eClub/eLeague Integration
22.4. Transactions
22.5. Web UI
22.5.1. eLeague Web UI
22.5.2. eClub WEB UI Tier
23. Project 2 Getting Started
24. Project 2 Testing
25. Project 2 Grading
III. Project 3
26. eSport Project 3 Description
26.1. Purpose
26.1.1. Goals
26.1.2. Objectives
26.2. Technical Overview
27. Project 3 Technical Details
27.1. Data Tier, Business Logic, and EJB/WAR Support
27.2. eLeague Access Restrictions
27.3. eClub Access Restrictions
27.4. Remote Client Authentication
27.5. Remote Test Authentication
27.6. eLeague Web-UI Access Restrictions
27.7. Interceptors and Validators
27.8. eSport JMS
27.8.1. eLeague JMS Publisher
27.8.2. eClub MDB Subscriber
27.9. eLeague EJBTimer
28. Project 3 Testing
29. Project 3 Grading

eLeague is planning an on-line league site that will allow league coordinators to organize teams into divisions, schedule contests, and track scores. At the same time, eClub is planning an on-line club management site that will allow clubs to organize players, coaches, and managers into teams. eClub is designing their software such that it can be used with eLeague.

For eLeague, the coordinator starts by adding clubs and their primary point of contact. Venues and Teams are added to the league by club coordinators. Contacts (e.g., coach(s), manager(s), etc.) are added for teams. Prior to the start of the season, the league will create a new set of divisions and assign teams to the appropriate divisions for that season. Divisions are arranged by group (e.g., U11, Bantam, Masters, etc.) and level (e.g., AA, A, 1, 2, etc.). The league will then schedule contests for each division, matching a home and away team with a venue from the home team's club. For simplicity, we'll assume that the venue is exclusively available for scheduling by the league, but the league still must manage scheduling conflicts for the venue contests within the league. Scores are added for the home and away team after each contest. A team schedule and division standings will be generated from the information.

For eClub, individuals register with the site, supplying basic information. For simplicity, we'll keep eClub to minimal information. The individuals can be players or parents and/or coaches. There is additional information tracked for players and coaches. Players will need a position and jersey number. Coaches need a certification number. Parents/guardians will need no further information. After tryouts, the club will create a set of teams and assign a head coach, a manager, and players. The team and team points of contact will be used to form a registration with eLeague. From the club site, a team can get roster information, game schedules, and division standings. The schedules and division standings are obtained from eLeague and and are not duplicated within eClub to make sure they are up to date.

Both eLeague and eClub have come to you to develop the initial phase of their applications. You are tasked with implementing a low-cost prototype, based on current standards, to automated much of this activity. At this point in the project we are primarily looking to build the data access tiers for both eLeague and eClub (two separate systems). We will also add a minor amount of business logic to coordinate the data access between the individual data access objects.

For eLeague, the system is initialized with a starting set of information pertaining to clubs (league's external view), contacts, venues, and a season defined with divisions and teams. New information can be added and updated.

For eClub, the system is initialized with an empty database that is designed to hold internal club information for teams, coaches, and players. The teams will be assigned a division within the league and individuals associated with the team will be interested in seeing scores and standings relative to their team.

The work done during this project focuses on the business objects (BOs), the data access objects (DAOs) of the data access tier, some initial business logic interfaces (BL), and business logic implementations (BLImpl). The DAOs will be based on the Java Persistence API (JPA). How you partition the implementations of your projects is up to you. A candidate starting point is provided at the end of this project description.

The business objects encapsulate the core business data and rules of the application. You will design the business objects and then map them to the database. You are required to implement CRUD (Create, Read, Update, and Delete) capability with JPA but you only need to implement the CRUD methods that are necessary to complete the provided end-to-end scenario. You will be required to encapsulate all object to relational (O/R) mapping within the DAOs, descriptor files, and/or class metadata annotations. You will be given a set of test data to initially populate your applications and be the source of data for the ingest requirement. To use the data, you will ingest using a parser supplied by the instructor. There is a sample thread in the projects/eSport/eLeague directory that shows how to use the parser as well as other aspects technology within the project.

The database will have a functional schema and indexes to provide query performance. Business objects will be validated in the database using schema constraints and within the JVM using the Validation API.

The business logic will provide a set of classes with concise methods that map easily to the provided end-to-end scenario. The business logic will ensure proper use of the overall application, delegating some business logic functionality to the business objects and full O/R responsibility to the DAOs. eLeague will have an ingest requirement as well as the requirement to manipulate and add to what was ingested. eClub will start fresh and obtain all data from users and coordinate with eLeague. However, for project 1, eClub will be unable to fully implement data exchange with eLeague because remote interfaces will not be implemented until project2. Some of that must be stubbed at this point. You are only required to implement enough business logic methods that it takes to implement the end-to-end scenarios specified later within this specification.

The test acceptance for the first project will be the unit tests and an integration test that takes the business logic, data access tier, and business objects through a provided end-to-end scenario that will be consistent during the semester. You are required to supply the following tests:

  • A unit test that implements the steps of the provided eLeague end-to-end scenario

  • A unit test that implements the steps of the provided eClub end-to-end scenario

  • At least one unit test per architectural layer (BO, DAO, BL) that demonstrates your ability to test at that level.

Focus on meeting the end-to-end scenario(s)!

The amount of work that you can implement within the bounds of this project spec could be endless if you attempted to account for everything and anything. It is very important that you limit your work to only what is necessary to implement (and test) the functionality required for the end-to-end scenario. That means you may have entities that are designed to be created but never modified while having other entities that go through a full lifecycle. When deciding to add or skip certain capabilities -- always ask yourself "does the business logic need this behavior to implement the end-to-end scenario".

Since the work is for separate applications, we will need to establish two separate application projects for this work; eLeague and eClub. The development can physically share resources (e.g., same database and application server), but should be easily separated. It is suggested that you form a root for the overall eSport work to coordinate integration testing, and then group the lower-level work under two mid-level projects; eLeague and eClub. See some suggested project layouts at the bottom of this specification. A sample set of projects that implement a thin eLeague thread has been made available. Please ignore the sibling eSportData project when using the example to craft your source tree. You will depend on projects within the sibling data tree -- not re-implement them and not copy them. See the Getting Started section towards the end of this specification for a more detailed sample project layout.

Inherit from course dependency/pom.xml or create own

You will likely copy significant portions of the thin thread example and other class examples into your project. Be aware that the thin thread and other example pom.xml files inherit from the class root project that provide dependencyManagement and pluginManagement duties. You will either need to also inherit from the course dependencies/pom.xml or compensate by re-defining the management sections in the root pom.xml of your project.

Since students in the class will be producing parallel implementations for the applications and submitting them for evaluation, it is asked that you come up with unique names for your artifacts. This could be done by replacing the "e" in eSport, eLeague, and eClub with a unique name that corresponds to your newsgroup or college login (ex. jcsMarket, jcsSales, jcsBidbot). You should also use this same pattern for java packages (ex. jcs.sales, jcs.bitbot), DB tables (e.g., JCS_), etc.

There should be no use of System.out.println() in the code and all implementations must use a logging API with log4j as the logging provider. You may leave debug in your code, but this should be able to be turned on/off with the proper logging priority changes in the log4j.xml configuration.

Note

There are a lot of technical details presented with this specification. This is done to provide clarity and a starting point for design discussion. However, it is not done so to specify specific class or method naming, project layout, or order of work. You are free to make many technical adjustments to the ideas presented. Implementation of the end-to-end scenario is the primary requirement.

Tip

This project provides an obvious opportunity to use compound business primary keys and the documentation below depicts some of the ramifications in doing so. Synthetic, single value primary keys are permitted in your solution without penalty. Single value primary keys are much easier and should be the first choice if you have limited experience and limited time to work with the more complex primary key and foreign key mechanism.

Design 2 sets of database schema that account for the following information. Although we will deploy the 2 database schemas to the same database for the project, they should be designed to be independently deployed to separate databases. eLeague and eClub are two independent applications. This will primarily affect your attempt re-use tables or to make primary key assumptions between the two.


Design a table to hold contest information. Contents are scheduled at venues for a home and away team. They are for a specific date, start and end time, and should never overlap with another contest for the same venue. Contests are always for teams in the same division.




Design an initial business interface and business logic for the applications. The core O/R mapping work will be done by the DAOs. However, it is the ultimate responsibility of these business logic implementations that either it or the business objects enforce the business rules of the application. The DAOs only perform O/R mapping and do not enforce such things as business ordering. The business logic is assumed to work within the context of a single, externally controlled transaction. Do not attempt to control the transaction of the EntityManager within these objects or you will NOT be portable to the EJB tier (without extra work). You need only implement the behavior required to implement the end-to-end use case listed in the testing section. Some of the anticipated methods are listed below.


LeagueMgmt/LeagueMgmtImpl

Encapsulate the actions required to create Clubs and manage Seasons and Divisions.

  1. addSeason - league coordinators will need to create a new season for the league. The league cannot have 2 or more concurrent seasons and will know the most current season, if any.

  2. addDivision - league coordinators will need to add divisions for a season. Club coordinators will be assigning their teams to the league's divisions for a season.

  3. getDivisions - users will need to know which divisions exist for the most current season. There will also need to be the capability to get historical information for previous seasons.

  4. createClub - league coordinators will need to create new clubs for the league. Once the club has been created, ClubMgmt can be used to perform management of the club details.

  5. assignTeamDivision - clubs will have to assign their teams to divisions after the league has created them for an upcoming season.

  6. getDivisionStandings - users will want to get divisional standings information for the most current season. This would normally consist of an ordered list of teams based on wins (you can ignore ties) and their win, loss, tie totals. Note that this may involve the creation of a transient object that is calculated on-demand by business objects and may not be persisted in the database. There will also need to be the capability to get historical information for previous seasons.

  7. getTeam - regular users and team/club officials will need to obtain team points of contact, schedule, and contest results for the most current season. There will also need to be the capability to get historical information for previous seasons.

ClubMgmt/ClubMgmtImpl

Encapsulate the actions required to manage Venues and Teams.

  1. addVenue - you will need to create one or more venues for the league to schedule contests for your club. The only thing that the club manages for the venue is keeping directional information up to date.

  2. addTeam - you will need to create teams that play for the club. Note that clubs span multiple seasons/divisions and may even sit out a season. Therefore, teams are usually added first and then later assigned to divisions.

  3. updateTeamContact - clubs and teams will need to update points of contact information.

ContestMgmt/ContestMgmtImpl

Encapsulate the actions required to schedule and manage contests between teams within the league.

  1. scheduleSeason - league officials will need to create a home and away schedule for each team in the division with each team playing each other at least once and possibly more (for smaller divisions), up to a specified number (default to 10) number of games. Your scheduling can be extremely simple as long as it does not schedule conflicting contests. Ideally a team would not play more than once on a single day and you might want to limit contests to a specific set of days of the week. However, the fact that you have placed the scheduling within the correct architectural area is the key point. How simple you make the algorithm is totally up to you and will not impact your grade. You may decide how much scheduling gets done by the business object(s) and how much gets done by the business logic.

  2. reportScore - division coordinators will need to be able to report the results of a contest.

LeagueTestUtil/LeagueTestUtilImpl

A useful tool during testing that encapsulates how to get the application back into a known state prior to running a test or to inspect values not normally exposed through the normal business interfaces.

  1. resetAll - sanely take the state of the system down to a coldstart.

  2. populate - you might want the database populated with a known state prior to running a test. This may delegate to the LeagueIngestor.

  3. get/doXXX - methods that are unsafe for the actual business logic, but are needed for development and test.

LeagueIngestor

The Ingestor written as a part of a separate requirement is also logically considered part of this tier.

  1. ingest - point an externally provided parser at a set of test data and use the DAOs to populate the system to a known state.


Note

Keep in mind that this project has two notions of a club. From the league's perpective it manages schedules, scores, and high level contact information for teams within a club. From the club's perspective, there is the need to manage coaches, players, and parents associated with the team. For that reason -- I am calling the club an organization here to avoid some confusion.

MemberMgmt/MemberMgmtImpl

Encapsulates the actions required to manage individuals registering with the club.

  1. createParent - parents will need to be able to register with the club.

  2. createPlayer - parents will need the ability to register their minors as players. All players will need at least one parent.

  3. addCoachRole - coaches will need to be able to register with the club. It is common that a coach is also a parent.

  4. getIndividual, Player, and Coach - created individuals will need to be retrieved.

OrgMgmt/OrgMgmtImpl

Encapsulate the actions required to create teams and get team information. Some of this information will come from contacting eLeague in future projects.

  1. createTeam - club officials will need to create teams.

  2. assignPlayers - club officials will need to assign players to teams

  3. assignCoach - club officials will need the ability to assign a coach to a team

  4. assignManager - club officials will need to ability to assign a manager to a team

  5. getTeamRoster - users assigned to a team will need the ability to get the roster for team. Rosters contain coach, manager, player, and parent information.

  6. getTeamSchedule - users will need the ability to get the schedule for a team for the most current season. This information will come from eLeague in a future project. Team schedules should have contest dates, venue, and scores.

ClubTestUtil/ClubTestUtilImpl

A useful tool during testing that encapsulates how to get the application back into a known state prior to running a test or to inspect values not normally exposed through the normal business interfaces.

  1. resetAll - sanely take the state of the system down to a coldstart.

  2. populate - it may be helpful to return the database to a known populated state between tests.

  3. get/doXXX - methods that are unsafe for the actual business logic, but are needed for development and test.

The following sketch of two directory structures can be used as a starting point for your overall application. The first is a simplified project layout that collapses the number of sub-projects into a single "Impl" project. It is suggested that you use this layout if you are new to maven and want the simplest configuration possible. The second is a more robust layout and is closer to a multi-developer environment. Use the later structure if you want to better simulate a work environment where the work of multiple developers needs clearer separation.



Project 1 persistence.xml definitions are for test

Since the work of project 1 will be deployed to the application server in follow-on projects, the persistence.xml definitions created in project 1 should be considered strictly for test and should be placed in the "src/test" tree to prevent it from being deployed to the application server. It is also suggested that if your persistence unit is called "X", the persistence unit name for project 1 in the "src/test" tree be called "X-test" to avoid confusion of what is intended to be used.

  1. Provide a JUnit test for your business objects (BOs) that test the manipulation of data. An example test might be to try forming a contest between teams in separate divisions or schedule a contest that conflicts with a team and/or venue schedule. These tests should be packaged with the BOs. There should be a separate project and test for both eLeague and eClub. It is anticipated that these tests will be a minimal demonstration of understanding.

  2. Provide a JUnit test for your eLeague and eClub JPA DAOs. This should test the implementation for the required CRUD operations for each type of object. It is understood that some of the operations will be handled by cascades, so you might not have a set of methods handling each type of business object. This test should be packaged with the DAOs.

  3. Provide a JUnit test for your business logic to test the basic functionality of your business logic design, including ingest. The ingestor test should be able to reference a known data file and ingest records into the database using the DAOs. These tests should be packaged with the business logic implementation.

  4. Provide a set of JUnit test programs to verify the following end-to-end scenario in eLeague. This test should be implemented as a JUnit test and packaged with the business logic implementation.

  5. Provide a set of JUnit test programs to verify the following end-to-end scenario in eClub. This test should be implemented as a JUnit test and packaged with the business logic implementation. Note that it is anticipated that you may only have time to create one team, with one player, manager, parent, and coach for the club.

Your project will be graded on completeness and quality of product. In order for you to receive full credit in each area, it must be a) complete, b) done well, and c) tested. The breakdown of grading will be as follows:

The following table contains examples of where projects have lost points in the past. Of course, each project submitted can introduce new issues or different severity levels of the same issues. Do not treat this as a complete list.

Table 20.1. Sample Lost Points

README   
Not provided10  
Projects cleanly builds with Maven   
groupIds, schema, java packaging, etc do not have a project-specific name mangler2  
Avoidable build errors2  
Key areas not building10  
Poluting project with do-nothing tests or tests that are not tests2  
Managing multiple copies of the same source file.1  
Large commented out blocks of code.2-5  
Non-portable references to external resources (e.g., ingested file)2  
Managed Schema   
Database columns not well defined and constrained (e.g., FKs, non-null, max size)1  
Improper use of DATE, TIME, and/or TIMESTAMP 1  
No definition of any indexes2  
Business Objects   
Improper/no TemporalType declaration for Dates1  
PK classes (if exist) did not implement required constructs1  
Use of Validation API   
Missing any declaration of validation criteria in BOs3  
Not explicitly performing validation2  
JPA DAO and JPA O/R Mapping   
Missing key relationships5  
Managing transactions in DAO1  
Relationships should not be modeled as entities2  
Using provider-specific mechanisms over JPA-provided technique1  
Not honoring dependencies. Attempt to delete entities with incoming relationships/FKs.1  
Walking the object tree functionally works but using a JPA-QL query would be much cleaner and more efficient. 2  
Ingest   
Re-used IDs from ingested XML file and did not generate IDs local to project2  
Did not include a unit test that *verified* injest worked1  
Business Logic   
Implemented stateful logic (Wrong!)5  
No separate testing of business logic. Relied too much on end-to-end as unit test.1  
Managing transactions in business logic1  
Did not implement all methods required for end-to-end1  
Not exhibiting good command of what it means for an entity to be managed and what you do and don't need to do in that state 1  
Persisting BOs from other application. Not preserving separation between applications.1  
End-to-end Integration Test   
Poluted, hard to follow, too much extra stuff2  
Missing resetAll and populate at start of scenarios5  
Missing step X1  

The project will continue along two parallel paths; eLeague and eClub. However, this time we will add several new Maven project types; EJB, EAR, WAR, Client (library) and (remote) Test. We will add all new project types to eLeague. We will add (or migrate to) the WAR type to eClub. These new modules will become siblings to your existing eLeague Impl -or- BO, DAO, and BLImpl leaf modules. For eClub, you have the option of:

The new projects will depend on your legacy work. The remote interface of the EJBs will also require specific design of what gets externalized to the client. The remote clients do not share the same address space we had in Project 1 and cannot lazily access relationships. We also cannot afford to serialize the entire contents of a database full of related information. Data Transfer Objects (DTOs) will be part of the EJB remote interface design.

You have finished a significant amount of eLeague during Project 1; the O/R mapping and core business logic of a non-trivial business model. You will now host the data access tier and business logic within EJB component(s). These EJB component(s) will directly supply the EntityManager, control transaction boundaries, supply a local interface, and other features (like security access control) used in the follow-on project. These EJB component(s) may directly implement an RMI remote and JAX-RS web interfaces. However, we will focus the bulk of our remote interfaces on modern WEB-based, REST-like, JAX-RS remote interfaces. Most of the remote access will be provided from the WEB tier using JAX-RS and a browser-based Web UI. You are to deploy eLeague using an EAR and eClub using a WAR.

The business logic for eClub will require additional work as well. With a remote interface for eLeague in hand and the ability to either simulate or operate with a live instance, we can now complete the rest of the the business logic that will also be hosted within an EJB tier to keep from significantly impacting any tests from project 1. The testing of eClub can always assume that a local instance of eLeague can be deployed locally at any time.

You may develop your Web UI in an alternate environment. However, it must be deployed as part of the application that runs within JBoss/Wildfly.

Tip

It may be helpful to browse the grading criteria at the end of this assignment before reading through the specifications. This will give you a better idea of what has to be completed to achieve a passing grade. A perfect score for the assignment will require that the entire specification be implemented. A passing score will require the demonstration of understanding of specific concepts covered. You can conceivably do well in the grading if you tackle each of the technical areas at least once. Example: Don't focus so much on completing the steps of the end-to-end that you ignore the transaction and Web UI aspects of the assignment. Of course, the opposite is true. Don't ignore the end-to-end scenario and demonstrate functionality in a vacuum. The end-to-end scenario should provide plenty of chances to demonstrate required technical parts of this assignment.

Create a Maven module layout that will support the additional architecture layers of the assignment. It is intended that eLeague be implemented with a multi-module approach (to mimic a complex application) and eClub be implemented with very few (possibly one) modules (to mimic a simplistic application).

To implement the full project 2 assignment, you will eventually need your existing implementation module(s) and new modules to implement a remote client, EJB, WEB, EAR, and test concepts.


League Implementation Module(s)

These are your existing implementation module(s) from project 1. They are intended to remain mostly unchanged when working on project 2. They represent your core data and business logic and business decisions in getting this far. We want to keep project 2 focused on the technical aspects of deploying this capabilty to the server-side and leave the detailed business decisions behind.

LeagueClient Module

This new module is going to make available to external clients of eLeague. This will contain data transfer objects (DTOs) and JAX-RS client code to make implementing the client calls easier.

LeagueEJB Module

This new module will host the EJB components and define a META-INF/persistence.xml that is appropriate for use on the server-side. Most of the implementation of these components will be based on the project 1 implementation.

LeagueWAR Module

This new module will host the JAX-RS interface for eLeague and any Web UI that is developed.

LeagueEAR Module

This new module will package the EJB(s), WAR(s), and necessary JAR(s) in a single deployment to the server. It will define identity information for the EJB and WAR that will impact base JNDI and URI name paths generated to access the hosted components. It will also be a location where we exclude unecessary dependency JARs to trim the EAR of unecessary artifacts.

League Remote Test Module

This new module will deploy the EAR to the server and execute integration tests that use remote interfaces to verify eLeague functionality.

Create an EJB tier for both applications to host your server-side, transactional, and persistence logic. Security will be added in the next project.

The EJB tiers must host your persistence unit and supply a set of EJB components that will provide access to your server-side logic.

Remote interface to the two applications will be demonstrated using two technologies: Java Remote Method Invocation (RMI) and HTTP/REST-like (using JAX-RS). Any data transferred using either will be part of the Data Transfer Objects (DTOs). DTOs used with RMI will need to be Java Serializable. DTOs used with JAX-RS will need to be marshaled as XML or JSON.

DTOs and and any classes written to support the remote interface will be placed in a JAR module that can be easily used by clients. Since eClub does not have any clients and we want to limit its complexity -- only eLeague will require a separate client module to host these classes.

The primary focus for implementing remote interfaces will be HTTP/REST-like interfaces using JAX-RS (server and client APIs). The EJBs already provide an injectable component to complete the desired functionality. It is intended that you add a set of one or more classes that expose access to this functionality using JAX-RS.

A candidate set of resource collection URIs is shown below. Add a {resourceId} to address a specific resource from the collection. Add an additional property name or query parameter to address a specific property of the resource. Use the appropriate verb (GET, POST, PUT, DELETE), query parameters, and payloads to make the intended requests. Make use of appropriate status codes (e.g, 200, 201, 400, 404, and 500) to communicate the results.

There is no requirement that you specifically make use of the specific URIs listed or break root level resources into separate JAX-RS classes. It is your choice. It is suggested that all URIs be exposed under the "/api" root URI for the targeted application context. The details here are provided as concrete suggestions. In the end, the primary requirement for remote interfaces is:

Create or identify classes that will express information passed between client and server within the remote interfaces.

XML marshaling should be implemented with JAXB. JSON marshalling can be implemented with JSONB, but it is recommended to use jackson2 since JSONB is not supported in Wildfly until we reach JavaEE 8 compliance. Client-side JSONB and server-side jackson will work 95% of the time -- but there are some differences.

<!-- JSON wiring for RESTEasy JAX-RS provider (javaee7)-->
<dependency>
  <groupId>org.jboss.resteasy</groupId>
  <artifactId>resteasy-jackson2-provider</artifactId>
</dependency>

The following sketch of directory structure can be used as a starting point for your overall application. It assumes you already have either a consolidated Impl -or- a BO, DAO, and BLImpl set of projects in place from Project 1. You will *not* have the option of consolidating the EJB, WAR, EAR, Client and remote Test into a single project for eLeague. They must be implemented as separate projects with proper dependencies between them declared. You *do* have the option of merging all modules into a single WAR module for eClub or many of the alternatives as long as eClub is deployed as a WAR.

Figure 23.1. Candidate Source Module Structure

`-- eSport
    |-- eLeague
    |   |-- eLeague (module(s) from project1)
    |   |-- eLeagueEJB
    |   |   |-- pom.xml
    |   |   `-- src
    |   |       `-- main
    |   |           |-- java
    |   |           |   `-- eleague
    |   |           |       `-- ejb
    |   |           `-- resources
    |   |               `-- META-INF
    |   |                   |-- beans.xml
    |   |                   |-- persistence.xml
    |   |                   |-- (ejb-jar.xml)
    |   |                   `-- (jboss-ejb3.xml)
    |   |-- eLeagueWAR
    |   |   |-- pom.xml
    |   |   `-- src
    |   |       `-- main
    |   |           |-- java
    |   |           |   `-- eleague
    |   |           |       |-- rs
    |   |           |       `-- web
    |   |           |-- resources
    |   |           `-- webapp
    |   |               |-- WEB-INF
    |   |               |   |-- beans.xml
    |   |               |   |-- web.xml
    |   |               |   |-- (jboss-web.xml)
    |   |               |   `-- (content)
    |   |               `-- index.jsp
    |   |-- eLeagueClient
    |   |   |-- pom.xml
    |   |   `-- src
    |   |       |-- main
    |   |       |   `-- java
    |   |       |       `-- eleague
    |   |       |           |-- dto
    |   |       |           `-- client
    |   |       `-- test
    |   |           `-- java
    |   |               `-- eleague
    |   |                   `-- dto
    |   |-- eLeagueEAR
    |   |   `-- pom.xml
    |   |-- eLeagueTest
    |   |   |-- pom.xml
    |   |   `-- src
    |   |       `-- test
    |   |           |-- java
    |   |           |   `-- eleague
    |   |           |       |-- rmi
    |   |           |       `-- rs
    |   |           `-- resources
    |   |               |-- jndi.properties
    |   |               `-- log4j.xml
    |   `-- pom.xml
    |-- eClub (many options)
    |   |-- (eClubEJB -- could be merged with or separate from WAR)
    |   |-- eClubWAR
    |   |   |-- pom.xml
    |   |   `-- src
    |   |       +-- main
    |   |       |   |-- java
    |   |       |   |   `-- eclub
    |   |       |   |       +-- bo
    |   |       |   |       +-- bl (or ejb)
    |   |       |   |       +-- client
    |   |       |   |       +-- rs
    |   |       |   |       `-- ui
    |   |       |   |-- resources
    |   |       |   |   `-- META-INF
    |   |       |   |       |-- persistence.xml
    |   |       |   |       `-- (ejb-jar.xml)
    |   |       |   `-- webapp
    |   |       |       |-- WEB-INF
    |   |       |       |   |-- beans.xml
    |   |       |       |   |-- web.xml
    |   |       |       |   |-- (jboss-web.xml)
    |   |       |       |   |-- (jboss-ejb3.xml)
    |   |       |       |   `-- content
    |   |       |       `-- index.jsp
    |   |       `-- test
    |   |           |-- java
    |   |           |   `-- eclub
    |   |           |       |-- ...
    |   |           |       |-- bl
    |   |           |       |   `-- eClubEndToEndTest.java
    |   |           |       `-- client (*IT.java)
    |   |           |           `-- eSportEndToEndIT.java
    |   |           `-- resources
    |   |               `-- jndi.properties
    |   `-- pom.xml
    `-- pom.xml

  1. Provide a JUnit integration test that verifies the eLeagueEAR can be deployed to the server, including successful deployment of the persistence unit and contained EJBs.

  2. Provide a JUnit unit test that verifies successul marshaling and demarshaling of an eLeague DTO class using Java Serialization. You may create a DTO class specifically for this test if none of your DTOs are used in your RMI interface.

  3. Provide a JUnit integration test that verifies a successful JNDI lookup and RMI communication with an eLeagueEJB.

  4. Provide a JUnit unit test that verifies successul marshaling and demarshaling of an eLeague DTO class to/from XML or JSON.

  5. Provide a JUnit integration test that verifies a successful HTTP communication with an eLeagueEJB.

  6. Create a single JUnit integration test case (e.g., eSportEndToEndIT) with a single @Test method that walks the two applications through the following end-to-end scenario. You may create a separate IT test in eLeague while developing the eLeague portions but the final end-to-end scenario should be placed in eClub since eClub depends on eLeague.

Your project will be graded primarily on the demonstration ability to implement concepts covered in this portion of the course. A perfect score will need to implement the full end-to-end scenario. A passing score will need to make sure to cover the grading criteria outlined below within the partial end-to-end implemented.

The following table contains examples of where projects have lost points in the past. Of course, each project submitted can introduce new issues or different severity levels of the same issues. Do not treat this as a complete list.

Table 25.1. Sample Lost Points

README   
Not provided10  
README did not indicate where X was located and it was not obvious even after ...2  
The WebUI is hard to navigate (fine) but README offered no assistance5  
Projects cleanly builds with Maven   
One of your IT tests assume the DB is setup correctly prior to running and that would only be true of we were running a common server database instance across our unit and IT tests. 2  
Testing does not produce consistent results - out of four runs of mvn install the process failed twice and succeeded twice. 2  
Build was not portable. I had to make changes. Sending me a copy of this beforehand would have caught this. 2  
Project 1 functionality   
DB schema is under-constrained1  
Project 1 end-to-end scenarios no longer exist/run2  
EJB Tier, remote interface, and EAR deployment   
Attempting to set the state of a @Stateless EJB. What do you think will happen to that state when you get a different bean instance the next time you call? 5  
With auto-create DDL turned on (hibernate.hbm2ddl.auto" value="create") your application deletes all data when redeployed. Deleting all data should be restricted to only an explicit call to resetAll(). 5  
Not separating a single call into separate transactions to drop and then create schema. First action may fail when not exist. 2  
You have methods that will only work in a @Local interface as part of your @Remote interface 2  
Reusing business logic classes instead if EJB components - repeating the same work of the reusable EJB component in each EJB that needs to reuse the functionality. Inject @Local interfaces. Do not repeat instantiation and setup of identical business logic/DAO classes. 2  
Injest was not implemented within a deployed EJB. It was mistakely implemented the same as project 1 in a JUnit client. 5  
You have not integrated the two applications at the EJB remote interface level. The second application does not make a single remote call to the first application. All interaction is occcuring outside of the server-side and results passed in. 5  
WAR/EJB deployment   
You are deploying EJBs from the first application within your second application's WAR. Look at your built/deployed artifact and correct dependencies. 5  
Web UI integration   
UI does not provide a path to satisfy a step in the end-to-end scenario (that the IT test shows works). 2  
EJB/Impl functionality called by UI fails and is not exercised by end-to-end IT test 2  
This is a real bust when tested on the deployment platform. This should have been easily noticed. Pressing resetAll() results in the following error displayed. 5  
Transactions   
Transaction scope not explicitly defined for EJB. You are accepting container defaults. 1  
No attempt to demonstrate transaction rollback 10  
Scenario shows business logic check but not a rollback of actions (store) to a transactional resource (DB). The requirement called for you to persist something all the way to the database - such that if you stopped in a breakpoint you would see the data - and then have the data thrown away due to a rollback. 7  
End-to-end Integration Test   
Poluted, hard to follow, too much extra stuff2  
Missing resetAll and populate at start of scenarios5  
Missing step X1  
No. I want different functionality in this step1  

The project will build on the core implementation from Projects 1 and 2. We will mostly extend existing projects with security and asynchronous logic.

JavaEE defines authentication and authorization to be independent of the overall API and capability. JBoss and other application servers provide default mechanisms behind the scenes to implement these features -- that can make it simple and easy to demonstrate. A switch to more realistic and sophisticated mechanisms should require no change to JavaEE-compliant application code. We will use the simple, default "other" security-domain defined within the standard JBoss installation. This uses the RealmUsersRoles login-module -- which is powered by two property files supplied and pre-populated by the course server files from ejava-wildfly(version) in your course examples source tree. You unzipped this file into your application server configuration as part of course setup and it would be wise to repeat that to make sure your server configuration is up to date with any changes added since then.


jboss.server.config.dir

${jboss.server.config.dir} is a reference to an internal JBoss variable that references the "standalone/configuration" directory.

$ ls standalone/configuration/application*.properties
standalone/configuration/application-roles.properties    
standalone/configuration/application-users.properties

We are going to have several types of user roles. Some of the users will have zero, one, or more of these roles. Because of the static nature of our demonstration authentication solution, all users will have a login configured before the application is even deployed to the server.


If a user has a login for one application, they will use the same account to access the other application (e.g., user2 has both eleague-clubcoord and eclub-coord roles).

The "sys" accounts are meant to be used by the application code when actions taken require an authenticated user but there is no caller context (e.g., async callbacks) or the identity of the incoming caller is not appropriate for the outgoing call being triggered (e.g., elevated permission access).


Based on the test data, the following are some other logins that may be useful. They have been added to your users and roles property files and are referencs either in the ingested XML file or part of the end-to-end demonstration steps.


Note

To clarify, your application will have a static set of logins and will dynamically ingest a set of Contacts at scenario startup. More Contacts and Members will be added during the scenario. A user with a login and no Contact/Member info within the database can login, but won't be able to do anything meaningful to them personally. A user with a Contact/Member defined and no login won't be able to access the protected areas of the system. Normally the login would be created at the same time the Contact/Member is added. Except for your JBoss configuration and your add user logic, no other part of your project should be aware of this tradeoff made for class project simplicity.

Some actions are open to any users; authenticated or not. Authentication will be performed using a JNDI login for RMI, BASIC for HTTP web services, and FORM for Web UIs. All users will have a password of "password1!".

We will optionally (for the assignment) use HTTPS to provide confidentiality between our client and server for communications. The server has a keystore for identity. The JUnit tests can conveniently use that as a trustStore through the base path defined by the "jboss.home" property.


# build/dependencies/pom.xml

<java.truststore>${jboss.home}/standalone/configuration/application.keystore</java.truststore>
...
<plugin>
    <artifactId>maven-failsafe-plugin</artifactId>
    <configuration>
        <systemPropertyVariables>
            <javax.net.ssl.trustStore>${java.truststore}</javax.net.ssl.trustStore>

jboss.home

jboss.home is a property you should define in your "$HOME/.m2/settings.xml" file and have it reference your installation directory for JBoss/Wildfly.



# .m2/settings.xml

    <profiles>
        <profile>
            <id>wildfly13</id>
            <properties>
                <jboss.home>.../apps/wildfly-13.0.0.Final</jboss.home>
            </properties>
        </profile>
    ...
    <activeProfiles>
      <activeProfile>wildfly13</activeProfile>
      <activeProfile>h2db</activeProfile>
    </activeProfiles>

SSL and/or HTTPS is not a assignment requirement

Although essential in securing communications, SSL and/or HTTPS is not a requirement for implmenting this assignment. The instructions above are provided for students wishing to satisfy a key but not very noticeable portion of a secure interface.

For asynchronous activity, we will implement a Schedule JMS Topic where eLeague will publish messages related to team schedules. eClub will subscribe to the topic using a Message Driven Bean (MDB) to keep members up to date on schedule changes. The JMS topic has already been added to your JMS Server within JBoss and has been assigned both an internal and external JNDI name. You will have to design the message type, message properties, and payload of the messages sent on that topic in order that the message carries a partable payload and can be filtered for subscriber-specific criteria via a JMS selector. Your DTOs already express a portable expression of data. Each club will only want to process messages for their teams and only events that impact upcoming schedules (i.e., not scores of completed Contests).


<jms-topic name="esport-eleague-contests" 
     entries="java:/topic/ejava/projects/esport/eleague-contests 
              java:jboss/exported/topic/ejava/projects/esport/eleague-contests"/>

eLeague will use EJB Timers to send out schedule reminders for upcoming Contests.

In this portion of the project we are going to send and receive a JMS message using publish/subscribe JMS techniques.


  1. Provide JUnit IT tests that verify the EJB functionality of eLeague accessed through its remote interface using new access control restrictions. The test should demonstrate that authentication and access control is in place for this EAR-based deployed application, the authenticated identity of the caller can be accessed by the server-side code, and status of the call is properly conveyed to the caller. For both RMI and JAX-RS, there should be:

  2. Provide JUnit IT tests that verify the EJB functionality of eClub using its new access control restrictions and ability to authenticate (if necessary) with eLeague. The test should demonstrate that authentication and access control is in place for this WAR-based deployed application. This is similar to above -- except must be demonstrated with eClub.

  3. Provide a JUnit IT tests that demonstrates the functionality of the JavaEE interceptor/validator. This should include:

  4. Implement the scripted use case below as an automated JUnit test and a selected portion manually accessed through the Web UI. The JUnit test/module must be delivered in a state that can be executed in a debugger -- whether directly within Eclipse (ideally) or using a remote debugging session to a Maven command-line build.

    Since this end-to-end test spans both applications the most likely place to host is within eClub.

    1. The applications deploy and/or started

    2. An eleague-admin (admin1) resets all eLeague tables (using the LeagueTestUtilEJB)

    3. An eleague-admin (admin1) populates the eLeague tables (using the LeagueIngestor)

    4. An eclub-admin (admin1) resets the eClub tables (using the ClubTestUtilEJB)

    5. An eclub-admin (admin1) populates the eClub tables (using the ClubTestUtilEJB) if anything necessary.

    6. The league coordinator(lmtucker) creates a Club in eLeague (using the LeagueMgmtEJB). League coordinator assigns the club representative (whoever you call them) to have the login of "user2" to match a login pre-defined in your Wildfly Server. This login has multiple roles assigned spanning both applications.

    7. The eleague-clubcoord (user2) adds Venue for Club in eLeague (using ClubMgmtEJB). The "user2" login has been pre-defined to have the role "eclub-coord".

    8. An eclub-member (user3) creates Parent in eClub (using MemberMgmtEJB). "user3" is a login pre-defined in your Wildfly Server with the role "eclub-member".

    9. An eclub-member (user3) creates Player in eClub (using MemberMgmtEJB) for their child

    10. An eclub-member (user3) adds a Coach role in eClub (using MemberMgmtEJB) to their identity

    11. The eclub-coord (user2) creates a Team in eClub (using OrgMgmtEJB)

    12. The eclub-coord(user2) assigns Players to a Team in eClub (using OrgMgmtEJB)

    13. The eclub-coord(user2) assigns Coach to a Team in eClub (using OrgMgmtEJB)

    14. The eclub-coord(user2) assigns Manager to a Team in eClub (using OrgMgmtEJB)

    15. An eclub-member(user3) views their team roster in eClub (using OrgMgmtEJB)

    16. An eleague-clubcoord (user2) adds a Team for Club in eLeague (using ClubMgmtEJB)

    17. An eleague-clubcoord (user2) adds a contact for a Team eLeague (using ClubMgmtEJB)

    18. An eleague-clubcoord (user2) assigns a Team to an existing Division eLeague (using ClubMgmtEJB; division=U13-A, refid="Division-845")

    19. The eleague-coord (lmtucker) schedules a Season eLeague (using ContestMgmtEJB)

    20. eLeague (eleague-sys) (thru ContestMgmtEJB) publishes schedule events to Schedule Topic

    21. eClub (eclub-sys) (using LeagueListenerMDB) receives messages related to unfinished contests. It obtains the e-mail addresses for members associated with the team referenced in the message. It logs the message and e-mail addresses.

    22. anonymous users views Team Schedule in eClub (using OrgMgmtEJB). eClub (eclub-sys) contacts eLeague (using LeagueMgmtEJB) for team season associated with eClub's team.

    23. EJB Timer fires a callback in eLeague (using ContestMgmtEJB (eleague-sys))

    24. eLeague (eleague-sys) (using ContestMgmtEJB) publishes reminder messages to the Schedule Topic (and received in eClub using LeagueListenerMDB).

    25. The eleague-coord (jtflynn) (for division=U13-A, refid="Division-845") reports a Score in eLeague involving our club team (using ContestMgmtEJB)

    26. anonymous user views Division Standings in eLeague (using LeagueMgmtEJB)


Your project will be graded primarily on the demonstration ability to implement concepts covered in this portion of the course. A perfect score will need to implement the full end-to-end scenario. A passing score will need to make sure to cover the grading criteria outlined below within the partial end-to-end implemented.

The following table contains examples of where projects have lost points in the past. Of course, each project submitted can introduce new issues or different severity levels of the same issues. Do not treat this as a complete list.

Table 29.1. Sample Lost Points

README   
The WebUI is hard to navigate (fine) but README offered no assistance5  
Projects cleanly builds with Maven   
Using rogue users that are not part of the standard class setup in your end-to-end. 3  
Initial build fails. Looks to depend on DB schema bleedover between unit and IT tests. 2  
Project 1 and 2 functionality   
Second application being deployed as EAR and not WAR 2  
Relying on persistence unit to create schema -- thus blowing away all DB data on deployment 2  
Missing scenario feature (e.g., wrong data) from project 2 end-to-end scenario. 1  
Attempting to set the state of a @Stateless EJB. What do you think will happen to that state when you get a different bean instance the next time you call? 2  
Not self managing schema. With the end-to-end having resetAll() in place, why did you rely on the JPA provider to initialize your schema? 1  
Client Security Login   
   
EJB Security   
Using credential logins for the JMS Connection from EJBs -- versus leveraging the @RunAs role 1  
Not relying on declaritive security to perform the role checks. You are also having the caller authorized for the role supply instance-specific information. For example, any division coordinator is allowed to report the score for any division. 2  
Not constraining authorized caller to manage only their information. Caller is passing references to information using identifiers that could be associated with any user versus "manage my stuff". By relying on those identifiers you are allowing them to "manage that stuff which may or may not be my stuff". 3  
EJB module not being associated with a specific security-domain. Relying on defaults. 2  
@RunAs takes a role -- not a principal 1  
WAR Security   
Requiring login to pages that should allow anonymous access. 1  
WAR is not properly locked down. 2  
Mixed use of BASIC and FORM. When signing in to perform action a Basic authentication popup appears and logout no longer functions, need to close browser to log out.    
EJB JMS Publisher   
Copied provided example wholesale and did not adjust to be your solution (e.g., features specific to the example are not required for project, comments specific to example are not appropriate for a project solution). 2  
Not closing resources (JMS 1.1) This eventually exhausts resources over time. 2  
Could not find testing of this anywhere to makeup for the fact that the end-to-end was not implemented 2  
EJB MDB Subscriber   
Not implemented 10  
Using System.out versus logging framework or better error reporting 1  
Java SE JMS Listener   
Did not provide your subscriber any credentials to interact with the server. 2  
Didn't work out of box. JMS topic mis-named 1  
EJB Timers   
It would be a better design to treat the timer() callback as an interface facade and not the triggering implementation mechanism. You have combined EJB Timer, JMS publishing, and business logic within a single method. 0  
End-to-end Integration Test   
Your configuration made it hard to run the end-to-end scenario in a debugger. All JNDI names and properties were solely expressed in the pom.xml rather than having suitable defaults in the IT test and overrides from the pom.xml. With that type of setup you did not have your pom.xml and surefire setup to allow remote debugging. 2  
Poluted, hard to follow, too much extra stuff2  
Missing resetAll and populate at start of scenarios5  
Missing step X1  
No. I want different functionality in this step1  



/**
 * Purchasing handles payment of purchased products.
 */
public interface Purchasing {
    /**
     * Creates an account for the user to use in purchasing products.
     * @param email
     * @param firstName
     * @param lastName
     * @return the Account created with primary key assigned
     */
    Account createAccount(String email, String firstName, String lastName);
    /**
     * Completes the purchase of the items in the user's shopping cart,
     * empties the cart, and returns the total cost paid.<p/>
     * 
     * Note that this capability is not yet fully defined.
     * @param email
     * @param password
     * @return amount charged as part of this checkout
     */
    double checkout(String email, String password);
}
/**

 * The catalog maintains a view of the inventory known to our application. 
 */
public interface Catalog {
    /**
     * Returns a list of products in the catalog chunked into page sizes.
     * @param offset
     * @param limit
     * @return list of products matching the paging criteria
     */
    List<Product> getProducts(int offset, int limit);
    /**
     * Adds the selected product to the users' shopping cart and returns
     * the count of items.
     * @param id
     * @param validEmail
     * @return number of items in cart
     */
    int addToCart(int id, String validEmail);
}
@Entity

@NamedQueries({
    @NamedQuery(name="blPurchasing.findAccountByEmail", 
                query="select a from Account a where a.email=:email")
})
public class Account {
    public static final String FIND_BY_EMAIL="blPurchasing.findAccountByEmail";
    
    @Id @GeneratedValue
    private int id;
    
    @Column(nullable=false, unique=true)
    private String email;
    
    @Column(nullable=false)
    private String password;
    
    @Column(nullable=false)
    private String firstName;
    
    @Column(nullable=false)
    private String lastName;
...
@Entity

public class Product {
    @Id @GeneratedValue
    private int id; 
    @Column(nullable=false)
    private String name;
    @Column(nullable=false)
    private double price;
    @Column(nullable=false)
    private int count;
...
@Entity

public class Cart {
    @Id
    private String email;
    
    @OneToOne
    @PrimaryKeyJoinColumn(referencedColumnName="email")
    private Account account;
    
    @ManyToMany
    private List<Product> products = new ArrayList<Product>();
...
/**

 * A user shall be able to establish an account with just a first 
 * and last name and a unique email address.
 */
@Test
public void establishAccount() {
    log.info("*** establishAccount ***");
    
    //the user will supply their email address, first and last name
    String email="jharb@ravens.com";
    String firstName="john";
    String lastName="harbaugh";
    Account account = purchasing.createAccount(email, firstName, lastName);
    
    //they will get back a generated password to use as a login for the account
    assertNotNull("no account returned", account);
    assertNotNull("no password assigned", account.getPassword());
}
/**

 * A user shall be able browse products in the catalog.
 */
@Test
public void browseCatalog() {
    log.info("*** browseCatalog ***");
    
        //the user will ask for product summaries in pages
    int pageSize=10;
    int offset=0;
    List<Product> products = catalog.getProducts(offset, pageSize);
    
        //they will receive <= a page size of product information
    assertNotNull("no products returned", products);
    assertTrue("no products provided", products.size() > 0);
    
        //they can page thru the entire set
    for (int i=0; products.size() != 0; i++) {
        offset += products.size();
        products = catalog.getProducts(offset, pageSize);
        assertTrue("this catalog never ends!!!", i<100);
    }
}
/**

 * A user shall be able to purchase a product in the catalog.
 */
@Test
public void purchaseProduct() {
    log.info("*** purchaseProduct ***");
    
        //the user selects a product
    Product product=null;
    Random random=new Random();
    for (int i=0; product == null; i++) {
        List<Product> products=catalog.getProducts(random.nextInt(100), 1);
        product=products.iterator().next();
        assertTrue("I can't find anything to buy!!!", i<1000);
    }
    
        //the user adds the product to their shopping cart by providing the 
        //product id and their credentials
    int count=catalog.addToCart(product.getId(), validEmail);
        //the user receives a count of the items in the cart
    assertEquals("somebody tweeked my cart!!!!", 1, count);
    
        //the user checks out with the cashier -- payment not yet implemented
    double total=purchasing.checkout(validEmail, validPassword);
    
        //the user gets a total amount back as their receipt
    assertEquals("price doesn't add up", product.getPrice(), total, .01);
}

  • DBMS based on a relational model

  • Introduced by E. F. Codd in 1970s

  • Some challenges by other forms but still remains a standard for corporate data stores

public class JDBCBookDAOImpl implements BookDAO {

    private Connection connection;
    
    public void setConnection(Connection connection) {
        this.connection = connection;
    }
    @Override
    public Book create(Book book) throws PersistenceException {
        try (PreparedStatement insertStatement=getInsertPreparedStatement(connection, book);
             PreparedStatement idStatement=getIdentityStatement(connection)){
            insertStatement.execute();
            
            try (ResultSet rs = idStatement.executeQuery()) {
                if (rs.next()) {
                    Field id = Book.class.getDeclaredField("id");
                    id.setAccessible(true);
                    id.set(book, rs.getLong(1));
                } else {
                    throw new PersistenceException("no identity returned from database");
                }                
            } catch (NoSuchFieldException ex) {
                throw new PersistenceException("Error locating id field", ex);
            } catch (IllegalAccessException ex) {
                throw new PersistenceException("Access error setting id", ex);
            }
            
            return book;
        } catch (SQLException ex) { 
            throw new PersistenceException("SQL error creating book", ex);
        }
    }
    ...
}


    private PreparedStatement getInsertPreparedStatement(Connection c, Book book) throws SQLException {
        PreparedStatement statement=connection.prepareStatement(
                "insert into JPADAO_BOOK (ID, DESCRIPTION, PAGES, TITLE) " +
                "values (null, ?, ?, ?)");
        statement.setString(1, book.getDescription());
        statement.setInt(2, book.getPages());
        statement.setString(3, book.getTitle());
        return statement;
    }
    
    private PreparedStatement getIdentityStatement(Connection c) throws SQLException {
        PreparedStatement statement = connection.prepareStatement("call identity()");
        return statement;
    }

Table of Contents

Purpose
1. Goals
2. Objectives
38. JPA Overview
38.1. Background
38.2. EntityManager
38.3. Entity
38.4. JPA Example
38.5. Entity States
38.5.1. Managed
38.5.2. Detached
38.6. Persistence Context
38.7. Persistence Unit
38.8. Example Layout
38.9. Persistence.xml
38.9.1. Application Example
38.9.2. Server Example
38.9.3. Optional hibernate.properties
38.9.4. Sample orm.xml
38.9.5. persistence.xml Elements
38.9.6. Entity Discovery
38.10. Schema Generation
38.10.1. Hibernate DDL Generation Example
38.10.2. javax.persistence Schema Generation
38.11. Basic Testing Usage Steps
38.12. Entity Manager Methods
38.12.1. Basic CRUD Operations
38.12.2. Membership Operations
38.12.3. State Synchronization Operations
38.12.4. Locking Operations
38.12.5. Query Operations
38.12.6. Other Operations
39. Entity Manager CRUD Methods
39.1. persist()
39.2. find()
39.3. getReference()
39.4. merge()
39.5. remove()
40. Entity Manager Membership Methods
40.1. contains()
40.2. clear()
40.3. detach()
41. Entity Manager State Synchronization Methods
41.1. flush()
41.2. FlushMode
41.3. refresh()
42. Entity Manager Locking Methods
42.1. Primary Lock Types
42.2. LockModeType
42.3. lock()
42.4. find(lock)
42.5. refresh(lock)
42.6. getLockMode()
43. Entity Manager Query Methods
43.1. JPA Queries
43.2. Native Queries
43.3. Criteria Queries
44. Other Entity Manager Methods
44.1. isOpen()
44.2. close()
44.3. getTransaction()
44.4. joinTransaction()
44.5. unwrap()
44.6. getDelegate()
44.7. getMetaModel()
44.8. getEntityManagerFactory()
44.9. setProperties()/getProperties()
45. JPA Maven Environment
45.1. JPA Maven Dependencies
45.1.1. JPA API classes
45.1.2. JPA Provider classes
45.1.3. Database
45.2. Supplying Runtime Properties
45.2.1. Turn on Resource Filtering in pom.xml
45.2.2. Use ${variable} References in Resource Files
45.2.3. Define Property Values in Parent pom.xml
45.2.4. Run with Filtered Values

<persistence 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" 
  version="2.1">

    <persistence-unit name="jpaDemo">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <mapping-file>META-INF/orm.xml</mapping-file>
        <properties>
            <!-- standard properties -->
            <property name="javax.persistence.jdbc.url" value="jdbc:h2:./target/h2db/ejava"/>
            <property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
            <property name="javax.persistence.jdbc.user" value="sa"/>
            <property name="javax.persistence.jdbc.password" value=""/>

            <!-- hibernate-specific properties -->
            <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
            <!-- set to 0 to improve error messages when needed
            <property name="hibernate.jdbc.batch_size" value="0"/>            
             -->
        </properties>
    </persistence-unit>
</persistence>

The above example:


<?xml version="1.0" encoding="UTF-8"?>
<persistence 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"
  version="2.1">

    <persistence-unit name="ejbsessionbank">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
        <jar-file>lib/info.ejava.examples.ejb-ejbsessionBankImpl-5.0.0-SNAPSHOT.jar</jar-file>
        <properties>
            <property name="hibernate.dialect"
                value="org.hibernate.dialect.H2Dialect"/>
            <property name="hibernate.show_sql" value="false"/>
            <!-- create is used here for demo project only -->
            <property name="hibernate.hbm2ddl.auto" value="create"/>
            <!--
            <property name="hibernate.jdbc.batch_size" value="0"/>
            -->
        </properties>
    </persistence-unit>
</persistence>

The above example:

hibernate.hbm2ddl.auto

Controls hibernate schema generation

create, drop, create-drop, update, validate, and none

hibernate.hbm2ddl.import_files

Allows for self-authored files to be used as well

  • Expressed thru hibernate.properties using hibernate.hbm2ddl properties

    hibernate.hbm2ddl.auto=create-drop
    
    
    hibernate.hbm2ddl.import_files=/ddl/mydb-tuningdrop.ddl,/ddl/mydb-tuning.ddl
    hibernate.connection.url=${jdbc.url}
    hibernate.connection.driver_class=${jdbc.driver}
    hibernate.connection.username=${jdbc.user}
    hibernate.connection.password=${jdbc.password}
    #hibernate.show_sql=true
    #hibernate.format_sql=true
  • Can be expressed thru persistence.xml but use standard properties instead to avoid confusion

javax.persistence.schema-generation.database.action

Values: drop-and-create, drop, create, none

javax.persistence.schema-generation.create-source

Values: metadata (default), script, metadata-then-script, script-then-metadata

javax.persistence.schema-generation.create-script-source - provides reference to create source

javax.persistence.schema-generation.drop-script-source - provides reference to drop source

javax.persistence.sql-load-script-source

Provides reference to script to execute after schema initialized

Useful in Pre-populating sample database tables with content

  • Database schema generation example

    
    <persistence-unit name="jpa-schemagen-test">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <properties>
            <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
            
            <!-- a database connection definition is required for database actions -->
            <property name="javax.persistence.jdbc.url" value="${jdbc.url}"/>
            <property name="javax.persistence.jdbc.driver" value="${jdbc.driver}"/>
            <property name="javax.persistence.jdbc.user" value="${jdbc.user}"/>
            <property name="javax.persistence.jdbc.password" value="${jdbc.password}"/>
        </properties>
    </persistence-unit>
  • Script schema generation example

    
    <persistence-unit name="jpa-schemagen-test">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <properties>
            <!-- a script file containing create and drop commands will be generated without interacting with database --> 
            <property name="javax.persistence.schema-generation.scripts.action" value="drop-and-create"/>
            <property name="javax.persistence.schema-generation.scripts.create-target" value="${project.build.outputDirectory}/ddl/${project.artifactId}-create.ddl"/>
            <property name="javax.persistence.schema-generation.scripts.drop-target" value="${project.build.outputDirectory}/ddl/${project.artifactId}-drop.ddl"/>

            <!-- otherwise we would get 1 line per statement without standard delimiter character -->
            <property name="hibernate.format_sql" value="true"/>
            <property name="hibernate.hbm2ddl.delimiter" value=";"/>
            <!-- required when no database connection specified -->
            <property name="hibernate.dialect" value="${hibernate.dialect}"/>
        </properties>    
    </persistence-unit>
    • Scripts written to target files

      target/classes/ddl/
      |-- jpa-schemagen-create.ddl
      `-- jpa-schemagen-drop.ddl
      
    • Create script contains commands to create schema

      create sequence hibernate_sequence start with 1 increment by 1;
      
      
      create table JPAUTIL_TABLET (
         id integer not null,
          maker varchar(255),
          primary key (id)
      );
    • Drop script contains commands to drop schema

      drop table JPAUTIL_TABLET if exists;
      
      
      drop sequence if exists hibernate_sequence;
public interface javax.persistence.EntityManager{
...
}
        
Author author = new Author();

author.setFirstName("dr");
author.setLastName("seuss");
author.setSubject("children");
author.setPublishDate(new Date());
logger.debug("creating author: {}", author);
em.persist(author);
logger.debug("created author: {}", author);
-creating author:ejava.examples.daoex.bo.Author@17e7691, id=0, fn=dr, ln=seuss, subject=children, pdate=Sun Sep 16 10:14:32 EDT 2012, version=0
-created author:ejava.examples.daoex.bo.Author@17e7691, id=50, fn=dr, ln=seuss, subject=children, pdate=Sun Sep 16 10:14:32 EDT 2012, version=0


//create initial author
Author author = new Author();
...
em.persist(author);
//create detached author with changes
Author author2 = new Author(author.getId());
author2.setFirstName("updated " + author.getFirstName());
...
//merge changes 
Author tmp = em.merge(author2);
em.getTransaction().begin();
em.getTransaction().commit();
...
//verify results
assertFalse("author2 is managed", em.contains(author2));
assertTrue("tmp Author is not managed", em.contains(tmp));
assertSame("merged result not existing managed", author, tmp);
...
//verify changes were made to the DB
Author author3 = em.find(Author.class, author.getId());
assertEquals("updated " + firstName, author3.getFirstName());            


void remove(Object entity);
        
em.persist(author);


//callers can detach entity from persistence context
logger.debug("em.contains(author)={}", em.contains(author));
logger.debug("detaching author");
em.getTransaction().begin();
em.flush();
em.detach(author);
logger.debug("em.contains(author)={}", em.contains(author));
em.getTransaction().commit();
//changes to detached entities do not change database
author.setFirstName("foo");
em.getTransaction().begin();
em.getTransaction().commit();
Author author2 = em.find(Author.class, author.getId());
logger.debug("author.firstName={}", author.getFirstName());
logger.debug("author2.firstName={}", author2.getFirstName());
assertNotEquals("unexpected name change", author.getFirstName(), author2.getFirstName());
-em.contains(author)=true
-detaching author
-em.contains(author)=false
-author.firstName=foo
-author2.firstName=dr


em.persist(author);
em.getTransaction().begin();
em.getTransaction().commit();
//change DB state out-of-band from the cache
em.getTransaction().begin();
String newName="foo";
int count=em.createQuery(
        "update jpaAuthor a set a.firstName=:name where a.id=:id")
    .setParameter("id", author.getId())
    .setParameter("name", newName)
    .executeUpdate();
em.getTransaction().commit();
assertEquals("unexpected count", 1, count);
//object state becomes stale when DB changed out-of-band
logger.debug("author.firstName={}", author.getFirstName());
assertNotEquals("unexpected name", newName, author.getFirstName());
//get the cached object back in sync
logger.debug("calling refresh");
em.refresh(author);
logger.debug("author.firstName=" + author.getFirstName());
assertEquals("unexpected name", newName, author.getFirstName());
-author.firstName=dr
-calling refresh
-author.firstName=foo
        
src
|-- main
|   |-- java
|   |   `-- ejava
|   |       `-- examples
|   |           `-- daoex
|   |               |-- bo
|   |               |   `-- Author.java
|   |               |-- dao
|   |               |   |-- AuthorDAO.java
|   |               |   `-- DAOException.java
|   |               `-- jpa
|   |                   `-- JPAAuthorDAO.java
|   `-- resources
|       `-- META-INF
|           |-- orm.xml
|           `-- persistence.xml (could be placed in src/test branch)
`-- test
    |-- java
    |   `-- ejava
    |       `-- examples
    |           `-- daoex
    |               `-- jpa
    |                   |-- JPAAuthorDAOTest.java
    |                   |-- JPACRUDTest.java
    |                   |-- JPAExtendedOnlyTest.java
    |                   |-- JPAMembershipTest.java
    |                   |-- JPASyncTest.java
    |                   `-- JPATestBase.java
    `-- resources
        |-- hibernate.properties (optional)
        `-- log4j.xml





H2Dialect.java

registerColumnType( Types.DECIMAL, "decimal($p,$s)" );
registerColumnType( Types.NUMERIC, "decimal($p,$s)" );
registerColumnType( Types.DOUBLE, "double" );








Notice impact of temporal DB-mapping does effect until data saved and retrieved from database

  • Every entity must have a primary key

  • Primary keys must be unique

  • Map to one ("simple") or more ("composite") properties

  • Properties must be of type

    • Java primitive types -- including object wrappers

    • java.lang.String

    • Custom classes made up of legal property types





GeneratedValue must be generated by provider

If an entity is defined to have its primary key automatically generated -- no matter the strategy -- the provider will insist on implementing the value. It is always an error to pass a detached/transient object to persist() with an id already assigned.

Don't default GenerationStrategy

One should always identify what the GenerationStrategy should be so that the strategy remains consistent over time. For example, Hibernate use to default to a form of IDENTITY in JavaSE environments and SEQUENCE in JavaEE environments.



Figure 48.9. IDENTITY Test (with Active Transaction)

  • Test (with Transaction Active)

    ejava.examples.orm.core.annotated.Gadget gadget = new Gadget(0);
    
    gadget.setMake("gizmo 1");
    //insert a row in the database
    //start with a tx already active
    logger.info("gadget (before persist; tx={}): {}", txActive(), gadget);
    em.persist(gadget);
    logger.info("created gadget (after persist, before flush; tx={}): {}", txActive(), gadget);
    em.flush(); 
    logger.info("created gadget (after flush; tx={}): {}", txActive(), gadget);            
    assertNotEquals(0, gadget.getId());     
  • Output (with Transaction Active)

    -gadget (before persist; tx=true): 1798443618, id=0, make=gizmo 1
    
    -insert into ORMCORE_GADGET (id, make) values (null, ?)
    -binding parameter [1] as [VARCHAR] - [gizmo 1]
    -created gadget (after persist, before flush; tx=true): 1798443618, id=1, make=gizmo 1
    
    -created gadget (after flush; tx=true): 1798443618, id=1, make=gizmo 1
    
  • Follow-on IDENTITY Allocations (with Transaction Active)

    -insert into ORMCORE_GADGET (id, make) values (null, ?)
    -binding parameter [1] as [VARCHAR] - [gizmo 2]
    -created gadget(tx=true): 370055648, id=2, make=gizmo 2
    
    -insert into ORMCORE_GADGET (id, make) values (null, ?)
    -binding parameter [1] as [VARCHAR] - [gizmo 3]
    -created gadget(tx=true): 911933063, id=3, make=gizmo 3
    
    -insert into ORMCORE_GADGET (id, make) values (null, ?)
    -binding parameter [1] as [VARCHAR] - [gizmo 4]
    -created gadget(tx=true): 568613487, id=4, make=gizmo 4
    
    • Provider inserts row into database with null ID

    • Provider queries database (not shown in debug) for primary key generated for the row

    • Provider eagerly inserts row during persist() and before flush() when transaction is open -- to determine primary key

Using IDENTITY doubles calls to database

When using the IDENTITY strategy, the provider must make at least two calls to the database. One for the INSERT and one to determine the primary key value generated by the database.


Figure 48.10. IDENTITY Test (persist() with Inactive Transaction)

  • Test

    em.getTransaction().rollback();
    
    logger.info("rolled back tx(tx={})", txActive());
    for (int i=0; i<3; i++) {
        Gadget g = new Gadget();
        g.setMake("gizmo " + counter++);
        em.persist(g);
        logger.info("created gadget(tx={}): {}", txActive(), g);
        if (i==0) {
            gadget=g;
        }
    }
    logger.info("starting tx(tx={}): {}", txActive(), gadget);
    em.getTransaction().begin();
    logger.info("tx started, flushing (tx={}): {}", txActive(), gadget);
    em.flush();
    logger.info("cache flushed (tx={}): {}", txActive(), gadget);
    em.getTransaction().commit();
    logger.info("tx committed (tx={}): {}", txActive(), gadget);
  • Output (persist() with inactive transaction)

    -rolled back tx(tx=false)
    
    
    -created gadget(tx=false): 1372646511, id=0, make=gizmo 5
    -created gadget(tx=false): 1202178366, id=0, make=gizmo 6
    -created gadget(tx=false): 1872410525, id=0, make=gizmo 7
    -starting tx(tx=false): 1372646511, id=0, make=gizmo 5
    • Provider could not insert rows or determine primary key while transaction inactive

    • Business logic could not rely on PK values being set (e.g., send event or log)

  • Output (active transaction following persist)

    -tx started, flushing (tx=true): 1372646511, id=0, make=gizmo 5
    
    -insert into ORMCORE_GADGET (id, make) values (null, ?)
    -binding parameter [1] as [VARCHAR] - [gizmo 5]
    -insert into ORMCORE_GADGET (id, make) values (null, ?)
    -binding parameter [1] as [VARCHAR] - [gizmo 6]
    -insert into ORMCORE_GADGET (id, make) values (null, ?)
    -binding parameter [1] as [VARCHAR] - [gizmo 7]
    -cache flushed (tx=true): 1372646511, id=5, make=gizmo 5
    -tx committed (tx=false): 1372646511, id=5, make=gizmo 5
    • Provider waited for next flush() cycle (not tx start) to perform INSERTs and determine primary key




Figure 48.13. SEQUENCE Test (with active transaction)

  • Test (with active transaction)

    Assume.assumeTrue(Boolean.parseBoolean(System.getProperty("sql.sequences", "true")));
    
    
    ejava.examples.orm.core.annotated.Fan fan = new Fan(0);
    fan.setMake("cool runner 1");
    //insert a row in the database
    logger.info("persisting fan(tx={}): {}", txActive(), fan);
    em.persist(fan);
    logger.info("created fan (before flush, tx={}):", txActive(), fan);
    em.flush(); 
    logger.info("created fan (after flush; tx={}): {}", txActive(), fan);            
    assertNotEquals(0, fan.getId());
  • Output (with active transaction)

    -persisting fan(tx=true): 1413306467, id=0, make=cool runner 1
    -call next value for FAN_SEQ
    -call next value for FAN_SEQ   #Current value=8 after this call, next result will be 11
    
    -created fan (before flush, tx=true): 1413306467, id=5, make=cool runner 1
    -insert into ORMCORE_FAN (make, id) values (?, ?)
    -binding parameter [1] as [VARCHAR] - [cool runner 1]
    -binding parameter [2] as [BIGINT] - [5]
    -created fan (after flush; tx=true): 1413306467, id=5, make=cool runner 1
    
    
    -persisting fan(tx=true): 1413306467, id=0, make=cool runner 1
    -call next value for FAN_SEQ
    -call next value for FAN_SEQ
    
    -created fan (before flush, tx=true): 1413306467, id=5, make=cool runner 1
    -insert into ORMCORE_FAN (make, id) values (?, ?)
    -binding parameter [1] as [VARCHAR] - [cool runner 1]
    -binding parameter [2] as [BIGINT] - [5]
    -created fan (after flush; tx=true): 1413306467, id=5, make=cool runner 1
    
    • Provider obtains primary key value prior to inserting row (x2 calls addressed later)

    • First primary key value (5) corresponds with the @SequenceGenerator.initialValue property

    • Primary key value available to business logic prior to insert into database

    • Provider inserts the row during next flush cycle

    • Inspecting database server through UI -- shows current value of 8 (2x calls to next value)


SEQUENCE allows individual primary keys to be allocated in blocks

The database advances its SEQUENCE by the allocationSize on each call to next value. The provider may use the returned value and allacationSize values above the returned value before returning to the database for a new value. The database and provider *must* have the same increment/allocationSize configured.

Figure 48.14. Follow-on SEQUENCE Allocations

  • Output prior to flush/commit

    -created fan(tx=true): 1289462509, id=6, make=cool runner 2
    -created fan(tx=true): 740265405, id=7, make=cool runner 3
    -created fan(tx=true): 1439003682, id=8, make=cool runner 4
    -call next value for FAN_SEQ
    -created fan(tx=true): 578969118, id=9, make=cool runner 5
    -created fan(tx=true): 493310435, id=10, make=cool runner 6
    -created fan(tx=true): 757436159, id=11, make=cool runner 7
    -call next value for FAN_SEQ
    -created fan(tx=true): 1682973478, id=12, make=cool runner 8
    ...
    -created fan(tx=true): 1501844857, id=22, make=cool runner 18
    -created fan(tx=true): 817994751, id=23, make=cool runner 19
    -call next value for FAN_SEQ
    -created fan(tx=true): 1312250810, id=24, make=cool runner 20
    -created fan(tx=true): 1296316112, id=25, make=cool runner 21
    ...
    • Provider calling database to get next allocation prior to exhausting current allocation

    • Provider knows to self-generate next allocationSize values for value returned prior to obtaining next allocation

    • Provider waits for next flush cycle to insert rows into database

  • Output during commit

    -committing (tx=true): 1289462509, id=6, make=cool runner 2
    -insert into ORMCORE_FAN (make, id) values (?, ?)
    -binding parameter [1] as [VARCHAR] - [cool runner 2]
    -binding parameter [2] as [BIGINT] - [6]
    -insert into ORMCORE_FAN (make, id) values (?, ?)
    -binding parameter [1] as [VARCHAR] - [cool runner 3]
    -binding parameter [2] as [BIGINT] - [7]
    -insert into ORMCORE_FAN (make, id) values (?, ?)
    -binding parameter [1] as [VARCHAR] - [cool runner 4]
    ...
    -insert into ORMCORE_FAN (make, id) values (?, ?)
    -binding parameter [1] as [VARCHAR] - [cool runner 21]
    -binding parameter [2] as [BIGINT] - [25]
    -tx committed (tx=false): 1289462509, id=6, make=cool runner 2
    
    • Rows inserted into database during next flush cycle triggered by commit

    • Database makes no correlation between sequence numbers returned and primary key value assigned to a row


Increase increment/allocationSize for faster ingest

The default increment/allocationSize requires two (2) calls per insert. A larger increment/allocationSize can reduce the number of calls by up to 50%, but will potentially leave gaps and exhaust the unique sequence values earlier in the lifetime of the database if clients terminate and restart prior to exhausting an allocation.

Figure 48.15. SEQUENCE Test (persist without active transaction)

  • Test

    logger.info("tx(tx={})", txActive());
    
    for (int i=0; i<20; i++) {
        Fan f = new Fan();
        f.setMake("cool runner " + counter++);
        em.persist(f);
        logger.info("created fan(tx={}): {}", txActive(), f);
        if (i==0) {
            fan=f;
        }
    }
    logger.info("starting tx(tx={}): {}", txActive(), fan);
    em.getTransaction().begin();
    logger.info("tx started, flushing (tx={}): {}", txActive(), fan);
    em.flush();
    logger.info("cache flushed (tx={}): {}", txActive(), fan);
    em.getTransaction().commit();
    logger.info("tx committed (tx={}): {}", txActive(), fan);
  • Output while transaction inactive

    -tx(tx=false)
    -created fan(tx=false): 1451387509, id=26, make=cool runner 22
    -call next value for FAN_SEQ
    -created fan(tx=false): 1238209644, id=27, make=cool runner 23
    -created fan(tx=false): 1371953731, id=28, make=cool runner 24
    -created fan(tx=false): 1947060963, id=29, make=cool runner 25
    -call next value for FAN_SEQ
    -created fan(tx=false): 1309934743, id=30, make=cool runner 26
    -created fan(tx=false): 833420622, id=31, make=cool runner 27
    -created fan(tx=false): 1601333072, id=32, make=cool runner 28
    ...
    -call next value for FAN_SEQ
    -created fan(tx=false): 1591063329, id=42, make=cool runner 38
    -created fan(tx=false): 2129344690, id=43, make=cool runner 39
    -created fan(tx=false): 223662325, id=44, make=cool runner 40
    -call next value for FAN_SEQ
    -created fan(tx=false): 1835794313, id=45, make=cool runner 41
    
    • Same as transaction active so far

  • Output when transaction activated

    -starting tx(tx=false): 1451387509, id=26, make=cool runner 22
    
    -tx started, flushing (tx=true): 1451387509, id=26, make=cool runner 22
    -insert into ORMCORE_FAN (make, id) values (?, ?)
    -binding parameter [1] as [VARCHAR] - [cool runner 22]
    -binding parameter [2] as [BIGINT] - [26]
    -insert into ORMCORE_FAN (make, id) values (?, ?)
    -binding parameter [1] as [VARCHAR] - [cool runner 23]
    -binding parameter [2] as [BIGINT] - [27]
    ...
    -insert into ORMCORE_FAN (make, id) values (?, ?)
    -binding parameter [1] as [VARCHAR] - [cool runner 41]
    -binding parameter [2] as [BIGINT] - [45]
    -cache flushed (tx=true): 1451387509, id=26, make=cool runner 22
    -tx committed (tx=false): 1451387509, id=26, make=cool runner 22
    
    • As with previous case -- nothing started with transaction opening

    • All inserts are delayed until next flush cycle




Figure 48.18. TABLE Test

logger.info("testTABLE");

logger.debug("table id before(tx={})={}", txActive(), getTableId());
//note that since PKs are generated, we must pass in an object that
//has not yet been assigned a PK value.
ejava.examples.orm.core.annotated.EggBeater eggbeater = new EggBeater(0);
eggbeater.setMake("done right 1");
//insert a row in the database
logger.info("persisting eggbeater (tx={}): {}", txActive(), eggbeater);
em.persist(eggbeater);
logger.info("created eggbeater (before flush; tx={}): {}", txActive(), eggbeater);
em.flush(); 
logger.info("created eggbeater (after flush; tx={}): {}", txActive(), eggbeater);
assertNotEquals(0, eggbeater.getId());   
logger.debug("table id after(tx={})={}", txActive(), getTableId());
-testTABLE
-select UID_VAL from ORMCORE_EB_UID where UID_ID='ORMCORE_EGGBEATER'
-extracted value ([UID_VAL] : [NUMERIC]) - [7]
-table id before(tx=true)=7

-persisting eggbeater (tx=true): 1049628186, id=0, make=done right 1
-select tbl.UID_VAL from ORMCORE_EB_UID tbl where tbl.UID_ID=? for update
-update ORMCORE_EB_UID set UID_VAL=?  where UID_VAL=? and UID_ID=?
-select tbl.UID_VAL from ORMCORE_EB_UID tbl where tbl.UID_ID=? for update
-update ORMCORE_EB_UID set UID_VAL=?  where UID_VAL=? and UID_ID=?

-created eggbeater (before flush; tx=true): 1049628186, id=8, make=done right 1
-insert into ORMCORE_EGGBEATER (make, id) values (?, ?)
-binding parameter [1] as [VARCHAR] - [done right 1]
-binding parameter [2] as [BIGINT] - [8]

-created eggbeater (after flush; tx=true): 1049628186, id=8, make=done right 1
-select UID_VAL from ORMCORE_EB_UID where UID_ID='ORMCORE_EGGBEATER'
-extracted value ([UID_VAL] : [NUMERIC]) - [17]
-table id after(tx=true)=17
  • Provider gets a primary key value during persist

  • Provider locks row (SELECT FOR UPDATE) during transaction and updates with new value

  • Provider inserts row during flush cycle with generated primary key value

  • Cannot explain the initial 2x ID requests but both incremented the value to a result of 17 (7+5+5=17)


Figure 48.19. Follow-on TABLE Allocations

-created ehhbeater(tx=true): 1275580924, id=9, make=done right 2
	-insert into ORMCORE_EGGBEATER (make, id) values (?, ?)
	-binding parameter [1] as [VARCHAR] - [done right 2]
	-binding parameter [2] as [BIGINT] - [9]
-table id after[2](tx=true)=17

-created ehhbeater(tx=true): 1726759945, id=10, make=done right 3
	-insert into ORMCORE_EGGBEATER (make, id) values (?, ?)
	-binding parameter [1] as [VARCHAR] - [done right 3]
	-binding parameter [2] as [BIGINT] - [10]
-table id after[3](tx=true)=17
...
-created ehhbeater(tx=true): 154468798, id=13, make=done right 6
	-insert into ORMCORE_EGGBEATER (make, id) values (?, ?)
	-binding parameter [1] as [VARCHAR] - [done right 6]
	-binding parameter [2] as [BIGINT] - [13]
-table id after[6](tx=true)=17

	-select tbl.UID_VAL from ORMCORE_EB_UID tbl where tbl.UID_ID=? for update
	-update ORMCORE_EB_UID set UID_VAL=?  where UID_VAL=? and UID_ID=?
-created ehhbeater(tx=true): 490475818, id=14, make=done right 7
	-insert into ORMCORE_EGGBEATER (make, id) values (?, ?)
	-binding parameter [1] as [VARCHAR] - [done right 7]
	-binding parameter [2] as [BIGINT] - [14]
-table id after[7](tx=true)=22
...
-created ehhbeater(tx=true): 360233196, id=18, make=done right 11
	-insert into ORMCORE_EGGBEATER (make, id) values (?, ?)
	-binding parameter [1] as [VARCHAR] - [done right 11]
	-binding parameter [2] as [BIGINT] - [18]
-table id after[11](tx=true)=22

	-select tbl.UID_VAL from ORMCORE_EB_UID tbl where tbl.UID_ID=? for update
	-update ORMCORE_EB_UID set UID_VAL=?  where UID_VAL=? and UID_ID=?	
-created ehhbeater(tx=true): 1912769093, id=19, make=done right 12
	-insert into ORMCORE_EGGBEATER (make, id) values (?, ?)
	-binding parameter [1] as [VARCHAR] - [done right 12]
	-binding parameter [2] as [BIGINT] - [19]
-table id after[12](tx=true)=27
...
-created ehhbeater(tx=true): 1947681232, id=23, make=done right 16
	-insert into ORMCORE_EGGBEATER (make, id) values (?, ?)
	-binding parameter [1] as [VARCHAR] - [done right 16]
	-binding parameter [2] as [BIGINT] - [23]
-table id after[16](tx=true)=27

	-select tbl.UID_VAL from ORMCORE_EB_UID tbl where tbl.UID_ID=? for update
	-update ORMCORE_EB_UID set UID_VAL=?  where UID_VAL=? and UID_ID=?
-created ehhbeater(tx=true): 783682673, id=24, make=done right 17
	-insert into ORMCORE_EGGBEATER (make, id) values (?, ?)
	-binding parameter [1] as [VARCHAR] - [done right 17]
	-binding parameter [2] as [BIGINT] - [24]
-table id after[17](tx=true)=32
...
-committing (tx=true): 1275580924, id=9, make=done right 2
-tx committed (tx=false): 1275580924, id=9, make=done right 2
...

TABLE Strategy Allows Clients to Self-Generate Groups of PK Values

As will SEQUENCE, the TABLE strategy allows each client to generate an allocationSize amount of primary key values before requiring a flush of the current batch or polling for a new table value.

Figure 48.20. TABLE Test (persist without active transaction)

  • Output prior to transaction

    -tx(tx=false)
    -created ehhbeater(tx=false): 150835665, id=29, make=done right 22
    -table id after[22](tx=false)=37
    -created ehhbeater(tx=false): 315885065, id=30, make=done right 23
    -table id after[23](tx=false)=37
    -created ehhbeater(tx=false): 423539130, id=31, make=done right 24
    -table id after[24](tx=false)=37
    -created ehhbeater(tx=false): 841313896, id=32, make=done right 25
    -table id after[25](tx=false)=37
    -created ehhbeater(tx=false): 1673518027, id=33, make=done right 26
    -table id after[26](tx=false)=37
    
    -select tbl.UID_VAL from ORMCORE_EB_UID tbl where tbl.UID_ID=? for update
    -update ORMCORE_EB_UID set UID_VAL=?  where UID_VAL=? and UID_ID=?
    -created ehhbeater(tx=false): 1042223174, id=34, make=done right 27
    -table id after[27](tx=false)=42
    ...
    -created ehhbeater(tx=false): 584643821, id=38, make=done right 31
    -table id after[31](tx=false)=42
    
    -select tbl.UID_VAL from ORMCORE_EB_UID tbl where tbl.UID_ID=? for update
    -update ORMCORE_EB_UID set UID_VAL=?  where UID_VAL=? and UID_ID=?
    -created ehhbeater(tx=false): 999999316, id=39, make=done right 32
    -table id after[32](tx=false)=47
    ...
    -created ehhbeater(tx=false): 1815337594, id=43, make=done right 36
    -table id after[36](tx=false)=47
    
    -select tbl.UID_VAL from ORMCORE_EB_UID tbl where tbl.UID_ID=? for update
    -update ORMCORE_EB_UID set UID_VAL=?  where UID_VAL=? and UID_ID=?
    -created ehhbeater(tx=false): 362311125, id=44, make=done right 37
    -table id after[37](tx=false)=52
    ...
    -created ehhbeater(tx=false): 1292683326, id=48, make=done right 41
    -table id after[41](tx=false)=52
    
    • Provider obtains next allocation value outside of transaction where rows inserted

    • Next allocation value requires transaction to update -- results in extra transaction per allocation

    • Provider assigns ID to object from allocation prior to transaction where rows are inserted

  • Output once transaction started

    -starting tx(tx=false): 150835665, id=29, make=done right 22
    -tx started, flushing (tx=true): 150835665, id=29, make=done right 22
    -insert into ORMCORE_EGGBEATER (make, id) values (?, ?)
    -binding parameter [1] as [VARCHAR] - [done right 22]
    -binding parameter [2] as [BIGINT] - [29]
    -insert into ORMCORE_EGGBEATER (make, id) values (?, ?)
    -binding parameter [1] as [VARCHAR] - [done right 23]
    -binding parameter [2] as [BIGINT] - [30]
    -insert into ORMCORE_EGGBEATER (make, id) values (?, ?)
    -binding parameter [1] as [VARCHAR] - [done right 24]
    -binding parameter [2] as [BIGINT] - [31]
    -insert into ORMCORE_EGGBEATER (make, id) values (?, ?)
    -binding parameter [1] as [VARCHAR] - [done right 25]
    -binding parameter [2] as [BIGINT] - [32]
    -insert into ORMCORE_EGGBEATER (make, id) values (?, ?)
    -binding parameter [1] as [VARCHAR] - [done right 26]
    -binding parameter [2] as [BIGINT] - [33]
    ...
    -insert into ORMCORE_EGGBEATER (make, id) values (?, ?)
    -binding parameter [1] as [VARCHAR] - [done right 41]
    -binding parameter [2] as [BIGINT] - [48]
    -cache flushed (tx=true): 150835665, id=29, make=done right 22
    -tx committed (tx=false): 150835665, id=29, make=done right 22
    
    • Provider inserts rows from cache during next flush cycle

    • Each row has a pre-assigned ID from provider

    • Database makes no correlation between ID within row and the table maintaining the IDs


  • Primary key consisting of multiple properties

  • Represented using a primary key class

    • Must implement Serializable

      public class MowerPK implements Serializable {
    • Must have a public no-arg constructor

      public MowerPK() {}
    • Must implement hashCode() and equals() methods

      @Override
      
      public int hashCode() { ... }
      @Override
      public boolean equals(Object obj) { ... }
    • Must define properties that match with the definition of the entity, including access

      private String make;
      private String model;
  • Accessed using same strategy as entity using it




Figure 49.4. Composite @IdClass Example Client

  • Create Entity with Composite ID

    ejava.examples.orm.core.annotated.Mower mower =  new Mower("acme", "power devil2");
    
    mower.setSize(21);
    //insert a row in the database
    logger.info("persisting mower(tx={}): {}", txActive(), mower);
    em.persist(mower);
    logger.info("created mower: {}", mower);
    em.flush();
    logger.info("flushed");
    -persisting mower(tx=true): 1756435781, make=acme, model=power devil2, size=21
    
    -created mower: 1756435781, make=acme, model=power devil2, size=21
    -insert into ORMCORE_MOWER (size, make, model) values (?, ?, ?)
    -binding parameter [1] as [INTEGER] - [21]
    -binding parameter [2] as [VARCHAR] - [acme]
    -binding parameter [3] as [VARCHAR] - [power devil2]
    -flushed
  • Get Instance by Composite ID

    //locate instance by ID, while instance still managed        
    
    Mower mower2 = em.find(Mower.class, new MowerPK("acme", "power devil2"));
    assertNotNull(mower2);
    logger.info("found mower: {}", mower2);
    assertEquals(mower.getSize(), mower2.getSize());        
    -found mower: 1756435781, make=acme, model=power devil2, size=21
    
    • Instance is found in cache by ID without having to query database

  • Remove Instance from Database

    em.remove(mower2);
    
    logger.info("removed mower: {}", mower2);
    em.flush();
    logger.info("removed mower after flush: {}", mower2);
    Mower mower3 = em.find(Mower.class, new MowerPK("acme", "power devil2"));
    assertNull(mower3);
    -removed mower: 1756435781, make=acme, model=power devil2, size=21
    
    -delete from ORMCORE_MOWER 
     where make=? and model=?
    -binding parameter [1] as [VARCHAR] - [acme]
    -binding parameter [2] as [VARCHAR] - [power devil2]
    -removed mower after flush: 1756435781, make=acme, model=power devil2, size=21
    -select mower0_.make as make1_7_0_, mower0_.model as model2_7_0_, mower0_.size as size3_7_0_ 
     from ORMCORE_MOWER mower0_ 
     where mower0_.make=? and mower0_.model=?
    -binding parameter [1] as [VARCHAR] - [acme]
    -binding parameter [2] as [VARCHAR] - [power devil2]
    • Row removed from database and instance detached

    • Primary key used to query database since instance no longer in cache










  • Map a single Java class to multiple tables in database

  • Uses a join of two or more tables and a single Java class

    1. One table is designated as primary

    2. Secondary and primary must have a column to join





  • Similar to @EmbeddedId except embedded object is not a primary key

  • Embedded object has no primary key -- housed in parent entity table

  • Embedded object represents some abstraction

  • Parent entity represents an abstraction with a primary key and the embedded object

    • The primary key could be the only thing the parent entity is providing






package ejava.jpa.example.validation;


import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
import javax.validation.Constraint;
import javax.validation.Payload;
/**
 * Defines a constraint annotation for expressing a minimum age.
 */
@Documented
@Constraint(validatedBy={MinAgeValidator.class})
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
public @interface MinAge {
    String message() default "too young";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default{};
    int age() default 0;
}
package ejava.jpa.example.validation;

...
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class MinAgeValidator implements ConstraintValidator<MinAge, Date>{
    int minAge;
    @Override
    public void initialize(MinAge constraint) {
        this.minAge = constraint.age();
    }
    @Override
    public boolean isValid(Date date, ConstraintValidatorContext ctx) {
        if (date==null) { return true; }
        //get today's date
        Calendar latestBirthDate = new GregorianCalendar();
        latestBirthDate.add(Calendar.YEAR, -1*minAge);
        
        //get calendate date of object
        Calendar birthDate = new GregorianCalendar();
        birthDate.setTime(date);
        if (birthDate.after(latestBirthDate)) {
            String errorMsg = String.format("%d is younger than minimum %d", 
                    getAge(birthDate), 
                    minAge);
            ctx.buildConstraintViolationWithTemplate(errorMsg)
                .addConstraintViolation();
            return false;
        } else {
            return true;
        }
    }
    
    private int getAge(Calendar birth) {
    ...
...
}

Define a sequence of validation groups to be tested in order and short-circuit upon failure

Define Constraints external to Bean Class

Provide access to persistence lifecycle events

Table of Contents

Purpose
1. Goals
2. Objectives
56. One-to-One Relationships
56.1. One-to-One: Uni-directional
56.2. @OneToOne Annotation
56.3. @JoinColumn Annotation
56.4. @JoinColumns Annotation
56.5. One-to-One: Bi-directional
56.6. Bi-directional Relationships and Ownership
56.7. Other Topics
56.8. Summary
57. One-to-Many Relationships
57.1. One-to-Many: Uni-directional
57.2. @OneToMany Annotation
57.3. One-to-Many: Bi-directional
57.4. @ManyToOne Annotation
57.5. One-to-Many: Element Collection
57.6. @ElementCollection Annotation
57.7. Other Topics
57.8. Summary
58. Relationship Capabilities
58.1. Fetching
58.1.1. fetch=LAZY
58.1.2. fetch=EAGER
58.2. Cascades
58.3. Orphan Removal
58.4. Summary
59. Object Collections
59.1. Object Identity
59.1.1. Instance Id
59.1.2. Primary Key Id
59.1.3. Switching Ids
59.1.4. Business Id
59.2. Collection Types
59.3. Summary
60. Foreign Keys
60.1. Primary Key Join
60.1.1. @PrimaryKeyJoin Annotation
60.2. Using Composite Primary Key Property as Foreign Key
60.2.1. Using @IdClass Composite Primary Key Property as Foreign Key
60.2.2. Using @EmbeddedId Composite Primary Key Property as Foreign Key
60.3. Using Foreign Key in Composite Primary Key
60.3.1. Using Foreign Key in @IdClass Composite Primary Key
60.3.2. Using Foreign Key in @EmbeddedId Composite Primary Key (@MapsId)
60.4. Join Tables
60.4.1. @JoinTable Annotation
60.5. Summary
61. Many-to-One Relationships
61.1. Many-to-One: Uni-directional
61.2. Summary
62. Many-to-Many Relationships
62.1. Many-to-Many: Uni-directional
62.2. Many-to-Many: Bi-directional
62.3. Summary


Uni-directional

Only one class ("owner") knows of the relationship

  • Uses the @OneToOne annotation

  • Defines mapping to database

    • Uses either @JoinColumn or @JoinTable

Bi-directional

Both classes know of the relationship

  • Both classes use the @OneToOne annotation

  • One class is considered the owner and maps relation to the database

    • Uses either @JoinColumn or @JoinTable

    • Changes here change the database

  • One class is considered the inverse and names the other entity's property

    • @OneToOne(mappedBy="owning-property")

    • Changes here do *not* change database


  • Relation realized through a foreign key

  • Foreign key represented by a separate column

  • Foreign key from owning entity table references primary key of inverse entity table


Figure 56.5. One-to-One Uni-directional Usage

  • Form the Relationship

    
    
    //create the owning side 
    ejava.examples.orm.rel.annotated.Person person = new Person();
    person.setFirstName("john");
    person.setLastName("doe");
    person.setPhone("410-555-1212");
    //create the inverse side 
    ejava.examples.orm.rel.annotated.Photo photo = new Photo();        
    photo.setImage(image);        
    //create the person and photo detached
    assertEquals(0, person.getId());
    assertEquals(0, photo.getId());
    //add photo to person and persist object tree
    person.setPhoto(photo); //this sets the FK in person
    logger.info("added photo to person:{}", person);
    em.persist(person);        
    assertNotEquals("personId not set", 0, person.getId());
    assertNotEquals("photoId not set", 0, photo.getId());
    logger.info("created person:{}", person);
    logger.info("     and photo:{}", photo);
    em.flush();
  • Output

    -added photo to person:Person@7ab802f4, id=0, name=john doe, phone=410-555-1212, 
        photo=Photo@608cd501, id=0. image=46080 bytes
    -
        call next value for hibernate_sequence
    -
        call next value for hibernate_sequence
    -Photo@608cd501: getId()=2
    -created person:Person@7ab802f4, id=1, name=john doe, phone=410-555-1212, 
        photo=Photo@608cd501, id=2. image=46080 bytes
    -     and photo:Photo@608cd501, id=2. image=46080 bytes
    -
        insert
        into
            ORMREL_PHOTO
            (image, PHOTO_ID)
        values
            (?, ?)
    -
        insert
        into
            ORMREL_PERSON
            (firstName, lastName, phone, PERSON_PHOTO, PERSON_ID)
        values
            (?, ?, ?, ?, ?)
    
    • Person and Photo are given an ID during the persist

    • Photo is inspected for its ID to be assigned to Person FK to Photo

    • Rows (with foreign key) are inserted into database on next flush cycle

  • Find Object with Relationship in Database

    //verify what we can get from DB
    
    em.flush(); em.clear();
    Person person2 = em.find(Person.class, person.getId());
    assertNotNull(person2);
    assertNotNull(person2.getPhoto());
    logger.info("found person:{}", person2);
  • Output

    -
        select
            person0_.PERSON_ID as PERSON_I1_27_0_,
            person0_.firstName as firstNam2_27_0_,
            person0_.lastName as lastName3_27_0_,
            person0_.phone as phone4_27_0_,
            person0_.PERSON_PHOTO as PERSON_P5_27_0_
        from
            ORMREL_PERSON person0_
        where
            person0_.PERSON_ID=?
    -Person@20cdb152, ctor()
    -Photo$HibernateProxy$2MfejJnf@0: ctor()
    -
        select
            photo0_.PHOTO_ID as PHOTO_ID1_28_0_,
            photo0_.image as image2_28_0_
        from
            ORMREL_PHOTO photo0_
        where
            photo0_.PHOTO_ID=?
    -Photo@57fdb8a4: ctor()
    -found person:Person@20cdb152, id=1, name=john doe, phone=410-555-1212, 
        photo=Photo@57fdb8a4, id=2. image=46080 bytes
    

Calls to em.clear() are for test purposes only!

Calls to em.clear() for are test purposes only and should not be a common thing in production code. We must clear the current instance from the cache if we want the provider to query the database for the rows versus pulling the existing instance from the cache. Calling em.clear() within production code will clear all instances from the persistence context and make your code have unwanted side-effects when called.

optional:boolean (default=true)

Designates whether relation is required. Default is true.

fetch:FetchType (default=EAGER)

Use EAGER or LAZY fetching of relationship when loading this entity. More of coverage of fetch in Fetching section

orphanRemoval:boolean (default=false)

Remote entity only exists for use by this relation. Automatically delete when relation terminated.

cascade:CascadeType[] (default=none)

Perform actions taken on this entity on related entity

targetEntity:Class

Define type for related class (if related Java type over-generalized)

mappedBy:String

Used by inverse side to specify owning entity property that maps relation to DB


  • No additional foreign key used to satisfy the bi-directional aspect of relation



Note

Notice only owning entity table is updated when relationship formed.








Uni-directional

Only one side ("owner") knows of the relationship

  • Uses the @OneToMany annotation

  • Defines mapping to database

    • Uses either @JoinColumn or @JoinTable

    Note

    @JoinTable adds the foreign key to the child table and not to the owning entity class table in this uni-directional case

Bi-directional

Both classes know of the relationship

  • Many side required to be owning side and maps relation to the database

    • Uses the @ManyToOne annotation

    • Uses either @JoinColumn or @JoinTable

    • Changes here change the database

  • One side required to be inverse and names the other entity's property

    • @OneToMany(mappedBy="owning-property")

    • Changes here do *not* change database




  • Relationship formed when inverse side added to owning collection








  • Same as fetch=LAZY case


fetch=EAGER does not always mean "done "efficiently"

fetch=EAGER is only a promise to fetch the related object before returning from the call. It does not always mean that a SQL JOIN was performed. There are times (e.g., fetch=EAGER on multiple collections within parent) when the provider will perform a SQL JOIN for some of the relationships and the functional equivalent of a fetch=LAZY for the remaining relationships.

Don't over user fetch=EAGER

Although useful at times, avoid haphazard use of fetch=EAGER when defining relationships and look to rely on custom queries to do this behavior instead. It is very easy to turn a fetch=LAZY into EAGER at query time. It is very difficult to do the opposite. Custom queries also allow us to incrementally build a complex parent object tree an optimized query at a time. fetch=EAGER will do the same, but may perform 10s, 100s, 1000s of more unoptimized queries of child rows we may never need.




Note

In this case, the primary key is used as the foreign key and must be set. To use the foreign key as the primary key -- use @MapsId

    //@PrimaryKeyJoinColumn  //the two tables will be joined by PKs
    @MapsId
    private Person identity;
    public Borrower(Person identity) {
        this.identity = identity;
    }


  • Setting primary key prior to persisting

  • Foreign key used twice -- once for relation and once for primary key




  • HOUSE_ID is both primary and foreign key for ROOM









  • No longer modeling separate primary key - derived from foreign key











  • Separate table created to hold foreign keys

  • Can be used for all relationship enumatations and directions


  • Join table name either derived from associated tables or explicitly named

  • Join table columns either derived from referenced table column or explicitly named


  • Unique constraint enforces the (1)-to-Many aspect of relationship









Uni-directional

Only one side ("owner") knows of the relationship

  • Uses the @ManyToMany annotation

  • Defines mapping to database

    • Must use @JoinTable

Bi-directional

Both classes know of the relationship

  • One side required to be owning side and maps relation to the database

    • Uses the @ManyToMany annotation

    • Must use @JoinTable

    • Changes here change the database

  • One side required to be inverse and names the other entity's property

    • @ManyToMany(mappedBy="owning-property")

    • Changes here do *not* change database





  1. Rows added to join table rather than updating entity class tables with foreign key




  • Owning and inverse sides now both need to be set



  • Parent defines default mapping for all derived types


  • Supplies type-specific column value


  • Accepts default type-specific column value


  • Two rows inserted into single base table with discriminator type supplied


  • Single table queried for objects


  • Single table "sparsely" filled based on type


  • Parent defines default mapping for all derived types, including PK generation

  • Using a common SEQUENCE allows sub-classes to have unique PKs across all tables


  • Subclasses name their specific entity class table


  • Rows for entities placed into separate tables


  • Query through parent type causes SQL "UNION ALL" of each concrete sub-class table


  • Table per concrete class

  • No unused columns

  • Parent columns repeated in each sub-class table


  • Each persist() must insert into concrete class table and parent class table(s)


  • Parent class table joined with each sub-class table during query of parent type


  • Entities span multiple tables


  • Parent class is not a legal entity -- has no @Id

Note

In this example, the implementation of BaseObject actually has an id attribute that the derived classes make use of. However, it is marked as @Transient in the base class and @Id in the derived Entity classes since MappedSuperClasses do not have primary keys. This specific example could have also used TABLE_PER_CLASS because of the availability of an id property in the base class.


  • "version" column name from parent being renamed

  • "id" property defined in this class given custom column name

  • Transient attribute in parent being reused to hold @Id for subclass using PROPERTY access


  • Entity accepts mapping defaults


  • Rows are inserted into type-specific entity class tables


  • Separate tables are accessed when obtaining each type


  • Separate tables per concrete class (like TABLE_PER_CLASS)

  • No unused columns (like TABLE_PER_CLASS)

Table of Contents

Purpose
1. Goals
2. Objectives
I. General Queries
68. JPA Query Types
68.1. JPA Query Language (JPA-QL) Queries
68.2. Native SQL Queries
68.2.1. SqlResultSetMappings
68.3. Criteria API Queries
68.4. Strongly Typed Queries
68.4.1. Metamodel API
68.4.2. Query using JPA Metamodel
68.4.3. Canonical Metamodel
68.4.4. Generating Canonical Metamodel Classes
68.5. Summary
69. JPA Query Overview
69.1. EntityManager Query Methods
69.2. Query.getSingleResult()
69.3. Query.getResultList
69.4. Query.getResultStream
69.5. Parameters
69.6. Paging Properties
69.7. Pessimistic Locking
69.8. Bulk Updates
69.9. Named Queries
69.10. Summary
II. JPAQL
70. JPA Query Language
70.1. Simple Entity Query
70.2. Non-Entity Queries
70.3. Multi-select Query
70.3.1. Multi-select Query with Object[]
70.3.2. Multi-select Query with Tuple
70.3.3. Multi-select Query with Constructor
70.4. Path Expressions
70.4.1. Single Element Path Expressions
70.4.2. Collection Element Path Expressions
70.5. Eager Fetching through JOINs
70.5.1. Lazy Fetch Problem
70.5.2. Adding Eager Fetch during Query
70.6. Distinct Results
70.7. Summary
71. JPAQL Where Clauses
71.1. Equality Test
71.2. Like Test
71.3. Formulas
71.4. Logic Operators
71.5. Equality Tests
71.6. Between
71.7. Testing for Null
71.8. Testing Empty Collection
71.9. Membership Test
71.10. Subqueries
71.11. All
71.12. Any
71.13. Summary
72. JPAQL Functions
72.1. String Functions
72.1.1. Base Query
72.1.2. LOWER
72.1.3. UPPER
72.1.4. TRIM
72.1.5. CONCAT
72.1.6. LENGTH
72.1.7. LOCATE
72.1.8. SUBSTRING
72.2. Date Functions
72.3. Order By
72.4. Aggregate Functions
72.4.1. COUNT
72.4.2. MIN/MAX
72.4.3. SUM/AVE
72.5. Group By
72.6. Having
72.7. Summary
III. Criteria API
73. JPA Criteria API
73.1. Criteria API Demo Template
73.2. Simple Entity Query
73.3. Non-Entity Query
73.4. Multi-select Query
73.4.1. Multi-select Query with Object[]
73.4.2. Multi-select Query with Tuple
73.4.3. Multi-select Query with Constructor
73.5. Path Expressions
73.5.1. Single Element Path Expressions
73.5.2. Collection Element Path Expressions
73.6. Eager Fetching through JOINs
73.6.1. Lazy Fetch Problem
73.6.2. Adding Eager Fetch during Query
73.7. Distinct Results
73.8. Summary
74. Criteria Where Clauses
74.1. Equality Test
74.2. Like Test
74.2.1. Like Test Literal
74.2.2. Like Test Literal Parameter
74.2.3. Like Test Concatenated String
74.2.4. Like Test Single Character Wildcard
74.3. Formulas
74.4. Logic Operators
74.5. Equality Tests
74.6. Between
74.7. Testing for Null
74.8. Testing Empty Collection
74.9. Membership Test
74.10. Subqueries
74.11. All
74.12. Any
74.13. Summary
75. Criteria Functions
75.1. String Functions
75.1.1. Base Query
75.1.2. LOWER
75.1.3. UPPER
75.1.4. TRIM
75.1.5. CONCAT
75.1.6. LENGTH
75.1.7. LOCATE
75.1.8. SUBSTRING
75.2. Date Functions
75.3. Order By
75.4. Aggregate Functions
75.4.1. COUNT
75.4.2. MIN/MAX
75.4.3. SUM/AVE
75.5. Group By
75.6. Having
75.7. Summary

Three fundamental query types within JPA

  • JPA Query Language (JPA) - entity/property/relationship-based

  • Native SQL - table/column-based

  • Criteria API - entity/property/relationship-based using Java classes


  • "c" is part of root query

  • "c" represents rows from Customer entity table(s)

  • "c.firstName and c.lastName" are property paths off root term

  • ":firstName" is parameter placeholder

  • "Customer.class" type parameter allows for a type-safe return result


  • Placeholder is replaced by runtime parameter

  • Zero-or-more results are requested

  • Entities returned are managed



  • "c" represents rows in table

  • specific columns (or *) are return for each row

  • "?" marks a positional parameter -- non-portable to use named parameters in native SQL queries

  • TypedQuery<T>s not supported in native SQL queries because of a conflict with legacy JPA 1.0 API


  • Query execution similar to other query types

  • User-provided SQL executed

Note

Legacy JPA 1.0 Native SQL query syntax already used the signature of passing in a Class for createNativeQuery(). In this context, it was an entity class that contained JPA mappings for the query -- not the returned entity type. This prevented createNativeQuery() from being updated to return a typed result in JPA 2.0.







  • "CriteriaBuilder" used as starting point to build objects within the query tree

  • "CriteriaQuery<T>" used to hold the definition of query

  • "Root<T>" used to reference root level query terms

  • "CriteriaBuilder.from()" used to designate the entity that represents root query term

    • Result used to create path references for query body

  • "CriteriaBuilder.select()" officially lists the objects returned from query

  • "CriteriaBuilder.where()" builds a decision predicate of which entities to include

  • "CriteriaBuilder.equal()" builds an equals predicate for the where clause

  • "Root<T>.get()" returns the property referenced in path expression

  • "CriteriaBuilder.parameter()" builds a parameter placeholder within query. Useful with @Temporal date comparisons


  • Query execution identical to JPA-QL case



  • Access to properties within entities done through type-safe accessors


  • Results identical to previous approaches



  • Construct or generate a canonical metamodel class to provide type-safe, easy access to properties


  • Use canonical metamodel class to provide type-safe, easy access to properties ("Customer_.firstName")


  • Result is identical to previous approaches


  • More work to get here but clean, result

  • Type-safe - queries will not compile if entity changes





  • :firstName and :lastName act as placeholders for runtime query parameters

  • Runtime parameters supplied using placeholder names

  • A parameter for each placeholder must be supplied - no defaults

  • A placeholder must exist for each parameter supplied - no extras


  • Appended numbers (?1) assign an ordinal value

  • No numbers supplied (?) cause default value based on order


  • Dates are specified as DATE, TIME, or TIMESTAMP


  • Change directly applied to database, not the cached entity

  • Number of entities changed returned



  • Bulk deletes do not trigger cascades

  • Entity instance exists in memory even after deleted from database



  • Keeping stale entities around will produce confusing results

  • "em.clear()" should be avoided except at end of transaction since un-manages everything




  • Example query uses Native SQL to return all columns for table



  • Individual elements of select are matched up against class constructor


  • Constructed class may be simple POJO -- no need to be an entity

  • Instances are not managed

  • Suitable for use as Data Transfer Objects (DTOs)


  • Each row returned as instance of provided class


  • A normal JOIN (implicit or explicit) may honor the fetch=LAZY property setting of the relation

  • Can be exactly what is desired

  • Can also cause problems or extra work if not desired


  • Sales are lazily fetched when obtaining Store


  • Accessing the Sale properties causes a LazyInitializationException when persistence context no longer active or accessible

One Row per Parent is Returned for fetch=LAZY

Note that only a single row is required to be returned from the database for a fetch=LAZY relation. Although it requires more queries to the database, it eliminates duplicate parent information for each child row and can eliminate the follow-on query all together when not accessed.








  • Compare entities and not primary/foreign key values


Can be used to determine membership in a collection


  • Defines a shorthand for a subquery




  • List all clerks that have all sales above $125.00 or none at all

  • -or- List all clerks with no sale <= $125.00


  • Manny excluded because has 1 sale below $125.00

  • Moe included because has only $150.00 sale

  • Jack included because has no sales that fail criteria


  • List all clerks that have all sales below $125.00 or none at all

  • -or- List all clerks with no sale >= $125.00


  • Manny excluded because has 1 sale above $125.00

  • Moe excluded because has only $150.00 sale

  • Jack included because has no sales that fail criteria


  • List all clerks that have at least one sale above $125.00


  • Manny included because has 1 sale above $125.00

  • Moe included because $150.00 sale qualifies him as well

  • Jack excluded because has no sales that meet criteria


  • List all clerks that have at least one sale below $125.00


  • Manny included because has 1 sale below $125.00

  • Moe excluded because his only $150.00 sale above criteria

  • Jack excluded because has no sales that meet criteria


  • Located two Sales that occurred prior to today's date


  • Located no sales on today's date


  • Update all sales to today


  • Now locating sales for today's date

Note

Bulk commands (i.e., update) invalidate cached entities. You must refresh their state with the database or detach/clear them from the persistence context to avoid using out-dated information.



  • Aliases may be assigned to select terms for named-access to results


  • Query defined to return instances of Tuple class

  • Tuples provide access using

    • get(index) - simular to Object[]

    • get(index, Class<T> resultType) - typed access by index

    • get(alias) - access by alias

    • get(alias, Class<T> resultType) - typed access by alias

    • getElements() - access thru collection interface




  • Individual elements of select() are matched up against class constructor


  • Constructed class may be simple POJO -- no need to be an entity

  • Instances are not managed

  • Suitable for use as Data Transfer Objects (DTOs)




  • All paths based off root-level FROM (or JOIN) terms

  • Paths use call chaining to change contexts

  • Paths -- used this way -- must always express a single element. Must use JOINs for paths involving collections

  • All paths based off root-level FROM (or JOIN) terms



  • Automatic INNER JOIN formed between Sale and Store because of the cross-entity path



  • A normal JOIN (implicit or explicit) may honor the fetch=LAZY property setting of the relation

  • Can be exactly what is desired

  • Can also cause problems or extra work if not desired


  • Sales are lazily fetched when obtaining Store


  • Accessing the Sale properties causes a LazyInitializationException when persistence context no longer active or accessible


One Row per Parent is Returned for fetch=LAZY

Note that only a single row is required to be returned from the database for a fetch=LAZY relation. Although it requires more queries to the database, it eliminates duplicate parent information for each child row and can eliminate the follow-on query all together when not accessed.













  • Compare entities and not primary/foreign key values



Can be used to test for an empty collection




  • Sub-select returns values from collection under test

  • Outer query tests for no existing (EMPTY)values




  • Sub-select returns values from collection under test

  • Outer query tests for existing (NOT EMPTY)values

Can be used to determine membership in a collection



  • Defines a shorthand for a subquery





  • List all clerks that have all sales above $125.00 or none at all

  • -or- List all clerks with no sale <= $125.00


  • Manny excluded because has 1 sale below $125.00

  • Moe included because has only $150.00 sale

  • Jack included because has no sales that fail criteria



  • List all clerks that have all sales below $125.00 or none at all

  • -or- List all clerks with no sale >= $125.00


  • Manny excluded because has 1 sale above $125.00

  • Moe excluded because has only $150.00 sale

  • Jack included because has no sales that fail criteria



  • List all clerks that have at least one sale above $125.00


  • Manny included because has 1 sale above $125.00

  • Moe included because $150.00 sale qualifies him as well

  • Jack excluded because has no sales that meet criteria



  • List all clerks that have at least one sale below $125.00


  • Manny included because has 1 sale below $125.00

  • Moe excluded because his only $150.00 sale above criteria

  • Jack excluded because has no sales that meet criteria




  • Located two Sales that occurred prior to today's date




  • Located no sales on today's date

  • Update with a bulk query

Note

Criteria API added Bulk Updates in JPA 2.1




  • Update all sales to today




  • Now locating sales for today's date

Note

Bulk commands (i.e., update) invalidate cached entities. You must refresh their state with the database or detach/clear them from the persistence context to avoid using out-dated information.

Table of Contents

Topics
76. SQL Tuning
76.1. Reasons for Inefficient SQL Performance
76.2. Execution Plan
76.3. Diagnostic Tools
76.3.1. Client/DAO Result
76.3.2. EXPLAIN PLAN
76.3.3. AUTOTRACE
76.3.4. Display Cursor Execution Plan within V$PLAN
76.4. Summary
77. Example Domain Model: Movies
77.1. Class Model
77.2. Database Schema
77.3. Database Size
77.4. Prepare DB Between Tests
78. Table Access
78.1. Full Table Scan
78.1.1. Full Table Scan: Unconstrained Access
78.1.2. Full Table Scan: Using Where (without Index)
78.1.3. RowId Scan: Using Where (with Index)
78.1.4. Full Table Scan: Invalidating Index using Function applied to Row Column
78.1.5. RowId Scan: Using Index with Function applied to Row Column
78.1.6. Full Table Scan: Invalidating Index by using Leading Wildcards
78.2. Order By
78.2.1. Order By using Sort
78.2.2. Order By using Index
78.2.3. Order By using Index DESC
78.2.4. Order By using Reverse Index DESC
78.3. Summary
79. Indexes
79.1. Index Range Scan
79.2. Unique Index Scan
79.3. Composite Index
79.3.1. Query Parts
79.3.2. First Term Indexed
79.3.3. First and Second Term Indexed (using Composite Index)
79.4. Index Fast Full Scan (with Composite Index)
79.4.1. Query Parts
79.4.2. Option: Use Range Scan and RowId Access
79.4.3. Option: Use Range Scan Alone with Composite Index
79.4.4. Option: Fast Full Scan
79.5. Summary
80. Joins
80.1. Foreign Keys
80.1.1. Query Parts
80.1.2. No Indexes
80.1.3. Perform Query with Support for Foreign Key Index
80.1.4. Foreign Key and Where Columns Indexed
80.1.5. Foreign Key, Where, and Join Columns Indexed
80.2. Join Types
80.2.1. Nested Loop Join
80.2.2. Hash Join
80.2.3. Sort Merge Join
80.3. Summary
81. JPA
81.1. Lazy and Eager Fetching
81.1.1. Get Parent
81.1.2. Get Parent and Children
81.2. Obtaining Instance Counts
81.2.1. Query Parts
81.2.2. Collection Size in DAO from Relation
81.2.3. Row Count in DAO from Query
81.2.4. Row Count in DB using Count() Query
81.2.5. Row Count in DB using Count Query without JOIN
81.3. Query Loops
81.3.1. Query Parts
81.3.2. Query Loops in DAO
81.3.3. Query Loops using DB Subquery
81.4. Paging
81.4.1. Query Parts
81.4.2. Paging within DAO
81.4.3. Paging within DB
81.5. Summary
82. H2 Execution Plans
82.1. Column Index
82.2. Summary
83. JPA/SQL Tuning Summary
83.1. Other Topics

Figure 76.3. EXPLAIN PLAN Example using Text SQL Commands

 
EXPLAIN PLAN FOR
select * from
    ( select
        movie0_.TITLE as col_0_0_,
        person2_.FIRST_NAME as col_1_0_,
        person2_.LAST_NAME as col_2_0_ 
    from JPATUNE_MOVIE movie0_ 
    inner join JPATUNE_DIRECTOR director1_ on movie0_.DIRECTOR_ID=director1_.PERSON_ID 
    inner join JPATUNE_PERSON person2_ on director1_.PERSON_ID=person2_.ID 
    order by title DESC ) 
where
    rownum <= :limit;
SET LINESIZE 100
SET PAGESIZE 0
select * from table(DBMS_XPLAN.DISPLAY());
plan FOR succeeded.
Plan hash value: 857441453
 
----------------------------------------------------------------------------------------------------
| Id  | Operation                 | Name           | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT          |                |   774K|   477M|       | 25766   (1)| 00:05:10 |
|*  1 |  COUNT STOPKEY            |                |       |       |       |            |          |
|   2 |   VIEW                    |                |   774K|   477M|       | 25766   (1)| 00:05:10 |
|*  3 |    SORT ORDER BY STOPKEY  |                |   774K|    45M|    53M| 25766   (1)| 00:05:10 |
|*  4 |     HASH JOIN             |                |   774K|    45M|    11M| 14333   (1)| 00:02:52 |
|*  5 |      HASH JOIN            |                |   271K|  8746K|  5568K|  5115   (1)| 00:01:02 |
|   6 |       INDEX FAST FULL SCAN| DIRECTOR_PK    |   271K|  2385K|       |   191   (1)| 00:00:03 |
|   7 |       TABLE ACCESS FULL   | JPATUNE_PERSON |  1637K|    37M|       |  1854   (1)| 00:00:23 |
|*  8 |      TABLE ACCESS FULL    | JPATUNE_MOVIE  |   774K|    20M|       |  7169   (1)| 00:01:27 |
----------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - filter(ROWNUM<=TO_NUMBER(:LIMIT))
   3 - filter(ROWNUM<=TO_NUMBER(:LIMIT))
   4 - access("MOVIE0_"."DIRECTOR_ID"="DIRECTOR1_"."PERSON_ID")
   5 - access("DIRECTOR1_"."PERSON_ID"="PERSON2_"."ID")
   8 - filter("MOVIE0_"."DIRECTOR_ID" IS NOT NULL)






  • Access paths - strategies used to access data within table

  • Indexes - good for when accessing small amounts of data out of much larger data set

  • Full table scans - good for small tables and unrestricted row access

  • Any row can be *functionally* accessed with either method

  • Use execution plans to help use the appropriate technique

Database engine derives all information from the table directly from the physical storage location for the table. Table storage is not arranged in any guaranteed order.

Order returned results by one or more columns

  • Can be unique or non-unique

  • Can be simple or composite

  • Can be normal (ascending) or descending

  • Can be reverse key (for monotonically incrementing column values) to balance B*-tree

  • Can be function-based (to address normalization uses)

  • Can be used to implement sort for "order by"

  • Can be used to implement the entire table (Index-organized Table(IOT))

  • Can be traversed in different ways

    • Unique Scan - used with "unique" or "primary key" indexes to return a single value

    • Range Scan - used with "unique" and "non-unique" indexes to return multiple matching rows

    • Full Scan - used with composite indexes where leading where column is not part of index (i.e., can use col2 of composite)

    • Fast Full Scan - used when all columns of query (where and select) are contained within composite index -- table is skipped

    • ...

  • Can be coalesced or rebuilt with

    ALTER INDEX (index-name) (COALESCE | REBUILD)
    • Coalesce - repairs index in place. Good for small repairs

    • Rebuild - totally rebuilds index. Good for large repairs

Indexes with multiple columns to match the where clause and optionally the select and join clauses as well.

Incorporate terms from select clause into composite index to bypass table access.

Improving performance of foreign key joins.

Used when driving table is small and join condition used to access second table

Used when driving table too large to fit into memory and join based on equality

Avoiding inefficient uses of JPA.

Cost impacts of lazy and eager fetch joins

Get just the root parent of an object graph

Get just the root parent of an object graph with fetch=LAZY relationships


  • Two one-to-many relationships (cast and genres) -- genres is a non-entity ElementCollection

  • One one-to-one relationship (director)

  • All use fetch=LAZY


  • Only parent table is queried

  • Children are lazily loaded


  • Unique scan of Movie primary key index to satisfy where clause and locate rowId

  • Row access by rowId to satisfy select clause


* "Thick" parent has an extra Movie.plot text property mapped to potentially make child joins more expensive. Thin parent does not have Movie plot mapped so the results can focus on access to smaller, related entities.

Fetch=LAZY Speeds Access to Single Entity Core Information

Choosing fetch=LAZY for relationships allows efficient access to the core information for that entity without paying a price for loading unnecessary relations.

Get just the root parent of an object graph with fetch=EAGER relationships

Figure 81.8. Relationships


<entity class="ejava.jpa.examples.tuning.bo.Movie">
    <attributes>
        <many-to-one name="director" fetch="EAGER">
            <join-column name="DIRECTOR_ID"/>
        </many-to-one>
        <one-to-many name="cast" fetch="EAGER" mapped-by="movie"/>
        
        <element-collection name="genres" fetch="EAGER">
            <column name="GENRE"/>
            <collection-table name="JPATUNE_MOVIEGENRE">
                <join-column name="MOVIE_ID"/>
                <unique-constraint>
                    <column-name>MOVIE_ID</column-name>
                    <column-name>GENRE</column-name>
                </unique-constraint>
            </collection-table>
        </element-collection>

        <transient name="plot"/>
    </attributes>
</entity>
public class MovieRole {

...
    @ManyToOne(optional=false, fetch=FetchType.LAZY,
            cascade={CascadeType.DETACH})
    @JoinColumn(name="ACTOR_ID")
    private Actor actor;

<entity class="ejava.jpa.examples.tuning.bo.MovieRole">
    <attributes>
        <many-to-one name="actor" fetch="EAGER">
            <join-column name="ACTOR_ID"/>
        </many-to-one>
    </attributes>
</entity>
public class Actor {

...
    @OneToOne(optional=false, fetch=FetchType.EAGER,
            cascade={CascadeType.PERSIST, CascadeType.DETACH})
    @MapsId
    @JoinColumn(name="PERSON_ID")
    private Person person;
public class Director {

...
    @OneToOne(optional=false, fetch=FetchType.EAGER,
            cascade={CascadeType.PERSIST, CascadeType.DETACH})
    @JoinColumn(name="PERSON_ID")
    @MapsId
    private Person person;

  • Movie.director, Movie.genres, Movie.cast relationships overridden in XML to be fetch=EAGER

  • MovieRole.actor relationship overridden in XML to be fetch=EAGER

  • Director.person and Actor.person already fetch=EAGER


  • Single query (as before), but this time it also brings in the entire object graph


  • Explain plan shows use of indexes and no full table scans (a missing FK index for MovieRole.movie could have been costly)

  • Execution plan is significantly more costly than lazy alternative


Fetch=EAGER for Single Entity adds Noticeable Overhead

Choosing fetch=EAGER for relationships will make access to a single entity within the relationship more expensive to access. Consider creating a value query, value query with a result class, or a native query when querying for only single entity under these conditions.

Get just the root parent and *ONLY* the parent of an object graph with fetch=EAGER relationships by using a NativeQuery and Result Class



  • We define an alternate JPA-QL query for provider to use that does not include relationships

  • Result class not mapped beyond this query -- does not have to be an entity class


  • Value query with result class constructor permits a bypass of fetch=EAGER relationships


  • Execution plan identical to fetch=LAZY case


* Thick/Thin or Eager/Lazy should not matter to this approach since we only access a specific set of fields in the Movie that does not include Movie.plot.

Use JPA Value or Native Queries to Override Relationship Definitions

JPA provides many escape hatches to achieve desired results. Use JPAQL value queries with Object[], Tuple, or custom result class -or- use SQL to provide efficient override of fetch=EAGER definitions.

Result Class Instances are not Managed

Even though we used an entity class in this example, it is being used as a simple POJO and the returned instance is not managed. Result Classes for value queries provide convenient type-safe access to results. However, they are unmanaged and cannot be used for follow-on entity operations.

Get parent and children in an object graph

Get parent and children in an object graph with fetch=LAZY relationships

Get parent and children in an object graph with JOIN FETCH query



  • EAGER aspects adding by query adding FETCH to JOIN query construct


  • Query identical to fetch=EAGER case



Make use of JPA Queries to Achieve Tuned-for-Use Query

JPA provides several means to access an entity and its relationships. The properties defined for the relationship help define the default query semantics -- which may not be appropriate for all uses. Leverage JOIN FETCH queries when needing to fully load specific relationships prior to ending the transaction.

Pulling back entire rows from table rather than just the count

Get size of a relationship collection.


  • DAO first gets Movie by primary key

  • DAO follows up by accessing size of relationship


  • Provider will issue follow-on query for all entities in relation

  • Entity data not used by DAO -- i.e. wasted


  • Provider will issue one query for entire object graph, to include entities in relation

  • Entity data not used by DAO -- i.e. even more data is wasted


Using Relation Collections to Just Obtain Size is Inefficient

Pulling back relationship graphs to just obtain the count in that relationship is inefficient and requires DB to do much more work than it has to.

Query for relationships and count resulting rows


  • DAO forms query for just related entities


  • Provider forms query for related entities


  • Provider forms query for related entities and fetch=EAGER relationship specification causes additional MovieRole.actor to be immediately obtained -- except through separate queries by primary key.


Using fetch=EAGER Relationships can Magnify Data Retrieval Issues

When the entity model over-uses fetch=EAGER, the negative results can be magnified when pulling back unnecessary information from the database.

Performing separate subqueries off initial query

Using DAO to perform initial query and follow-on queries based on results


  • DAO makes N separate queries based on results of first query

  • Distinct was not required in query since it was issued per-movie. Distinct was handed within the DAO using a Java Set to hold the results and hashcode()/equals() implemented within Person class.


  • SQL generated queries single table


  • Range scan of compound index(actor_id, person_id) used to satisfy the person_id in where clause and movie_id for select clause

* Query was optimized to bypass Actor table


  • Range scan on compound_index(movie_id, actor_id) to satisfy where condition for Movie.id and locate MovieRole rowId

  • Unique scan of Person primary key index to obtain rowId for Person.id=MovieRole.actor.id

  • Person row accessed by rowId to satisfy select clause

* Query was optimized to bypass Actor table

** Compound index(movie_id, actor_id) permitted MovieRole table and lookup of MovieRole.actorId to be bypassed.


Beware of Query Loops Driven from Query Results

Query loops driven off of previous query results can be a sign the follow-queries could have been combined with the initial query to be accomplished within the database within a single statement.

Also beware of how the loop size can grow over the lifetime of the application. The number may grow significantly after testing/initial deployment to a significant size (i.e., from 100 to 100K loops). Defining as a single statement and applying paging limits can help speed the originally flawed implementation.

Expressing nest query as subquery to resolve within database.


  • Distinct was required to remove duplicates


  • SQL generated uses a non-correlated subquery


  • Range scan of MovieRole composite(actor_id, movie_id) used to satisfy subquery where clause for person_id and obtain movie_id

  • Range scan of MovieRole composite(movie_id, actor_id) used to join movie_ids with root query and obtain person_id

  • Unique scan of Person primary key index to locate rowId

  • Person row accessed by rowId

* cast4_ and movierole0_ are both MovieRoles. cast4_ is joined with Movie and subject Person in subquery. movierole0_ is joined with Movie and associated Person in root query.

** execution plan indicates subquery could have been re-written as a JOIN


Delegate Query Logic to Database

If possible, express query as a single transaction to the database rather than pulling data back and re-issuing subqueries from the DAO. Apply paging constraints in order to address issues with excess growth over time.

Placing reasonable limits on amount of data returned

Implementing paging within the DAO

Figure 81.66. DAO Code/JPAQL

public Collection<Person> oneStepFromPersonByDAO(Person p, Integer offset, Integer limit, String orderBy) {

    Collection<Person> result = new HashSet<Person>();
    //performing core query
    List<String> movieIds = createQuery(
            "select role.movie.id from MovieRole role " +
            "where role.actor.person.id=:personId", String.class)
            .setParameter("personId", p.getId())
            .getResultList();
    
    //loop through results and issue sub-queries
    int pos=0;
    for (String mid: movieIds) {
        List<Person> people = createQuery(
                "select role.actor.person from MovieRole role " +
                "where role.movie.id=:movieId", Person.class)
                .setParameter("movieId", mid)
                .getResultList();
        if (offset==null || (offset!=null && pos+people.size() > offset)) {
            for(Person pp: people) {
                if (offset==null || pos>=offset) {
                    result.add(pp);
                    if (limit!=null && result.size() >= limit) { break; }
                }
                pos+=1; 
            }
        } else {
            pos+=people.size();
        }
        if (limit!=null && result.size() >= limit) { break; }
    }
    return result;
}

  • DAO logic gets complicated when self-implementing paging.

  • OrderBy is not implemented by this DAO algorithm. All rows in the table would be required in order to perform ordering within the DAO.




  • This example performs better or equal to previous loop/subquery example when earlier pages are requested -- thus fewer rows returned

  • fetch=EAGER/LAZY have no impact on this test since the entity queried for has no relationships

Performance Degrades in DAO as Row Sizes Increase

Paging within the Java code starts off near equivalent to the database solution for small row sizes but gradually gets worse when row sizes increase and later pages are requested.

Implementing paging within the database


  • JPA calls to TypedQuery.setFirstResult(), setMaxResults(), and adding the orderBy to the text of the query are abstracted behind withQuery() and createQuery() helper functions within the DAO example class and are not shown here


  • Generated SQL is essentially the same as the looping/subquery example except with the addition of "order by" and paging constructs.


  • Execution plan is similar as described in the looping/subquery example

  • Extra cost to sort rows prior to paging operation added


DB-based Paging Scales

Since only the requested page of rows is returned, delegating paging to the database provides consistent performance for varying row quantities.

Doing Paging within DB enables Sorting

Implementing paging across multiple subquery calls to the database can get ugly and error-prone. Implementing paging for a single query issued to the database is quite trivial.




  • Performs stateless actions on server-side

  • Maintains no conversational state

    • Each method ignorant of what went before it and after it

    • Persistence Context re-instantiated with each call (Transaction-Scope)

  • Methods take in a set of parameters and return a result

  • May maintain implementation state when pooled

    • example: DataSource, JMS resources, references to other EJBs

    • Not the default behavior. Only an option when number of instances needed is limited and initialization is expensive

    • In no case should business state ever be maintained outside of the scope of a method or a back-end database or other stateful resource.

  • Examples:

    • TaxCalculator.calcTax(doubt amount)

    • BuyerMgmt.placeBid(int auctionId, double amount)

    • Dmv.getExpiredLicenses()

  • Good for implementing stateless access to resources

  • Contains no means to provide individualized access other than through separate deployments and call parameters


Figure 85.5. Example Stateful EJB

@javax.ejb.Stateful

public class ReservationEJB implements ReservationRemote {
    @PersistenceContext(unitName="ejbjpa-hotel", type=PersistenceContextType.EXTENDED)
    private EntityManager em;
    
    @EJB
    private HotelMgmtLocal hotelMgmt;
    
    @Resource
    private SessionContext ctx;
    
    private List<Guest> guests = new LinkedList<Guest>();
    @Override
    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    public int addGuest(Guest guest) {
        if (guest!=null) {
            guests.add(guest);
            em.persist(guest); //<== no transaction active yet
        }
        return guests.size();
    }
    @Override
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    @Remove
    public List<Guest> reserveRooms() throws RoomUnavailableExcepton {
        List<Room> rooms = hotelMgmt.getAvailableRooms(null, 0, guests.size());
        if (rooms.size() < guests.size()) {
            //no rollback needed, we didn't do anything
            throw new RoomUnavailableExcepton(String.format("found on %d out of %d required", 
                    rooms.size(), guests.size()));
        }
        //assign each one of them a room
        List<Guest> completed = new ArrayList<Guest>(guests.size());
        Iterator<Room> roomItr = rooms.iterator();
        for (Guest guest: guests) {
            Room room = roomItr.next();
            try {
                //the room could be unavailable -- depending on whether pessimistic lock created
                guest = hotelMgmt.checkIn(guest, room); //<== will attempt to also persist guest
                completed.add(guest);
            } catch (RoomUnavailableExcepton ex) {
                //rollback any previous reservations
                ctx.setRollbackOnly();
                throw ex;
            }
        }
        return completed;
    }
}

  • Used to cache resources for client on server-side

  • Maintains conversational state

    • Object can cache values between calls

      • example: iterator

      • Persistence Context (with cache of entities) can be retained between calls (Extended-Scope)

    • Lifetime ends on timeout or specific client call

  • Maintains implementation state

    • Not sharable between clients

    • All resources allocated to perform work for one instance

  • Able to react to transaction completion/rollback (i.e., get callback)

    • example: commit data cache

    • example: issue message

  • Good for caching client state and back-end resource state over a multi-call session

  • Inefficient to scale cached state for each user and across multiple servers


  • Used to receive JMS messages

  • Similar to Stateless EJB, but with no callable interface

  • Typically provides a JMS facade for injected Session EJBs

  • Good for implementing a JMS interface facade to business logic

  • Unable to be called outside the scope of a JMS/JCA call -- should delegate to Session EJBs

  • Has no client context (i.e., anonymous)

    • Can be assigned access roles using @javax.annotation.security.RunAs to be permitted to invoke methods of protected EJBs

Figure 85.13. Example JAX-RS Interface



import java.net.URI;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
@Path("/products")
public class ProductsResource {
        @Inject
        private InventoryMgmtEJB ejb;
        @Context 
        private UriInfo uriInfo;
    /**
     * Updates a product with the values of the object passed in
     * @param id
     * @param product
     * @return updated product if successful
     */
    @PUT @Path("{id}")
    @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    @JsonbAnnotation  //helps trigger JSON-B response marshaling when DTO also JAXB
    public Response updateProduct(@PathParam("id") int id, 
       @JsonbProperty Product product  //annotation helps trigger JSON-
                                       //request demarshaling when DTO also JAXB
       ) {
        logger.debug("{} {}", request.getMethod(), uriInfo.getAbsolutePath());
        try {
            Product p = ejb.updateProduct(product);
            logger.info("updated pojo product={}", p);
            return Response.ok(p)
                    .tag("" + p.getVersion())
                    .build();
        } catch (Exception ex) {
            return ResourceHelper.serverError(logger, "update product", ex).build();
        }
    }
}

  • EJBs are based on Plain Old Java Objects (POJOs)

  • Standard POJO rules apply...

$ mvn clean test                        
...
[INFO] Deleting .../ejb-basic-ejb/target
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ ejb-basic-ejb ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory .../ejb-basic-ejb/src/main/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ ejb-basic-ejb ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 5 source files to .../ejb-basic-ejb/target/classes
[INFO] 
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ ejb-basic-ejb ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ ejb-basic-ejb ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to .../ejb-basic-ejb/target/test-classes
[INFO] 
[INFO] --- maven-surefire-plugin:2.17:test (default-test) @ ejb-basic-ejb ---
[INFO] Surefire report directory: .../ejb-basic-ejb/target/surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running info.ejava.examples.ejb.basic.pojo.GreeterTest
16:57:12,237 INFO  (GreeterTest.java:41) -*** dto ***
16:57:12,243 DEBUG (GreeterEJB.java:41) -sayHello(Name [firstName=thing, lastName=one])
16:57:12,249 INFO  (GreeterTest.java:27) -*** pojoGreeter ***
16:57:12,250 DEBUG (GreeterEJB.java:27) -sayHello(cat inhat)
16:57:12,252 INFO  (GreeterTest.java:35) -*** badName ***
16:57:12,255 DEBUG (GreeterEJB.java:27) -sayHello()
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.239 sec - in info.ejava.examples.ejb.basic.pojo.GreeterTest

Results :

Tests run: 3, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.815 s
...
  • Add essential EJB aspects to POJO classes

  • Deploy to server and test

17:19:52,979 INFO  [org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service thread 1-1) 
JNDI bindings for session bean named GreeterEJB in deployment unit deployment "ejb-basic-ejb.jar" are as follows:

        java:global/ejb-basic-ejb/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote
        java:app/ejb-basic-ejb/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote
        java:module/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote
        java:jboss/exported/ejb-basic-ejb/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote
        java:global/ejb-basic-ejb/GreeterEJB
        java:app/ejb-basic-ejb/GreeterEJB
        java:module/GreeterEJB

17:19:53,012 INFO  [org.jboss.weld.deployer] (MSC service thread 1-1) JBAS016005: Starting Services for CDI deployment: ejb-basic-ejb.jar
17:19:53,029 INFO  [org.jboss.weld.deployer] (MSC service thread 1-2) JBAS016008: Starting weld service for deployment ejb-basic-ejb.jar
17:19:53,485 INFO  [org.jboss.as.server] (DeploymentScanner-threads - 1) JBAS018559: Deployed "ejb-basic-ejb.jar" (runtime-name : "ejb-basic-ejb.jar")
  • Useful when need to deploy EJB with dependent artifacts or without WAR

19:50:07,583 INFO  [org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service thread 1-1) JNDI bindings for session bean named GreeterEJB in deployment unit subdeployment "ejb-basic-ejb.jar" of deployment "ejb-basic-ear-4.0.0-SNAPSHOT.ear" are as follows:

        java:global/ejb-basic-ear/ejb-basic-ejb/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote
        java:app/ejb-basic-ejb/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote
        java:module/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote
        java:jboss/exported/ejb-basic-ear/ejb-basic-ejb/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote
        java:global/ejb-basic-ear/ejb-basic-ejb/GreeterEJB
        java:app/ejb-basic-ejb/GreeterEJB
        java:module/GreeterEJB

19:50:07,588 INFO  [org.jboss.weld.deployer] (MSC service thread 1-1) JBAS016005: Starting Services for CDI deployment: ejb-basic-ear-4.0.0-SNAPSHOT.ear
19:50:07,596 INFO  [org.jboss.weld.deployer] (MSC service thread 1-2) JBAS016008: Starting weld service for deployment ejb-basic-ear-4.0.0-SNAPSHOT.ear


<project>
    <groupId>info.ejava.examples.ejb.basicejb</groupId>
    <artifactId>ejb-basic-ear</artifactId>
    <packaging>ear</packaging>

    <dependencies>
        <!-- The EAR must have a scope=compile dependency on the EJB -->
        <dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>ejb-basic-ejb</artifactId>
            <version>${project.version}</version>
            <type>ejb</type>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- provide properties here to impact the EAR packaging -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-ear-plugin</artifactId>
                <configuration>
                    <defaultLibBundleDir>lib</defaultLibBundleDir>
                    <!-- eliminates use of version in EAR JNDI name portion -->
                    <applicationName>${project.artifactId}</applicationName>
                    <modules>
                        <!-- eliminates use of the version in the EJB JNDI name -->
                        <ejbModule>
                            <groupId>${project.groupId}</groupId>
                            <artifactId>ejb-basic-ejb</artifactId>
                            <bundleFileName>ejb-basic-ejb.jar</bundleFileName>
                        </ejbModule>
                    </modules>
                </configuration>
            </plugin>
        </plugins>
    </build>    
  • Useful in WAR deployments

20:47:51,929 INFO  [org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] 
(MSC service thread 1-2) JNDI bindings for session bean named GreeterEJB in deployment unit deployment "ejb-basic-war.war" are as follows:

        java:global/ejb-basic-war/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote
        java:app/ejb-basic-war/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote
        java:module/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote
        java:jboss/exported/ejb-basic-war/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote
        java:global/ejb-basic-war/GreeterEJB
        java:app/ejb-basic-war/GreeterEJB
        java:module/GreeterEJB

20:47:51,970 INFO  [org.jboss.weld.deployer] (MSC service thread 1-2) JBAS016005: Starting Services for CDI deployment: ejb-basic-war.war
20:47:51,988 INFO  [org.jboss.weld.deployer] (MSC service thread 1-1) JBAS016008: Starting weld service for deployment ejb-basic-war.war
20:47:52,353 INFO  [org.wildfly.extension.undertow] (MSC service thread 1-1) JBAS017534: Registered web context: /ejb-basic-war

<project>
    <groupId>info.ejava.examples.ejb.basicejb</groupId>
    <artifactId>ejb-basic-war</artifactId>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>ejb-basic-ejb</artifactId>
            <version>${project.version}</version>
            <type>ejb</type>
            <scope>compile</scope>
        </dependency>     
    </dependencies>

    <build>
        <finalName>ejb-basic-war</finalName>
    </build>
</project>

Support for embedded EJB development will require standard POJO and EJB dependencies


        <!-- for embedded code/EJBs -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.ejb</groupId>
            <artifactId>javax.ejb-api</artifactId>
            <scope>provided</scope>
        </dependency>
  • Unit tests test POJO classes in a single Maven phase

  • IT tests test components deployed to the server using multiple phases Maven Lifecycle Reference

    pre-integration-test

    Deploy artifacts

    integration-test

    Execute tests

    post-integration-test

    Undeploy artifacts

    verify

    Evaluate test results (possibly fail build after artifacts undeployed)

  • IT tests require remote access

    • Access Type

      • JBoss Remoting

      • EJB Client

    • JNDI Properties

    • JNDI Name

  • IT tests test EJB deployed to server

  • InitialContextFactory looks for custom naming extensions when encountering naming prefix ("ejb:")

  • java.naming.factory.url.pkgs lists base package names to start looking for extensions

  • jboss-ejb-client.jar must be in the classpath

  • Provider extracts "ejb" prefix from "ejb:/..." JNDI name

    ejb:/ejb-basic-ejb/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote
  • Provider obtains list of Java package prefixes from "jndi.naming.factory.url.pkgs"

    #jndi.properties
    java.naming.factory.url.pkgs=org.jboss.ejb.client.naming
    
  • Provider searches for "org.jboss.ejb.client.naming.ejb" Java package in classpath

    $ mvn dependency:tree
    ...
    [INFO] +- info.ejava.examples.common:jboss-rmi-client:pom:5.0.0-SNAPSHOT:test
    [INFO] |  |  +- org.jboss:jboss-ejb-client:jar:4.0.10.Final:test
    [INFO] |  |  +- org.jboss.remoting:jboss-remoting:jar:5.0.5.Final:test
    [INFO] |  |  +- org.wildfly:wildfly-naming-client:jar:1.0.9.Final:test
    [INFO] |  |  +- org.wildfly.wildfly-http-client:wildfly-http-naming-client:jar:1.0.12.Final:test
    ...
    
  • Provider locates an ObjectFactory class in the "org.jboss.ejb.client.naming" Java package that starts with "ejb"

    $ jar tf ~/.m2/repository/org/jboss/jboss-ejb-client/4.0.10.Final/jboss-ejb-client-4.0.10.Final.jar | grep org.jboss.ejb.client.naming
    org/jboss/ejb/client/naming/ejb/ejbURLContextFactory.class
    
  • Provider uses that ObjectFactory to resolve the remaining portion of the JNDI name

    ejb:/ejb-basic-ejb/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote
...

import static org.junit.Assert.*;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import info.ejava.examples.ejb.basic.dto.Greeting;
import info.ejava.examples.ejb.basic.dto.Name;
import info.ejava.examples.ejb.basic.ejb.BadRequestException;
import info.ejava.examples.ejb.basic.ejb.GreeterRemote;
...
public class GreeterIT {
    private static final Logger logger = LoggerFactory.getLogger(GreeterBase.class);
    protected Properties jndiProperties; // varies whether using Remoting or EJBClient
    protected String jndiName; // varies whether accessing EJB, WAR or EAR deployment and access
    protected Context jndi;
    protected GreeterRemote greeter;
    @Before
    public void setUp() throws Exception {
        jndi = new InitialContext(jndiProperties);
        greeter = (GreeterRemote) jndi.lookup(jndiName);
    }   
    @After
    public void tearDown() throws Exception {
        if (jndi != null) {
            jndi.close(); // produces errors with JBoss Remoting
        }   
    }   
$ mvn clean verify
...
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ ejb-basic-test ---
[INFO] Deleting .../ejb-basic-example/ejb-basic-test/target
...
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ ejb-basic-test ---
[INFO] No sources to compile
[INFO] 
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ ejb-basic-test ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 4 resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ ejb-basic-test ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 5 source files to .../ejb-basic-example/ejb-basic-test/target/test-classes
[INFO] 
[INFO] --- maven-surefire-plugin:2.17:test (default-test) @ ejb-basic-test ---
[INFO] 
[INFO] --- maven-jar-plugin:2.5:jar (default-jar) @ ejb-basic-test ---
[WARNING] JAR will be empty - no content was marked for inclusion!
[INFO] Building jar: .../ejb-basic-example/ejb-basic-test/target/ejb-basic-test-4.0.0-SNAPSHOT.jar
...

[INFO] --- cargo-maven2-plugin:1.4.3:redeploy (cargo-prep) @ ejb-basic-test ---
Sep 28, 2014 11:58:23 PM org.xnio.Xnio <clinit>
INFO: XNIO version 3.2.2.Final
Sep 28, 2014 11:58:23 PM org.xnio.nio.NioXnio <clinit>
INFO: XNIO NIO Implementation Version 3.2.2.Final
Sep 28, 2014 11:58:23 PM org.jboss.remoting3.EndpointImpl <clinit>
INFO: JBoss Remoting version 4.0.3.Final

...
[INFO] --- maven-failsafe-plugin:2.17:integration-test (integration-tests) @ ejb-basic-test ---
[INFO] Failsafe report directory: .../ejb-basic-example/ejb-basic-test/target/failsafe-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------

Running info.ejava.examples.ejb.basicejb.ejbclient.GreeterRemotingWARIT
23:58:26,441 INFO  (GreeterIT.java:43) -using jndiName=ejb-basic-war/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote
...
23:58:27,811 INFO  (GreeterIT.java:43) -using jndiName=ejb:ejb-basic-ear/ejb-basic-ejb/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote
...
23:58:28,167 INFO  (GreeterIT.java:43) -using jndiName=ejb-basic-ear/ejb-basic-ejb/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote
...
23:58:28,289 INFO  (GreeterIT.java:43) -using jndiName=ejb:/ejb-basic-war/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote

Results :

Tests run: 12, Failures: 0, Errors: 0, Skipped: 0

[INFO] 
[INFO] --- cargo-maven2-plugin:1.4.3:undeploy (cargo-post) @ ejb-basic-test ---
[INFO] 
[INFO] --- maven-failsafe-plugin:2.17:verify (verify) @ ejb-basic-test ---
[INFO] Failsafe report directory: .../ejb-basic-example/ejb-basic-test/target/failsafe-reports
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 11.036 s

Two primary ways to run the application server

  • Standalone. Can be local or remote

  • Embedded within IDE. Shares same JVM.


  • root-logger sets default versbosity to INFO

  • additional logger sets verbosity for info.ejava.* to DEBUG


  • EJB class used to create logger named after the class' fully qualified name

  • @PostConstruct logging at INFO level

  • business method logging at DEBUG level


  • Console configured (by default) to only output INFO and above


  • server.log will print all verbosity levels

  • Many/most build commands can be automated through Maven or other scripts

  • Used in interactive development and automated/headless builds

Plugin Page

  • Copies resource files from src tree to target tree

  • Can optionally "filter" variables and replace them with build time values (from environment's settings.xml)

  • Useful in customizing environment-specific properties

    • server URLs

    • server port#s


  • Source file uses variables to template source file


  • Concrete values are put in place for specific environment


  • Properties defined either within pom.xml, settings.xml, or -Dsystem-properties

  • includes/exclused aids in filtering wrong files

  • avoid filtering binary files -- corrupts them (Ant has same issue)

Plugin Page

  • Runs unit tests

  • Runs after tests compiled and before IT tests


  • Plugin always enabled by default. Declaration used only to customize

  • Configuration defines environment for JVM

  • Easy way to supply system properties (-Dsystem-property=value)

  • values can be hard-coded or use properties to allow easier overrides


  • Useful when turning off all unit tests to concentrate on IT tests

  • Can turn off problem test(s)


  • Just defining version here

Figure 92.8. Run Unit Tests

$ mvn clean test

...
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ ejb-basic-ejb ---
...
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ ejb-basic-ejb ---
...
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ ejb-basic-ejb ---
...
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ ejb-basic-ejb ---
...
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ ejb-basic-ejb ---
...
[INFO] --- maven-surefire-plugin:2.17:test (default-test) @ ejb-basic-ejb ---

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running info.ejava.examples.ejb.basic.pojo.GreeterTest
02:11:38,695 INFO  (GreeterTest.java:41) -*** dto ***
02:11:38,701 DEBUG (GreeterEJB.java:45) -sayHello(Name [firstName=thing, lastName=one])
02:11:38,707 INFO  (GreeterTest.java:27) -*** pojoGreeter ***
02:11:38,708 DEBUG (GreeterEJB.java:31) -sayHello(cat inhat)
02:11:38,711 INFO  (GreeterTest.java:35) -*** badName ***
02:11:38,713 DEBUG (GreeterEJB.java:31) -sayHello()
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.238 sec - in info.ejava.examples.ejb.basic.pojo.GreeterTest

Results :

Tests run: 3, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.871 s

  • Just defining version here

Plugin Page

  • Manages deployment of components to containers

  • Not specific to JBoss/Wildfly

  • Specializations for Maven and JBoss/Wildfly

  • Can run embedded within each IT module build or use remote instance

  • Typically configured within module with IT tests


  • Plugin declaration enacts parent-defined behavior

  • Module specifies artifacts to deploy

  • Module specification is not required if deployment is self (i.e., the primary EJB/WAR/EAR produced by this module)


  • Used to simply undeploy module from server

  • Requires artifact to be present

  • Profile keeps behavior from running under conditions that would fail build

Figure 92.11. Parent Definition


<pluginManagement>
    <plugins>
        <plugin>
            <groupId>org.codehaus.cargo</groupId>
            <artifactId>cargo-maven2-plugin</artifactId>
            <version>${cargo-maven2-plugin.version}</version>
            <configuration>
                <container>
                    <containerId>${cargo.containerId}</containerId>
                    <type>remote</type>
                    <log>target/server.log</log>
                    <output>target/output.log</output>
                </container>
                <configuration>
                    <type>runtime</type>
                    <properties>
                        <cargo.hostname>${jboss.mgmt.host}</cargo.hostname>
                        <cargo.jboss.management.port>${jboss.mgmt.port}</cargo.jboss.management.port>
                    </properties>
                </configuration>
            </configuration>
            <dependencies>
                <dependency>
                    <groupId>org.wildfly</groupId>
                    <artifactId>wildfly-controller-client</artifactId>
                    <version>${wildfly.version}</version>
                </dependency>
            </dependencies>
            <executions>
               <execution>
                   <id>cargo-prep</id> 
                       <phase>pre-integration-test</phase>
                   <goals>
                        <goal>redeploy</goal>
                   </goals>
               </execution>
                <execution>
                    <id>cargo-post</id>
                    <phase>post-integration-test</phase>
                    <goals>
                        <goal>undeploy</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</pluginManagement>

  • Parent defines boiler-plate portion

  • Child will provide module-specific information


  • Builds a new packaged, deployable artifact

  • Deploys and leaves on server

  • (optional)-DskipTests bypasses any unit tests

  • Notice that build completes immediately after deploy with no IT tests or undeploy


  • Undeploys atifact from server

  • Useful in automating cleanup

  • -Pundeploy activates latched profile

Plugin Page

  • Runs integration (IT) tests

  • Must be declared, not configured in by default

  • Runs after deployer completes and prior to undeployer


  • Plugin definition causes IT tests to run

  • Configuration defines environment for JVM


  • Can turn off problem or lengthy test(s)


  • Defines version# and wires plugin into build phases when declared by child module

Figure 92.17. Run IT Tests



$ mvn clean verify

$ mvn clean verify
...
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ ejb-basic-test ---
...
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ ejb-basic-test ---
...
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ ejb-basic-test ---
...
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ ejb-basic-test ---
...
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ ejb-basic-test ---
...
[INFO] --- maven-surefire-plugin:2.17:test (default-test) @ ejb-basic-test ---
...
[INFO] --- maven-jar-plugin:2.5:jar (default-jar) @ ejb-basic-test ---
...
[INFO] --- cargo-maven2-plugin:1.4.3:redeploy (cargo-prep) @ ejb-basic-test ---
Oct 01, 2014 2:28:02 AM org.xnio.Xnio <clinit>
INFO: XNIO version 3.2.2.Final
Oct 01, 2014 2:28:02 AM org.xnio.nio.NioXnio <clinit>
INFO: XNIO NIO Implementation Version 3.2.2.Final
Oct 01, 2014 2:28:02 AM org.jboss.remoting3.EndpointImpl <clinit>
INFO: JBoss Remoting version 4.0.3.Final
...
[INFO] --- maven-failsafe-plugin:2.17:integration-test (integration-tests) @ ejb-basic-test ---

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
...

Results :

Tests run: 12, Failures: 0, Errors: 0, Skipped: 0
...
[INFO] --- cargo-maven2-plugin:1.4.3:undeploy (cargo-post) @ ejb-basic-test ---
...
[INFO] --- maven-failsafe-plugin:2.17:verify (verify) @ ejb-basic-test ---
...
-------------------------------------------------------
[INFO] BUILD SUCCESS


  • Runs all phases and stops just prior to installing into local repository

  • Cannot bypass unit tests (unless includes/excludes used)

  • Deploy artifacts to server

  • Run IT tests in IDE

  • Connect with Debugger


  • Not required when using embedded server (just use debug-as)

  • Standalone server must be restarted after making this edit




  • Debugger Client port must match what JBoss debugger listen port


  • You can optionally add modules now to resolve source code references


  • Execution will stop at server breakpoint

  • No source code will show up if not yet in search path


  • Add as many projects as you wish

Note

You may have to stop and re-run your test for the source code path to take effect.


  • Notice we are seeing variables as well as line of execution

This chapter focuses on injecting relevant resources into an EJB using modern injection techniques. From the points made in the previous chapter -- you know that there are more tedious and verbose techniques from the older EJB specs that pre-date @Annotations and ease-of-use enhancements. They will not be included here.

Inject access to other EJBs

Table of Contents

Purpose
98. Server-side Resources
98.1. SQL DataSource (defined in Server - standalone.xml)
98.2. Server-side Persistence Units
98.2.1. transaction-type=JTA (default)
98.2.2. transaction-type=RESOURCE_LOCAL
98.3. persistence.xml Placement
98.3.1. EJB persistence.xml Placement
98.3.2. WAR persistence.xml Placement
98.4. Reference External @Entities
98.4.1. Reference External @Entities: EAR Deploy
98.4.2. Reference External @Entities: WAR Deploy
98.5. Summary
99. Persistence Unit/Context Injection
99.1. @PersistenceContext Injection
99.2. @PersistenceUnit Injection
99.3. Context and Dependency Injection (CDI)
99.4. Summary
100. Managed Entities and Remote Interfaces
100.1. Problem: Provider Proxy Classes Marshaled to Client
100.1.1. Potential Solution: Add JPA Provider Classes to Client Classpath
100.1.2. More Scenario Details
100.1.3. Candidate Solution: Cleansed DTOs
100.2. Problem: Lazy Load Exception
100.2.1. Lazy Load Scenario Details
100.2.2. Candidate Solution: Load thru "Touching" Object Tree in Remote Facade
100.2.3. Candidate Solution: Load thru Fetching Object Tree in Query
100.2.4. Candidate Solution: Abstract Remote Interface View with DTO
100.3. Summary
101. Persistence Context Propagation
101.1. Stateless Persistence Context Interaction
101.1.1. Stateless EJB Example Check-in
101.1.2. Stateless EJB Example Client Check-in
101.1.3. EJB Gets Available Rooms from DB
101.1.4. EJB Gets Specific Room
101.1.5. EJB Adds Guest
101.1.6. EJB associates Guest with Room
101.2. Stateful Facade Persistence Context Interaction
101.2.1. Example Stateful Reservation EJB Caches Guest Requests for Client
101.2.2. Example Stateful Reservation EJB Acting on Cached State
101.2.3. Stateful EJB Example Client Check-in
101.2.4. Stateful EJB Persists Guests Prior to Active JTA Transaction
101.2.5. Stateless EJB Populates Propagated Persistence Context with Rooms
101.2.6. Stateful EJB Method Activates Transaction and flush()es Guests in EntityManager Cache
101.2.7. Stateful EJB uses pre-loaded Rooms and Guests without accessing DB (until association)
101.3. Transaction Rollbacks
101.3.1. Stateless Transaction Rollback
101.3.2. Stateful Transaction Rollback
101.4. Pessamistic Locking
101.5. Summary
ejb-jpa-example-war
|-- META-INF
`-- WEB-INF
...
    `-- lib
        `-- ejb-jpa-example-blimpl-5.0.0-SNAPSHOT.jar

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0">

    <persistence-unit name="ejbjpa-hotel">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>

        <!-- located in WEB-INF/lib/ejb-jpa-example-blimpl-${project.version}.jar -->
        <class>info.ejava.examples.ejb.ejbjpa.bo.Guest</class>
        <class>info.ejava.examples.ejb.ejbjpa.bo.Room</class>
        <class>info.ejava.examples.ejb.ejbjpa.bo.Floor</class>

        <properties>
            <property name="hibernate.dialect" value="${hibernate.dialect}"/>
...
        </properties>
    </persistence-unit>            
</persistence>
@PersistenceContext

Injected with EntityManager

Transaction Scoped (default)
  • persistence context only sees a single Tx

  • container injects EntityManager with Tx active

@PersistenceContext(unitName="ejbjpa-hotel", type=PersistenceContextType.TRANSACTION)

private EntityManager em;
Extended Scope
  • persistence context may see multiple Tx

  • only relevant for Stateful EJBs

@PersistenceContext(unitName="ejbjpa-hotel", type=PersistenceContextType.EXTENDED)

private EntityManager em;
@PersistenceUnit

Injected with EntityManagerFactory

  • May be used to implement BEAN-managed transactions


  • EJB will instantiate persistence context if does not yet exist

  • Stateless EJBs may only have transaction-scope persistence contexts

  • Stateful EJBs may have transaction-scope or extended persistence contexts

  • EJBs can share persistence contexts

    • Stateless EJB can propagate its tx-scope persistence context to a called EJB

    • Stateful EJB can propagate its tx-scoped or extended persistence context to a called EJB

    • Stateless EJB can work with extended persistence context provided by upstream Stateful client

    • Stateful EJB cannot transform propagated tx-scope persistence context into an extended

  • EJB Facade can act as sharing point for common persistence context

guest = hotelMgmt.checkIn(guest, room); //<== will attempt to also persist guest



[HotelMgmtEJB] *** HotelMgmtEJB(1086999670):init ***
[HotelMgmtEJB] checkin(guest=Guest [id=65, name=member 0], room=Room [number=1, occupant=null])
[HotelMgmtEJB] *** HotelMgmtEJB(1086999670):destroy ***
[HotelMgmtEJB] *** HotelMgmtEJB(99312186):init ***
[HotelMgmtEJB] checkin(guest=Guest [id=66, name=member 1], room=Room [number=100, occupant=null])
[HotelMgmtEJB] *** HotelMgmtEJB(99312186):destroy ***
[HotelMgmtEJB] *** HotelMgmtEJB(545116383):init ***
[HotelMgmtEJB] checkin(guest=Guest [id=67, name=member 2], room=Room [number=102, occupant=null])
[HotelMgmtEJB] *** HotelMgmtEJB(545116383):destroy ***
[HotelMgmtEJB] *** HotelMgmtEJB(605810979):init ***
[HotelMgmtEJB] checkin(guest=Guest [id=68, name=member 3], room=Room [number=201, occupant=null])
[HotelMgmtEJB] *** HotelMgmtEJB(605810979):destroy ***
[stdout] Hibernate: 
[stdout]     update
[stdout]         EJBJPA_ROOM 
[stdout]     set
[stdout]         FLOOR_ID=?,
[stdout]         OCCUPANT_ID=? 
[stdout]     where
[stdout]         ROOM_NUMBER=?
[stdout] Hibernate: 
...
[ReservationEJB] *** ReservationEJB(12230192):destroy ***

  • Rejects checkin when invalid

  • Completes check-in when valid


  • Stateless EJB commits each of these check-ins


  • Additional check-in rejected -- nothing committed


  • Each check-in occured in own transaction

  • Later error did not impact previous completed transactions


  • Same stateful process as before -- but with one additional Guest (one too many)


  • Client attempts to check-in to all Rooms

  • Checked exception will be thrown


  • Five (5) Guests are flushed to database prior to the rollback


  • Persisted Guests removed from database as a part of transaction rollback

  • Early check-ins never flushed to DB -- discarded as part of rollback


  • All check-ins associated with Rooms removed as a part of rollback


  • setLockMode(LockModeType.PESSIMISTIC_WRITE) - locks row (or table) for remainder of transaction

  • select ... FOR UPDATE issued for query

  • competing client thread is blocked until end of transaction

Unit of work that accesses one more more resources (usually databases)

  • set of one or more activities related to each other

  • must be completed together or not at all

EJB is primarily a transaction framework/platform for application code

Figure 102.4. Container-Managed Transactions

Container-Managed Transactions
@Stateless

@TransactionManagement(TransactionManagementType.CONTAINER)
public class HotelMgmtEJB implements HotelMgmtRemote, HotelMgmtLocal {
...
    @PersistenceContext(unitName="ejbjpa-hotel")
    private EntityManager em;
    private HotelDAO dao;                                                                                                                                                                                                                                                                                                                                   
    private HotelMgmt hotelMgmt;
     
    @PostConstruct
    public void init() {
        dao = new JPAHotelDAO();
        ((JPAHotelDAO)dao).setEntityManager(em);
        hotelMgmt = new HotelMgmtImpl();
        ((HotelMgmtImpl)hotelMgmt).setHotelDao(dao);
    }
    @Override
    @TransactionAttribute(TransactionAttributeType.SUPPORTS)
    public Room getRoom(int number) {
        return hotelMgmt.getRoom(number);
    }
    
    @Override
    @TransactionAttribute(TransactionAttributeType.REQUIRED)    
    public Guest checkIn(Guest guest, Room room) throws RoomUnavailableExcepton {
        return hotelMgmt.checkIn(guest, room);
    }

Container manages all aspects of transaction for CMT

For Container Managed Transactions (CMT), the transaction management code is in the Container and *not* within the method body of the EJB.


EJB code manages begin/commit aspects of transaction for BMT

For Bean Managed Transactions (BMT), the transaction management code is in the EJB method. This adds extra non-business code and even the simplest scenario begins to get complicated.

  • Declarative interface to transactions

  • Declarative attributes tell container to execute a method

    • within a transaction (MADATORY, REQUIRED, REQUIRES_NEW, SUPPORTS)

    • without a transaction (SUPPORTS, UNSUPPORTED, NEVER)

  • Container completes transaction protocol prior to marshaling return objects



  • getUserTransaction()

    • Returned instance can use used to demarcate transactions

    • Restricted to EJBs using bean-managed transactions

  • getRollbackOnly()

    • Tests whether current transaction is going to be rolled back at the end

    • Restricted to EJBs using container-managed transactions

  • setRollbackOnly()

    • Allows EJBs to trigger the current transaction to be rolled back at the end

    • Restricted to EJBs using container-managed transactions


Resources created/modified within the transaction -- including resources operated on by called EJBs -- will be rolled back.


Even though the application did not throw an exception and gracefully returned a result -- the IT test shows the entity was not stored during the callback

No automatic rollback by default


Checked exceptions, but default, do not trigger rollbacks


An error in processing can be reported with an exception without rollback


IT test verifies entity thrown with the exception was persisted

Checked/Application exceptions can be configured to trigger a rollback


Annotation tells container to automatically rollback transaction if thrown


An error in processing can be reported with an exception without rollback


IT test verifies entity thrown with the exception was persisted

  • Programmatic interface to transactions

  • Interactions through javax.transaction.UserTransaction interface

    • Everything between utx.begin() and utx.commit() is within a single transaction

    • Bean is in control


  • Can inject @PersistenceContext/EntityManager or @PersistenceUnit/EntityManagerFactory

  • EntityManager already SYNCHRONIZED with UserTransaction by default

  • EntityManager can be manually synchronized using em.joinTransaction()

    • Useful in Stateful Session EJBs were may interact with EntityManager over multiple methods


  • Stateful session EJBs using container-managed transactions can receive transaction events

  • EJBs using bean-managed transactions are in control of their transaction and do not need events


Table of Contents

Purpose
1. Goals
2. Objectives
106. REST-like Concepts
106.1. REST
106.2. "REST-like"
106.3. HTTP Protocol embraced
106.4. Resource
106.5. Uniform Resource Indentifiers (URIs)
106.6. Methods
106.6.1. Method Safety
106.6.2. Idempotent
106.7. Response Codes
106.8. Links
106.9. Summary
107. JAX-RS Basics
107.1. JAX-RS Client Basics
107.2. JAX-RS Server Basics
107.3. JAX-RS Maven Aspects
107.4. Summary
108. JAX-RS Resource/EJB Integration
108.1. EJB Injection
108.2. Candidate EJB/Business Tier Exceptions for Web API Status
108.2.1. Client Error
108.2.2. Service Error
108.3. Resource/EJB method
108.4. Summary
109. JAX-RS Content
109.1. JSON Content
109.1.1. JSON-B JSON Marshaling/Demarshaling
109.1.2. Jackson JSON Marshaling/Demarshaling
109.2. XML Content
109.2.1. Common JAXB Annotations
109.2.2. JAXB Maven Aspects
109.3. Content Handling
109.3.1. Client Marshal Request Content
109.3.2. API Receive Request Content
109.3.3. API Send Response Content
109.3.4. Demarshal Response Content
109.4. JAX-RS Client Maven Aspects
109.5. Summary
110. Resource Examples
110.1. JAX-RS Resource Class
110.2. JAX-RS GET Resource Collection
110.2.1. Server-side GET Resource Collection
110.2.2. Client-side GET Resource Collection
110.3. JAX-RS Resource POST Method
110.3.1. Server-side POST Resource Collection
110.3.2. Client-side POST Resource Collection
110.4. JAX-RS GET Resource Single Method
110.4.1. Server-side GET Single Resource
110.4.2. Client-side GET Single Resource
110.5. JAX-RS PUT (Nested) Resource Method
110.5.1. Server-Side JAX-RS PUT Resource Method
110.5.2. Client-side JAX-RS PUT Resource Method
110.6. JAX-RS DELETE Resource Method
110.6.1. Server-side JAX-RS DELETE Resource Method
110.6.2. Client-side JAX-RS DELETE Resource Method
110.7. Summary
  • Architectural Style for creating web services

  • Provide interoperability between computer systems on the Internet

  • Uses a uniform and predefined set of stateless operations

  • Defined in 2000 by Roy Fielding in his doctoral dissertation that was also used to design HTTP 1.1 [11]

  • An address (of varying detail) to access a particular resource

  • Technically, URIs can be either URNs or URLs

    • Uniform Resource Name [13]

      • Resource identity

      • Globally unique

      • Example:

        urn:info.ejava.products:1
        <core xmlns="urn:activemq:core">
        
    • Uniform Resource Locator [14]

      • Resource's location on a network

      • Contains protocol information -- "how to get it"

      • Example:

        http://127.0.0.1/jaxrsInventoryWAR/api/products/1
        https://127.0.0.1/jaxrsInventoryWAR/api/products/1
        ftp://127.0.0.1/info.ejava.products:1
        
    • Commonly -- URIs are a partial URL

      • Commonly lack protocol and physical location

      • Relative to some point in the application

      • Example:

        /jaxrsInventoryWAR/api/products/1
        /api/products/1
        products/1
        
  • Example resource collection URI

    /api/products
    /api/categories
    /api/customers
    /api/todo_lists
    
  • Example individual resource URIs (with mandatory path {parameter}s

    /api/products/{productId}
    /api/categories/{categoryId}
    /api/customers/{customerId}
    /api/customers/{customerId}/sales
    
  • Example nested resource URIs

    /api/products/{productId}/instructions
    /api/categories/{categoryId}/products
    /api/customers/{customerId}/purchases
    /api/todo_lists/{listName}/todo_items
    
  • URIs may express variable parameters

    • Use query parameters for optional variables

      http://127.0.0.1:8080/jaxrsInventoryWAR/api/categories?name=&offset=0&limit=0
      
    • Nested path parameters may express mandatory variables

      http://127.0.0.1:8080/jaxrsInventoryWAR/api/products/{id}
      http://127.0.0.1:8080/jaxrsInventoryWAR/api/products/1
      id=>1
      
  • URI naming conventions

    • Use a plural name for resource collections

      /api/todo_lists
      
    • Use an ID below the plural resource collection to refer to a specific resource

      /api/todo_lists/{listName}
      


[11] "Architectural Styles and the Design of Network-based Software Architectures. Doctoral dissertation", Roy Thomas Fielding, University of California, Irvine, 2000 HTML Version

  1. Declare root URI using class that extends Application

    import javax.ws.rs.ApplicationPath;
    
    import javax.ws.rs.core.Application;
    @ApplicationPath("api")
    public class TodosApplication extends Application {
    }
  2. Declare resource class and its path below root

    import javax.ws.rs.Path;
    
    @Path("greetings")
    
    public class GreetingsResource {
  3. Declare resource method

    import javax.ws.rs.GET;
    
    import javax.ws.rs.Path;
    import javax.ws.rs.Produces;
    import javax.ws.rs.core.MediaType;
    import javax.ws.rs.core.Response;
    import javax.ws.rs.core.Response.ResponseBuilder;
    @GET
    
    @Path("hi")
    @Produces(MediaType.TEXT_PLAIN)
    public Response sayHi() {
       //...
    }
  4. Implement a response

    public Response sayHi() {
    
        String entity = "hi";
        ResponseBuilder rb = Response.ok(entity);            
        return rb.build();
    }
  • Use Web API resource classes as HTTP facades

  • Inject EJB/business logic components to perform details of work

  • Report obvious status from EJB/business logic and error messages

  • Report accurate status and error messages from Web API

Request failed because of a client request error

public class ClientErrorException extends Exception {

    public ClientErrorException(String msg) {
        super(msg);
    }
}

  1. Define basic EJB method

    GreetingEJB.java
    
    import javax.ejb.TransactionAttribute;
    import javax.ejb.TransactionAttributeType;
    GreetingEJB.java
    
    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    public String greet(String name) {
        return String.format("hello %s", name); //core business code
    }
  2. Define Resource method in terms of calling EJB method

    GreetingsResource.java
    
    @GET
    @Path("greet")
    @Produces(MediaType.TEXT_PLAIN)
    public Response greet(@QueryParam("name") String name) {
        ResponseBuilder rb=null;
        String entity = greetingEJB.greet(name);
        rb = Response.ok(entity);
        return rb.build();
    }
  3. Add error logic to EJB/business method

    GreetingEJB.java
    
    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    public String greet(String name) throws InvalidRequestException {
        try {
            if (name==null || name.isEmpty()) {
                throw new InvalidRequestException("Unable to greet, name not supplied");
            }
            
            return String.format("hello %s", name); //core business code
        } catch (RuntimeException ex) {
            throw new InternalErrorException("Internal error greeting name[%s]", name);
        }
    }
  4. Add minimal exception handling in resource method

    GreetingsResource.java
    
    @GET
    @Path("greet")
    @Produces(MediaType.TEXT_PLAIN)
    public Response greet(@QueryParam("name") String name) {
        ResponseBuilder rb=null;
        try {
            String entity = greetingEJB.greet(name);
            rb = Response.ok(entity);
        } catch (Exception ex) {
            rb=Response.serverError()
                .entity(String.format("unexpected error greeting name[%s]", name));
        }
        return rb.build();
    }
  5. Add complete error reporting logic to resource method

    GreetingsResource.java
    
    @GET
    @Path("greet")
    @Produces(MediaType.TEXT_PLAIN)
    public Response greet(@QueryParam("name") String name) {
        ResponseBuilder rb=null;
        try {
            String entity = greetingEJB.greet(name);
            rb = Response.ok(entity);
        } catch (InvalidRequestException ex) {
            rb = Response.status(Status.BAD_REQUEST)
                    .entity(ex.getMessage());
        } catch (InternalErrorException ex) {
            rb = Response.status(Status.INTERNAL_SERVER_ERROR)
                    .entity(ex.getMessage());
        } catch (Exception ex) {
            rb=Response.serverError()
                .entity(String.format("unexpected error greeting name[%s]", name));
        }
        return rb.build();
    }
  • Web content is shared using many standardized MIME Types

  • We will address only 2 of them -- both now required by JAX-RS

    • XML - JAXB support was part of original JAX-RS spec

    • JSON - requirement for JSONB support was added to JAX-RS in 2.1 as a part of JavaEE 8

  • No other MIME Types are required by JAX-RS

    • Any can be added thru standard JAX-RS marshaling/demarshaling framework

    • Many are immediately available thru vendor extensions

  • We will show manual approaches to marshaling/demarshaling first

    • However, content is automatically marshaled/demarshaled by JAX-RS provider

    • Manual marshaling/demarshaling approaches mainly useful within debug

  • Sample DTO Class

    public class MessageDTO implements Serializable {
    
        private String text;
        public MessageDTO() {}
        public MessageDTO(String message) {
            this.text = message;
        }
        public String getText() {
            return text;
        }
        public void setText(String text) {
            this.text = text;
        }
        @Override
        public String toString() {
            return text;
        }
    }
    • Implements Serializable to be legal for @Remote interfaces

    • Typical JavaBean

      • Default CTOR

      • public setters/getters

JSON is content type most preferred by Javascript UIs

{"text":"sample text"}

Direct dependency on API modules useful for pure DTO libraries

Direct dependency on API modules useful for pure DTO libraries


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<message>
    <text>sample text</text>
</message>

Content providers only necessary for IT tests. Application server will have all API and implementation modules server-side.

# client

private UriBuilder getBaseUrl(String...path) {
    UriBuilder builder = UriBuilder.fromUri(baseUrl);        
    if (path!=null) {
        for (String p:path) {
            builder = builder.path(p);
        }
    }
    return builder;
}
# client

    static String TODO_LISTS_PATH = "todo_lists";
# client

public Response getTodoLists(Integer offset, Integer limit) {
    URI uri = getBaseUrl(TODO_LISTS_PATH).build();
    WebTarget target = client.target(uri);
    if (offset!=null) {
        target=target.queryParam(OFFSET, offset);
    }
    if (limit!=null) {
        target=target.queryParam(LIMIT, limit);
    }
    return target.request(mediaType)
          .buildGet()
          .invoke();
}
# client

static <T> T getEntity(Response response, Class<T> type) {
    if (Response.Status.Family.SUCCESSFUL.equals(response.getStatusInfo().getFamily())) {
        return response.readEntity(type, type.getAnnotations());
    } else {
# client

Response response = todosClient.getTodoLists(null, null);
TodoListListDTO todoLists = getEntity(response, TodoListListDTO.class);
private ResponseBuilder getBadRequestResponse(Exception ex) {

    logger.debug(ex.getMessage());
    return Response.status(Status.BAD_REQUEST)
            .entity(new MessageDTO(ex.getMessage()));        
}
@POST

@Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response createTodoList(TodoListDTO todoList) {
    ResponseBuilder rb = null;
    try {
        TodoListDTO entity = todosMgmt.createTodoList(todoList);
        URI location = uriInfo.getBaseUriBuilder()
                .path(TodoListsResource.class)
                .path(TodoListsResource.class, "getTodoList")
                .build(entity.getName());
        rb = Response.created(location)                    
                .contentLocation(location)
                .entity(entity);
    } catch (InvalidRequestException ex) {
        rb = getBadRequestResponse(ex);
    } catch (InternalErrorException ex) {
        rb = getInternalErrorResponse(ex);
    } catch (Exception ex) {
        rb = getUndexpectedErrorResponse("Unexpected error creating TodoList", ex);
    }
    return rb.build();
}

Table of Contents

Purpose
1. Goals
2. Objectives
111. JavaEE Security Access Control Points
112. EJB Security
112.1. Declarative EJB Access Control
112.2. Programmatic Security
112.3. Optional Role Mapping: ejb-jar.xml
112.4. EJB Security Setup: META-INF/jboss-ejb3.xml
112.5. Summary
113. JBoss/Wildfly Security
113.1. Security Realm
113.2. Security Realm References
113.3. Security Domain
113.3.1. "other" Security Domain
113.4. Wildfly Built-in Authentication and Authorization
113.5. Summary
114. EJB Security RMI Client
114.1. JBoss Remoting
114.1.1. jndi.properties
114.1.2. JBoss Remoting JNDI Name
114.1.3. JBoss Remoting Authentication with JNDI InitialContext
114.1.4. Example Changing Users with JBoss Remoting
114.1.5. Optional Fixed Credentials
114.2. EJBClient
114.2.1. jndi.properties
114.2.2. EJBClient JNDI Name
114.2.3. EJBClient Authentication with JNDI InitialContext
114.2.4. Example Changing Users with EJBClient
114.2.5. Optional Fixed Credentials
114.3. Security Sanity Check
114.3.1. whoAmI
114.3.2. isCallerInRole
114.3.3. Client Issues Security Query Calls
114.3.4. Example Access Violation
114.3.5. Example Access Granted
114.4. Summary
115. run-as
115.1. Default security-identity: use-caller-identity
115.2. Run-as security-identity: role-name
115.3. Run-as principal: identity
115.4. Invoking Protected EJB thru Run-as Proxy
115.5. Summary
116. JAX-RS Resource Security
116.1. Web Security Setup
116.1.1. Assign WAR security-domain: jboss-web.xml
116.1.2. Assign WAR auth-method: web.xml
116.2. JAX-RS Resource Class
116.2.1. JAX-RS Debug Methods
116.3. JAX-RS Client Authentication
116.3.1. Authorization Header
116.3.2. JAX-RS Client Authorization Filter
116.3.3. JAX-RS Client Authorization Filter Registration
116.3.4. Protect BASIC Credentials with HTTPS
116.4. Declarative Access Control
116.4.1. Two intermediate contexts defined to access Nested Pinger Resource
116.4.2. Same Nested Pinger Resource Exposed
116.4.3. Declarative Access Control Constraints
116.4.4. Nested Resource Called from Two URIs
116.5. Summary
117. Web Tier Access Control
117.1. Authentication
117.2. Security Constraints (web.xml)
117.3. FORM-based Login
117.4. BASIC Authentication
117.5. Summary
  • EJB access restrictions

    • Declarative

    • Programmatic

  • EJB assignment to Security Domain

  • Server definition of Security Domain

  • Server Security Domain authentication and authorization



  • Access restrictions can also be defined in the ejb-jar.xml deployment descriptor

  • Legacy implementation (covered below) [15]

    
    <server xmlns="urn:jboss:domain:7.0">
        <management>
            <security-realms>
            ...
            </security-realms>
    ...
        <subsystem xmlns="urn:jboss:domain:security:2.0">
            <security-domains>
              ...
            </security-domains>
        </subsystem>
  • Latest implementation: "Elytron" (not covered) [16]

    
    <subsystem xmlns="urn:wildfly:elytron:3.0" final-providers="combined-providers" 
                                               disallowed-providers="OracleUcrypto">
        <security-domains>
          ...
        </security-domains>
        <security-realms>
          ...
        </security-realms>
    </subsystem>

# standalone.xml
<server xmlns="urn:jboss:domain:7.0">
  <management>
      <security-realms>
          <security-realm name="ApplicationRealm">
              <server-identities>
                  <ssl>
                      <keystore path="application.keystore" 
                                relative-to="jboss.server.config.dir" 
                                keystore-password="password" 
                                alias="server" 
                                key-password="password" 
                                generate-self-signed-certificate-host="localhost"/>
                  </ssl>
              </server-identities>
              <authentication>
                  <local default-user="$local" allowed-users="*" skip-group-loading="true"/>
                  <properties path="application-users.properties" 
                              relative-to="jboss.server.config.dir"/>
              </authentication>
              <authorization>
                  <properties path="application-roles.properties" 
                              relative-to="jboss.server.config.dir"/>
              </authorization>
          </security-realm>
      </security-realms>

<subsystem xmlns="urn:jboss:domain:security:2.0">
    <security-domains>
        <security-domain name="other" cache-type="default">
            <authentication>
                <login-module code="Remoting" flag="optional">
                    <module-option name="password-stacking" value="useFirstPass"/>
                </login-module>
                <login-module code="RealmDirect" flag="required">
                    <module-option name="password-stacking" value="useFirstPass"/>
                </login-module>
            </authentication>
        </security-domain>

<subsystem xmlns="urn:jboss:domain:ejb3:5.0">
    ...
    <default-security-domain value="other"/>
    ...
</subsystem>

<subsystem xmlns="urn:jboss:domain:undertow:6.0" default-server="default-server" 
                                                 default-virtual-host="default-host" 
                                                 default-servlet-container="default" 
                                                 default-security-domain="other" 
                                                 statistics-enabled="true">
  ...
</subsystem>
  • JBoss Remoting and JNDI InitialContext

  • EJBClient and JNDI InitialContext

  • Changing Users

  • Access Violations

  • Access Granted

@Stateless

public class SecurePingEJB implements SecurePingRemote, SecurePingLocal {    
    @Resource
    SessionContext ctx;
  • No caller context

  • Elevate access

  • Run-as role-name and identity

  • Setup WAR security

  • HTTP BASIC authentification with JAX-RS

  • HTTPS private connections

  • JAX-RS Filters

  • Declarative Access Control

$ jar tf target/securePingJaxRsWAR-5.0.0-SNAPSHOT.war
...
WEB-INF/beans.xml
WEB-INF/web.xml
WEB-INF/jboss-web.xml
  • BASIC - username and password passed in "Authentication" header Base64 encoded

  • FORM - credentials submitted as part of a form response

  • CLIENT-CERT - client public key authenticated as part of HTTPS connection

  • DIGEST - an encyrpted form of BASIC

  • EXTERNAL


# web.xml
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  version="4.0">
    ...
</web-app>

# web.xml
<login-config>
  <auth-method>BASIC</auth-method>
  <realm-name>ApplicationRealm</realm-name>
</login-config>
  • May need separate WARs for mixed solutions using BASIC (API) and FORM

  • Wildfly legacy security offers the following option


# web.xml
  <!-- if mixing JAX-RS BASIC with HTML FORM
  http://undertow.io/undertow-docs/undertow-docs-1.3.0/index.html#servlet-security
   -->
  <auth-method>BASIC?silent=true,FORM</auth-method>
  • API will silently accept BASIC Authorization header if supplied

  • API will not provide any response codes or headers making browser believe it accepts BASIC

  • Web UI will act as if it only uses FORM

@ApplicationPath("api")

public class SecurePingJaxRsApplication extends Application {
@Path("ping")

public class SecurePingResource {
    //this injection requires CDI, which requires a WEB-INF/beans.xml file be in place to activate
    @EJB(beanName="SecurePingEJB", beanInterface=SecurePingLocal.class)
    private SecurePing secureService;
    
    @Context
    private SecurityContext ctx;
  • Lock down access to web pages and commands

  • Identify user prior to EJB interaction


  • Anything accessed via specified url-pattern must have admin role-name

  • Communication must be encrypted (i.e., switch to HTTPS)


  • Obtain missing user credentials using FORM when navigating to protected urls


  • Servlet accessible via multiple URLs

Example Creates Security Hole on Purpose

The example creates a security hole on purpose to be able to demonstrate EJB security backs the WEB security. The servlet mapped above is accessible through multiple URLs -- each restricted differently but attempting to provide the same functionality. If you access the servlet through the anonymous URL you will encounter many access failures communicating with the EJB. If you access the servlet using the admin URL you will be able to access all functionality.


  • Assigning web-tier to same security-domain as EJB tier

  • Interpose on contructor, EJB business and lifecycle methods

  • Interceptor lifecycle is same as the bean it interposes on (i.e., Stateless/Stateful)

  • Extends Java Interceptors specified in EJB Spec

  • Not specific to EJBs -- any POJO

  • EJB Bean Class

  • Business method(s)

  • Normalization and validation concerns

  • Decouple data manipulation/validation from business/DAO logic

Figure 120.8. Validator Base Class

public class ValidatorInterceptor {

    @Inject
    private Validator validator;
    private Class<?>[] groups;
    
    protected ValidatorInterceptor() {}
    public ValidatorInterceptor(Class<?>[] groups) { this.groups = groups; }
    
    @AroundInvoke
    public Object invoke(InvocationContext ctx) throws Exception {
        logger.debug("validating method: {}, groups: {}", ctx.getMethod(), Arrays.toString(groups));
        //validate each parameter
        for (Object param: ctx.getParameters()) {
            logger.debug("validating param: {}, groups: {}", param, Arrays.toString(groups));
            Set<ConstraintViolation<Object>> violations = validator.validate(param, groups);
            if (!violations.isEmpty()) {
                Exception ex = new InvalidParam(param.toString(), getErrors(violations));
                logger.debug("aborting call, found error: {}", ex.getMessage());
                throw ex;
            }
        }
        return ctx.proceed();
    }
    
    private List<String> getErrors(Set<ConstraintViolation<Object>> violations) {
        List<String> errors = new ArrayList<String>(violations.size());
        for (ConstraintViolation<Object> v: violations) {
            errors.add(v.toString());
        }
        return errors;
    }
}

  • Grabs parameters from InvocationContext

  • Validates each of them against assigned validation group


  • Define @InterceptorBinding (@Validation) and @Interceptor role for CDI

  • Define validation group(s) for base class


  • Defines @InterceptorBinding (@Validator) and @Interceptor role

  • Defines @AroundInvoke for business method

  • Subjects each parameter to normalization rules

  • Details of normalization omitted (initial caps for each name/word)

  • Communication between applications that exchange messages

    • Message forms a single, encapsulated, unit of communication between applications

  • De-couples Producer and Consumer of the Message

  • Message-Oriented-Middleware (MOM)

    • category of application communication

    • uses asynchronous message passing versus synchronous request/reply

  • Advantages

    • Producer and Consumer operate independently

      • Messages can be persisted when consumer unavailable

      • Messages can be retrieved even after producer is unavailable

    • Qualities of service can be applied independent of clients

    • Resource utilization can be applied by messaging provide

  1. Obtain JMSContext from ConnectionFactory (JavaSE) or injection (JavaEE container)

  2. Obtain Destination from JNDI lookup (JavaSE) or injection (JavaEE container)

  3. Create Message

    import javax.jms.MapMessage;
    
    MapMessage message = jmsContext.createMapMessage();
    
  4. Set Message Properties (optional)

    import javax.jms.JMSException;
    
    message.setJMSType("saleUpdate"); //JMSType is a pre-defined property
    
    message.setStringProperty("awayClub", ...);//"awayClub" is example of user-defined String property
    message.setIntProperty("awayTeamId", ...); //"awayTeamId" is example of user-defined int property
  5. Set Message Payload

    //this example is a MapMessage - each Message type has specific interface
    
    message.setLong("id", item.getId());
    message.setString("name", item.getName());
    message.setString("seller", item.getOwner().getUserId());
    message.setLong("startDate", item.getStartDate().getTime());
    message.setLong("endDate", item.getEndDate().getTime());
    message.setDouble("minBid", item.getMinBid());
    message.setDouble("bids", item.getBids().size());
    message.setDouble("highestBid", 
            (item.getHighestBid() == null ? 0.00 : item.getHighestBid().getAmount()));            
  6. Create JMSProducer

    import javax.jms.Producer;
    
    JMSProducer producer = context.createProducer();
    
  7. Set JMSProducer options

    //JMSProducer supports method chaining
    
    producer.setPriority(Message.DEFAULT_PRIORITY)
            .setTimeToLive(Message.DEFAULT_TIME_TO_LIVE)
            .setDeliveryMode(Message.DEFAULT_DELIVERY_MODE)
            .setDeliveryDelay(Message.DEFAULT_DELIVERY_DELAY);
  8. Send Message to Destination

    producer.send(sellTopic, message);
    
  1. Obtain JMSContext from ConnectionFactory (JavaSE) or injection (JavaEE container)

  2. Obtain Destination from JNDI lookup (JavaSE) or injection (JavaEE container)

  3. Create a JMSConsumer for a specific destination

    import javax.jms.JMSConsumer;
    
    try (JMSConsumer syncConsumer = context.createConsumer(destination);
    
         JMSConsumer asyncConsumer = context2.createConsumer(destination)) {
    }
  4. Call receive() to obtain the next message

    Message message=consumer.receiveNoWait();
    
  5. Get Message Properties (optional)

    String level = message.getStringProperty("level");//"level" is user-defined property            
    
    logger.debug("receive ({}, mode={}, pri={}{}):{}",++count,
            message.getJMSDeliveryMode(),       //"JMSDeliveryMode" is pre-defined property
            message.getJMSPriority(),           //"JMSPriority" is pre-defined property
            (level==null?"":", level="+level),
            message.getJMSMessageID());         //"JMSMessageID" is pre-defined property
  6. Get Message Payload

    TextMessage m1 = ...
    
    TextMessage text = m1.request.getText();
    ObjectMessage m2 = ...
    XxxDTO dto = (XxxDTO)m2.getObject();
  7. Alternately, call JMS 2.0 receiveBody(T) to get just the payload

  1. Implement a javax.jms.MessageListener

    public class BuyerMDB implements MessageListener {
    
        public void onMessage(Message message) {
  2. Annotate the class as @MessageDriven

    
    
    @MessageDriven(activationConfig={
            @ActivationConfigProperty(
                    propertyName="destinationType",
                    propertyValue="javax.jms.Topic"),            
            @ActivationConfigProperty(
                    propertyName="destination",
                    propertyValue="java:/jms/topic/ejava/examples/asyncMarket/topic1"),            
            @ActivationConfigProperty(
                    propertyName="messageSelector",
                    propertyValue="JMSType in ('forSale', 'saleUpdate')"),
            @ActivationConfigProperty(
                    propertyName="acknowledgeMode",
                    propertyValue="Auto-acknowledge")            
    })
    public class BuyerMDB implements MessageListener {
        @PermitAll
        public void onMessage(Message message) {
  3. Grant container permission to invoke onMessage()

    
    
    @MessageDriven(...)
    public class BuyerMDB implements MessageListener {
        @PermitAll
        public void onMessage(Message message) {
  4. Process Messages as they are received through onMessage() callback

  • Client EJB

    @Stateless
    
    public class AuctionMgmtEJB implements AuctionMgmtRemote, AuctionMgmtLocal {
        @EJB
        private AuctionMgmtActionEJB actions;
  • Worker EJB

    @Stateless
    
    public class AuctionMgmtActionEJB {
        public Date doWorkSync(long delay) {
        }    
        @Asynchronous
        public Future<Date> doWorkAsync(long delay) {
        }    
import java.util.concurrent.ExecutionException;

import java.util.concurrent.Future;
/**

 * Perform action async from this caller
 */
@Override
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void workAsync(int count, long delay) {
    DateFormat df = new SimpleDateFormat("HH:mm:ss.SSS");
    
    List<Future<Date>> results = new ArrayList<Future<Date>>();
      //issue requests
    long startTime = System.currentTimeMillis();
    for (int i=0; i<count; i++) {
        logger.info("{} issuing async request, delay={}", df.format(new Date()), delay);
        Future<Date> date = actions.doWorkAsync(delay);
        results.add(date);
        logger.info("async waitTime={} msecs", System.currentTimeMillis()-startTime);
    }
      //process results
    for (Future<Date> f: results) {
        logger.info("{} getting async response", df.format(new Date()));
        try {
            Date date = f.get();
        } catch (ExecutionException | InterruptedException ex) {
            logger.error("unexpected error on future.get()", ex);
            throw new EJBException("unexpected error during future.get():"+ex);
        }
        logger.info("{} got async response", df.format(new Date()));
    }
    long asyncTime = System.currentTimeMillis() - startTime;
    logger.info("workAsync time={} msecs", asyncTime);
}    
08:08:15,861 [Client] (default task-1) sellTopic=ActiveMQTopic[asyncMarket-topic1]

08:08:15,861 [Client] (default task-1) 08:08:15.861 issuing async request, delay=3000
08:08:15,863 [Client] (default task-1) async waitTime=2 msecs
08:08:15,863 [Client] (default task-1) 08:08:15.863 issuing async request, delay=3000
08:08:15,864 [Client] (default task-1) async waitTime=3 msecs
08:08:15,864 [Client] (default task-1) 08:08:15.864 issuing async request, delay=3000
08:08:15,864 [Client] (default task-1) async waitTime=3 msecs
08:08:15,864 [Client] (default task-1) 08:08:15.864 getting async response
08:08:15,865 [Worker] (EJB default - 8) async method 224 starting 3000 delay at 08:08:15.865
08:08:15,865 [Worker] (EJB default - 6) async method 222 starting 3000 delay at 08:08:15.865
08:08:15,865 [Worker] (EJB default - 7) async method 223 starting 3000 delay at 08:08:15.865
08:08:18,870 [Worker] (EJB default - 7) async method 223 completed 3000 delay at 08:08:18.870
08:08:18,870 [Worker] (EJB default - 8) async method 224 completed 3000 delay at 08:08:18.870
08:08:18,870 [Worker] (EJB default - 6) async method 222 completed 3000 delay at 08:08:18.870
08:08:18,873 [Client] (default task-1) 08:08:18.873 got async response
08:08:18,873 [Client] (default task-1) 08:08:18.873 getting async response
08:08:18,873 [Client] (default task-1) 08:08:18.873 got async response
08:08:18,873 [Client] (default task-1) 08:08:18.873 getting async response
08:08:18,873 [Client] (default task-1) 08:08:18.873 got async response
08:08:18,873 [Client] (default task-1) workAsync time=3012 msecs
  • Perform similar role of job schedulers

    • e.g., cron

  • Three types

    • Single Action Timer

    • Interval Timer

    • Calendar Timer

  • Timers are specific to an EJB

    • Each EJB may have one @Timeout method but many timers

      import javax.ejb.Timeout;
      
      @Timeout
      
      public void execute(Timer timer) {
    • Timer can hold Serializable context information

      public long sellProduct(String sellerId, AuctionItem item) throws ResourceNotFoundException {
      
          ...
          timerService.createTimer(item.getEndDate(), new Long(item.getId()));
          timerService.createSingleActionTimer(item.getEndDate(), 
                                               new TimerConfig(new Long(item.getId()), false));
    • If you need multiple @Timeout method behaviors - create multiple EJBs

import javax.annotation.Resource;

import javax.ejb.TimerService;
@Resource

private TimerService timerService;
  • Starting point for all EJB Timer programmatic manipulation

    • Timer Creation

      • createCalendarTimer(ScheduleExpression schedule, ...)

      • createIntervalTimer(Date initialExpiration, long intervalDuration, ...)

      • createSingleActionTimer(Date expiration, ...)

    • Get Timers

      • getTimers() - get Timers associated with this EJB

      • getAllTimers() - get Timers associated with this module

    Start by creating non-persistent Timers

    EJB Timers are created as perisistent=true by default. This sounds reasonable until you begin refactoring your application and start seeing "EJB not found", etc. on follow-on redeploys or many more EJB Timers firing that you believe should be. For programmatic EJB Timers - always pass in the optional TimerConfig and set persistent to false. For the Schedule annotation, set the persistent attribute to false.

    It is always desirable to be able to easily coldstart your application in development with a reboot of the application server or a redeploy of the application and not have to worry about artifacts of a previous implementation approach.