# Devonfw Guide V2.4.0

User Manual:

Open the PDF directly: View PDF .
Page Count: 986

Devonfw Guide
v2.4.0

1. Quick Start with Devonfw . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
2. Devonfw Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.1. Building Blocks of the Platform. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.2. Devonfw Technology Stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.3. Custom Tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.4. Devonfw Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
3. Why should I use Devonfw? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
3.1. Objectives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
3.2. Features. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
4. Download and Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
4.1. Prerequisites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
4.2. Download . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
4.3. Setup the workspace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
5. Running Sample Application. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
5.1. Basics of My Thai Star . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
5.2. Running My Thai Star . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
6. Getting Started Guides . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
6.1. Service Layers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
7. OASP4J. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
7.1. OASP4J Technology stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
7.2. OASP4J Tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
8. Configuring and Running OASP4J Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
8.1. Configuring the Application. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
8.2. Running the Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
9. OASP4Fn . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
9.1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
9.2. What is Serverless Computing?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
9.3. What does OASP4Fn provide on Serverless? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
10. OASP4Fn Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
10.1. Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
10.2. Import and Use the Library in Source Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
11. Running OASP4Fn . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
11.1. Run. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
11.2. Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
11.3. Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
12. Getting Started Guides . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
12.1. Client Layers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
13. OASP4JS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

13.1. OASP4JS Technology Stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
13.2. OASP4JS Tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
14. Download and Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
14.1. Download My Thai Star . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
14.2. Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
15. Running OASP4JS Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
15.1. Run the Server. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
15.2. Run the MyThaiStar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
16. Devon4sencha. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
16.1. Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
16.2. The Universal Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
16.3. Legal Considerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
17. Sencha Cmd . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
17.1. Workspaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
18. Devon4sencha Sample application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
18.1. Running the sample restaurant application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
19. Topical Guides for Devonfw . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
19.1. A Closer Look. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
20. Devonfw Outline . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
20.1. Why do we use an open source core . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
20.2. Devonfw and Open Source. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
20.3. Contributing to Devonfw . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
21. Devcon User Guide. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
21.1. Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
21.2. Download Devcon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
21.3. Devcon structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
21.4. Devcon basic usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
21.5. First steps with Devcon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
21.6. Devcon command reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
22. The Devon IDE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
22.1. Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
22.2. Cobigen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
22.3. IDE Plugins: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
23. Creating your First Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
23.1. Using Devcon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
23.2. Using the Archetype. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
24. Design Philosophy : Jump the Queue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
24.1. Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
24.2. User Stories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
24.3. UI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
24.4. Model. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81

25. Devonfw Distribution Structure. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
25.1. Understanding the structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
26. Database Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
26.1. Dependencies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
26.2. Database configuration. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
26.3. Further Details on Database Configurations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
27. CRUD operations and DAO implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
27.1. Create CRUD functionality for an entity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
28. Bean-Mapping using Dozer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
28.1. Why use Bean-Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
28.2. Bean-Mapper Dependency. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
28.3. Bean-Mapper Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
29. Write Unit Test Cases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
29.1. Unit Test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
29.2. TDD Test-driven development . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
30. Logging and Auditing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
30.1. Logging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
30.2. Auditing with Hibernate Envers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
31. Getting Started Cobigen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
31.1. Preparing Cobigen for first use. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
31.2. Creating a CRUD with Cobigen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
32. Creating user permissions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
Step 1: Define the permissions and roles

143

Step 2: Generate the PermissionConstants class

143

32.3. Fixing context problems. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
33. Transfer-Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
33.1. Business-Transfer-Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
33.2. Service-Transfer-Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
34. Deployment on Tomcat (Client/Server) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
34.1. General description of the packaging process . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
34.2. Preparing the client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
34.3. Preparing the server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
34.4. Packaging the apps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
34.5. Deploy on Tomcat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
34.6. Running Bootified War . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
35. Cookbook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
35.1. Devonfw Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
36. The Reporting module - Report generation with JasperReports . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
36.1. Include Reporting in a Devon project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
36.2. Basic implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
36.3. Working with templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157

36.4. Subreports . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
37. The Winauth-AD module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
37.1. Authentication with Active Directory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
38. The Winauth-SSO module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
38.1. Single Sign On . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
39. The i18n module. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
39.1. Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
39.2. The i18n Module. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
40. Devon-locale module(i18n resource converter) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
40.1. Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
40.2. Devon locale structure and working . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
41. The async module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
41.1. Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
41.2. The async module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
42. The Integration Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
42.1. Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
42.2. Stack of technologies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
42.3. Installing Apache Active MQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
42.4. Using Apache Active MQ with Docker. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
42.5. Integration module details. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
43. Microservices in Devonfw . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
43.1. Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
43.2. Microservices schema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
43.3. How to create microservices in Devonfw? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
43.4. Devonfw archetypes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
43.5. Create New Microservices infrastructure application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
43.6. Create New Microservices Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
43.7. How to use microservices in Devonfw . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
43.8. How to modify the UserDetails information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
43.9. How to start with a microservice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
43.10. Calls between microservices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
44. Compose for Redis module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
44.1. Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
44.2. Include Compose for Redis in a Devon project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
44.3. Basic implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
44.4. Available operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
45. Cookbook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
45.1. Advanced Topics. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
46. Devon Security Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
46.1. Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
46.2. Authorization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241

46.3. Suggestions on the access model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
46.4. Vulnerabilities and Protection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
47. Data-Access Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
47.1. Persistence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
47.2. Database Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
47.3. Data-Access Layer Security . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
48. Set up and maintain database schemas with Flyway . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
48.1. Why use flyway . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
48.2. How it works for setting up the database and maintaining it. . . . . . . . . . . . . . . . . . . . . . . . . . . 274
48.3. How to set up a database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
49. Logic Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278
49.1. Component Interface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278
49.2. Component Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278
49.3. Passing Parameters Among Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
49.4. Logic Layer Security . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
50. Service Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282
50.1. Types of Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282
50.2. Versioning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282
50.3. Interoperability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
50.4. Protocol. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
50.5. Details on Rest Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
50.6. Details of SOAP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
50.7. Service Considerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297
50.8. Security. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
51. Web Services (JAX WS) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
51.1. What are Web Services? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
51.2. Why use Web Services? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
51.3. Characteristics of Web Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300
51.4. Components of SOAP based Web Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301
51.5. Apache CXF and JAX WS. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302
51.6. Creation of Web Service using Apache CXF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302
51.7. Soap Custom Mapping. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305
51.8. SOAP Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305
52. Batch Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
52.1. Batch architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
52.2. Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309
53. Integrating Spring Data in OASP4J . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
53.1. Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
53.2. Existing Structure of Data Access Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
53.3. Structure of Data Access Layer with Spring Data. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332
53.4. Query Creation in Spring Data JPA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339

53.5. Query creation by method names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
53.6. Implementing Query with QueryDsl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341
53.7. Paging and Sorting Support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342
53.8. References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
54. WebSocket . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
54.1. WebSocket configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
55. File Upload and Download . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347
55.1. File download from CXF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347
55.2. File upload from CXF. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348
55.3. MIME Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351
56. Docker. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
56.1. Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
56.2. Install Docker on Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
56.3. Use Docker manually . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
56.4. Fabric8io plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353
56.5. Docker tips . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354
57. Production Line for Beginners (Practical Guide) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355
57.1. Production Line description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355
57.2. Jenkins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355
57.3. Nexus. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357
58. Production Line . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360
58.1. Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360
58.2. Continuous Delivery benefits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360
58.3. The Production Line . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361
58.4. Devonfw Continuous Delivery infrastructure. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361
58.5. Production Line implementation for Devonfw projects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
59. Devon in Bluemix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 365
59.1. Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 365
59.2. Bluemix Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 365
59.3. Logs in Bluemix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368
60. Configuring and Running Bootified WAR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 370
60.1. Steps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 370
61. Deployment on Wildfly . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372
62. Deploy oasp4j application to WebSphere Liberty . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378
62.1. Setup and Configure WebSphere Liberty . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378
63. Deployment on WebLogic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383
63.1. Steps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383
63.2. Sencha. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 384
63.3. Packaging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 384
64. Cobigen advanced use cases: SOAP and nested data. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385
64.1. Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385

64.2. CobiGen with SOAP functionality (without nested data) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385
64.3. CobiGen with nested data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386
65. Compatibility guide for JAVA and TOMCAT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387
65.1. What this guide contains . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387
65.2. Using JAVA7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387
65.3. Using java8. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389
65.4. Using Tomcat8. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389
65.5. Using Tomcat7 for deploying. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389
65.6. Linux and Windows Compatibility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389
66. Dockerfile for the maven based spring.io projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
66.1. Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
66.2. Dockerfile Commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
66.3. Multi-stage builds. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392
67. Introduction to generator-jhipster-DevonModule . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396
67.1. Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396
67.2. Download and install the generator-jhipster-DevonModule . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396
67.3. Registering the generator-jhipster-DevonModule in your project . . . . . . . . . . . . . . . . . . . . . . . 397
67.4. Generate files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 397
67.5. Interesting links . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398
68. Cookbook OSS Compliance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399
68.1. Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399
68.2. Obligations when using OSS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399
68.3. Automate OSS handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 400
68.4. Configure the Mojo Maven License Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 400
68.5. Retrieve licenses list . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403
68.6. Create an OSS inventory. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403
68.7. Create a THIRD PARTY file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403
68.8. Download and package OSS SourceCode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 404
68.9. Handle OSS within CI-process . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405
69. Topical Guides for Service Layers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 406
70. OASP4J . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407
70.1. A Closer Look. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407
71. Creating New OASP4J Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 408
71.1. Getting Devonfw distribution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 408
71.2. Initialize the distribution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 408
71.3. Create the Server Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 409
71.4. Import and Run the Application. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 410
71.5. What is generated? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414
72. OASP4J Application Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 418
72.1. The OASP4J project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 418
72.2. The components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 418

72.3. The component structure (layers) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 419
73. OASP4J Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 420
74. OASP4J Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421
74.1. Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421
74.2. OASP4J Component example. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 422
75. OASP4J Component Layers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 425
75.1. Layers implementation. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 425
75.2. Data Access layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431
76. OASP4J and Spring Boot Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440
76.1. Internal Application Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440
76.2. Externalized Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 445
77. OASP4J Validations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 447
77.1. My Thai Star Validations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 447
77.2. Add your own Validations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 448
78. Testing with OASP4J . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 454
78.1. Testing: My Thai Star. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 454
78.2. Testing: Jump the Queue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455
78.3. Additional Test functionalities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 459
78.4. Running the Tests with Maven . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 460
79. OASP4J Deployment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 462
79.1. The server Project. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 462
79.2. Running the app with Maven . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463
79.3. Packaging the app with Maven. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 464
79.4. Deploying the war file on Tomcat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465
80. OASP4J . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467
80.1. Cookbook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467
81. OASP4J Project without Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 468
81.1. Add Property . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 468
81.2. Remove annotation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 468
82. Create your own Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 470
82.1. Basics. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 470
83. Creating Component’s Structure with Cobigen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 481
83.1. Importing Cobigen templates

481

83.2. Cobigen Health Check

481

83.3. Visitor component structure

483

83.4. Access Code component structure

492

83.5. Run the app

492

84. OASP4J Adding Custom Functionality. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 493
84.1. Returning the Access Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 493
84.2. List visitors with their access code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 496
85. Spring boot admin Integration with OASP4J. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 500

85.1. Configure the Spring boot admin for the OASP4J App. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 500
85.2. Register the client app. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 502
85.3. Loglevel management. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 504
85.4. Notification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 505
85.5. Integrate Spring boot admin with module. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 505
86. OASP4Fn. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 507
86.1. A Closer Look. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 507
87. Creating new OASP4Fn Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 508
87.1. Install tools. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 508
87.2. Postman . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 509
87.3. Starting our project through a template. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 509
87.4. Local database set up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 509
87.5. AWS credentials . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 512
87.6. Adding Typings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 513
87.7. Start the development. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 513
87.8. Generating the configuration files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 517
87.9. Build and run your handlers locally . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 517
88. Application structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 520
89. Application Program Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 521
90. Adapters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 522
90.1. Types of adapters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 522
91. Application Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 523
91.1. File structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 523
91.2. Handler configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 524
91.3. Default configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 524
92. Command Line Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 526
92.1. Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 526
93. Testing OASP4Fn Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 528
93.1. Install global dependencies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 528
93.2. Writing the tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 528
94. OASP4Fn Application Deplyment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 533
95. Topical Guides for Client Layers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 534
96. OASP4JS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 535
96.1. A Closer Look. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 535
97. Creating new OASP4JS Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 536
97.1. End Result is Jump The Queue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 536
97.2. Installing Global Tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 538
97.3. Creating Basic Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 539
97.4. Working with Google Material and Covalent Teradata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 540
97.5. Alternative : ng-seed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 543
97.6. Start the development. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 543

97.7. Making Calls to Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 564
98. An OASP4JS Application Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 568
98.1. Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 568
99. OASP4JS Architecture Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 575
99.1. Basic. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 575
99.2. Additional Functionalities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 575
100. Angular Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 577
100.1. What are Angular Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 577
100.2. Create a new component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 577
100.3. Toolbars . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 578
100.4. Root Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 578
100.5. Routing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 578
100.6. Forms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 579
100.7. Teradata Covalent components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 579
101. Angular Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 581
101.1. What are Angular Services? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 581
101.2. Dependency Injection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 581
101.3. Create a new service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 582
101.4. Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 582
101.5. Guards. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 582
101.6. Server communication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 583
102. OASP4JS Deployment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 584
103. OASP4JS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 585
103.1. Cookbook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 585
104. OASP4JS NPM-Yarn Workflow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 586
104.1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 586
105. OASP4JS i18n internationalization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 596
106. OASP4JS i18n approach . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 597
106.1. Install NGX Translate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 597
106.2. Configure NGX Translate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 597
106.3. Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 598
106.4. Service translation. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 600
107. Accessibility Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 601
107.1. Key Concerns of Accessible Web Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 601
107.2. Visual Assistance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 604
107.3. Accessibility with Angular Material . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 605
108. Angular CLI common issues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 608
109. Angular CLI update guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 609
110. Devon4Sencha . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 610
110.1. A Closer Look . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 610
111. Creating a new application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 611

112. Application architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 615
112.1. MVC (Model, View, Controller): . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 615
112.2. MVVM (Model, View, Controller, ViewModel, ViewController): . . . . . . . . . . . . . . . . . . . . . . . . 615
112.3. Structure of a Devon4sencha application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 616
112.4. Universal Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 620
113. Project Layout. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 623
113.1. Main Template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 623
114. Code conventions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 627
114.1. Controllers, ViewModels and ViewControllers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 627
114.2. Internationalization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 627
114.3. Event Names and listeners . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 627
114.4. Code style . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 628
115. Sencha Client and Sencha Architect project generation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 629
115.1. Getting ready. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 629
115.2. Generating . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 631
115.3. Deploying . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 631
116. Creating a new page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 632
116.1. Step 1: ViewModel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 632
116.2. Step 2: ViewController . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 632
116.3. Step 3: View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 632
116.4. Step 4: MenuItem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 633
116.5. Step 5: Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 634
117. Create a CRUD page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 636
117.1. CRUD Packaging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 636
117.2. Step 1: Add a CRUD page to the application menu. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 636
117.3. Step 2: Controller, a view factory and REST endpoints definition . . . . . . . . . . . . . . . . . . . . . . 637
117.4. Step 3: Create a model. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 639
117.5. Step 4: Create the Store. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 639
117.6. Step 5: Create the view and viewController . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 640
117.7. Step 6: Create i18n literals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 642
118. Complete CRUD example (Create, Read, Update and Delete) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 643
118.1. Extending the CRUD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 652
119. Extending the CRUD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 653
119.1. Refreshing the grid after changing some tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 653
119.2. Double-click functionality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 654
120. Extending the CRUD: ViewModels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 655
121. Pagination . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 666
122. Extending the CRUD: searching and filtering tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 669
123. Extending the CRUD: Adding an inbox to search tables. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 673
124. Extending the CRUD: Change the state of a table. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 680
125. Extending the CRUD: Editing menu order . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 684

125.1. Create Edit Order View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 684
125.2. Drag and drop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 691
126. Extending the CRUD: Working with layouts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 694
127. Extending the CRUD: Grid editable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 699
127.1. Creating the grid. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 699
127.2. Cellediting plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 703
127.3. Rowediting plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 707
127.4. Add empty records . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 708
128. Extending the CRUD: Custom plugin. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 710
129. The class system. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 713
129.1. Ext . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 713
129.2. Components. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 713
130. Rest endpoints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 720
130.1. URL template resolution. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 721
130.2. Query string. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 722
130.3. Alternative PUT format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 722
130.4. Automatic JSON response decode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 723
130.5. Base Url configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 723
131. Sencha data package. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 725
131.1. Devon.restproxy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 725
131.2. Pagination . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 726
131.3. Sorting. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 727
132. Ext.window.Window . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 729
132.1. Basic Window . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 729
132.2. Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 738
133. Layouts and Containers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 740
133.1. Containers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 740
133.2. Layouts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 741
134. Grids . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 742
134.1. Basic Grid Panel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 742
135. Forms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 745
135.1. Basic Form Panel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 745
136. Devon4Sencha . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 747
136.1. Cookbook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 747
137. CORS support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 748
137.1. Configure CORS support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 748
137.2. Bypass CORS security check during development . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 748
138. Security . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 751
138.1. Login process . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 751
138.2. User security info . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 752
138.3. Controlling visibility of controls based on user roles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 752

139. Theming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 753
139.1. Configuring Theme Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 753
139.2. Using a Theme in an Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 754
139.3. Making changes in the Theme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 755
139.4. Adding customs UI’s . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 756
140. Error processing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 759
141. Internationalization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 761
141.1. Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 761
141.2. Message bundles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 761
141.3. Change language . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 763
142. Mocks with Simlets: simulating server responses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 764
142.1. Simlet Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 764
142.2. JSON Simlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 764
143. Simlets in Devon4Sencha. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 766
143.1. Use Simlets in your client application. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 768
143.2. Simlets Benefits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 769
144. Devon4Sencha Best Practices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 770
144.1. Bad formed code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 770
144.2. Excessive or unnecessary nesting of component structures. . . . . . . . . . . . . . . . . . . . . . . . . . . 770
144.3. Memory leaks caused by failure to cleanup unused components . . . . . . . . . . . . . . . . . . . . . . 771
144.4. Poor folder structure for source code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 774
144.5. Use of global variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 776
144.6. Use of id . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 776
144.7. Unreliable referencing of components. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 777
144.8. Failing to follow upper or lowercase naming conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 778
144.9. Making your code more complicated than necessary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 779
144.10. Nesting callbacks are a nightmare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 780
144.11. Caching and references . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 782
144.12. Identation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 783
144.13. One class per file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 784
144.14. Too much work to return. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 784
144.15. Comments or Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 785
144.16. Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 786
144.17. Be lazy. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 787
144.18. Knowing this . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 787
144.19. Additional resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 788
145. Devon4Sencha Tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 789
145.1. Code Analysis Tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 789
145.2. Analysing code with Sencha Cmd . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 789
145.3. Sonar for JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 789
146. How to do effective Devon4Sencha code reviews . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 791

146.1. Benefits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 791
146.2. Best practices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 791
146.3. How to handle code reviews . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 792
146.4. On mindset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 792
146.5. Code review tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 792
147. Devon4Sencha testing tools. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 794
147.1. Tools for testing your Sencha code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 794
147.2. Sencha Test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 794
148. Adapting devon4sencha apps to microservices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 796
148.1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 796
148.2. Security changes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 796
149. Versions used to create this guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 800
149.1. How to start a cordova project from a sencha project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 800
149.2. Steps to do on the Sencha project. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 800
149.3. Steps to do on the Cordova project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 802
149.4. Steps to run the Sencha project in the Cordova project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 804
150. IDE and Project Setup with Eclipse Oomph. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 806
151. IDE Setup with the Oomph Installer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 807
151.1. Quick start guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 807
151.2. Detailed Walkthrough. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 807
151.3. Tweaking the installer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 813
151.4. Packaging the Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 814
152. Devon IDE Oomph Setup Definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 815
152.1. P2 Director (Devon 2.1.1) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 815
152.2. Compounds . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 815
152.3. Projects Import import.cobigen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 819
152.4. Products . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 819
153. Using Oomph in the IDE. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 820
153.1. Checking out a Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 820
153.2. Updating a Oomph based Product and Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 821
154. Oomph Tasks Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 822
155. Adding Content to the Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 824
155.1. Structure of the Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 824
155.2. Adding a Catalog to the Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 825
155.3. Adding a Product or a Project to a Catalog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 825
156. Creating an Oomph Product based on devon-ide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 826
156.1. Tasks already provided by the Product Catalog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 826
156.2. Customizing the devon-ide Product. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 826
157. Creation of Projects in the devon Project Catalog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 832
157.1. Tasks already provided by the Project Catalog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 832
157.2. Example Github Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 832

157.3. Additional Project Configurations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 836
158. Troubleshooting Oomph Setups. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 837
158.1. Unkown Variables show up on the Variables Page during installation . . . . . . . . . . . . . . . . . 837
158.2. P2 Task fails . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 837
158.3. Packaged IDE : Setup Tasks are not triggered . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 837
159. Contributing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 839
160. Wiki Contributions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 840
160.1. Text styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 840
160.2. Titles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 840
160.3. Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 841
160.4. Tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 841
160.5. Source Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 841
161. Code Contributions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 843
161.1. Notes on Code Contributions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 843
161.2. Introduction to Git and GitHub. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 843
161.3. Structure of our projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 843
161.4. Contributing to our projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 844
161.5. Reviewing Pull Requests. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 846
162. Development Guidelines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 848
163. Working with forked repositories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 849
163.1. Fork a repository . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 849
163.2. Configuring a remote for a fork . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 849
163.3. Syncing a fork . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 850
164. Contributor Covenant Code of Conduct . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 852
164.1. Our Pledge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 852
164.2. Our Standards. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 852
164.3. Our Responsibilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 852
164.4. Scope . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 853
164.5. Enforcement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 853
164.6. Attribution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 853
165. Appendix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 854
166. devonfw Release notes 2.4 “EVE”. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 855
166.1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 855
166.2. Changes and new features. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 855
167. Devonfw Release notes 2.3 "Dash" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 862
167.1. Release: improving & strengthening the Platform. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 862
167.2. An industrialized platform for the ADcenter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 862
167.3. Changes and new features. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 863
168. Devonfw Release notes 2.2 "Courage". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 869
168.1. Production Line Integration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 869
168.2. OASP4js 2.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 869

168.3. A new OASP Portal. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 869
168.4. New Cobigen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 869
168.5. MyThaiStar: New Restaurant Example, reference implementation & Methodology

870

showcase
168.6. The new OASP Tutorial. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 870
168.7. OASP4j 2.4.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 871
168.8. Microservices Netflix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 872
168.9. Devonfw distribution based on Eclipse OOMPH . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 872
168.10. Visual Studio Code / Atom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 872
168.11. More I18N options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 872
168.12. Spring Integration as devonfw Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 872
168.13. Devonfw Harvest contributions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 872
168.14. More Deployment options to JEE Application Servers and Docker/CloudFoundry . . . . . . 873
168.15. Devcon on Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 873
168.16. New OASP Incubators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 873
169. Release notes Devonfw 2.1.1 "Balu" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 875
169.1. Version 2.1.2: OASP4J updates & some new features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 875
169.2. Version 2.1.1 Updates, fixes & some new features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 876
169.3. Version 2.1 New features, improvements and updates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 877
170. Frequently Asked Questions (FAQ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 883
170.1. Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 883
170.2. Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 885
170.3. Configuration issues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 885
170.4. Crosscutting concerns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 889
171. Working with Git and Github . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 890
171.1. What is a version control system . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 890
171.2. What are Git and Github . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 890
171.3. Devon and OASP4J Workflow for Git . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 890
171.4. OASP Issue Work . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 898
171.5. Code Contribution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 899
172. Devonfw Dist IDE Developers Guide. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 901
172.1. Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 901
172.2. Updating OASP4Js module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 904
172.3. Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 909
172.4. Updating node.js. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 912
173. Devcon Command Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 916
173.1. dist . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 916
173.2. doc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 920
173.3. github . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 920
173.4. help . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 921
173.5. oasp4j . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 922

173.6. oasp4js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 926
173.7. project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 929
173.8. sencha . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 935
173.9. workspace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 938
173.10. system . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 939
174. Devcon Command Developer’s guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 941
174.1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 941
174.2. Creating your own Devcon modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 942
174.3. Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 957
175. Devon module Developer’s Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 958
175.1. Creation of module template. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 958
175.2. Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 959
175.3. Structure of created module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 961
176. List of Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 964
176.1. Devonfw Release 2.2.0. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 964
177. Steps to use Devon distribution in Linux OS. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 968

Devonfw Guide v2.4.0

1

Devonfw Guide v2.4.0

Chapter 2. Devonfw Introduction

Welcome to Devonfw, the Devon Framework. This is a product of the CSD industrialization effort to
bring a standardized platform for custom software development within Capgemini APPS2. This
platform is aimed at engagements where clients do not force the use of a determined technology so
we can offer a better alternative coming from our experience as a group.
Devon framework is a development platform aiming for standardization of processes and
productivity boost, that provides an architecture blueprint for Java/JavaScript applications,
alongside a set of tools to provide a fully functional out-of-the-box development environment.

2.1. Building Blocks of the Platform

Devonfw uses a state-of-the-art open source core reference architecture for the server (today
considered as commodity in the IT-industry) and on top of it an ever increasing number of highvalue assets that are developed by Capgemini.

2.2. Devonfw Technology Stack
Devonfw is composed of an Open Source part that can be freely used by other people and
proprietary addons which are Capgemini IP and can be used only in Capgemini engagements.The
Open Source part of Devonfw is called The Open Application Standard Platform (OASP) . It consist of

2

Devonfw Guide v2.4.0

2.2.1. Back-end solutions
• OASP4J: server implemented with Java. The OASP platform provides an implementation for
Java based on Spring and Spring Boot.
• OASP4FN: serverless implementation based on node.js.
• Dot Net implementation. (Upcoming)

2.2.2. Front-end solutions
For client applications, Devonfw includes two possible solutions based on JavaScript:
• OASP4JS: the OASP implementation based on Angular framework.
• devon4sencha: a client solution based on the Sencha framework.
Check out the links for more details.

2.3. Custom Tools
2.3.1. Pre-installed Software
• Eclipse: pre-configured and fully functional IDE to develop Java based apps.
• Java: all the Java environment configured and ready to be used within the distribution.
• Maven: to manage project dependencies.
• Node: a Node js environment configured and ready to be used within the distribution.
• Sencha: Devonfw also includes a installation of the Sencha Cmd tool.
• Sonarqube: a code quality tool.
• Tomcat: a web server ready to test the deploy of our artifacts.

2.3.2. Devcon
For project management and other life-cycle related tasks, Devonfw provides also Devcon, a
command line and graphic user interface cross platform tool.
With Devcon, users can automate the creation of new projects (both server and client), build and
run those and even, for server projects, deploy locally on Tomcat.

3

Devonfw Guide v2.4.0

All those tasks can be done manually using Maven, Tomcat, Sencha Cmd, Bower, Gulp, etc. but with
Devcon users have the possibility of managing the projects without the necessity of dealing with all
those different tools.

2.3.3. Cobigen
Cobigen is a code generator included in the context of Devonfw that allows users to generate all the
structure and code of the components, helping to save a lot of time wasted in repetitive tasks.

2.4. Devonfw Modules
As a part of the goal of productivity boosting, Devonfw also provides a set of modules to the
developers, created from real projects requirements, that can be connected to projects for saving all
the work of a new implementation.
The current available modules are:

4

Devonfw Guide v2.4.0

• async: module to manage asynchronous web calls in a Spring based server app.
• i18n: module for internationalization.
• integration: implementation of Spring Integration.
• microservices: a set of archetypes to create a complete microservices infrastructure based on
_Spring Cloud_Netflix.
• reporting: a module to create reports based on Jasper Reports library.
• winauth active directory: a module to authenticate users against an Active Directory.
• winauth single sign on: module that allows applications to authenticate the users by the
Windows credentials.

5

Devonfw Guide v2.4.0

Chapter 3. Why should I use Devonfw?
Devonnfw aims at providing a framework which is oriented at development of web applications
based on the Java EE programming model using the Spring framework project as the default
implementation.

3.1. Objectives
3.1.1. Standardization
It means that to stop reinventing the Wheel in thousands of projects, hundreds of centers, dozens of
countries. This also includes rationalize, harmonize and standardize all development assets all over
the group and industrialize the software development process

3.1.2. Industrialization of Innovative technologies & “Digital”
devonfw needs to standardize & industrialize. But not just large volume, “traditional” custom
software development. devonfw needs to offer a standardized platform which contains a range of
state of the art methodologies and technology options. devonfw needs to support agile development
by small teams utilizing the latest technologies for Mobile, IoT and the Cloud

3.1.3. Deliver & Improve Business Value

3.1.4. Efficiency
• Up to 20% reduction in time to market with faster delivery due to automation and reuse.
• Up to 25% less implementation efforts due to code generation and reuse.
• Flat pyramid and rightshore, ready for juniors.

3.1.5. Quality
• State of the Art architecture and design.
• Lower cost on maintenance and warranty.

6

Devonfw Guide v2.4.0

• Technical debt reduction by reuse.
• Risk reduction due to assets continuous improvement.
• Standardized automated quality checks.

3.1.6. Agility
• Focus on business functionality not on technical.
• Shorter release cycles.
• DevOps by design - Infrastructure as Code.
• Continuous Delivery Pipeline.
• On and Off-premise flexibility.
• PoCs and Prototypes in days not months.

3.2. Features
3.2.1. Everything in a single zip
The Devonfw distributions is packaged in a zip file that includes all the Custom Tools, Software and
configurations.
Having all the dependencies self-contained in the distribution’s zip file, users don’t need to install
or configure anything. Just extracting the zip content is enough to have a fully functional Devonfw.

3.2.2. Devonfw, the package
Devonfw package provides:
• Implementation blueprints for a modern cloud-ready server and a choice on JS-Client
technologies (either open source AngularJs or a very rich and impressive solution based on
commercial Sencha UI).
• Quality documentation and step-by-step quick start guides.
• Highly integrated and packaged development environment based around Eclipse and Jenkins.
You will be ready to start implementing your first customer-specific use case in 2h time.
• Iterative eclipse-based code-generator that understands "Java" and works on higher
architectural concepts than Java-classes.
• Example application as a reference implementation.
• Support through large community + industrialization services (Standard Platform as a service)
available in the iProd service catalog.

7

Devonfw Guide v2.4.0

In this section, you will learn how to setup the Devonfw environment and start working on first
project based on Devonfw.
The Devonfw environment contains all software and tools necessary to develop the applications
with Devonfw.

4.1. Prerequisites
In order to setup the environment, following are the prerequisites:
• Internet connection (including details of your proxy configuration, if necessary)
• 2GB of free disk space
• The ZIP containing the latest Devonfw distribution

The Devonfw distributions can be obtained from the TeamForge releases library and are packaged
in a zip file that includes all the needed tools, software and configurations

Using devcon

NOTE

You can do it using devcon with the command devon dist install, learn more here.
After a successful installation, you can initialize it with the command devon dist

4.3. Setup the workspace
1. Unzip the Devonfw distribution into a directory of your choice. The path to the Devonfw
distribution directory should contain no spaces, to prevent problems with some of the tools.
2. Run the batch file "create-or-update-workspace.bat".

8

Devonfw Guide v2.4.0

This will configure the included tools like Eclipse with the default settings of the Devonfw
distribution.
The result should be as seen below

The working Devonfw environment is ready!!!
Note : If you use a proxy to connect to the Internet, you have to manually configure it in Maven,
Sencha Cmd and Eclipse. Next section explains about it.

4.3.1. Manual Tool Configuration
Maven
Open the file "conf/.m2/settings.xml" in an editor

9

Devonfw Guide v2.4.0

Remove the comment tags around the  section at the beginning of the file.
Then update the settings to match your proxy configuration.

If your proxy does not require authentication, simply remove the  and
lines.
Sencha Cmd
Open the file software/Sencha/Cmd/default/sencha.cfg in an editor

10

Devonfw Guide v2.4.0

Search for the property definition of "cmd.jvm.args" (around line 45).
Comment the existing property definition and uncomment the line above it.
Then update the settings to match your proxy configuration.

Eclipse
Open eclipse by executing "eclipse-main.bat".

11

Devonfw Guide v2.4.0

In the Eclipse preferences dialog, go to "General - Network Connection".

Switch from "Native" to "Manual"

12

Devonfw Guide v2.4.0

Thats All!!!

13

Devonfw Guide v2.4.0

Chapter 5. Running Sample Application
Devonfw showcases "My Thai Start" as a sample application, which has the most common features
of a Devonfw application and its proposed best practices. The sample application also serves as a
final test to make sure that your environment is setup correctly. Devonfw distribution comes with a
latest Master version of My Thai Star application.

5.1. Basics of My Thai Star
The My Thai Star application is a solution for managing the online booking and orders of a
restaurant, it is addressed as a showcase app but designed with real requirements although trying
to serve as example of common use cases in web apps (master-detail model, login, authorization
based on roles, pagination, search with filters, etc.).

Setup Devonfw environment and follow the below steps:

14

Devonfw Guide v2.4.0

5.2. Running My Thai Star
5.2.1. Server
All Spring boot application have their own embedded Tomcat server. This feature help us to deploy
the application in the develop time without the need to create an server. To run the application
with this mode, you need to open Eclipse of your Devonfw distribution and perform the following
steps:
Step 1: Import the project
First of all, import above Sample Application into Eclipse using the following steps:
1. Open Eclipse by executing "eclipse-main.bat" from "Devon-dist" folder.
2. Select "File → Import".
3. Select "Maven → Existing Maven Projects" as shown below:

4. Select the directory "workspaces/examples/my-thai-star" as shown below and later press "OK":

15

Devonfw Guide v2.4.0

5. Then press "Finish".

6. Wait for Eclipse to finish importing the sample projects. This process might take several

16

Devonfw Guide v2.4.0

minutes, depending on the speed of your internet connection.
Step 2: Run the application
Using Spring Boot features, run your Java back-end applications using the Run as → Java application
over the SpringBootApp.java main class as shown below:

Once, you see the console messages like :

Tomcat started on port(s): 8081 (http)
Started SpringBootApp in 15.985 seconds (JVM running for 16.833)
you can start consuming the Java back-end.
Step 3: Test the application
To see the back-end services results, you can use Postman plugin for Chrome, although you can use
any other similar application.
Now, with Postman, you can do a simple GET request to obtain the information of a dish with id=1
(http://localhost:8081/mythaistar/services/rest/dishmanagement/v1/dish/1). And you will get a
result like this:

17

Devonfw Guide v2.4.0

Now, Server is running successfully!!!

5.2.2. Client
Make sure that the SERVER is Up and Running!
Step 1: Install Dependencies
To run MyThaiStar front-end, you need to globally install the following dependencies:
1. Node
2. Angular CLI
3. Yarn
Install or update the project

If older versions already exist on your machine, then in order to update Angular CLI globally run
following commands sequentially:

$npm uninstall -g angular-cli @angular/cli$ npm cache clean
$npm install -g @angular/cli If you have a previous version of this project, you must update the node modules as per your operating system. There are two different ways to do it, using npm or yarn. 1. Using NPM Go to the project folder "workspaces\examples\my-thai-star\angular" and run the following commands: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 18 Devonfw Guide v2.4.0 For Windows:$ rmdir /s node_modules
$rmdir /s dist$ npm install
For Linux or macOS:

$rm -rf node_modules dist$ npm install
To test the application as a PWA, you will need a small http server:

$npm i -g http-server Or run yarn using below steps. 2. Using Yarn The project is also tested with the latest Yarn version. After installing the above dependencies, you can go to the project folder "workspaces\examples\my-thai-star\angular" and execute the following command for yarn installation: yarn install After finishing, you will see something like: Otherwise, if you have a previous version of yarn, then run the following commands:$ rm -rf node_modules dist
$yarn If you face any problem with installing dependencies, kindly refer following links: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 19 Devonfw Guide v2.4.0 1. NPM and Yarn Workflow 2. My Thai Start - Angular Step 2: Run the application The simple way to run the My Thai Star Angular client is using npm or yarn commands:$ npm run serve

# OASP4J server

If everything goes well, the console output will be something like this:

Alternatively, you can also run using yarn.

$yarn serve # OASP4J server Step 3: Test the application Now, go to your browser and open localhost:4200 and you can see MyThaiStar client running! You can login to the application using: username: waiter password: waiter Also shown below: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 20 Devonfw Guide v2.4.0 This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 21 Devonfw Guide v2.4.0 Chapter 6. Getting Started Guides 6.1. Service Layers This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 22 Devonfw Guide v2.4.0 Chapter 7. OASP4J For Java based back-end solutions, OASP includes OASP4 Java(OASP4J) that provides a standardized architecture blueprint, an open best-of-breed technology stack as well as industry proven best practices and code conventions for a cloud ready Spring based server. Included in the Devon framework as a default server solution, OASP4J is the result of applying OASP principles in a Java based technology stack. With OASP4J, developers are able to create web application back-end in a fast and reliable way. Even it simplifies the tasks for generating web services (REST, SOAP) that can be consumed by web clients. 7.1. OASP4J Technology stack As mentioned earlier, OASP4J is not only a framework but alos a set of tools and conventions. OASP4J provides a Java back-end solution based on the following technologies: • Spring framework as the main development framework. • Spring Boot as project accelerator. • Maven as project and dependencies management tool. The Maven projects use the POM file to store all the necessary information for building the project (project configuration, dependencies, plugins, etc.). You can get more details about POM files here. Some of the main features of Spring Boot are: • Creation of stand-alone Spring applications in an easy way. • Embedded Tomcat directly (no need to deploy WAR files). • Provide 'starter' POMs to simplify Maven configuration. • Automatically configure Spring (whenever possible). • Provide production-ready features such as metrics, health checks and externalized configuration. • No requirement for XML configuration. For persistence and data access, OASP4J implements: • JPA and Hibernate • QueryDsl as query manager • H2 instance embedded as out-of-the-box database. It will be launched every time, when the application has been started. Therefore, the developers are able to start working with a real data access from scratch. • Flyway as a tool for version control of the database. • Apache CXF service framework. It provides the support for REST services through JAX-RS and SOAP services through JAX-WS This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 23 Devonfw Guide v2.4.0 7.2. OASP4J Tools 7.2.1. IDE As part of the Devon framework, OASP4J projects are integrated in a customized Eclipse instance that provides several pre-configurations and pre-installed plugins focusing on the code quality and productivity boosting. 7.2.2. Devon Tools Besides all the methodologies regarding the mentioned frameworks, the Devon/Oasp ecosystem also has other tools that are crucial for boosting the productivity and enhancing the organization of the Java projects. • Cobigen : a generic incremental generator for end to end code generation that will allow us to automate the generation of the main parts of the components of our apps. Starting from an Entity, Cobigen can generate all its CRUD functionality for us, starting from the service and ending up in the persistance data layer. • Devcon : A Devon is an internal tool to manage Devon based projects. Among many other tasks, it can create, run or deploy OASP4J applications avoiding users to do it manually. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 24 Devonfw Guide v2.4.0 Chapter 8. Configuring and Running OASP4J Application Devonfw distribution comes with a latest Master version of My Thai Star application. 8.1. Configuring the Application 8.1.1. Step 1: Setup the Devonfw Environment Configure the Devonfw environment using following steps given here. 8.1.2. Step 2: Import the project First of all, import above Sample Application into Eclipse using the following steps: 1. Open Eclipse by executing "eclipse-main.bat" from Devon-dist folder. 2. Select "File → Import". 3. Select "Maven → Existing Maven Projects" as shown below: 4. Select the directory "workspaces/examples/my-thai-star" as shown below: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 25 Devonfw Guide v2.4.0 5. Press "Finish" 6. Wait for Eclipse to finish importing the sample projects. This process might take several This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 26 Devonfw Guide v2.4.0 minutes, depending on the speed of your internet connection. 8.2. Running the Application 8.2.1. Step 1: Run the application Using Spring Boot features, run your Java back-end applications using the Run as → Java application over the SpringBootApp.java main class as shown below: Once you see the console messages like : Tomcat started on port(s): 8081 (http) Started SpringBootApp in 15.985 seconds (JVM running for 16.833) you can start consuming the Java back-end. 8.2.2. Step 2: Test the application You can use Postman plugin for Chrome to see the back-end services results, although you can use any other similar application. Now, with Postman, you can do a simple GET request to obtain the info of a dish with id=1 (http://localhost:8081/mythaistar/services/rest/dishmanagement/v1/dish/1). And you get a result like: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 27 Devonfw Guide v2.4.0 This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 28 Devonfw Guide v2.4.0 Chapter 9. OASP4Fn 9.1. Introduction Serverless is a framework that allows developers to build auto-scalable applications, pay-perexecution, event-driven apps on AWS Lambda, Microsoft Azure, IBM OpenWhisk and Google Cloud Platform. OASP4Fn is a npm package full of functionality independent of the goal of the developments made over this framework. It provides many different features following this approach in order to allow the developers to use the features that fit in their needs. Also, to build, test and deploy applications in an easy, fast and clean way using the Serverless framework. 9.2. What is Serverless Computing? Serverless computing consists of the following concepts and benefits: • Functions as a Service (FaaS). • Cloud provider automatically manages starting, stopping and scaling instances for functions. • More cost-efficient. • The business or person that owns the system does not have to purchase, rent or provision servers or virtual machines for the back-end code to run on. • It can be used in conjunction with the code written in traditional server style, such as microservices. • Functions in FaaS are triggered by the event types defined by the provider. For example: ◦ HTTP requests. ◦ AWS S3 updates. ◦ Messages added to a message bus. Besides the automatic horizontal scaling, the biggest benefit is that you only pay for the compute that you need. Depending on your traffic scale and shape, this may be a huge economic win in many projects. It solves common and inefficient situations such as occasional requests, where an application with a few small requests per minute makes the CPU idle most of time, or like inconsistent traffic where the traffic profile of an application is very speaky. For example: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 29 Devonfw Guide v2.4.0 In a traditional environment, you may need to increase your total hardware capability by a factor of 10 to handle the spikes, even though they only account for less than 4% of total machine uptime. 9.3. What does OASP4Fn provide on Serverless? The following picture shows what OASP4Fn offers in order to facilitate Serverless development. Currently, the OASP4Fn architecture defines a series of adapters to help developers to build applications and make use of different cloud providers. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 30 Devonfw Guide v2.4.0 9.3.1. TypeScript The Serverless framework is not prepared by default to use TypeScript. OASP4Fn allows the developer to use TypeScript as the programming language in any development, as it provides the types definitions and a pre-configured web-pack building system that automates the transpilation of the different handlers. 9.3.2. Infrastructure as Code The service made with OASP4Fn must follow a specified structure, that along with a configuration file will allow the user to avoid having to configure his service manually. /handlers /Http /get handler1.ts handler2.ts … handlerN.ts /post handler1.ts handler2.ts /put … /S3 ... /{EventName} /{TriggerMethod} {HandlerName}.ts The logic of our application must be stored in a folder, called handlers, inside it we will have a folder for each event used to trigger the handler and inside a folder with the name of the method that triggers the handler. Furthermore of the specified before, in the file oasp4fn.config.js, it is specified the configuration of the events, deployment information and the runtime environment in which the handlers will run. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 31 Devonfw Guide v2.4.0 9.3.3. Annotations In addition to the configuration file, we can specify to each handler a concrete configuration, related to the event that triggers the handler, using a dummy function that adds or modifies the configuration specified in OASP4Fn configuration file. // ... oasp4fn.config({path: 'attachments/{id}'}); export async function getAttachment (event: HttpEvent, context: Context, callback: Function) { // ... } These annotations will be only interpreted by the framework, so they do not inject or add any kind of functionality to the actual handler. 9.3.4. Cloud adapters OASP4Fn also comes with a simple interface that allows the user to have access to different services of cloud providers using adapters. That interface makes use of this adapters to retrieve data to the user through Promises, and let the user query that retrieved data. Currently available adapters: • AWS ◦ AWS DynamoDB ◦ AWS S3 ◦ AWS Cognito 9.3.5. Command Line Interface OASP4Fn provides a simple command line interface, that using the resources and the information provided by Infrastructure as Code, will help the user generate the proper files to build, deploy and test our application. Usage: oasp4fn [provider] [options] or: fun [provider] [options] Supported Providers: aws (by default aws) Options: -o, --opts file file with the options for the yml generation -p, --path directory directory where the handlers are stored -e, --express generates an express app.ts file -h, --help display the help This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 32 Devonfw Guide v2.4.0 Chapter 10. OASP4Fn Installation 10.1. Requirements In order to develop, build, test and deploy the Serverless applications with OASP4Fn, it is necessary to install the following packages globally: • typescript as it is the programming language supported. • ts-node is a TypeScript execution environment to run TypeScript applications locally. • mocha is the testing framework. • nodemon to run, monitor changes and restart automatically the node server in the development environment. • serverless to use the Serverless framework. • @oasp/oasp4fn to make the commands oasp4fn and its shortcut fun globally available. To install them, run the following command in a terminal:$ npm install -g typescript ts-node mocha nodemon serverless @oasp/oasp4fn

10.1.1. Database Dependencies
Running DynamoDB Local
Go to the folder where you unzip the DynamoDB, and run the command:

$java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar Create tables Go back to the project folder and run the command:$ npm run database
or

$yarn database This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 33 Devonfw Guide v2.4.0 10.2. Import and Use the Library in Source Code After installing OASP4Fn, you can import the package in your source code using the import line. It must be added in the imports section: import oasp4fn from '@oasp/oasp4fn'; import s3 from '@oasp/oasp4fn/dist/adapters/fn-s3'; import dynamo from '@oasp/oasp4fn/dist/adapters/fn-dynamo'; // Then, oasp4fn will be already available in the code oasp4fn.setDB(dynamo, {endpoint: 'https://...'}); oasp4fn.setStorage(s3); oasp4fn.config({path: 'getItem/{id}'}); //... This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 34 Devonfw Guide v2.4.0 Chapter 11. Running OASP4Fn 11.1. Run Execute the command:$ npm run fun
This command will generate the necessary files to deploy and build the handlers.

11.2. Start
Execute the command:

$npm run offline 11.3. Testing Before executing any test, you must create a new database for this purpose:$ npm run database:test
or

$yarn database:test Then, you can test the correct behavior of the business logic using the command:$ npm run test

Also, you can visualize if some of the changes are wrong when you save it, without executing every
time the previous command. To do this, you can run the next command on a new shell:

$npm run test:auto This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 35 Devonfw Guide v2.4.0 Chapter 12. Getting Started Guides 12.1. Client Layers This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 36 Devonfw Guide v2.4.0 Chapter 13. OASP4JS [OASP4JS] is the OASP front-end implementation based on Angular framework. It supports the development of Angular applications. OASP4JS includes Google Material Design as a main visual language, to take the maximum advantage of Angular possibilities and Material components. This makes it possible to build a modular, well-designed and responsive front-end applications. 13.1. OASP4JS Technology Stack OASP4JS works on the top of Angular but also, it provides several tools, libraries and code conventions to make your Angular apps easier to develop based on the following technologies: • Angular Framework as the main development framework. • Google Material2 as visual language and components. • Covalent Teradata as Component and utilities library working with Google Material. • Yarn as a Project dependencies management tool. The main advantages of these technologies are: • Teradata provides: ◦ 4 available layouts that fits nowadays design necessities. ◦ Several tools and utilities regarding style conventions such as text size, padding, margins… ◦ Complex components as: Data tables, Chips with autocomplete, pagination… • Google Material component library is composed by a number of fancy components like tabs, cards, buttons… • Yarn is quite faster than NPM and provides some more functionalities to manage dependencies. 13.2. OASP4JS Tools 13.2.1. IDE There is no integrated IDE with the framework. Therefore, you are free to use any IDE that fits your needs. Even though, we recommend the use of Visual Studio Code, along with a Guide of the most interesting plugins to make your development even easier, with Typescript and Angular. 13.2.2. Angular/Cli This Angular CLI helps the developer to automatize some common processes. It comes with Webpack as a main bundler. It is widely used in the Angular community, thanks to the boost of the productivity that it provides at the time of creating new projects from scratch, serving and testing the project, creating new components, services, directives… This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 37 Devonfw Guide v2.4.0 13.2.3. Testing Testing helps the developers to be sure that all the typescripts in services and components are working properly. Moreover, it can also tests the HTML tags and properties. There are many options to test an Angular app, the default option is Karma and Jasmine. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 38 Devonfw Guide v2.4.0 Chapter 14. Download and Setup 14.1. Download My Thai Star You can download or clone the latest version of My Thai Star application from the GitHub repository. It is recommended to use the Git Bash to work with the repository of GitHub. You can download Git and Git Bash for Windows system here. Also, you can learn more about the Devonfw best practices to work with Git and GitHub here. To clone the repository, open the Git Bash console and run the following command: git clone https://github.com/oasp/my-thai-star.git 14.2. Setup 14.2.1. Install Dependencies To run MyThaiStar front-end, you need to globally install the following dependencies: 1. Node, 2. Angular CLI 3. Yarn Once you have installed these dependencies, you can go to the project folder: "workspaces\examples\my-thai-star\angular" and execute the following command for the installation of project specific dependencies: yarn install After finishing, you will see something like: OR, execute the following command: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 39 Devonfw Guide v2.4.0 npm install Now, the environment setup is ready! This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 40 Devonfw Guide v2.4.0 Chapter 15. Running OASP4JS Application 15.1. Run the Server Make sure that the SERVER (OASP4J) is Up and Running! Otherwise, you wont be able to access any back-end functionalities. You can setup the server part using steps mentioned here. 15.2. Run the MyThaiStar 15.2.1. Step 1: Install Dependencies To run MyThaiStar front-end, you need to globally install the following dependencies: 1. Node 2. Angular CLI 3. Yarn Install or update the project If older versions already exist on your machine, then in order to update Angular CLI globally run following commands sequentially:$ npm uninstall -g angular-cli @angular/cli
$npm cache clean$ npm install -g @angular/cli
If you have a previous version of this project, you must update the node modules as per your
operating system. There are two different ways to do it, using npm or yarn.
1. Using NPM
Go to the project folder
"workspaces\examples\my-thai-star\angular" and run the following commands:
For Windows:

$rmdir /s node_modules$ rmdir /s dist
$npm install For Linux or macOS: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 41 Devonfw Guide v2.4.0$ rm -rf node_modules dist
$npm install To test the application as a PWA, you will need a small http server:$ npm i -g http-server
Or run yarn using below steps.
2. Using Yarn
The project is also tested with the latest Yarn version. After installing the above dependencies, you
can go to the project folder
"workspaces\examples\my-thai-star\angular"
and execute the following command for yarn installation:

yarn install
After finishing, you will see something like:

Otherwise, if you have a previous version of yarn, then run the following commands:

$rm -rf node_modules dist$ yarn
If you face any problem with installing dependencies, kindly refer following links:
1. NPM and Yarn Workflow
2. My Thai Start - Angular

15.2.2. Step 2: Run the application
The simple way to run the My Thai Star Angular client is using npm or yarn commands:

42

Devonfw Guide v2.4.0

1. Using NPM
Run the following command:

$npm run serve # OASP4J server Also, there are following alternatives in order to run My Thai Star Angular client with the different server technologies and environments:$ npm run serve:aot
$npm run serve:pwa$ npm run serve:prod
$npm run serve:prod:aot$ npm run serve:prodcompose
$npm run serve:prodcompose:aot Docker compose$ npm run serve:node
$npm run serve:node:aot server # # # # # # AOT compilation with OASP4J server Build and run the app as PWA Production server AOT compilation with production server Production server with Docker compose AOT compilation with production server with # Node.js or local Serverless server # AOT compilation with Node.js or local Serverless If everything goes well, the console output will be something like this: 2. Using Yarn Alternatively, you can also run using yarn. Make use of one of the following commands:$ yarn serve
$yarn serve:aot$ yarn serve:pwa
$yarn serve:prod$ yarn serve:prod:aot
$yarn serve:prodcompose$ yarn serve:prodcompose:aot
compose
$yarn serve:node$ yarn serve:node:aot
server

#
#
#
#
#
#
#

OASP4J server
AOT compilation with OASP4J server
Build and run the app as PWA
Production server
AOT compilation with production server
Production server with Docker compose
AOT compilation with production server with Docker

# Node.js or local Serverless server
# AOT compilation with Node.js or local Serverless

43

Devonfw Guide v2.4.0

Step 3: Test the application
Now, go to the browser and open

localhost:4200
and you can see MyThaiStar client running! You can login to the application using:
as shown below:

44

Devonfw Guide v2.4.0

Chapter 16. Devon4sencha
16.1. Introduction

Devon4Sencha is an alternative view layer for web applications developed with Devon Framework.
It is based on the proprietary libraries developed by Sencha. As it requires a license for commercial
applications it won’t be provided as Open Source (The companion OASP project).
These libraries provide support for creating SPA (Single Page Applications) with a very rich set of
components for both desktop and mobile. They also provide a clear programming model based on
the MVC and MVVM patterns. Sencha also has an excellent documentation for all the APIs in the
framework. Visit their website for more examples on this.
Devon4Sencha builds on top of Sencha libraries to further standardize the way applications are
created and to seamlessly integrate with the back-end application. We provide:
• Basic application template
• Basic layout for a tabbed based interface
• Security (CORS)
• Internationalization
• Better organization of source code (following Sencha MVC pattern)
• Ajax communication simplification
Devon4sencha uses the latest Sencha Version 6. The most important change in the version 6 is that
it merges two frameworks: Ext JS (web) and Sencha Touch (mobile) into one single framework.
Now, in Ext JS 6, you can maintain a single code. For some of the views, you may need to have a
separate view code, but there will be a lot of shared code.
They merged the common code and put them as a core framework, and they brought a concept
called toolkit. A toolkit is a package with visual components, such as button, panels, and so on.
There are two toolkits: classic and modern. Ext JS visual components are placed in the classic
toolkit, and Sencha Touch components are placed in the modern toolkit.
You can simply choose the toolkit that you want to target. If you are writing an application that only
targets mobile devices, you can choose modern, and if you are targeting only for desktop, then you
can choose the classic toolkit.

45

Devonfw Guide v2.4.0

16.2. The Universal Application
If you want to target both desktop and mobile devices, then in Ext JS 6, you can create a universal
application, which will use both the toolkits. Instead of adding the preceding toolkit config
mentioned before, you have to add the following builds config section that specifies which build
uses which toolkit and theme:

"builds": {
"classic": {
"toolkit": "classic",
"theme": "theme-triton"
},
"modern": {
"toolkit": "modern",
"theme": "theme-neptune"
}
},
The basic idea here is to have two set of toolkits in a single framework in order to target the desktop
and mobile devices. When building the application it will create two different bundles for classic
and modern.
When accessing from a desktop, the classic bundle will be used. When accessing from a
tablet/mobile the modern bundle will be selected.
The sample restaurant application is an example of an universal application targeting both
toolkits.

16.3. Legal Considerations
Devon4Sencha is based on Sencha Javascript libraries that are not free and this has to be taken into
consideration before starting with a project.
The license model for Sencha is "per developer" and each engagement should take care of having
enough licenses for developers working on the view layer.
Client should be informed of this fact and may be provided with licenses also if they retain the IP
for the project developed within Capgemini.

46

Devonfw Guide v2.4.0

Chapter 17. Sencha Cmd
To make your Devon4sencha application development easy, you will find, included in the Devon
distribution, a tool called Sencha Cmd. It’s available for Windows, Mac, and Linux.
Sencha Cmd is a cross-platform command line tool that provides many automated tasks around the
full life-cycle of your applications from generating a new project to deploying an application to
production.
It provides a collection of powerful features that work together and in conjunction with the Sencha
Ext JS framework like code generation, javascript compiler, workspace management, mobile native
packaging, build scripts, theming and so on.
The workspace management feature allows the import of "packages" into your application. That
way you can modularize and create packages for sharing functionality between applications.
Devon provides one of these packages called devon-extjs.
The most usual way to use Cmd during application development, is just running sencha app watch to
compile and test the application on a browser. This command must be launched from the
application root folder and starts a web server with the client application ready to access from the
browser.
Sencha Cmd is not a must for Devon4sencha development application, but using it makes your life
easier. So, it’s highly recommended to use it.

17.1. Workspaces
With Sencha Cmd Workspaces we can share code and packages between Sencha applications. This
is useful because Sencha distribution files occupy quite a bit of disk space.

Using devcon

NOTE

You can create a new Sencha workspace using devcon with the devon sencha

The easiest way to manually create a Sencha CMD workspace for your application is simply copying
the

example

workspace

of

devon4sencha

(copy

the

complete

directory

at

workspaces\examples\devon4sencha into workspaces\main\ and delete the
example application’s directory called ExtSample.
You can also create a workspace from scratch:
• Create the workspace:

sencha generate workspace /path/to/workspace

47

Devonfw Guide v2.4.0

• Copy ExtJS distribution to /path/to/workspace/ext.
• Copy

devon4sencha

package

from

the

example

workspace

to

/path/to/workspace/packages/devon-extjs.
• Copy

application

template

StarterTemplate

from

the

example

workspace

to

/path/to/workspace/StarterTemplate.
• Now, we can create several applications that share the ExtJS distribution and devon4sencha
package:

sencha generate app -ext --starter StarterTemplate MyApp1 /path/to/workspace/MyApp1
sencha generate app -ext --starter StarterTemplate MyApp2 /path/to/workspace/MyApp2

17.1.1. Sencha packages
With Sencha Cmd packages we can share and distribute code between Sencha applications. For
example, devon4sencha is distributed as a Sencha Cmd package that can be shared between
applications.
This code will be automatically added to the final application when building the application with
Sencha Cmd.

During application development, we can use the Sencha Cmd app watch feature to compile and
test the application in a browser. This command must be launched from the application root folder.

$cd devon4sencha$ cd ExtSample
$sencha app watch Any code changes to the application files will be detected by Sencha Cmd and it will update the application being served. It requires a manual refresh on the browser though to effectively see the changes. Since this is intended for development and in order to speed up things, only the first "build" defined on app.json will be processed. This means that if you are developing an "universal" app for desktop and mobile you will have several "build" sections defined. To trigger a concrete "build" you can name it after the watch argument$ sencha app watch modern

48

Devonfw Guide v2.4.0

Speed up development

When in development, you can speed things up even more if you are not modifying
NOTE

the sass styles. You can set skip.sass=1 on the build.properties file of your
workspace

application

(i.e:

in

the

restaurant

application

it

would

be

devon4sencha/ExtSample/.sencha/app/build.properties). Don’t forget to revert to 0 if
you modify styles or change to other build type (classic/modern)

49

Devonfw Guide v2.4.0

Chapter 18. Devon4sencha Sample
application
Devon comes with a sample java application simulating the backend for a restaurant application.
Devon4sencha’s sample application completes the restaurant application with a user-friendly UI.

18.1. Running the sample restaurant application

Using devcon

NOTE

You can automate the following steps using devcon with the devon sencha run

The following steps assume that you already have setup your Devon environment, and that you
have the devonfw-sample-server running on a server on port 8081. Refer to the Devonfw Server
documentation for instructions on how to setup the Devon environment or the devon sample
application.
1. Open a Devon command prompt by executing the batch file console.bat.

2. Enter the workspaces\examples\devon4sencha\ExtSample directory

50

Devonfw Guide v2.4.0

3. Execute the the command sencha app watch. It might take some time the first time you run this
command.
This command will compile the devon4sencha sample application and start a webserver to
serve it and will automatically recompile the application if it detects any changes to the
application’s files.
On first run, you probably will see something similar to the image below. We do not distribute
ExtJS itself with our sample application. Instead, the ExtJS SDK is downloaded on first use if not

If you have a valid ExtJS license, you can simply copy the ExtJS 6 SDK into
workspaces\examples\devon4sencha\ext and it will automatically get picked up.
If you see an error while executing this command, first try to delete the local
sencha
NOTE

repository

located

ENVIRONMENT>\software\Sencha\Cmd\default\repo.

at
If

there

was

devon foo saySomething -message hello
where:
• foo is the module.
• saySomething is the command of the foo module to be executed.
• '-message' is the parameter that the command saySomething needs to be executed.
• hello is the value for the message parameter.
Parameters
As its mentioned before from the point of view of the commands, we have two types of parameters:
the mandatory parameters and the optional parameters. The mandatory parameters must be
provided by the user specifying the parameter identifier and the value in the command line. The
optional parameters must be also provided to the app but, if the user do not specify it, devcon will
use a default value for them.
Global parameters
Devcon handles a third type of parameter that has nothing to do with command parameters. We
are referring it as global parameters.
The global parameters are a set of parameters that works in global context, which means it will
affect the behaviour of the command in the first phase i.e. before launching the command module
itself.
As these parameters act in a global context, we do not need to provide the values for them. They
work as flags to define some internal behaviour of devcon.
In the current version of Devcon we have the following global parameters :
• global parameter gui: defined with -g or --gui
• global parameter help: defined with -h or --help.

58

Devonfw Guide v2.4.0

• global parameter prompt: defined with -p or --prompt.
• global parameter stacktrace: defined with -s or --stacktrace.
• global parameter version: defined with -v or --version.
gui parameter

As we saw earlier the global parameter gui (-g) is the way we will launch the Devcon’s graphical
user interface. So to complete that operation we only need to execute

devon -g

help parameter

The global parameter help is very useful to show overall help info of Devcon or also for showing
more detailed info of each module and command supported. For example, if you don’t know
anything about how to start with Devcon, the option -h (or --help) will show a summary of the
devcon usage, listing the global parameters and the available modules alongside a brief description
of each one.

C:\>devon -h
Hello, this is Devcon!
usage: devon <> <> [parameters...]
Devcon is a command line tool that provides many automated tasks around
the full life-cycle of Devon applications.
-h,--help
show help info for each module/command
-v,--version
show devcon version
List of available modules:
> help: This module shows help info about devcon
> sencha: Sencha related commands
> dist: Module with general tasks related to the distribution itself
> doc: Module with tasks related with obtaining specific documentation
> github: Module to create a new workspace with all default configuration
> workspace: Module to create a new workspace with all default configuration
As a global parameter, if you use the -h parameter with a module, it will show the help info related
to given module including a basic usage and a list of the available commands in given module.

C:\>devon foo -h
Hello, this is Devcon!
usage: foo <> [parameters...]
This is only a test module.
Available commands for module: foo
> saySomething: This command is for say something

59

Devonfw Guide v2.4.0

In the same way, as a global parameter, if we use the -h parameter with a command, instead of
launching the command the help info related to the command will be shown

D:\>devon foo saySomething -h
Hello, this is Devcon!
usage: saySomething [-message] [-signature]
This command is to say something
-message
the message to be written
-signature
the signature
Even if you specify the needed parameters, the behaviour will be the same as we stated that the
global parameters affect how devcon behaves before launching the commands

D:\>devon foo saySomething -message hello -signature John -h
Hello, this is Devcon!
usage: saySomething [-message] [-signature]
This command is to say something
-message
the message to be written
-signature
the signature

prompt parameter

With this global parameter, you can ask devcon to prompt for all parameters (both optional and
mandatory) when launching a command.
To give an example, you can use the oasp4j create command (that creates a new server project
based on OASP4J model). In this case we would need to provide several parameters so the
command call would look like

D:\devon-dist>devon oasp4j create -servername myServer -groupid com.capgemini
-packagename com.capgemini.myServer -version 1.0
As you can see the command is defined by devon oasp4j create words and the rest of the command
line attributes are parameters.
With the global parameter -p Devcon gives the user the option to avoid defining any parameter
when launching the command and provide step by step all parameters after that, so the usage of
some commands can be way easier.
Going back to the previous example if we use the -p parameter we get

60

Devonfw Guide v2.4.0

D:\devon-dist>devon oasp4j create -p
Hello, this is Devcon!
Command: devon oasp4j create
Description: This command is used to create new server project
Parameter: serverpath - where to create
->
Parameter: servername - Name of project
-> myServer
Parameter: packagename - package name in server project
-> com.capgemini.myServer_
[...]
As you can see with the -p parameter Devcon asks for each parameter related to a command (the
optional ones can be left blank as the serverpath in the example) and the user can provide them one
on one, getting rid of the concern of knowing what parameters needs a command.
version parameter

This is a simple option that returns the devcon running version and is defined with -v (or -version). As the help option this will show the devcon version even though we have defined a
command with all required parameters.

D:\>devon -v
Hello, this is Devcon!
devcon v.1.0.0

D:\>devon foo saySomething -message hello -signature John -v
Hello, this is Devcon!
devcon v.1.0.0

21.5. First steps with Devcon
This section describes how to start using devcon from scratch. For this, you can use the global
option -h (help) in order to figure out which commands and parameters you need to define. But in a
very first approach, only the command devon will be enough. Therefore, the first step is to look for
a module that fits your requirements. As mentioned above, you can do this with the help option
(defined as -h or --help) or with a simple command devon. If you do not specify any information, you
will see a summary of the general help information with an example of usage, a list with global
parameters and the available modules.

61

Devonfw Guide v2.4.0

D:\>devon
Hello, this is Devcon!
usage: devon <> <> [parameters...]
Devcon is a command line tool that provides many automated tasks around
the full life-cycle of Devon applications.
-h,--help
show help info for each module/command
-v,--version
show devcon version
List of available modules:
> help: This module shows help info about devcon
> sencha: Sencha related commands
> dist: Module with general tasks related to the distribution itself
> doc: Module with tasks related with obtaining specific documentation
> github: Module to create a new workspace with all default configuration
> workspace: Module to create a new workspace with all default configuration
Once you have the list of modules and an example of how to use it, you may need to get the devon
distribution to go deeper in module *dist*, for that you can again use the help option after the
module definition.

D:\>devon dist -h
Hello, this is Devcon!
usage: dist <> [parameters...]
Module with general tasks related to the distribution itself
Available commands for module: dist
> s2: Initializes a Devon distribution for use with Shared Services.
Now, you know that the dist module has two commands, the install command and the s2 command
and you can see a brief description of each one therefore you can decide which one you need to
use. In case you have to get a devon distribution, it can be found by the install command with the
help option.

D:\>devon dist install -h
Hello, this is Devcon!
usage: install [-password] [-path] [-type] [-user]
the Devon distribution
-path
-type
the type of the distribution, the options are:
-user

62

Devonfw Guide v2.4.0

So now you know that the install command of the dist module needs:
• the type of distribution that can be 'oaspide' or 'devondist'.
With all the information, you can launch a fully functional command such as:

D:\>devon dist install -user john -password 1234 -path D:\Temp\MyDistribution -type
devondist
Regarding the order of the command parameters, devcon will order them internally so that you
don’t have to concern about that point and you can specify them in the order you want. The only
requirement is that all mandatory parameters should be provided.

21.6. Devcon command reference
For a full reference of all the available commands in Devcon, see the Devcon Command Reference

63

Devonfw Guide v2.4.0

Chapter 22. The Devon IDE
22.1. Introduction
The Devon IDE is the general name for two distinct versions of a customized Eclipse which comes in
a Open Source variant, called OASP4J-IDE, and a more extended version included in the "Devon
Dist" which is only available for Capgemini engagements.

Devonfw comes with a fully featured IDE in order to simplify the installation, configuration and
maintenance of this instrumental part of the development environment. As it is being included in
the distribution, the IDE is ready to be used and some specific configuration of certain plugins only
takes a few minutes.

As with the remainder of the distribution, the advantage of this approach is that you can have as
many instances of the -ide "installed" on your machine for different projects with different tools,
tool versions and configurations. No physical installation and no tweaking of your operating system
required. "Installations" of the Devon distribution do not interfere with each other nor with other
installed software.

22.1.2. Multiple Workspaces
There is inbuilt support for working with different workspaces on different branches. Create and
update new workspaces with a few clicks. You can see the workspace name in the title-bar of your
IDE so you do not get confused and work on the right branch.

22.2. Cobigen
In the Devon distribution we have a code generator to create CRUD code, called Cobigen. This is a
generic incremental generator for end to end code generation tasks, mostly used in Java projects.
Due to a template-based approach, CobiGen generates any set of text-based documents and
document fragments.

64

Devonfw Guide v2.4.0

Cobigen is distributed in the Devon distribution as an Eclipse plugin, and is available to all Devon
developers for Capgemini engagements. Due to the importance of this component and the scope of
its functionality, it is fully described here.

22.3. IDE Plugins:
Since an application’s code can greatly vary, and every program can be written in lots of ways
without being semantically different, IDE comes with pre-installed and pre-configured plugins that
use some kind of a probabilistic approach, usually based on pattern matching, to determine which
pieces of code should be reviewed. These hints are a real time-saver, helping you to review
incoming changes and prevent bugs from propagating into the released artifacts. Apart from
Cobigen mentioned in the previous paragraph, the IDE provides CheckStyle, SonarQube, FindBugs
and SOAP-UI. Details of each can be found in subsequent sections.

22.3.1. CheckStyle
What is CheckStyle
set of coding standards. Checkstyle does this by inspecting your Java source code and pointing out
items that deviate from a defined set of coding rules.
With the Checkstyle IDE Plugin, your code is constantly inspected for coding standard deviations.
Within the Eclipse workbench, you are immediately notified with the problems via the Eclipse
Problems View and source code annotations similar to compiler errors or warnings. This ensures
an extremely short feedback loop right at the developers fingertips.
Why use CheckStyle
If your development team consists of more than one person, then obviously a common ground for
coding standards (formatting rules, line lengths etc.) must be agreed upon - even if it is just for
practical reasons to avoid superficial, format related merge conflicts. Checkstyle Plugin helps you
define and easily apply those common rules.
The plugin uses a project builder to check your project files with Checkstyle. Assuming the IDE
Auto-Build feature is enabled, each modification of a project file will immediately get checked by

65

Devonfw Guide v2.4.0

Checkstyle on file save - giving you immediate feedback about the changes you made. To use a
simple analogy, the Checkstyle Plug-in works very much like a compiler but instead of producing
.class files, it produces warnings where the code violates Checkstyle rules. The discovered
deviations are accessible in the Eclipse Problems View, as code editor annotations and via
Installation of CheckStyle
After IDE installation, IDE provides default checkstyle configuration file which has certain check
rules specified . The set of rules used to check the code is highly configurable. A Checkstyle
configuration specifies which check rules are validated against the code and with which severity
violations will be reported. Once defined a Checkstyle configuration can be used across multiple
projects. The IDE comes with several pre-defined Checkstyle configurations. You can create custom
configurations using the plugin’s Checkstyle configuration editor or even use an existing Checkstyle
configuration file from an external location.
You can see violations in your workspace as shown in below figure.

Usage
So, once projects are created, follow steps mentioned below, to activate checkstyle:
1. Open the properties of the project you want to get checked.

66

Devonfw Guide v2.4.0

2. Select the Checkstyle section within the properties dialog .

3. Activate Checkstyle for your project by selecting the Checkstyle active for this project check box
and press OK

Now Checkstyle should begin checking your code. This may take a while depending on how many
source files your project contains. The Checkstyle Plug-in uses background jobs to do its work - so
while Checkstyle audits your source files you should be able to continue your work. After
should be some warnings from Checkstyle. This warnings point to the code locations where your
code violates the preconfigured Checks configuration.

67

Devonfw Guide v2.4.0

You can navigate to the problems in your code by double-clicking the problem in you problems
view. On the left hand side of the editor an icon is shown for each line that contains a Checkstyle
violation. Hovering with your mouse above this icon will show you the problem message. Also note
the editor annotations - they are there to make it even easier to see where the problems are.

22.3.2. FindBugs
What is FindBugs
FindBugsis an open source project for a static analysis of the Java bytecode to identify potential
software bugs. Findbugs provides early feedback about potential errors in the code.
Why use FindBugs
It scans your code for bugs, breaking down the list of bugs in your code into a ranked list on a 20point scale. The lower the number, the more hardcore the bug.This helps the developer to access
these problems early in the development phase.
Installation and Usage of FindBugs
IDE comes preinstalled with FindBugs plugin.
You can configure that FindBugs should run automatically for a selected project. For this right-click
on a project and select Properties from the popup menu. via the project properties. Select FindBugs
→ Run automatically as shown below.

68

Devonfw Guide v2.4.0

To run the error analysis of FindBugs on a project, right-click on it and select the Find Bugs… →

Plugin provides specialized views to see the reported error messages. Select Window → Show View
→ Other… to access the views. The FindBugs error messages are also displayed in the Problems
view or as decorators in the Package Explorer view.

69

Devonfw Guide v2.4.0

22.3.3. SonarLint
what is SonarLint
SonarLint is an open platform to manage code quality. It provides on-the-fly feedback to developers
on new bugs and quality issues injected into their code..
Why use SonarLint
It covers seven aspects of code quality like junits, coding rules,comments,complexity,duplications,
architecture and design and potential bugs. SonarLint has got a very efficient way of navigating, a
balance between high-level view, dashboard and defect hunting tools. This enables to quickly
uncover projects and / or components that are in analysis to establish action plans.
Installation and usage of SonarLint
IDE comes preinstalled with SonarLint. To configure it , please follow below steps:
First of all, you need to start sonar service. For that , go to software folder which is extracted from
Devon-dist zip, choose sonarqube→bin→-→and
execute startSonar bat file.

70

Devonfw Guide v2.4.0

If your project is not already under analysis, you’ll need to declare it through the SonarQube web
interface as described here. Once your project exists in SonarQube, you’re ready to get started with
SonarQube in Eclipse.
SonarLint in Eclipse is pre-configured to access a local SonarQube server listening on
http://localhost:9000/. You can edit this server, delete it or add new ones.By default, user and
password is "admin".If sonar service is started properly, test connection will give you successful
result.

For getting a project analysed on sonar, refer this http://docs.sonarqube.org/display/SONAR/
Linking a project to one analysed on sonar server.

In the SonarQube project text field, start typing the name of the project and select it in the list box:

71

Devonfw Guide v2.4.0

Click on Finish. Your project is now associated to one analyzed on your SonarQube server.
Changing Binding
At any time, it is possible to change the project association.
To do so, right-click on the project in the Project Explorer, and then SonarQube > Change Project
Association.

Unbinding a Project
To do so, right-click on the project in the Project Explorer, and then SonarQube > Remove
SonarQube Nature.

72

Devonfw Guide v2.4.0

Additional settings (such as markers for new issues) are available through Window > Preferences >
SonarLint

To look for sonarqube analysed issue, go to Window→Show View→ Others→SonarLint→SonarLint
Issues. Now you can see issues in soanrqube issues tab as shown

goto Dashboard.you can see all the statistics of analysis of the configured projects on sonar server.

73

Devonfw Guide v2.4.0

Chapter 23. Creating your First Application
In devonfw, you can create new project using two different ways:
• Using Devcon and jumpthequeue Project
• Using Archetype

23.1. Using Devcon
You can refer the following Creating New OASP4J Application page for more details.

23.2. Using the Archetype
In order to create a new application, you must use the archetype provided by devon which uses the
maven archetype functionality.
There are two alternatives for using the archetype to create a new application. One is to create
using command line. Another way is within eclipse, which is a more visual manner.

23.2.1. Using the Command Line
Step 1: Open the console
Open the Devonfw console by executing the batch file console.bat from the Devonfw distribution. It
is a pre-configured console which automatically uses the software and configuration provided by
the Devonfw distribution.
Step 2: Change the directory
You can create the project anywhere you want, but it is a good practice to create the projects in
your workspace directory. Therefore, run the following command in the console to change to the
directory to workspaces\main.

cd workspaces\main
This is the default location on Devonfw to create new applications. It is also possible to have your
own workspace.
Step 3: Create the new application
To create a new application, you need to execute one of the following commands:
• WAR packaging (arguments before archetype:generate identify the OASP4J archetype):

74

Devonfw Guide v2.4.0

mvn -DarchetypeVersion= -DarchetypeGroupId=io.oasp.java.templates
-DarchetypeArtifactId=oasp4j-template-server archetype:generate -DgroupId
= -DartifactId= -Dversion
= -Dpackage= -DdbType=
For example

mvn -DarchetypeVersion=2.5.0 -DarchetypeGroupId=io.oasp.java.templates
-DarchetypeArtifactId=oasp4j-template-server archetype:generate -DgroupId
=io.oasp.application -DartifactId=sampleapp -Dversion=0.1-SNAPSHOT -Dpackage
=io.oasp.application.sampleapp -DdbType=h2

This will create a new directory inside workspaces\main with the name of your application with the
created application inside.

23.2.2. Using the Eclipse
If you use a proxy to connect to the Internet, then the above steps will not work as
NOTE

Eclipse has a known bug where the archetype discovery does not work behind a
proxy. In this case, please use the command line version documented above.

To create a new application using Eclipse, you should have installed Devonfw distribution. Then,
follow below steps to create a new application:
Step 1 - Create a new Maven Project
Open Eclipse from a Devonfw distribution, by executing the batch file eclipse-main.bat, then go to
File > New > Maven Project. If you don’t see the option, click File > New > Other and use the filter to
search the option Maven Project
Step 2 - Choose the archetype
In the New Maven Project wizard, you need to choose the oas4j-template-server archetype, as shown
in below image.

75

Devonfw Guide v2.4.0

If you are not able to access the archetype even after checking the Include snapshot
archetypes option, then try adding the archetype repository manually. You can do it
with the Configure button located next to the Catalogs dropdown and then clicking
the Add Remote Catalog button. Finally, you need to add the repository URL
https://repo1.maven.org/maven2 and as Description you can use Maven Central.

NOTE

Use the Verify button to check the connection. Subsequently, you will see a message
with the amount of found archetypes.
Step 3 - Configure the application properties
Fill the Group Id, Artifact Id, Version and Package for your project. Also in Properties available from
archetype

section

update

dbtype

as

required

with

any

of

values

76

Devonfw Guide v2.4.0

• Click on the Finish button and the project will be ready for execution.

23.2.3. What is generated

77

Devonfw Guide v2.4.0

Chapter 24. Design Philosophy : Jump the
Queue

24.1. Introduction
When visiting public (free) or private (paid) events there are often large queues creating significant
waiting times. Ideally the organizer of the event would like to streamline the entry of people. That
would be possible by allowing people privileged access or, alternatively, offer an incentive for
people to arrive in time or to get into line more efficiently. A way of doing this would be to have a
website or application which would allow visitors to "Jump the Queue". This document describes
the design for such an application: “JumpTheQueue”.
Note that the document is intended to reflect a hypothetical but real use case. The
design reflects this by trying to be complete. But the implementation is simplified in
NOTE

order to serve as an example. Where implementation differs from design, this is
noted with an icon or symbol like : ⇒ and a comment about the nature of the
difference.

24.2. User Stories

78

Devonfw Guide v2.4.0

As a < type of user >, I want < some goal > so that < some reason >

24.2.1. Epic: Register Event
As a visitor to an event I want to be able to visit a website or use an app - called JumpTheQueue,
which provides me with a code (and optional date/time) after registration so that I can get a
User Story: register
As a user of JumpTheQueue, I want to register with my name and either Email, Phone number or
both so that I comply with the requirements to obtain the access code
Acceptance Criteria

A full name is mandatory. Either Email, Phone or both can be given. These must be valid. Validation
is a separate, asynchronous process.
US: terms and conditions
As a user of JumpTheQueue, I accept that the organiser of the event can store my personal data so
they can send me commercial notices (“spam”)
As a user of JumpTheQueue, I want to read the statement “By pressing ‘Request it’ you agree to the
Terms and Conditions” so that I can implicitly agree to aforementioned terms & conditions.
US: List queued visitors
As a user of JumpTheQueue I want to show the list of users ahead of me in the queue with optional
data & time so I can see how long I have to wait and optionally at what time I have to appear at the
access gate.
Etcetera
As a user of JumpTheQueue, I have to confirm either Email or Phone number by replying to a
message send to the account if entered so the data can be verified.
⇒ this is not further developed nor implemented
As a visitor , I want to go the the privileged access queue with my valid (i.e. validated) access code
so I can get direct access.
⇒ this is not further developed nor implemented

79

Devonfw Guide v2.4.0

24.3. UI
24.3.1. Flow
The basic flow of the application can be:

• Fill in a form to give your name and private data and then press a button with “Register”
• In case of validation errors, an error message will be shown
• If there are no errors then an Access Code will be generated which will be shown on a next
page. The access code can optionally be provided with proposed access time.
• From this page you can show a view of the current Queue, with the list of people queued

24.3.2. Mock-ups

80

Devonfw Guide v2.4.0

24.4. Model

81

Devonfw Guide v2.4.0

⇒ the Event item is not further developed nor implemented

24.4.1. Predicates

82

Devonfw Guide v2.4.0

Definition

< function name > = < parameters > => < *pure* function >
or

< function name > = trivial : < trivial description >

isnull = (v) => v === null
notnull = (v) => !isnull(v)
isempty = (s: string) => s.length === 0
notempty = (s: string) => !notempty(s)
isEmailAddress = trivial: notnull + notempty + consists of @
isTelephoneNumber = trivial: notnull + notempty + consists of sequence of numbers or
spaces (i.e. “4 84 28 81”)

24.4.2. Types
Definition

type < alias > :: < type defs > with predicated: < list of predicates >
or

type < alias > :: trivial: < trivial description >

83

Devonfw Guide v2.4.0

type ID :: trivial: Unique Atomic Identifier
type NamedItem :: string
with predicates: notnull, notempty
type TelephoneNumber :: string
with predicates: isTelephoneNumber
type Option :: None | T
type Result :: Error | T
type Error :: trivial: Error information with code & error description

24.4.3. Entities & Value Objects
Sequence (Entity)
Field

Type

Id

ID

Number

nameItem

AccessCode (Entity)
Field

Type

Id

ID

Code

NamedItem

Valid

boolean

Visitor

NamedItem

Telephone

Option

Email

Option

Request
Field

Type

Name

NameItem

Telephone

Option

Email

Option

ProvidedAccessCode
Field

Type

Name

NamedItem

84

Devonfw Guide v2.4.0

ProvidedAccessCode
Code

NamedItem

QueueName

NamedItem

Date&Time

Option

There must be a 1 - 1 relationship between a ProvidedAccessCode and an AccessCode.

24.4.4. Service Catalog
Definition

< service/function name > :: < parameters> => < return type >

registerEvent :: ( sequence: Sequence ) => Result
Send Sequence and obtain an AccessCode or Error result.

showList :: ( accesscode: NamedItem ) => Result>
Send AccessCode and receive an ordered list of access code with visitor name etc or Error result.

85

Devonfw Guide v2.4.0

Chapter 25. Devonfw Distribution Structure
In this section, you will find outlook on the Devonfw distribution structure that you will find right
after downloading the zip. In short, the use of each file and folder will be explained here.
Therefore, after unzipping the Devonfw distribution, you will find the following directory structure
as shown in the image:

25.1. Understanding the structure
In the above image, you can find different folders and executable .bat files. You will find below the
use of create-or-update-workspace.bat and update-all-workspaces.bat files. These are the scripts
which you will execute to obtain the whole Devonfw structure:
• The create-or-update-workspace.bat file will create the conf directory that stores the Maven
local repository and two configuration files (the settings.json with distribution information and
the settings.xml with the Maven connection settings). Alongside with that it will create the
eclipse-main.bat to start Eclipse.
• The update-all-workspaces.bat file sets some Eclipse preferences for workspaces and creates
all the Eclipse .bat launchers related to the projects in the workspace directory.
Hence, after executing these two scripts, following structure will be generated:

86

Devonfw Guide v2.4.0

Thereon, You will have the new conf directory as expected and the eclipse-main.bat and the eclipseexamples.bat related to the main and examples directories within the workspaces folder.
This is the final structure. A detailed explanation for each file and folder is given below:

25.1.1. conf
As mentioned previously, this directory is generated by executing the create-or-updateworkspace.bat. This is the directory where Maven will store its local repository, in a .m2/repository
path. Moreover, in the conf directory, you can find two settings files. The settings.json with
distribution information and the settings.xml with the Maven connection settings.

25.1.2. doc
Here, you can find, the documentation related to both, the starting development tasks with the
framework and implementing its more advanced features in pdf format.

25.1.3. scripts
This folder stores the scripts referenced in the .bat files in the root directory. These scripts are
related to internal tasks of the distribution.

25.1.4. settings
This directory stores the elements required for internal functionality of the distribution. Here, you
can find the configuration files of different software, included in the distribution, such as Eclipse,
Maven, Sonarqube and several more.

87

Devonfw Guide v2.4.0

25.1.5. software
All the software resources that the distribution needs are stored in this folder. Internally, the
distribution will search here for available software. Therefore, all the programs, plugins and tools
needed by the distribution must be located in this directory.

25.1.6. system
This is another directory with internal elements. In system folder, you can find some files related to
the environment configuration.

25.1.7. workspaces
This is the directory to store all the projects. One must keep in mind that the content of this folder
will be associated with a Eclipse .bat launcher files through the update-all-worksapces.bat script. So
if you want the separated Eclipse instances for two different projects, you must declare these
projects in separate directories within the workspaces folder.
To conclude, if you have a workspaces/project01 and a workspaces/project02 projects, then the
update-all-workspaces.bat script will create a eclipse-project01.bat launcher and a eclipseproject02.bat launcher in the root folder of the distribution. Thus, you can have access to the
different Eclipse instances with different configurations for each project.

Using devcon

NOTE

You can automate this operation using devcon with the devon workspace create

25.1.8. console.bat
This script launches the distribution’s cmd. Meaning, within this cmd, you have access to the
software located in the software folder, so that you can use the tools "installed" in that folder
although you don’t have this installed on your machine. Therefore, it is important to always run
this cmd (launching the console.bat script) to make use of the software related to the distribution.

25.1.9. create-or-update-workspace.bat
This script is already explained at the beginning of this chapter.

25.1.10. EclipseConfigurator.log
This is a file for internal usage and records the logs of the create-or-update-workspace.bat and the
update-all-workspaces.bat scripts.

88

Devonfw Guide v2.4.0

25.1.11. eclipse-project.bat
These files are used to have different Eclipse instances related to the different projects located into
the workspaces directory. Therefore, for each project in the workspaces directory, the update-allworkspaces.bat script will create an Eclipse launcher with structure eclipse-.bat. In
such a way, you can have different Eclipse environments with different configurations related to
the different projects of the workspace directory.

25.1.12. s2-create.bat and s2-init.bat
These scripts relate to the Shared Services functionality included in Devonfw. The s2-init.bat
configures the settings.xml file to connect to an Artifactory Repository. The s2.create.bat generates a
new project in the workspaces directory and does a checkout of a Subversion repository inside.
Each script needs to be launched from the distribution’s cmd (launching the console.bat script) and
some parameters to work properly.

25.1.13. update-all-workspaces.bat
This script is already explained at the beginning of this chapter.

25.1.14. variables.bat
This script is related to the internal functionality of the distribution. The script stores some
variables that are used internally by the distribution scripts.

89

Devonfw Guide v2.4.0

Chapter 26. Database Configuration
In this tutorial, you will see how to configure your application to connect with a real database of
your choice. For different Database Configuration we only need to give input to archetype at time of
project creation which DB you need to configure i.e. if you are using command line tool for

mvn -DarchetypeVersion= -DarchetypeGroupId=io.oasp.java.templates
-DarchetypeArtifactId=oasp4j-template-server archetype:generate -DgroupId
= -DartifactId= -Dversion= -Dpackage= -DdbType=
If you want to configure database such as MySQL, MS SQL or PostGre SQL, refer below sections.

26.1. Dependencies
Dependency for database in pom.xml file will be added automatically. For e.g if we are configuring
mysql database in our application the below dependency will be there in your pom.xml file:
MySQL:

mysql
mysql-connector-java

Note: This driver should NOT be used in a production environment because of the license issues.
See below for an alternative.

26.2. Database configuration
Database

configuration

will

be

automatically

generated

in

src/resources/config/application.properties_ file. Update the required values accordingly.

90

Devonfw Guide v2.4.0

MySQL:

server.port=8081
server.context-path=/
db>
# Enable JSON pretty printing
spring.jackson.serialization.INDENT_OUTPUT=true
# Flyway for Database Setup and Migrations
flyway.enabled=true
flyway.clean-on-validation-error=true
Database configuration will be automatically generated in src/resources/application.properties_
file. Update the required values accordingly.
MySQL:

spring.application.name=myapplication
server.context-path=/
security.expose.error.details=false
security.cors.enabled=false
spring.jpa.hibernate.ddl-auto=validate
# Datasource for accessing the database
spring.jpa.database=mysql
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming
.ImplicitNamingStrategyJpaCompliantImpl
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming
.PhysicalNamingStrategyStandardImpl
spring.batch.job.enabled=false
# Flyway for Database Setup and Migrations
flyway.locations=classpath:db/migration,classpath:db/type/mysql

26.3. Further Details on Database Configurations
26.3.1. MySQL
The use of the MySQL has already been illustrated in the above example. However, as mentioned,
the GPL licensed (native) MySQL driver should not be used in a production environment. As an
alternative, the free and liberally licensed "mariaDB" (a MySQL clone) library could be used.
The dependency declaration consists of:

91

Devonfw Guide v2.4.0

1.5.4

And the library can be used such as MySQL but with a slight change in the configuration:

26.3.2. PostgreSQL
The below dependency will be added in pom for postgres database:

org.postgresql
postgresql

Ultimately, the following configuration will be added for the postgresql driver and database:
Configuration in file : src/resources/application.properties_ file

spring.application.name=myapplication
server.context-path=/
security.expose.error.details=false
security.cors.enabled=false
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.database=postgresql
spring.jpa.hibernate.naming.implicitstrategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl
spring.jpa.hibernate.naming.physicalstrategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.batch.job.enabled=false
flyway.locations=classpath:db/migration,classpath:db/type/postgresql
Configuration in file : src/resources/config/application.properties_ file

92

Devonfw Guide v2.4.0

server.port=8081
server.context-path=/
spring.datasource.url=jdbc:postgresql://localhost:5432/<>
spring.jackson.serialization.INDENT_OUTPUT=true
flyway.enabled=true
flyway.clean-on-validation-error=true

26.3.3. Microsoft MSSQL Server
The Microsoft JDBC drivers are not available on Maven Central; they need to be downloaded from
the Microsoft site.
Once downloaded, they should be installed in the local Maven repository (.m2 folder on the local
machine). It can be done with the following command:

mvn install:install-file -DgroupId=com.microsoft.sqlserver -DartifactId=sqljdbc4
-Dversion= -Dpackaging=jar -DgeneratePom=true -Dfile=
Once installed, the below library will be there in the project’s pom.xml file. The dependency
declaration should be something like

com.microsoft.sqlserver
mssql-jdbc
6.4.0.jre8

Ultimately, the following configuration will be added for the MSSQL server driver and database:
Configuration in file : src/resources/application.properties_ file

spring.application.name=mssqltest
server.context-path=/
security.expose.error.details=false
security.cors.enabled=false
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.database=mssql
spring.jpa.hibernate.naming.implicitstrategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl
spring.jpa.hibernate.naming.physicalstrategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.batch.job.enabled=false
flyway.locations=classpath:db/migration,classpath:db/type/mssql

93

Devonfw Guide v2.4.0

Configuration in file : src/resources/config/application.properties_ file

server.port=8081
server.context-path=/
spring.datasource.url=jdbc:mssql:TODO
spring.jackson.serialization.INDENT_OUTPUT=true
flyway.enabled=true
flyway.clean-on-validation-error=true
For further information see: MS SQL Server and MS JDBC Driver

26.3.4. DB2
The dependency with DB2 is explained below:

com.ibm.db2.jcc
db2jcc4
10.1

com.ibm.db2
9.7

com.ibm.db2
db2java
9.7

And the properties are explained below:
Configuration in file : src/resources/application.properties_ file

94

Devonfw Guide v2.4.0

spring.application.name=db2test
server.context-path=/
security.expose.error.details=false
security.cors.enabled=false
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.database=db2
spring.jpa.hibernate.naming.implicitstrategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl
spring.jpa.hibernate.naming.physicalstrategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.batch.job.enabled=false
flyway.locations=classpath:db/migration,classpath:db/type/db2
Configuration in file : src/resources/config/application.properties_ file

server.port=8081
server.context-path=/
spring.datasource.url=jdbc:db2:TODO
spring.jackson.serialization.INDENT_OUTPUT=true
flyway.enabled=true
flyway.clean-on-validation-error=true
If you want to learn more about URL format, you can see SQLJ type 4 connectivity and SQLJ type 2
connectivity URL syntax.
NOTE

The IBM Drivers are not freely distributed, so you can’t find them in Maven. You
need to contact IBM or just find the license in required IBM software product.

95

Devonfw Guide v2.4.0

Chapter 27. CRUD operations and DAO
implementation
27.1. Create CRUD functionality for an entity
In this tutorial, we are going to create an entity for the application and provide services for the
typical Create, Read, Update and Delete operations for that entity.
If you want to create the application from scratch:
• Launch the console.bat script.
• Go to a desired directory and use the Maven command

mvn -DarchetypeVersion=2.5.0 -DarchetypeGroupId=io.oasp.java.templates
-DarchetypeArtifactId=oasp4j-template-server archetype:generate
-DgroupId=com.capgemini.devonfw.application -DartifactId=tutorial -Dversion=0.1
-SNAPSHOT -Dpackage=devonfw.tutorial
• Open Eclipse and Import the new tutorial project as Existing Maven Project
If you want to know more about how to create a new application you can visit the Create New
Application section.
Before continue it is important to keep in mind the packaging convention that Devonfw proposes.
Devonfw uses a strict packaging convention to map technical layers and business components to
the code. Devonfw uses the following Java-Package schema:

...[.]*
In our example application we find the different classes in this packages:
• Entity and DAO: devonfw.tutorial.tablemanagement.dataaccess.api[.]
• Logic: devonfw.tutorial.tablemanagement.logic[.]
• Services: devonfw.tutorial.tablemanagement.services[.]
This convention is based on the OASP4J conventions, which you can consult in the OASP4J Coding
conventions documentation

27.1.1. Step 1: Add the database schema
As first step we are going to add the database schema to our database.
In the script resources/db/migration/V0001__Create_schema.sql we add:

96

Devonfw Guide v2.4.0

CREATE CACHED TABLE PUBLIC.RESTAURANTTABLE(
id BIGINT NOT NULL,
modificationCounter INTEGER NOT NULL,
number BIGINT NOT NULL CHECK (NUMBER >= 0),
state INTEGER,
waiter_id BIGINT
);
And in the same path, we are going to create a new file to add the default data to the
RestaurantTable created. We create V0002__Master_data.sql file.

INSERT
1, 2);
INSERT
2, 0);
INSERT
3, 0);
INSERT
4, 0);
INSERT
5, 0);

INTO RESTAURANTTABLE (id, modificationCounter, number, state) VALUES (101, 1,
INTO RESTAURANTTABLE (id, modificationCounter, number, state) VALUES (102, 1,
INTO RESTAURANTTABLE (id, modificationCounter, number, state) VALUES (103, 1,
INTO RESTAURANTTABLE (id, modificationCounter, number, state) VALUES (104, 1,
INTO RESTAURANTTABLE (id, modificationCounter, number, state) VALUES (105, 1,

27.1.2. Step 2: Create the JPA entity
We are going to create a Table entity and its related interface (that will be reused between all the
objects involved with tables in the different layers).
This tutorial uses the same use-cases and scenario as the OASP4J sample
NOTE

application: Modelling a restaurant.
Do not confuse Table with a DB-table. In this context, we mean a table where the
guests of the restaurant are seated.

Create the devonfw.tutorial.tablemanagement.common.api package in the tutorial-server-core
project, by right-clicking on the project, and selecting New > Package.
Create the interface Table inside the devonfw.tutorial.tablemanagement.common.api package (you can
do it by right-clicking on the package and selecting New > Interface), and copy & paste the following
code:

package devonfw.tutorial.tablemanagement.common.api;
import devonfw.tutorial.general.common.api.ApplicationEntity;
import devonfw.tutorial.tablemanagement.common.api.datatype.TableState;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;

97

Devonfw Guide v2.4.0

/**
* This is the interface for a table of the restaurant. It has a unique {@link
#getNumber() number} can be
and may have a
*/
public interface Table extends ApplicationEntity {
/**
* @return the unique table number.
*/
@NotNull
@Min(0)
Long getNumber();
/**
* @param number is the new {@link #getNumber() number}.
*/
void setNumber(Long number);
/**
*/
TableState getState();
/**
* @param state is the new {@link #getState() state}.
*/
void setState(TableState state);
/**
devonfw.tutorial.staffmanagement.common.api.StaffMember#getId() ID} of the waiter
*
currently responsible for this table.
*/
Long getWaiterId();
/**
* Sets the field 'waiterId'.
*
* @param waiterId New value for waiterId
*/
void setWaiterId(Long waiterId);
}

NOTE

You may have compilation errors related to TableState that is not yet implemented.
We will take care of that in the next step.

98

Devonfw Guide v2.4.0

As you can see, Table extends ApplicationEntity class, as is recommended for standard mutable
entities of an application. This class provides the necessary methods for a mutable entity (ID getter
and setter basically).
In the above Table class, we save the state of the table by using a TableState enum, which we will
create now:
Create the package devonfw.tutorial.tablemanagement.common.api.datatype, and inside this package,
create a new class (actually an enum) called TableState and copy & paste the code below (as
mentioned before you can use the right-click option over the datatype package and select New >
Enum.

99

Devonfw Guide v2.4.0

package devonfw.tutorial.tablemanagement.common.api.datatype;
/**
state} of a
*/
public enum TableState {
/** The state of a free {@link devonfw.tutorial.tablemanagement.common.api.Table}.
*/
FREE,
/** The state of a reserved {@link
devonfw.tutorial.tablemanagement.common.api.Table}. */
RESERVED,
/** The state of a occupied {@link
devonfw.tutorial.tablemanagement.common.api.Table}. */
OCCUPIED;
/**
* @return {@code true} if {@link #FREE}, {@code false} otherwise.
*/
public boolean isFree() {
return (this == FREE);
}
/**
* @return {@code true} if {@link #RESERVED}, {@code false} otherwise.
*/
public boolean isReserved() {
return (this == RESERVED);
}
/**
* @return {@code true} if {@link #OCCUPIED}, {@code false} otherwise.
*/
public boolean isOccupied() {
return (this == OCCUPIED);
}
}

100

Devonfw Guide v2.4.0

It is possible that Eclipse removed the import of the TableState enum in the Table
NOTE

interface, if you saved the file before creating the TableState class.
If Eclipse shows errors still, after you’ve created the TableState enum, open the
Table interface and press Ctrl-Shift-O to automatically fix the 'class' imports.

Finally,

we

should

create

the

entity

implementation.

Create

the

package

devonfw.tutorial.tablemanagement.dataaccess.api, create the class TableEntity inside it and paste
the following code:

package devonfw.tutorial.tablemanagement.dataaccess.api;
import devonfw.tutorial.general.dataaccess.api.ApplicationPersistenceEntity;
import devonfw.tutorial.tablemanagement.common.api.Table;
import devonfw.tutorial.tablemanagement.common.api.datatype.TableState;
import javax.persistence.Column;
import javax.persistence.Entity;
/**
restaurant. A table has a unique
TableState#isOccupied() occupied}
* and may have a {@link
devonfw.tutorial.staffmanagement.dataaccess.api.StaffMemberEntity waiter}
* assigned.
*/
@Entity
// Table is a reserved word in SQL/RDBMS and can not be used as table name
@javax.persistence.Table(name = "RestaurantTable")
public class TableEntity extends ApplicationPersistenceEntity implements Table {
private static final long serialVersionUID = 1L;
private Long number;
private Long waiterId;
private TableState state;
@Override
@Column(unique = true)
public Long getNumber() {
return this.number;
}
@Override
public void setNumber(Long number) {

101

Devonfw Guide v2.4.0

this.number = number;
}
@Override
@Column(name = "waiter_id")
public Long getWaiterId() {
return this.waiterId;
}
@Override
public void setWaiterId(Long waiterId) {
this.waiterId = waiterId;
}
@Override
public TableState getState() {
return this.state;
}
@Override
public void setState(TableState state) {
this.state = state;
}
}

Validation
We want tables to never have negative numbers, so we are going to add a validation to our
TableEntity. Change the definition of the getNumber method of the TableEntity class as follows:

@Min(0)
@Column(unique = true)
public Long getNumber() {
return this.number;
}

You may need to solve the import of the @Min annotation by right clicking over the
NOTE

annotation and selecting import javax.validation.constraints.Min. You can read more

102

Devonfw Guide v2.4.0

27.1.3. Step 3: Create persistence layer
Data Access Objects (DAOs) are part of the persistence layer. They are responsible for a specific
entity and should be named as Dao[Impl]. The DAO offers the so called CRUDfunctionalities (create, retrieve, update, delete) for the corresponding entity. Additionally a DAO
may offer advanced operations such as search or locking methods.
For each DAO there is an interface named Dao that defines the API. For CRUD support and
common

naming

methods

we

derive

it

from

the

interface

devonfw.tutorial.general.dataaccess.api.dao.ApplicationDao, which was automatically generated
while using the OASP4J archetype to generate your application
For the sake of simplicity, in the rest of this tutorial, we will no longer specifically
NOTE

tell you to create java packages for new java classes.
Instead, we ask you to pay attention to the first line of each new java file, and
create, if necessary, the class' package.

Create the following DAO interface for our Table entity:
Listing 1. TableDao.java

package devonfw.tutorial.tablemanagement.dataaccess.api.dao;
import devonfw.tutorial.general.dataaccess.api.dao.ApplicationDao;
import devonfw.tutorial.tablemanagement.dataaccess.api.TableEntity;
import java.util.List;
/**
*/
public interface TableDao extends ApplicationDao, MasterDataDao
{
/**
* Returns a list of free restaurant tables.
*
*/
List getFreeTables();
}

Define querys
Before we proceed to the implementation of this DAO interface, we will create the SQL query.
OASP4J advises to specify all queries in one mapping file called orm.xml located in
src/main/resources/META-INF. So we are going to create a query to return all free tables that we will

103

Devonfw Guide v2.4.0

use in TableDaoImpl.
Listing 2. src/main/resources/META-INF/orm.xml

To avoid redundant occurrences of the query name we are going to use a constants class where we
are going to define the constants for each named query:
Listing 3. NamedQueries.java

package devonfw.tutorial.general.common.api.constants;
/**
* Constants of the named queries defined in NamedQueries.xml.
*
*/
public abstract class NamedQueries {
// put your query names from NamedQueries.xml as constants here
/** @see
devonfw.tutorial.tablemanagement.dataaccess.impl.dao.TableDaoImpl#getFreeTables() */
public static final String GET_FREE_TABLES = "get.free.tables";
}
Note that changing the name of the java constant can be done easily with refactoring (right-clicking
over the property and Refactor > Rename. Further you can trace where the query is used by
searching the references of the constant.
Implementation of DAO interface
Implementing a DAO is quite simple. We create a class named DaoImpl that extends
ApplicationMasterDataDaoImpl class and implements our DAO interface.
This is the DAO implementation for our TableDao interface:

104

Devonfw Guide v2.4.0

Listing 4. TableDaoImpl.java

package devonfw.tutorial.tablemanagement.dataaccess.impl.dao;
import java.util.List;
import javax.inject.Named;
import javax.persistence.Query;
import
import
import
import

devonfw.tutorial.general.common.api.constants.NamedQueries;
devonfw.tutorial.tablemanagement.dataaccess.api.TableEntity;
devonfw.tutorial.tablemanagement.dataaccess.api.dao.TableDao;

/**
*/
@Named
public class TableDaoImpl extends ApplicationMasterDataDaoImpl implements
TableDao {
/**
* The constructor.
*/
public TableDaoImpl() {
super();
}
@Override
public Class getEntityClass() {
return TableEntity.class;
}
@Override
public List getFreeTables() {
Query query = getEntityManager().createNamedQuery(NamedQueries.GET_FREE_TABLES,
TableEntity.class);
return query.getResultList();
}
}
As you can see ApplicationMasterDataDaoImpl already implements the CRUD operations so you
only have to implement the additional methods that you have declared in your Dao
interface.

105

Devonfw Guide v2.4.0

The business logic of our application is defined in the logic layer, as proposed by the OASP4J Guide.
The logic layer also maps entities from the dataaccess layer to/from transfer objects, so we do not
expose internal details of the applications implementation to higher layers.
In Devonfw applications, there are several different types of Transfer Objects (short TO). One is the
Entity Transfer Object (ETO) used to transfer a representation of an Entity.
As a first step, we will define an ETO for the Table entity, to be used in the interface of our logic
layer.
Create the following file:
Listing 5. TableEto.java

package devonfw.tutorial.tablemanagement.logic.api.to;
import devonfw.tutorial.general.common.api.to.AbstractEto;
import devonfw.tutorial.tablemanagement.common.api.Table;
import devonfw.tutorial.tablemanagement.common.api.datatype.TableState;
import javax.validation.constraints.Max;
/**
*/
public class TableEto extends AbstractEto implements Table {
private static final long serialVersionUID = 1L;
private Long waiterId;
@Max(value = 1000)
private Long number;
private TableState state;
/**
* The constructor.
*/
public TableEto() {
super();
}
@Override
public Long getNumber() {
return this.number;

106

Devonfw Guide v2.4.0

}
@Override
public void setNumber(Long number) {
this.number = number;
}
@Override
public Long getWaiterId() {
return this.waiterId;
}
@Override
public void setWaiterId(Long waiterId) {
this.waiterId = waiterId;
}
@Override
public TableState getState() {
return this.state;
}
@Override
public void setState(TableState state) {
this.state = state;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((this.state == null) ? 0 : this.state.hashCode());
result = prime * result + ((this.waiterId == null) ? 0 : this.waiterId.
hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;

107

Devonfw Guide v2.4.0

}
if (getClass() != obj.getClass()) {
return false;
}
if (!super.equals(obj)) {
return false;
}
TableEto other = (TableEto) obj;
if (this.state != other.state) {
return false;
}
if (this.waiterId == null) {
if (other.waiterId != null) {
return false;
}
} else if (!this.waiterId.equals(other.waiterId)) {
return false;
}
return true;
}
}
In Devonfw, we define CRUD logic into a management class. So we are going to create our
Tablemanagement interface and implementation:
Listing 6. Tablemanagement.java

package devonfw.tutorial.tablemanagement.logic.api;
import devonfw.tutorial.tablemanagement.logic.api.to.TableEto;
import java.util.List;
import javax.validation.Valid;
/**
* Interface for TableManagement component.
*
*/
public interface Tablemanagement {
/**
* Returns a restaurant table by its id 'id'.
*
* @param id The id 'id' of the restaurant table.
* @return The restaurant {@link TableEto} with id 'id'
*/
TableEto findTable(Long id);
/**
* Returns a list of all existing restaurant tables.

108

Devonfw Guide v2.4.0

*
*/
List findAllTables();
/**
* Returns a list of all existing free restaurant tables.
*
*/
List findFreeTables();
/**
* Deletes a restaurant table from the database by its id 'id'.
*
* @param tableId Id of the restaurant table to delete
*/
void deleteTable(Long tableId);
/**
* Creates a new restaurant table and store it in the database.
*
* @param table the {@link TableEto} to create.
* @return the new {@link TableEto} that has been saved with ID and version.
*/
TableEto saveTable(@Valid TableEto table);
}
Listing 7. TablemanagementImpl.java

package devonfw.tutorial.tablemanagement.logic.impl;
import
import
import
import
import
import
import
import

devonfw.tutorial.general.common.api.constants.PermissionConstants;
devonfw.tutorial.general.common.api.exception.IllegalEntityStateException;
devonfw.tutorial.tablemanagement.common.api.datatype.TableState;
devonfw.tutorial.tablemanagement.dataaccess.api.TableEntity;
devonfw.tutorial.tablemanagement.dataaccess.api.dao.TableDao;
devonfw.tutorial.tablemanagement.logic.api.Tablemanagement;
devonfw.tutorial.tablemanagement.logic.api.to.TableEto;

import java.util.List;
import java.util.Objects;
import
import
import
import

javax.annotation.security.RolesAllowed;
javax.inject.Inject;
javax.inject.Named;
javax.validation.Valid;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

109

Devonfw Guide v2.4.0

/**
*/
@Named
public class TablemanagementImpl extends AbstractComponentFacade implements
Tablemanagement {
/** Logger instance. */
private static final Logger LOG = LoggerFactory.getLogger(TablemanagementImpl.
class);
/** @see #getTableDao() */
private TableDao tableDao;
/**
* The constructor.
*/
public TablemanagementImpl() {
super();
}
@Override
@RolesAllowed(PermissionConstants.FIND_TABLE)
public TableEto findTable(Long id) {
LOG.debug("Get table with id '" + id + "' from database.");
return getBeanMapper().map(getTableDao().findOne(id), TableEto.class);
}
@Override
@RolesAllowed(PermissionConstants.FIND_TABLE)
public List findAllTables() {
LOG.debug("Get all restaurant tables from database.");
List tables = getTableDao().findAll();
return getBeanMapper().mapList(tables, TableEto.class);
}
@Override
@RolesAllowed(PermissionConstants.FIND_TABLE)
public List findFreeTables() {
LOG.debug("Get all free restaurant tables from database.");
List tables = getTableDao().getFreeTables();
return getBeanMapper().mapList(tables, TableEto.class);
}
@Override
@RolesAllowed(PermissionConstants.DELETE_TABLE)

110

Devonfw Guide v2.4.0

public void deleteTable(Long tableId) {
TableEntity table = getTableDao().find(tableId);
if (!table.getState().isFree()) {
throw new IllegalEntityStateException(table, table.getState());
}
getTableDao().delete(table);
}
@Override
@RolesAllowed(PermissionConstants.SAVE_TABLE)
public TableEto saveTable(@Valid TableEto table) {
Objects.requireNonNull(table, "table");
TableEntity tableEntity = getBeanMapper().map(table, TableEntity.class);
// initialize
if (tableEntity.getState() == null) {
tableEntity.setState(TableState.FREE);
}
getTableDao().save(tableEntity);
LOG.debug("Table with id '{}' has been created.", tableEntity.getId());
return getBeanMapper().map(tableEntity, TableEto.class);
}
/**
* @return the {@link TableDao} instance.
*/
public TableDao getTableDao() {
return this.tableDao;
}
/**
*/
@Inject
public void setTableDao(TableDao tableDao) {
this.tableDao = tableDao;
}
}

NOTE

You may have problems with the PermissionConstants properties because are not
implemented yet. We will do that in the next step.

At this point we have defined all the necessary classes in the logic layer, so we have our API ready,

111

Devonfw Guide v2.4.0

with the exception of finishing its security aspect.
Secure the application
OASP4J proposes role-based authorization to cope with the authorization of executing use cases of
an application. OASP4J use the JSR250 annotations, mainly @RolesAllowed, as you have seen, for
authorizing method calls against the permissions defined in the annotation body.
So, finally, we have to create a class to declare the actual roles we use as values for the
@RolesAllowed annotation:

package devonfw.tutorial.general.common.api.constants;
/**
* Contains constants for the keys of all
*
*/
public abstract class PermissionConstants {
to retrieve table. */
public static final String FIND_TABLE = "FindTable";
to save table. */
public static final String SAVE_TABLE = "SaveTable";
to remove table. */
public static final String DELETE_TABLE = "DeleteTable";
}

27.1.5. Step 5: Create REST endpoints
Web applications need to get data from the server, so we have to expose the methods defined in the
logic layer to these applications. We need a class that exposes methods as URLs to allow the
applications to get the data. By convention, we call this class managementRestServiceImpl.
This is an example of a REST API for our Table use case using JAX-RS.
Also note that the implementation does not follow the dogmatic RESTFUL approach as Devonfw
proposes a more pragmatic way to use REST. Please refer to the guide Creating Rest Service for
Listing 8. TablemanagementRestServiceImpl.java

package devonfw.tutorial.tablemanagement.service.impl.rest;
import java.util.List;

112

Devonfw Guide v2.4.0

import
import
import
import
import
import
import
import
import
import
import
import

javax.inject.Inject;
javax.inject.Named;
javax.ws.rs.Consumes;
javax.ws.rs.DELETE;
javax.ws.rs.GET;
javax.ws.rs.NotFoundException;
javax.ws.rs.POST;
javax.ws.rs.Path;
javax.ws.rs.PathParam;
javax.ws.rs.Produces;
javax.ws.rs.core.MediaType;

import org.springframework.transaction.annotation.Transactional;
import devonfw.tutorial.tablemanagement.logic.api.Tablemanagement;
import devonfw.tutorial.tablemanagement.logic.api.to.TableEto;
/**
*
* The service class for REST calls in order to execute the methods in {@link
Tablemanagement}.
*/
@Path("/tablemanagement/v1") ②
@Named("TablemanagementRestService")
@Consumes(MediaType.APPLICATION_JSON) ①
@Produces(MediaType.APPLICATION_JSON)
@Transactional
public class TablemanagementRestServiceImpl {
private Tablemanagement tableManagement;
/**
*
* This method sets the field tableManagement.
*
*
*
* @param tableManagement the new value of the field tableManagement
*/
@Inject
public void setTableManagement(Tablemanagement tableManagement) {
this.tableManagement = tableManagement;
}
/**

113

Devonfw Guide v2.4.0

*
*
*
*
* @param id the ID of the {@link TableEto}
*
*/
@GET
@Path("/table/{id}/")
public TableEto getTable(@PathParam("id") String id) {
Long idAsLong;
if (id == null) {
}
try {
idAsLong = Long.parseLong(id);
} catch (NumberFormatException e) {
throw new BadRequestException("id is not a number");
} catch (NotFoundException e) {
}
return this.tableManagement.findTable(idAsLong);
}
/**
*
*
*
*
* @return list of all existing restaurant {@link TableEto}s
*/
@GET
@Path("/table/")

114

Devonfw Guide v2.4.0

public List getAllTables() {
List allTables = this.tableManagement.findAllTables();
return allTables;
}
/**
*
*
*
*
* @return list of all existing free {@link TableEto}s
*/
@GET
@Path("/freetables/")
public List getFreeTables() {
return this.tableManagement.findFreeTables();
}
/**
*
*
*
*
* @param table the {@link TableEto} to be created
*
* @return the recently created {@link TableEto}
*/
@POST
@Path("/table/")
public TableEto saveTable(TableEto table) {
return this.tableManagement.saveTable(table);
}
/**
*
*
*
*
* @param id ID of the {@link TableEto} to be deleted

115

Devonfw Guide v2.4.0

*/
@DELETE
@Path("/table/{id}/")
public void deleteTable(@PathParam("id") Long id) {
this.tableManagement.deleteTable(id);
}
}
① We send and receive the information in JSON format.
② We specify the version of the entire API inside its path.
As you can see, we have defined the REST URLs for our Table user case. Now, for example, you can
find all tables on this URL:

http://:/application-name/services/rest/tablemanagement/v1/table/

DTO conversion
In the logic API, the methods of the classes should return Data Transfer Object (DTO) instead of
entities. So, in OASP4J we have a mechanism to convert the entities into DTOs.
This is an example of how to convert an entity into a DTO:

// Conversion for lists
getBeanMapper().mapList(tableList, TableDto.class);
// Conversion for objects
getBeanMapper().map(table, TableDto.class);
In the example, we use the function getBeanMapper(). This function provides us an API to convert
entities into DTOs. In the logic layer, we only have to extend the class AbstractComponentFacade to get

To add pagination support to our Table CRUD, the first step is creating a new Table TO that extends
the SearchCriteriaTo class. This class forms the foundation for every request which needs search or
pagination functionality.
Listing 9. TableSearchCriteriaTo.java

package devonfw.tutorial.tablemanagement.logic.api.to;
import io.oasp.module.jpa.common.api.to.SearchCriteriaTo;

116

Devonfw Guide v2.4.0

import devonfw.tutorial.tablemanagement.common.api.datatype.TableState;
/**
*
net.sf.mmm.util.transferobject.api.TransferObject TO}
*/
public class TableSearchCriteriaTo extends SearchCriteriaTo {
/** UID for serialization. */
private static final long serialVersionUID = 1L;
private Long waiterId;
private Long number;
private TableState state;
/**
*
* The constructor.
*/
public TableSearchCriteriaTo() {
super();
}
/**
*
* @return waiterId
*/
public Long getWaiterId() {
return this.waiterId;
}
/**
*
* @param waiterId the waiterId to set
*/
public void setWaiterId(Long waiterId) {
this.waiterId = waiterId;

117

Devonfw Guide v2.4.0

}
/**
*
* @return state
*/
public TableState getState() {
return this.state;
}
/**
*
* @param state the state to set
*/
public void setState(TableState state) {
this.state = state;
}
/**
*
* @return number
*/
public Long getNumber() {
return this.number;
}
/**
*
* @param number the number to set
*/
public void setNumber(Long number) {
this.number = number;
}
}
Now we will create a new POST REST endpoint (pagination request have to be POST) in our

118

Devonfw Guide v2.4.0

TablemanagementRestServiceImpl class.

/**
*
* @param searchCriteriaTo the pagination and search criteria to be used for finding
tables.
*/
@Path("/table/search")
@POST
public PaginatedListTo findTablesByPost(TableSearchCriteriaTo
searchCriteriaTo) {
return this.tableManagement.findTableEtos(searchCriteriaTo);
}

NOTE

Make sure to press Ctrl-Shift-O after inserting this new method, to make Eclipse
auto-import the dependencies of PaginatedListTo and TableSearchCriteriaTo.

Consequently we have to declare this new method findTableEtos in the table management classes
in our logic layer:
Listing 10. Tablemanagement.java

/**
* Returns a list of restaurant tables matching the search criteria.
*
* @param criteria the {@link TableSearchCriteriaTo}.
*/
PaginatedListTo findTableEtos(TableSearchCriteriaTo criteria);
Listing 11. TablemanagementImpl.java

@Override
public PaginatedListTo findTableEtos(TableSearchCriteriaTo criteria) {
criteria.limitMaximumPageSize(MAXIMUM_HIT_LIMIT); ①
PaginatedListTo tables = getTableDao().findTables(criteria);
return mapPaginatedEntityList(tables, TableEto.class);
}
① As you can see, we have limited the maximum results per page to prevent clients from
requesting pages with too big a size.
And finally, we have to define our pagination method in our DAO class.

119

Devonfw Guide v2.4.0

Listing 12. TableDao.java

/**
TableSearchCriteriaTo}.
*
* @param criteria is the {@link TableSearchCriteriaTo}.
*/
PaginatedListTo findTables(TableSearchCriteriaTo criteria);
Listing 13. TableDaoImpl.java

@Override
public PaginatedListTo findTables(TableSearchCriteriaTo criteria) {
TableEntity table = Alias.alias(TableEntity.class);
EntityPathBase alias = Alias.$(table); JPAQuery query = new JPAQuery(getEntityManager()).from(alias); Long waiterId = criteria.getWaiterId(); if (waiterId != null) { query.where(Alias.$(table.getWaiterId()).eq(waiterId));
}
Long number = criteria.getNumber();
if (number != null) {
query.where(Alias.$(table.getNumber()).eq(number)); } TableState state = criteria.getState(); if (state != null) { query.where(Alias.$(table.getState()).eq(state));
}
return findPaginated(criteria, query, alias);
}

NOTE

While auto-completing the new imports using Ctrl-Shift-O after adding the above
methods, select com.mysema.query.alias as the import for the Alias class.

In this case, we have used QueryDSL to create the query. You can read more about QueryDSL at
www.querydsl.com.

27.1.7. Step 7: Sort the results
In OASP4J exists a special TO (Transfer Object) called ´OrderByTo to transmit sorting parameters
from client to server. This is the JSON format that the server expects when using this TO:

120

Devonfw Guide v2.4.0

{
sort: [
{
name:"sortingCriteria1",
direction:"ASC"
},
{
name:"sortingCriteria2",
direction:"DESC"
},
...
]
}
Devonfw proposes to use POST as the HTTP method for endpoints implementing search or
pagination support.
By default, in Devonfw, SearchCriteriaTo class is already embedding this sorting TO, so we only
need to manage sorting in TableDaoImpl.java because our pagination method does not need any
modification.
If our method needs sorting but not pagination we need to manually add to our own transfer object
the following variable (and its setter and getter methods):

private List sort;
We are going to modify the method findTables in our TableDaoImpl. Insert the following line right
before the final return statement:

Now add the following method to TableDaoImpl:

121

Devonfw Guide v2.4.0

private void addOrderBy(JPAQuery query, EntityPathBase alias,
TableEntity table, List sort) {
if (sort != null && !sort.isEmpty()) {
for (OrderByTo orderEntry : sort) {
if ("number".equals(orderEntry.getName())) {
if (OrderDirection.ASC.equals(orderEntry.getDirection())) {
query.orderBy(Alias.$(table.getNumber()).asc()); } else { query.orderBy(Alias.$(table.getNumber()).desc());
}
} else if ("waiterId".equals(orderEntry.getName())) {
if (OrderDirection.ASC.equals(orderEntry.getDirection())) {
query.orderBy(Alias.$(table.getWaiterId()).asc()); } else { query.orderBy(Alias.$(table.getWaiterId()).desc());
}
} else if ("state".equals(orderEntry.getName())) {
if (OrderDirection.ASC.equals(orderEntry.getDirection())) {
query.orderBy(Alias.$(table.getState()).asc()); } else { query.orderBy(Alias.$(table.getState()).desc());
}
}
}
}
}
As you can see, we have added a private method to add sorting filter to our query depending on the

27.1.8. Step 8: Test the example
In order to test the example we are going to use the user chief to obtain the tables. To be able to
access to that data we need first to grant permissions to the chief user. We can do it specifying the
role

and

the

permissions

in

the

access-control-schema.xml

file

located

in

src/main/resources/config/app/security/.

122

Devonfw Guide v2.4.0

Now if we run the application we can access to the tables data with the URL

http:////services/rest/tablemanagement/v1/table/
And, after logging as chief, the server response should be:

[{"id":101,"modificationCounter":1,"revision":null,"waiterId":null,"number":1,"state":
"OCCUPIED"},{"id":102,"modificationCounter":1,"revision":null,"waiterId":null,"number"
:2,"state":"FREE"},{"id":103,"modificationCounter":1,"revision":null,"waiterId":null,"
number":3,"state":"FREE"},{"id":104,"modificationCounter":1,"revision":null,"waiterId"
:null,"number":4,"state":"FREE"},{"id":105,"modificationCounter":1,"revision":null,"wa
iterId":null,"number":5,"state":"FREE"}]

123

Devonfw Guide v2.4.0

Chapter 28. Bean-Mapping using Dozer
28.1. Why use Bean-Mapping
A mapping framework is useful in a layered architecture, where you can create layers of
abstraction by encapsulating changes to particular data objects vs. propagating these objects to
other layers (i.e. External service data objects, domain objects, data transfer objects, internal
service data objects). A mapping framework is an ideal and can be used within Mapper type classes
that are responsible for mapping data from one data object to another.
The challenge in distributed systems is passing the domain objects between different systems.
Typically, you don’t want internal domain objects to be exposed to the outside world and not allow
external domain objects to bleed into your system.
Mapping between the data objects has been traditionally addressed by hand coding value object
assemblers (or converters) that copy data between the objects. Most programmers will develop
some sort of custom mapping framework and spend countless hours and thousands of lines of code
mapping to and from their different data object.
A generic mapping framework solves these problems. Dozer (which is configured and used in
Devonfw) is an open source mapping framework that is robust, generic, flexible, reusable, and
configurable.
Typically, Dozer works as shown below:

For decoupling, you sometimes need to create separate objects (beans) for a different view. For
example, for an external service, you will use a transfer-object instead of the persistence entity, so
internal changes to the entity do not implicitly change or break the service.
Therefore, you have the need to map similar objects which creates a copy. This is advantageous as
the modifications to the copy has no side-effect on the original source object. However, to

124

Devonfw Guide v2.4.0

implement such mapping code by hand is very tedious and error-prone as shown below (if new
properties are added to beans but not to mapping code):

public PersonTo mapPerson(PersonEntity source) {
PersonTo target = new PersonTo();
target.setFirstName(source.getFirstName());
target.setLastName(source.getLastName());
...
return target;
}
Therefore, BeanMapper is used for this purpose, which indirectly makes this task a lot easier.

28.2. Bean-Mapper Dependency

io.oasp.java
oasp4j-beanmapping

So, (oasp4j-beanmapping) uses Dozer as dependency in its pom.xml file as shown below:

28.3. Bean-Mapper Usage
Then, you can get the BeanMapper via dependency-injection which is typically provided by an
abstract base class (e.g. AbstractUc). Now, your problem can be solved easily:

PersonEntity person = ...;
...
return getBeanMapper().map(person, PersonTo.class);

125

Devonfw Guide v2.4.0

So, in the above piece of code, getBeanMapper() method provides an mapper (dozer) instance , and
when map() method is called, it maps PersonEntity (source object) to PersonTo(DEstination object).
Additionally, it supports the mapping of entire collections.
Dozer has been configured as a Spring bean in Devonfw, using dependency injection. This is done
in BeanDozerConfiguration.java which is present in resources/common/configuration folder of xxxcore project, created using oasp4j template server archetype.
In this class, you can give path of mapping file (dozer-mapping.xml), which is generally placed at
config/app/common/dozer-mapping.xml.

126

Devonfw Guide v2.4.0

Chapter 29. Write Unit Test Cases
29.1. Unit Test
In computer programming, unit testing is a software testing method by which individual units of
source code, sets of one or more computer program modules together with associated control data,
usage procedures, and operating procedures, are tested to determine whether they are fit for the
use. Intuitively, one can view a unit as the smallest testable part of an application. For more
information, visit wikipedia.

29.1.1. Unit Test in Eclipse
In order to understand how the Unit Tests works in Eclipse, lets discuss how to create and run a
simple test.
Step 1: Create a new class
Create a class with the name MyClass.java (you can create a new application as per need). In
Eclipse, right click on the package in the application and then select New > Class. Name it MyClass
and press Finish.
Step 2: Create a JUnit test
In Project Explorer, over the new MyClass.java class, then right click and go to New > Other > and
select JUnit Test Case. Name it MyClassTest (name by default) and select source folder and package
in the application to create the test. e.g. src/test/java (this is a good practice).
Step 3: Implement the test
Fist of all, check the dependencies of the module in pom.xml file.

io.oasp.java.modules
oasp4j-test
test

In a OASP4J project, you have your own Component Test methods, so you need your new JUnit Test
class to extend AbstractComponentTest class of the OASP4J module test.

127

Devonfw Guide v2.4.0

@SpringBootTest(classes = { SpringBootApp.class })
public class MyClassTest extends AbstractComponentTest {
@Test
public void test() {
assertThat(false).isTrue();
}
}
This is a very simple test that verifies if the boolean value true is true. And is the case to start
testing OASP4J application. As you can imagine that the test is going to fail, but you will see the
details in later part.
You are including the @SpringBootTest annotation in order to define an application
context to your test. Without the context of the application, the test gets a fatal
error, because you can’t test a non-running application.
NOTE

You can include a configuration location in the last annotation, if you need it, or use
@ContextConfiguration(locations = { "classpath:my-bean-context.xml" }). For this
tutorial, it’s unnecessary because your test is the most simplest test you can
perform.

Step 4: Run the test
Eclipse provides a very helpful view to test the applications. If you can’t see, press the menu:
Windows > Show View > Other and select JUnit.
Now, over the test, press right click Run As > JUnit test

In the above image, Eclipse shows a red rectangle because one of the tests has been failed (in this
case, a single test). The failure of the test is marked with a blue cross but you can observe three
different marks:
• Red cross: the test has some fatal error as, e.g context error, null pointer exceptions, etc.
• Blue cross: the test fails in some test method like asserThan() (like your case)
• Green check: the test is OK
In above case, you have a simple failure because your test has a assertThat(false).isTrue()
meaning check if true == false. Now, let’s discuss how to fix the failure and run the test again.

128

Devonfw Guide v2.4.0

@SpringBootTest(classes = { SpringBootApp.class })
public class MyClassTest extends AbstractComponentTest {
@Test
public void test() {
assertThat(true).isTrue();
}
}
Now, you need to run the test again. And you will get the next result as shown in below image.

Evidently, the test ends successfully and Eclipse shows a green rectangle and the test with a green
check.
With the discussed knowledge base, you can start testing all the applications.

29.2. TDD Test-driven development
Test-driven development (TDD) is a software development process that relies on the repetition of a
very short development cycle: first the developer writes an (initially failing) automated test case
that defines a desired improvement or new function, then produces the minimum amount of code
to pass that test, and finally refactors the new code to acceptable standards.
The process of TDD is described as follows:
• Create a test
• Run all the tests
• Write the implementation code
• Run all the tests
• Refactor

29.2.1. TDD in Eclipse
Now, you are acquainted with the skills of creating, writing and running the test. Therefore, you
The goal is create a simple calculator that has two methods: add(int,int) and sub(int,int).

129

Devonfw Guide v2.4.0

Step 1: Create a test
The idea is very simple, you will create the tests for the methods of a class that needs to be
implemented later. It will allow you to get the control of the result and verify that the code is
working properly from the beginning.
You need to create a test called CalculatorTest in test package and a class Calculator in the java
package.
In this test class, you will include a variable of a class Calculator and the test to the future add() and
sub() methods of Calculator class.
Calculator.java

public class Calculator {
public Calculator() {}
public Object add(int a, int b) {
return null;
}
public Object sub(int a, int b) {
return null;
}
}
Thus, you have the wire of your calculator. In this case, the implementation is very simple, but you
can scale it to a more complex logic. Now, you need to include the test data required to run the class
CalculatorTest.
CalculatorTest.java

@SpringBootTest(classes = { SpringBootApp.class })
public class CalculatorTest extends AbstractComponentTest {
private Calculator calculator = new Calculator();
@Test
}
@Test
public void subTest() {
assertThat(this.calculator.sub(1, 2)).isEqualTo(-1);
}
}

130

Devonfw Guide v2.4.0

Step 2: Run the test new test
Run the test and the result is as shown below:

Obviously, the test shows some failures as expected because the Calculator doesn’t work yet.
The fact, this is more of a metaphoric step, as the implementation is in progress and it is obvious to
get errors after running the test. As it is the cycle of the TDD, you need to write a test that will fail
certainly so that the code to satisfy the test can be written. Surely, this will help to keep the code
simple and clean.
Methods named add() and sub(), returns Object as return value because if the
NOTE

methods

return

an

int,

you

will

get

a

"red

cross

error"

pointing

NullPointerException instead of "blue cross error" of assetThat(). It’s just for this
tutorial.

Step 3: Write the implementation code
So far, you have seen a perfect test and an awful implementation of the Calculator. Let’s start with
the implementation.
Let’s implement the method add() and see what happens.

public class Calculator {
public Calculator() {}
public int add(int a, int b) {
return a + b;
}
public Object sub(int a, int b) {
return null;
}
}

Step 4: Run the test -againIf you run the test, you will get the following result:

131

Devonfw Guide v2.4.0

Now, you have a success result for the method add() and a failure result for the method sub().
Clearly, it’s not necessary to get all the tests results as OK to run the tests, you can check the result of
the test and work on to satisfy it. This the idea of TDD.
Step 5: Refactor
Now, let’s implement the method sub()

public class Calculator {
public Calculator() {}
public int add(int a, int b) {
return a + b;
}
public int sub(int a, int b) {
return a - b;
}
}

Step 6: Run the test -return to step 2If you run the application, you will get the following result:

Finally, here is your first application implemented with TDD methodology!
Therefore, in this tutorial, you have dealt with a very simple application, so you don’t need another
round of the TDD cycle, but in the real applications, you may need to repeat the cycle several times
to get a successful result.

132

Devonfw Guide v2.4.0

Chapter 30. Logging and Auditing
30.1. Logging
We use SLF4J as API for logging. The recommended implementation is Logback for which we
provide additional value such as configuration templates and an appender that prevents logforging and reformatting of stack-traces for operational optimizations.

30.1.1. Usage
Maven Integration
SLF4J and logback):

io.oasp.java.modules
oasp4j-logging
2.5.0

Configuration
The configuration file is logback.xml and is to put in the directory src/main/resources of your main
application. For details consult the logback configuration manual. OASP4J provides a production
ready configuration here. Simply copy this configuration into your application in order to benefit
from the provided operational and [security] aspects. We do not include the configuration into the
oasp4j-logging module to give you the freedom of customizations (e.g. tune log levels for
components and integrated products and libraries of your application).
The provided logback.xml is configured to use variables defined on the config/application.properties
file. On our example, the log files path point to ../logs/ in order to log to tomcat log directory when
starting tomcat on the bin folder. Change it according to your custom needs.
Listing 14. config/application.properties

log.dir=../logs/

Logger Access
The general pattern for accessing loggers from your code is a static logger instance per class. We
pre-configured the development environment so you can just type LOG and hit [ctrl][space] (and
then [arrow up]) to insert the code pattern line into your class:

133

Devonfw Guide v2.4.0

public class MyClass {
private static final Logger LOG = LoggerFactory.getLogger(MyClass.class);
...
}
Please note that in this case we are not using injection pattern but use the convenient static
alternative. This is already a common solution and also has performance benefits.
How to log
We use a common understanding of the log-levels as illustrated by the following table. This helps
for better maintenance and operation of the systems by combining both views.
Table 1. Loglevels

Loglevel

Description

Impact

FATAL

Only used for fatal
Operator has to react
errors that prevent the immediately
application to work at
all (e.g. startup fails or
shutdown/restart
required)

all

ERROR

An abnormal error
indicating that the
processing failed due to
technical problems.

Operator should check
for known issue and
otherwise inform
development

all

WARNING

A situation where
something worked not
as expected. E.g. a
user validation failure
occurred.

No direct reaction
required. Used for
problem analysis.

all

INFO

Important information No direct reaction
such as context,
required. Used for
duration,
analysis.
success/failure of
request or process

all

DEBUG

Development
information that
context for debugging
problems.

development and
testing

TRACE

Like DEBUG but
No direct reaction
exhaustive information required. Used for
and for code that is run problem analysis.
very frequently. Will
typically cause large
log-files.

No direct reaction
required. Used for
analysis.

Active Environments

none (turned off by
default)

134

Devonfw Guide v2.4.0

Exceptions (with their stacktrace) should only be logged on FATAL or ERROR level. For business
exceptions typically a WARNING including the message of the exception is sufficient.

30.1.2. Operations
Log Files
We always use the following log files:
• Error Log: Includes log entries to detect errors.
• Info Log: Used to analyze system status and to detect bottlenecks.
• Debug Log: Detailed information for error detection.
The log file name pattern is as follows:

_log___.log
Table 2. Segments of Logfilename

Element

Value

Description

info, error, debug

Type of log file

e.g. mywebserver01

Name of server, where logs are
generated

e.g. myapp

Name of application, which
causes logs

YYYY-MM-DD_HH00

date of log file

Example: error_log_mywebserver01_myapp_2013-09-16_0900.log
Error log from mywebserver01 at application myapp at 16th September 2013 9pm.
Output format
We use the following output format for all log entries to ensure that searching and filtering of log
entries work consistent for all logfiles:

[D: ] [P: ] [C: ][T: ][L: ][M: ]
• D: Date ( ISO8601: 2013-09-05 16:40:36,464)
• P: Priority (the log level)
• C: Correlation ID (ID to identify users across multiple systems, needed when application is
distributed)
• L: Logger name (use class name)

135

Devonfw Guide v2.4.0

• M: Message (log message)
Example:

[D: 2013-09-05 16:40:36,464] [P: DEBUG] [C: 12345] [T: main] [L: my.package.MyClass][M: My message...]

30.1.3. Logging and Auditing Security
In order to prevent log forging attacks we provide a special appender for logback in oasp4j-logging.
If you use it (see [configuration]) you are safe from such attacks.

30.1.4. Correlating separate requests
In order to correlate separate HTTP requests to services belonging to the same user / session, we
provide a servlet filter called "DiagnosticContextFilter". This filter first searches for a configurable
HTTP header containing a correlation id. If none was found, it will generate a new correlation id.
By default the HTTP header used is called "CorrelationId".

30.2. Auditing with Hibernate Envers
For database auditing we use hibernate envers. If you want to use auditing ensure you have the
following dependency in your pom.xml file:

io.oasp.java.modules
oasp4j-jpa-envers

Make sure that entity manager (configured in beans-jpa.xml) also scans the package from the
oasp4j-jpa[-envers] module in order to work properly.

...

io.oasp.module.jpa.dataaccess.api
...

Now let your DAO implementation extend from AbstractRevisionedDao instead of AbstractDao and
The DAO now has a method getRevisionHistory(entity) available to get a list of revisions for a given
entity and a method load(id, revision) to load a specific revision of an entity with the given ID.
To enable auditing for a entity simply place the @Audited annotation to your entity and all entity

136

Devonfw Guide v2.4.0

classes it extends from.

@Entity(name = "Drink")
@Audited
public class DrinkEntity extends ProductEntity implements Drink {
...
When auditing is enabled for an entity an additional database table is used to store all changes to
the entity table and a corresponding revision number. This table is called _AUD
per default. Another table called REVINFO is used to store all revisions. Make sure that these tables
are available. They can be generated by Hibernate with the following property (only for
development environments).

database.hibernate.hbm2ddl.auto=create
Another possibility is to put them in your database migration scripts like so.

CREATE CACHED TABLE PUBLIC.REVINFO(
id BIGINT NOT NULL generated by default as identity (start with 1),
timestamp BIGINT NOT NULL,
user VARCHAR(255)
);
...
CREATE CACHED TABLE PUBLIC._AUD(
,
revtype TINYINT,
rev BIGINT NOT NULL
);

137

Devonfw Guide v2.4.0

Chapter 31. Getting Started Cobigen
In Devonfw we have a server-side code generator called Cobigen. Cobigen is capable to create CRUD
code from an entity or generate the content of the class that defines the user permissions. Cobigen
is distributed in the Devonfw distribution as an Eclipse plugin, and is available to all Devonfw
developers.
If you want to go deeper in Cobigen you can visit the documentation of the Cobigen core.

31.1. Preparing Cobigen for first use
Before you can use Cobigen, you have to install the templates to be used by Cobigen. The Devonfw
distribution

comes

with

a

set

of

default

templates

in

the

directory

workspaces\main\CobiGen_Templates.
1. Open Eclipse by executing the batch file eclipse-main.bat
2. Select "File - Import"
3. Select "General - Existing projects into workspace"
4. Select the directory workspaces\main\CobiGen_Templates
5. Finish the import.
NOTE

document CobiGen.pdf for further information.

31.2. Creating a CRUD with Cobigen
In an earlier chapter about CRUD functionality you saw the individual steps necessary to
implement a basic CRUD functionality.
Using Cobigen, you can save most of these steps, and get a working result in far less time. We are
going to explain how to use Cobigen to generate the code and classes related to the CRUD operations
of an entity but you can know more about the Cobigen usage in Eclipse.
Cobigen needs a starting point to generate the code of a CRUD case. In this example the starting
point is the StaffMemberEntity class, modelling a member of the staff of our restaurant. So we are
going to create a CRUD for the new StaffMemberEntity class.
Step 1: Entity creation

We are going to create a StaffMember entity. First, we are going to add the database schema to our
database.
In the script resources/db/migration/V0001__Create_schema.sql we add:

138

Devonfw Guide v2.4.0

CREATE TABLE STAFFMEMBER(
id BIGINT NOT NULL,
modificationCounter INTEGER NOT NULL,
firstname VARCHAR(255),
lastname VARCHAR(255),
);
And in the same path, we are going to create a new file (if it do not exist) to add the default data to
the StaffMember created. We create V0002__Master_data.sql file.

INSERT INTO STAFFMEMBER (id, login, firstname,
(0, 'chief', 'Charly', 'Chief', 0);
INSERT INTO STAFFMEMBER (id, login, firstname,
(1, 'cook', 'Carl', 'Cook', 0);
INSERT INTO STAFFMEMBER (id, login, firstname,
(2, 'waiter', 'Willy', 'Waiter', 0);
INSERT INTO STAFFMEMBER (id, login, firstname,
(3, 'barkeeper', 'Bianca', 'Barkeeper', 0);
Now,

we

create

a

new

lastname, modificationCounter) VALUES
lastname, modificationCounter) VALUES
lastname, modificationCounter) VALUES
lastname, modificationCounter) VALUES

StaffMember

entity

in

the

package

devonfw.tutorial.staffmanagement.dataaccess.api with the following code:
Listing 15. StaffMemberEntity.java

package devonfw.tutorial.staffmanagement.dataaccess.api;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import devonfw.tutorial.general.dataaccess.api.ApplicationPersistenceEntity;
import devonfw.tutorial.general.dataaccess.api.StaffMember;
//Add imports with respect to the package structure.
/**
*
persistent entity} for
*
*/
@Entity
@Table(name = "StaffMember")
public class StaffMemberEntity extends ApplicationPersistenceEntity implements
StaffMember {

139

Devonfw Guide v2.4.0

private static final long serialVersionUID = 1L;
private String name;
private String firstName;
private String lastName;
/**
* The constructor.
*/
public StaffMemberEntity() {
super();
}
@Column(name = "login", unique = true)
@Override
public String getName() {
return this.name;
}
@Override
}
@Override
public String getFirstName() {
return this.firstName;
}
@Override
public void setFirstName(String firstName) {
this.firstName = firstName;
}
@Override
public String getLastName() {
return this.lastName;
}
@Override
public void setLastName(String lastName) {

140

Devonfw Guide v2.4.0

this.lastName = lastName;
}
}

Step 2: Generate classes
To generate the rest of the classes concerning the StaffMember CRUD, we only have to do a right
click on the StaffMemberEntity.java class in Eclipse Project Explorer and select "CobiGen '
Generate".

This action opens a code generator wizard, like this:

In this wizard you can select which classes you want to generate, organized by layer. In this
• CRUD DAO’s
• CRUD REST services
• CRUD logic layer (all in one)

141

Devonfw Guide v2.4.0

• Entity infrastructure
• TO’s
and continue.
In the next step you can select the fields of the entity that you want to expose via the REST service.

Afterwards, click on "Finish" to let CobiGen do its work.
It is possible that you will see a final dialog containing some warnings about
ambiguous imports. You should review the mentioned files, and fix the imports
yourself.

NOTE

In many cases, the imports are easily fixable by letting Eclipse auto-complete them
by pressing "Ctrl-Shift-O".
Cobigen also works incrementally. Cobigen merges your changes and updates all classes based on
the Entity class' fields. So you can use Cobigen to generate the structure and the different classes
and then develop custom parts of your CRUD.

142

Devonfw Guide v2.4.0

Chapter 32. Creating user permissions
In OASP4J applications the roles and permissions are defined by the PermissionConstants class. The
content of this class is bound with the permissions defined in the access-control-schema.xml file.
Cobigen let us to automatically generate (or update) the content of the PermissionConstants class
from the access-control-schema.xml content. To achieve this we only have to follow two simple
steps.
Step 1: Define the permissions and roles
In

Eclipse

open

the

access-control-schema.xml

located

in

/oasp4j-sample-

core/src/main/resources/config/app/security/access-control-schema.xml and define the permissions to
the roles or group of roles like:

Step 2: Generate the PermissionConstants class
Right click on the access-control-schema.xml and select Cobigen > Generate…
This action opens a code generator wizard, like this:

In this case you have only one option. Select Permissions Constants and press Finish. You should see

143

Devonfw Guide v2.4.0

now

the

new

Permissions

in

the

file

/oasp4j-sample-

core/src/main/java/io/oasp/gastronomy/restaurant/general/common/api/constants/PermissionConstan
ts.java

public static final String FIND_STAFFMEMBER = "FindStaffMember";
public static final String SAVE_STAFFMEMBER = "SaveStaffMember";
public static final String DELETE_STAFFMEMBER = "DeleteStaffMember";

It is possible that you can’t press Finish button in CobiGen.

NOTE

This happens because you are using an old version of CobiGen and the wizard can’t
merge the class PermissionConstants. To work around this you need to delete the
class PermissionConstants.java and try again. Cobigen will generate for us the class
and will fill it with the updated content.

32.3. Fixing context problems
When launching the Cobigen > Generate wizard you may find problems related to the context, like
the following one

144

Devonfw Guide v2.4.0

This happens because you need to update the templates. So do again right click on the accesscontrol-schema.xml and select this time the Cobigen > Health Check option and you will see a
window with a message like the following

Now upgrade the template to constants/security_permissions and press OK. You now should be able
to use Cobigen to generate the PermissionConstants class content.

145

Devonfw Guide v2.4.0

Chapter 33. Transfer-Objects
The technical data model is defined in form of persistent entities. However, passing persistent
entities via call-by-reference across the entire application will soon cause problems:
• Changes to a persistent entity are directly written back to the persistent store when the
transaction is committed. When the entity is send across the application also changes tend to
take place in multiple places endangering data sovereignty and leading to inconsistency.
• You want to send and receive data via services across the network and have to define what
section of your data is actually transferred. If you have relations in your technical model you
• Modifications to your technical data model shall not automatically have impact on your
external services causing incompatibilities.
To prevent such problems transfer-objects are used leading to a call-by-value model and decoupling
changes to persistent entities.

For each persistent entity we create or generate a corresponding entity transfer object (ETO) that
has the same properties except for relations. In order to centralize the properties (getters and
setters with their javadoc) we use a common interface for the entity and its ETO.
If we need to pass an entity with its relation(s) we create a corresponding composite transfer object
(CTO) that only contains other transfer-objects or collections of them. This pattern is illustrated by
the following UML diagram from our sample application.

Figure 1. ETOs and CTOs

Finally, there are typically transfer-objects for data that is never persistent. A common example are
search criteria objects (derived from SearchCriteriaTo in our sample application).

146

Devonfw Guide v2.4.0

The logic layer defines these transfer-objects (ETOs, CTOs, etc.) and will only pass such objects

33.2. Service-Transfer-Objects
If we need to do service versioning and support previous APIs or for external services with a
different view on the data, we create separate transfer-objects to keep the service API stable (see
service layer).

147

Devonfw Guide v2.4.0

Chapter 34. Deployment on Tomcat
(Client/Server)
After setting up functional server and client applications, we may want to package both in the same
.war file. To package the single war, follow the given steps.

34.1. General description of the packaging process
The application packaging is based on Maven package functionality. The general overview of the
packaging process is as follows:

34.2. Preparing the client
Firstly (1), both client applications (the Sencha and the Angular one) should contain a java directory
with a pom.xml file which executes the build process (the "production" build, creating a single,
compressed Javascript file from all the application files) through the command (2) mvn install. We
must verify that the information about the groupId, the artifactId and the version are provided
within the pom.xml file where we should find something like

...
com.capgemini.devonfw
extjs-sample
1.0.0-SNAPSHOT
...
So from the client application, in the java directory we launch the command

148

Devonfw Guide v2.4.0

myClientApp\java>mvn install
After that, if the process goes right, the client app should have been "installed" in the local Maven
repository of our environment so in the \conf\.m2\repository\com\capgemini\devonfw\extjssample\1.0.0-SNAPSHOT directory we should find the .jar file with the client app packaged

34.3. Preparing the server
The Java server application contains a pom.xml file (3). In this pom.xml file we should add the
dependency to the .jar client that we have just created using the references to the groupId,
artifactId and version that we have specified in the client pom.xml.
So in the pom.xml file of our server project we should add:

com.capgemini.devonfw
extjs-sample
1.0.0-SNAPSHOT
zip
web
runtime

And in the plugins of the pom.xml we should add a reference to the package again within the
tag:

149

Devonfw Guide v2.4.0

org.apache.maven.plugins
maven-war-plugin

com.capgemini.devonfw
extjs-sample
zip
web
jsclient

If you are using a Sencha project as client app you must comment all the
NOTE

tags from the exec-maven-plugin inside the jsclient profile as this
configuration is related to oasp4js projects.

Now

verify

that

the

server

redirects

to

the

client

checking

the

…

\MyServerApp\server\src\main\webapp\index.jsp file that should be
Listing 16. index.jsp

<%
response.sendRedirect(request.getContextPath() + "/jsclient/");
%>
Then we have to add some unsecured resources in the method configure(HttpSecurity http) of the
general/service/impl/config/BaseWebSecurityConfig.java class.
Edit the unsecureResources to have something like that:

@Override
public void configure(HttpSecurity http) throws Exception {
String[] unsecuredResources =
"/services/rest/logout", "/jsclient/**"};
(...)
}

150

Devonfw Guide v2.4.0

34.4. Packaging the apps
Finally we are going to package both client and server applications into the same .war file. To do
that we must execute the package Maven command (4) from within the projects root directory (the
parent of the server project).

mvn package -P jsclient

34.5. Deploy on Tomcat
To deploy packaged Web Application Archive (.war) file that is integrated with client (Angular or
Sencha Client) on Tomcat7/Tomcat 8, make below changes in java core application pom.xml file.
Example: For "oasp4j" project, make following changes in core application’s "pom.xml" which is
located in "oasp4j/samples/core/pom.xml".
• Modify dependency "spring-boot-starter-web" and add exclusions.

...

org.springframework.boot
spring-boot-starter-web

org.springframework.boot
spring-boot-starter-tomcat

org.springframework.boot
spring-boot-starter-tomcat
provided

...
• Comment

the

code

inside

core\src\main\java\io\oasp\gastronomy\restaurant\general\service\impl\config\ServletInitializer.
java. This is not needed as we will be overriding the 'configure' method inside
core\src\main\java\io\oasp\gastronomy\restaurant\SpringBootApp.java.

151

Devonfw Guide v2.4.0

public class SpringBootApp extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(SpringBootApp.class);
}
/**
* Entry point for spring-boot based app
*
* @param args - arguments
*/
public static void main(String[] args) {
SpringApplication.run(SpringBootApp.class, args);
}
}
• Activate the 'jsclient' profile in server/pom.xml. Please see the snippet below.

jsclient

true

.....
.....

Build the project and create packaged .war file.
To deploy this .war file on Tomcat 7, follow the steps given below:
1. Go

to

Tomcat

installation

folder

(TOMCAT_HOME)

→

Copy

.war

file

to

"TOMCAT_HOME/webapps" folder .
2. If Tomcat is running, stop it by running "shutdown.bat" file under "TOMCAT_HOME/bin" folder.
3. Delete "TOMCAT_HOME/temp" and "TOMCAT_HOME/work" folders if present. These folders
contain temporary files. (Mandatory to get desired output)
4. Start Tomcat by running "startup.bat" under "TOMCAT_HOME/bin" folder.
5. By default Tomcat will start on port "8080".

152

Devonfw Guide v2.4.0

34.6. Running Bootified War
To run bootified war file , follow the steps given below:
1. cd oasp4j\samples
2. Execute 'mvn clean install'
3. cd oasp4j\samples\server\target.
4. Execute 'java -jar oasp4j-sample-server-bootified.war'

34.6.1. Application context root
In the case of bootified war, the context root will be '/' and not 'oasp4j-sample-server'.
So, to access the application after the bootified war is launched , one has to access it via
http://localhost:8080/login or if the user wants to have a context root , then they can define the
context 'oasp4j-sample-server' in oasp4j\samples\core\src\main\resources\application.properties.
Make sure oasp4j\samples is built by executing 'mvn clean install' for this oasp4j\samples project
and access it via http://localhost:8080/oasp4j-sample-server. The context root defined in
oasp4j\samples\core\src\main\resources\config\application.properties will not be available since it
is excluded from the war that is generated.

153

Devonfw Guide v2.4.0

Chapter 35. Cookbook
35.1. Devonfw Modules

154

Devonfw Guide v2.4.0

Chapter 36. The Reporting module - Report
generation with JasperReports
Reporting is a fundamental part of the larger movement towards the improved business
intelligence and knowledge management. Often, the implementation involves extract, transform,
and load (ETL) procedures in coordination with a data warehouse and then using one or more
reporting tools. With this module, Devon provides an implementation of one of these reporting
tools based on the Jasper Reports library.
JasperReports is an open source Java reporting tool that can write to a variety of targets, such as:
screen, a printer, into PDF, HTML, Microsoft Excel, RTF, ODT, Comma-separated values or XML files.
It can be used in Java-enabled applications, including Java EE or web applications, to generate
dynamic content. It reads its instructions from an XML or .jasper file.

36.1. Include Reporting in a Devon project
The Reporting module provides you a report generator for your Devon applications. To implement
the Reporting module in a Devon project, you must follow these steps:

Include the starter in your pom.xml, verify that the version matches the last available version of the
module.

com.devonfw.starter
devonfw-reporting-starter
${devonfw.version} 36.1.2. Step 2: Properties configuration NOTE This step is only needed in case you are going to generate .txt reports. In order to use the Reporting module for creating txt reports, it is necessary to define some parameters related to the size of the elements in the application.properties file in the project. # Reporting module params devon.reporting.txtConfig.CharWidth=7 devon.reporting.txtConfig.CharHeight=13.9 devon.reporting.txtConfig.PageWidthInChars=80 devon.reporting.txtConfig.PageHeightInChars=47 This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 155 Devonfw Guide v2.4.0 36.2. Basic implementation First and foremost, you need to add the scanner for dependency injection. To do so, you must add the following annotations in the SpringBoot main class: @Configuration @ComponentScan(basePackages = { "com.devonfw.module.reporting" }) @EnableAutoConfiguration public class MyBootApp { [...] } Remember to include the package of the module in the basePackages attribute of the @ComponentScan annotation alongside the packages for the rest of the relevant Spring Boot components. @ComponentScan(basePackages = { "com.devonfw.module.reporting" , "my.other.component.location.package" }) As you can see, the basePackages of the @ComponentScan points to the Reporting module package. Now, you can start using the module. 36.2.1. The injection of the module To access the module functionalities, you need to inject the module in a private property, it can be done using the @Inject annotation public class MyClass { @Inject private Reporting reportManager; [...] } Hereafter, you can use the reportManager object in order to access the module functionalities, it will be discussed later. 36.2.2. The Report entity Basically, for configuring the report, you need to instantiate a Report object and define only two properties: • the data: the information that the report should show. • the template: the .jrxml file that the report engine will use to format, order and set the style of This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 156 Devonfw Guide v2.4.0 the report. Report myReport = new Report(); myReport.setData(getMockData()); myReport.setTemplatePath("path\to\the\template\fooTemplate.jrxml"); The setData method needs the collection of HashMap with the pairs key/value to bind template fields with the data. In the setTemplatePath, you need to pass the location of the template which will be used to create the report. You can learn more about how to create Jasper templates here and here. In the Report object, you can also add parameters that can be used within the template: HashMap params = new HashMap<>(); params.put("ReportTitle", "Foo"); params.put("ReportDescription", "Report generated using the Devon Reporting module"); myReport.setParams(params); 36.2.3. Using the reportManager Once the Report object is defined and configured, you can generate the report. Following example shows a basic implementation for the creation of a report in pdf file File file = new File("D:\\Temp\\pdf_Report.pdf"); reportManager.generateReport(myReport, file, ReportFormat.PDF); Therefore, once the Report object is defined, the report generation is very simple, it only needs: • a report manager (the object with the injection of the module). • the Report object with the data and the template defined. • a file to write the report results. • a format for the report (you can choose between pdf, xls, xlsx, doc, docx, txt, html, Pptx and several more). 36.3. Working with templates With reference to previous sections, the Reporting module works using the Jasper Reports templates. These templates are basically xml files (with extension jrxml) with some custom structure. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 157 Devonfw Guide v2.4.0 36.3.1. The parts of a template The jrxml templates are divided into several blocks of information. These blocks can be of two types: • blocks with static information. • blocks with dynamic information. The static information is the information defined by the template itself or by the parameters passed to the template and it remains unchanged over the different pages of the report. The dynamic information is the information defined by the data that is passed to the Report object as it is the report’s main content. A basic jrxml structure would be like below: 36.3.3. Using parameters in the template After the parameter definition, you can use the parameters within the template with a structure shown below: 36.3.4. Defining Fields The fields are the elements linked with the reports dynamic data. The fields can be defined in the templates in this way and after the tag. 36.3.5. Using fields in the template After the field definition, you can use the fields inside the tag as the part of the dynamic data. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 159 Devonfw Guide v2.4.0 [...] [...] 36.3.6. Creating templates with GUI software Working with xml can be sometimes complex and it adds a layer of difficulty when trying to visualize a graphic result. For that reason, Jaspersoft provides a software to manage the Reports and this software includes a complete functionality to generate and export jrxml templates. It is about Jaspersoft Studio and you can get it from the Jaspersoft site here. In the similar way, the Jaspersoft site provides the users with many documentation and examples of how to use Jaspersoft studio, how to install it and how to generate templates: • Getting Started with Jaspersoft Studio • Designing a Report with Jaspersoft Studio • Creating a custom template with Jaspersoft Studio 36.4. Subreports A subreport is a report included inside another report. This allows the creation of very complex layouts with different portions of a single document filled using different data sources and reports. To know more about subreports, refer this link. A basic example of the subreports usage with the Reporting module is below: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 160 Devonfw Guide v2.4.0 File file = File.createTempFile("subreport_", ".pdf"); this.reportManager.generateSubreport(masterReport, subreports, file, ReportFormat. PDF); • The masterReport is the report that will house the sub-reports. It is defined as it is explained in the previous section. • The subreports is a List of reports to be included within the main report. • The rest of parameters are explained in the previous section. 36.4.1. Defining a Subreport The subreport definition is same as for a regular report, the only point is to define the setDataSourceName. List subreports = new ArrayList<>(); [...] Report sureport01 = new Report(); sureport01.setName("subreport01"); sureport01.setDataSourceName("subreport01DataSource"); sureport01.setData(getSubreport01MockData()); sureport01.setTemplatePath(path\to\the\template\sureport01Template.jrxml); this.subreports.add(sureport01); The DataSourceName is the name, that will be later used to bind the subreport with its data, so that it has to be defined in the master report template in order to pass it to the subreport as a parameter. [...] [...] This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 161 Devonfw Guide v2.4.0 36.4.2. How to pass a parameter to a subreport You can pass a parameter to a subreport using the setParams method of the master report. // You will have a HashMap for "global" parameters HashMap allParams = new HashMap<>(); Then, when defining a subreport, you can add its parameters to the global parameters: HashMap subreport01Params = new HashMap<>(); subreport01Params.put("City", "Valencia"); allParams.putAll(subreport01Params); And during the master report definition: this.masterReport.setParams(allParams); Finally, in the master report template, you will define the parameter and pass it to the subreport. [...] [...] 36.4.3. Concatenated reports Other functionality of the Reporting module is to generate concatenated reports. A concatenated report is a set of reports printed in a single file. In other words, you can have several reports and generate a single file to contain them all. A basic example of this: this.reportManager.concatenateReports(reports, file, ReportFormat.PDF); This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 162 Devonfw Guide v2.4.0 The reports parameter is a List of Report objects. The rest of the parameters are same as explained in the previous sections. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 163 Devonfw Guide v2.4.0 Chapter 37. The Winauth-AD module This Devonfw module allows the applications to authenticate the users against an Active Directory. 37.1. Authentication with Active Directory Active Directory (AD) is a directory service that Microsoft developed for Windows domain networks. It is included in the most Windows Server operating systems as a set of processes and services. Initially, Active Directory was only in charge of centralized domain management. Starting with Windows Server 2008, however, Active Directory became an umbrella title for a broad range of directory-based identity-related services. For more information, visit wikipedia. 37.1.1. Include Winauth-ad in a Devon project Winauth-ad module provides a simple authentication for your Devon applications. To implement authentication in your Devon project, follow the next steps: Step 1: Add the starter Include the starter in pom.xml. Verify that the version matches the last available version of the module. com.devonfw.starter devonfw-winauth-ad-starter${devonfw.version}

Step 2: Security configuration
Create
a
variable
of
the
class
general/service/impl/config/BaseWebSecurityConfig.java

in

@Inject

NOTE

For previous versions of the oasp4j based apps, you may find BaseWebSecurityConfig
in a different location: general/configuration/BaseWebSecurityConfig.java

Remember to add the package of the module to the @ComponentScan annotation in the Spring Boot
main class.

164

Devonfw Guide v2.4.0

"my.other.components.package" })

Step 3: Define the provider
Also,

in

the

BaseWebSecurityConfig.java

class,

the

LDAP

provider

to

the

AuthenticationManagerBuilder in the configureGlobal(AuthenticationManagerBuilder auth) method

;

For previous version of oasp4j apps, you won’t find 'configureGlobal' method.
Therefore, you should add it in the init() method instead (in such cases, you should
find an AuthenticationManagerBuilder available in the class).

NOTE

@PostConstruct
public void init() throws Exception {
this.authenticationManagerBuilder
.LdapAuthenticationProvider());
}

Step 4: Implement the UserDetailsContextMapper
Implement the class UserDetailsContextMapper to build the UserDetails with the data of the user.

import
import
import
import

@Named
@Component
public class UserDetailsContextMapperImpl implements UserDetailsContextMapper {
private static final Logger LOG = LoggerFactory.getLogger
(UserDetailsContextMapperImpl.class);
@Inject
private AuthenticationSource authenticationSource;
@Inject
@Inject
private AccessControlProvider accessControlProvider;

165

Devonfw Guide v2.4.0

/**
* @return authenticationSource
*/
public AuthenticationSource getAuthenticationSource() {
return this.authenticationSource;
}
/**
* @param authenticationSource new value of authenticationSource.
*/
public void setAuthenticationSource(AuthenticationSource authenticationSource) {
this.authenticationSource = authenticationSource;
}
/**
* @param accessControlProvider new value of accessControlProvider.
*/
public void setAccessControlProvider(AccessControlProvider accessControlProvider) {
this.accessControlProvider = accessControlProvider;
}
/**
*/
}
/**
*/
}
@Override
public UserDetails mapUserFromContext(DirContextOperations ctx, String username,
Collection authorities) {
UserData user = new UserData(username, "", authorities);
try {

166

Devonfw Guide v2.4.0

String
String
FirstName
String
String

givenname = attributes.get("givenname").toString().substring(11); //
sn = attributes.get("sn").toString().substring(4);// LastName
memberOf = attributes.get("memberof").toString().substring(10); // Groups

PrincipalProfileImpl userProfile = new PrincipalProfileImpl();
userProfile.setName(cn);
userProfile.setFirstName(givenname);
userProfile.setLastName(sn);
userProfile.setId(cn);
userProfile.setGroups(groups);
// determine granted authorities for spring-security...
Collection accessControlIds = groups;
Set accessControlSet = new HashSet<>();
for (String id : accessControlIds) {
boolean success = this.accessControlProvider.collectAccessControls(id,
accessControlSet);
if (!success) {
LOG.warn("Undefined access control {}.", id);
}
}
for (AccessControl accessControl : accessControlSet) {
}
user.setUserProfile(userProfile);
} catch (Exception e) {
e.printStackTrace();
("Authentication failed.", e);
in Active Directory."
throw exception;
}
return user;
}
@Override
public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
}
}

167

Devonfw Guide v2.4.0

Therefore, the above code builds the user with the Active Directive information.
NOTE

And the map of the groups in the configuration.
You can build any User you want. For e.g. you could use a query to Active Directory
(like the example) or a query to your own User database.

Step 5: Configure the LDAP-AD connection
Now, you need to configure the LDAP parameters in application.properties. By default, the winauthad module works with a LDAP Authentication and a query to AD to have the authorization, so you
need to define all these properties. If you are using a customized UserDetails without AD query, you
don’t need to define the AD properties. The same happens, if you don’t use the Role Mapping class.

#Server configuration
#LDAP
devon.winauth.ldap.url=ldap://mydomain.com/
devon.winauth.ldap.encrypt=true
devon.winauth.ldap.keyPass=keyPass
devon.winauth.ldap.userDn=cn=user,DC=mydomain,DC=com
devon.winauth.ldap.patterns=ou=Users
devon.winauth.ldap.userSearchFilter=(sAMAccountName={0})
devon.winauth.ldap.userSearchBase=
devon.winauth.ad.rolePrefix=^(.*)CN=([^,]*),.*,DC=MYDOMAIN,DC=COM$#Roles mapping devon.winauth.groups.Chief=S-ESPLAN devon.winauth.groups.Waiter=S-ECOMU7 devon.winauth.groups.Cook=dlescapgemini.grado-a devon.winauth.groups.TESTGROUP=testGroup Now you can run your application and show the login form with the Active Directory authentication. NOTE As you can see the property password is encrypt. You can find more information about it here. Also you can put the password without encrypt by default. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 168 Devonfw Guide v2.4.0 37.1.2. Using the UserDetailsContextMapper with AD As mentioned above, you can implement your own UserDetailsContextMapper or use the UserDetailsContextMapper given in this tutorial. If you use the last one, you need to keep in mind the next points. Roler and Groups mapper Winauth-ad includes a group mapper that gives a simple tool to map the groups of the Active Directory with a roles/groups of your application. To use it, you need to configure the mapping as shown below: #Roles mapping devon.winauth.groups.SESPLAN=S-ESPLAN devon.winauth.groups.ECOMU7=S-ECOMU7 devon.winauth.groups.GradoA=dlescapgemini.grado-a devon.winauth.groups.TESTGROUP=testGroup Now, if you ask the server for the current user of the application, you will see the user data with his groups. Service CurrentUser If you use the basic UserDetailsContextMapper that winauth-ad implements, you need to modify the service currentuser in the class general/service/impl/rest/SecurityRestServiceImpl.java. @Produces(MediaType.APPLICATION_JSON) @GET @Path("/currentuser/") @PermitAll public UserDetailsClientToAD getCurrentUser(@Context HttpServletRequest request) { if (request.getRemoteUser() == null) { throw new NoActiveUserException(); } return UserData.get().toClientTo(); } NOTE You need to import the classes UserData and UserDetailsClientToAD of the winauth-ad module. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 169 Devonfw Guide v2.4.0 Chapter 38. The Winauth-SSO module This Devonfw module allows the applications to authenticate the users using the Windows credentials. The basic result is that the login is made automatically through the browser avoiding the login form (in some browsers like Firefox, you may need to authenticate once). 38.1. Single Sign On Single sign-on (SSO) is a property of access control of multiple related, but independent software systems. With this property, a user logs in with a single ID and password to gain access to a connected system or systems without using different usernames or passwords, or in some configurations seamlessly sign on at each system. For more information, visit wikipedia. 38.1.1. Include Winauth SSO in a Devon project Winauth SSO module provides a simple Single sign-on authentication for your Devon applications. If you want to implement this kind of authentication in a Devon project, follow the next steps: Step 1: Add the starter Include the starter in pom.xml. Verify that the version matches the last available version of the module: com.devonfw.starter devonfw-winauth-sso-starter${devonfw.version}

Step 2: Inject the module
The class general/service/impl/config/BaseWebSecurityConfig.java uses the @Inject annotation to

170

Devonfw Guide v2.4.0

import com.devonfw.module.winauthsso.common.api.WinauthSSO;
{...}
public abstract class BaseWebSecurityConfig extends WebSecurityConfigurerAdapter {
{...}
@Inject
private WinauthSSO sso;
{...}
}

NOTE

For previous versions of the oasp4j based apps, you may find BaseWebSecurityConfig
in a different location: general/configuration/BaseWebSecurityConfig.java.

Step 3: Define the security entry point and filter
Also, add the winauth SSO configuration down in the void configure(HttpSecurity) method in the
BaseWebSecurityConfig.java class

@Override
public void configure(HttpSecurity http) throws Exception {
//Winauth SSO configuration
http.
[...]
).exceptionHandling()
.authenticationEntryPoint(this.sso.getSSOFilterEntryPoint());
}
And that’s all! Now, you have a simple SSO Authentication implemented.
Hereafter, when you access the app, the browser should redirect you to the main page of your app
Henceforth, in the server console, you should see a message as shown below:

waffle.spring.NegotiateSecurityFilter
}\{Your-User}

: successfully logged in user: {Your-Domain

You need to be careful with the service’s current user because SSO by default is not
NOTE

compatible with the information of the UserDetailsClientTo class. You need to adapt
this class or use a customized SSO User Details (next chapter in the wiki).

171

Devonfw Guide v2.4.0

38.1.2. Customized Winauth SSO User Details
According to the recent steps, you have a very simple authentication and authorization
functionality with the Windows credentials. In the standard scenario, you may want to implement
your own User Details. Therefore, let’s discuss how to implement it for SSO authentication.
Step 1: Create customized filter
The idea is to rebuild the default filter NegotiateSecurityFilter, you can create a complete new
filter or, like this example, just modify some methods. In this case, you need to modify boolean
setAuthentication(…), this method is called by the method void doFilter(…) (you can modify this
method too) when the authentication is successful, so let’s discuss how to build a custom
UserDetails.

/**
* This is a dummy implementation of a customized NegotiateSecurityFilter.
*
* @author jhcore
*/
public class NegotiateSecurityFilterCustomized extends NegotiateSecurityFilter {
/** The Constant LOGGER. */
private static final Logger LOGGER = LoggerFactory.getLogger
(NegotiateSecurityFilterCustomized.class);
private Usermanagement usermanagement = new UsermanagementDummyImpl();
private AccessControlProvider accessControlProvider;
/**
* The constructor.
*
* @param accessControlProvider is the provider that help us to get the permissions
*/
public NegotiateSecurityFilterCustomized(AccessControlProvider
accessControlProvider) {
super();
this.accessControlProvider = accessControlProvider;
}
/**
* The constructor.
*/
public NegotiateSecurityFilterCustomized() {
super();
}
@Override
public void doFilter(final ServletRequest req, final ServletResponse res, final
FilterChain chain)
throws IOException, ServletException {

172

Devonfw Guide v2.4.0

// Here you can customize your own filer functionality
super.doFilter(req, res, chain);
}
@Override
protected boolean setAuthentication(final HttpServletRequest request, final
HttpServletResponse response,
final Authentication authentication) {
try {
String principal[] = authentication.getPrincipal().toString().split("\\\\", 2);
(profile));
SecurityContextHolder.getContext().setAuthentication(auth);
} catch (Exception e) {
NegotiateSecurityFilterCustomized.LOGGER.warn("error authenticating user");
NegotiateSecurityFilterCustomized.LOGGER.trace("", e);
}
return true;
}
private Object getAutoritiesByProfile(UserProfile profile) {
Set authorities = new HashSet<>();
Collection accessControlIds = new ArrayList<>();
Set accessControlSet = new HashSet<>();
for (String id : accessControlIds) {
boolean success = this.accessControlProvider.collectAccessControls(id,
accessControlSet);
if (!success) {
}
}
for (AccessControl accessControl : accessControlSet) {
}
return authorities;
}
}

173

Devonfw Guide v2.4.0

The above example uses the UsermanagementDummyImpl, which is generated during the creation of the
new Devon application. Feel free to customize your own filter, just use the above class with a
customized Usermanagement.
Step 2: Inject and configure Winauth SSO
Now, let’s discuss how to create a Winauth SSO variable and to configure the filter.

import com.devonfw.module.winauthsso.common.api.WinauthSSO;
{...}
public abstract class BaseWebSecurityConfig extends WebSecurityConfigurerAdapter {
{...}
@Inject
private WinauthSSO sso;
@Bean
public AccessControlProvider accessControlProvider() {
return new AccessControlProviderImpl();
}
{...}
}
As shown above, the Filter needs a AccessControlProvider, there is a one which is configured in the
WebSecurityConfig, so you just need to pass it to the filter by param.
Step 3: Configure the Custom Filter and the security entry point
Add the winauth SSO configuration down in the void configure(HttpSecurity) method

@Override
public void configure(HttpSecurity http) throws Exception {
...
// Set the custom filter
this.sso.setCustomFilter(new NegotiateSecurityFilterCustomized
(accessControlProvider()));
// Add the Filter to the app authentication process
).exceptionHandling()
.authenticationEntryPoint(this.sso.getSSOFilterEntryPoint());
}

174

Devonfw Guide v2.4.0

And that’s all! Now, you have a simple SSO Authentication with a custom UserDetails and you can
use the server current user by default.

175

Devonfw Guide v2.4.0

Chapter 39. The i18n module
39.1. Introduction
Internationalization is the process of designing a software application so that it can potentially be
adapted to specific local languages and regions without engineering changes. This constitutes much
more than just "translating text or correct number formats". Think of internationalization as
readiness for localization. Sometimes written as "i18n", internationalization evolved from a
growing demand for multilingual products and applications.

39.2. The i18n Module
The i18n module provides easy creation and maintenance of distinct translations for your Devon
applications. OASP, Angular and Sencha - three distinct platforms currently forming Devon. Each
provides their own specific mechanisms for L10N. I18n module fits within the context of the
facilities offered by each platform. I18n artifacts are available at compile/build time and not just
"run-time". It is based on a namespaced name-value system ("properties").

39.2.1. Conceptual schema

As shown in above diagram, spring applications will have properties file defined for respective
locales. This file will contains the translations of the applications texts as well as display format of
elements like dates, numbers etc. These should be places within the src/main/resources/locale
folder/namespace as shown in below dig.

176

Devonfw Guide v2.4.0

I18n will convert these files to json data which will be return from REST services. The json files will
reflect in structure the "dot namespaces" from the property file. So: main.intro.name = Devon fw
control panel Becomes

{"main":
{"intro":
{
"name":"Devon fw control panel"
}
}
}
To retrieve the response in the above said JSON format, User should make the Rest Service call by
hitting the rest service. Url will be in form

/locales/
For example:
http://localhost:8081/oasp4j-sample-server/services/rest/i18n/locales/en_US
Filters:
If user is interested in retrieving the value of the property, he can get the same by using the feature
‘Filters’. For Example: /locale/en_US?filter=main.info In the example above, key
‘main.info’ is supplied as filter. Rest Service retrieves the corresponding value for this key and the
JSON format of the value is returned to the user.

39.2.2. Exception behavior
In case of usage of filter and no data returned (no properties were found), an empty JSON response

177

Devonfw Guide v2.4.0

is

returned

denoting

no

results.

In

case

of

an

invalid

Locale,

user

will

DevonfwUnknownLocaleException.

39.2.3. I18N mechanism integration
Currently three libraries are available for server-side i18n. Those libraries are:
• mmm-util-core and mmm-util-cli
• Standard library (i18n module use com.google.code.gson to convert java property file to json)
• Customized library (User needs to develop this module)
In devonfw-i18n module, config.properties file contains ‘i18n.input.name‘ property to denote
library used for i18n.User needs to set module name in config.properties file. User can either set
‘standard’ or ‘mmm’ or customized module name.
mmm-util-core and mmm-util-cli
For server-side i18n mmm library is recommended. More details for mmm can be found at http://mm-m.sourceforge.net/apidocs/net/sf/mmm/util/nls/api/package-summary.html For example , if user
wants to use mmm library for internationalization, he has to make an entry in config.properties as

i18n.input.name=mmm
‘mmm’ is default mechanism for i18n.
Reading property files via MMM implementation:
net.sf.mmm.util.nls.base.ResourceBundleSynchronizer is used to create .properties files for the
locales via MMM implementation. Steps to create locales, property files, via MMM: Below are steps
to

produce

locale

files

at

location

\src\main\resources\com\capgemini\devonfw\module\i18n\common\api\nls - Right click on 'i18n'
module. - Go to Run As > Run Configurations - Right click on 'Java Application' - Click on 'New' - In
the

dialog

box

that

ResouceBundleSyncronizer)

is

displayed
and

,

provide
provide

name
main

for

the
class

configuration
name

(eg.
as

net.sf.mmm.util.nls.base.ResouceBundleSyncronizer

178

Devonfw Guide v2.4.0

• Click on Arguments tab besides Main tab.
• Enter program Arguments as "--locale " eg. "--locale en"

Apply the changes and click 'Run' button. File in config.properties file will have below property:

i18n.input.name=mmm
To enable i18n functionality in oasp4j based application we need to follow below steps: - Maven
clean and build your application - Maven clean build devonfw-i18n with below dependency
commented:

org.slf4j
slf4j-log4j12
1.6.1

• Add following starter to your oasp4j application. Verify that the version matches the last

179

Devonfw Guide v2.4.0

available version of the module:

com.devonfw.starter
devonfw-i18n-starter
${devonfw.version} • Comment below statement from SpringBootApp.java: @EntityScan(basePackages = { "test.cg.i18nConfigSample" }, basePackageClasses = { AdvancedRevisionEntity.class }) • Add below statement to SpringBootApp.java class: @ComponentScan(basePackages = { "com.devonfw.module.i18n", "my.other.component" }, basePackageClasses = { AdvancedRevisionEntity.class }) Here my.other.component refers to any other package which user needs to scan. User should provide basePackages from @EntityScan annotation. Refer below figure for example: • Add below statement to ServiceConfig.java : @ComponentScan(basePackages = { "com.devonfw.module" }) • In config.properties set module name which you want to use for i18n- Available modules are "mmm" and "standard". Note: You can create add module as well. Refer to section add own module in i18n. • Once above changes are done clean build your project in eclipse and launch SpringBootApp.java. User can view i18n REST service in available REST webservices (http://localhost:8081/oasp4j-sample-server/services/rest/) • To test i18n REST service, the general format of the service will be as follows: /locale/ eg. localhost:8081/oasp4j-sample-server/services/rest/i18n/locales/en_US Standard library (i18n module use com.google.code.gson to convert java property file to json) To use standard library from i18n module, user needs to set ‘i18n.input.name’ property value to This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 180 Devonfw Guide v2.4.0 ‘standard’ in config.properties. i18n.input.name=standard This library use com.google.code.gson to convert java property file to json. This data will be returned to user via REST call. Customized library(Adding own module in I18n) To add own module in i18n user needs to follow below step: 1. Create new module which will be able to return json data from method call. 2. Add dependency of this module in devonfw-i18n module. 3. In config.properties set i18n.input.name =USER_MODULE_NAME 4. In class com.devonfw.module.i18n.logic.impl.I18nImpl modify getResourceObject() method add your switch case in it. 5. Clean and build your application and launch SpringBootApp.java. You can view i18n REST service in available REST webservices (http://localhost:8081/oasp4j-sample-server/services/rest/) 6. To test i18n REST service, the general format of the service will be as follows: /locale/ eg. localhost:8081/oasp4j-sample-server/services/rest/i18n/locales/en_US This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 181 Devonfw Guide v2.4.0 Chapter 40. Devon-locale module(i18n resource converter) 40.1. Introduction Devon-locale is a standalone module; basically it is resource converter for internationalization. Currently, each part of an application - OASP4J, Angular2 etc - use their native resource formats for internationalization. This becomes unwieldy in large systems, especially if part of the texts is shared by the server, client and/or different client apps. Devon-locale solves this problem by using one resource file as input and translating it to one or more target formats. Currently we have java property file as input file, user will mention key value pair in java property file. Current version of devon-locale is converting or translating it to JSON (format used by angular application) and EXTJS (format used by Sencha application) format. So, conceptually this module is a "translator", a custom made program which is able to translate - or transform - to any possible destination format. 40.2. Devon locale structure and working Devon-locale will convert java properties file to destination format(EXTJS or JSON). We will create IR(Intermediate representation) from java properties file. This IR will be same for all output formats. We will pass this Intermediate representation to various output adapters which will generate respective output files. Currently JsonTargetAdapter and ExtJsTargetAdapter are available. IR is tree like structure created from properties file key value pair. 40.2.1. Devon locale basic usage • Import source code in eclipse from path https://github.com/devonfw/devonfw-locale • Export as runnable jar. • Go to command prompt navigate to the path of jar. • Command to run devon-locale — Read from file and write to file java -jar devon-locale.jar -input D:\Test_Dist\Devondist_2.1.0_SNAPSHOT\workspaces\i18nWs\TestRepo\devonlocale\src\main\resources\sample.properties -informat java -outformat ANGULAR -output D:\temp.json OR java -jar devon-locale.jar -i D:\Test_Dist\Devondist_2.1.0_SNAPSHOT\workspaces\i18nWs\TestRepo\devonlocale\src\main\resources\sample.properties -f java -t ANGULAR -o D:\temp.json — Read from console and write to console This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 182 Devonfw Guide v2.4.0 java -jar devon-locale.jar -informat java -outformat ANGULAR -input "farewell=Tschüß" OR java -jar devon-locale.jar -f java -t ANGULAR -i "farewell=Tschüß" As you can see in above example there are various input params which are described in below table: Name Description Optional input, i File containing source translation True, default value [console in] informat, f Format of the source translation. Possible values: "java" True, default value "java" output, o File with target translation True, default value"[console out] outformat, t Format of the target translation True, default value "angular" Possible values: "angular" or "extjs" Alternatively we can launch devon-locale from eclipse , we need to run DevonLocale.java as java application. Sample.properties file used to test above module. object.man.name=John a.b.c.d.e=f zz=bb noFiles=n' y as pas de fichiers oneFile = y a un fichier multipleFiles = y a {2} fichiers pattern = Il {0} sur {1}. sampleStmt=At 10:16 AM on July 31, 2009, we detected 7 we paid$1000. He is at position {{0}} \n
greetings = Hallo.
farewell = Tschüß.
inquiry = Wie geht's?
sampleDate=12-10-2017
sampleMultiline= hi \
I am \
Sneha
nextline=new

spaceships on the planet Mars.

This sample.properties contains

183

Devonfw Guide v2.4.0

a.
b.
c.
d.

Dates
Multiline input
positional parameters ({0}, {1}, etc)
Special character like üß

184

Devonfw Guide v2.4.0

Chapter 41. The async module
41.1. Introduction
JAX-RS 2.0 offers a new feature which provides an asynchronous processing at both, the server and
the client side.
In the synchronous request/response processing model, client connection is accepted and processed
in a single I/O thread by the server. Generally, a pool of I/O threads is available at server side.
Therefore, when a request is received at server side, the server dedicates it to one of the threads for
further processing. The thread blocks, until the processing is finished and returned.
The idea behind the asynchronous processing model is to separate connection acceptance and
request processing operations. Technically, it means to allocate two different threads, one to accept
the client connection (acceptor) and the other to handle heavy and time consuming operations
(worker) releasing the first one.

41.2. The async module
The above concept is implemented in Devonfw through the async module. Therefore, you can
include it in your project and will be able to apply the async process of requests in the REST
services of your apps in a few steps, avoiding the complex details of the implementation.

To access the functionality of the async module, you will need to include its starter in your project’s
pom.xml. Verify that the version matches the last available version of the module.

com.devonfw.starter
devonfw-async-starter
${devonfw.version} 41.2.2. Injecting the module Add the reference to the module in your REST service using the @Inject annotation This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 185 Devonfw Guide v2.4.0 import com.devonfw.module.async.common.api.Async; ... @Service("myRestService") @Path("/rest") public class Rest { @Inject private Async async; [...] } Remember to add the package of the module to the @ComponentScan annotation in the Spring Boot main class. @ComponentScan(basePackages = { "com.devonfw.module.async" , "my.other.components.package" }) 41.2.3. Call the module Before calling the module, you will need to complete two previous steps: • Wrap your long process in the run method of a class that implements the AsyncTask. public class MyAsyncTask implements AsyncTask { @Override public Object run() { // Here your code for long process } } • Provide an Async Response. To do so, use the @Suspended annotation and add the AsyncResponse object in your REST method. Now, you can call the module using its execute method. The request will be bound to the async response and the async task provided. The call will be like the following: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 186 Devonfw Guide v2.4.0 import com.devonfw.module.async.common.api.Async; import javax.ws.rs.container.AsyncResponse; import javax.ws.rs.container.Suspended; ... @Service("myRestService") @Path("/rest") public class Rest { @Inject private Async async; @GET @Path("/asynctask") @Produces(MediaType.TEXT_PLAIN) public void asyncTask(@Suspended final AsyncResponse response) { this.async.execute(response, new MyAsyncTask()); } That’s all. After above simple steps, you will have an async process implemented. Now, let’s see some more features of the module. 41.2.4. Passing parameters You can also pass the parameters to be used, in the long task process. In this case, the module call would be: @GET @Path("/asynctask/{id}") @Produces(MediaType.TEXT_PLAIN) public void asyncTask(@Suspended final AsyncResponse response, @PathParam("id") String id) { this.async.execute(response, new MyAsyncTask(id)); } And the wrapper class: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 187 Devonfw Guide v2.4.0 public class MyAsyncTask implements AsyncTask { private String id; public MyAsyncTask(String id) { this.id = id; } @Override public Object run() { // Here your code for long process with access to 'this.id' } } 41.2.5. Module Configuration Internally, the Async module process can be configured in two main parameters: • core pool size: Sets the ThreadPoolExecutor’s core pool size. • time out: The amount of time that the process will wait for the long task, to be finished before return. A timeout of < 0, will cause an immediate return of the process. A timeout of 0, will wait indefinitely. The default values provided in the module are: • core pool size: 10. • time out: ◦ milliseconds: 0. ◦ status: 503 , service unavailable (available status 400,403,404,500 and 503). ◦ response Content: Operation timeout (the time out response message). ◦ mediatype: text/plain (you can respond the timeout in json, xml, html, etc. formats). However, you can edit those values by overriding the configuration properties in your app. To do it, you can use the application.properties to add the properties you want to define. Table 3. application.properties file Property Application Property Name core pool size devonfw.async.corePoolSize time out milliseconds devonfw.async.timeout.milliseconds time out status devonfw.async.timeout.status time out response content devonfw.async.timeout.responseContent time out media type devonfw.async.timeout.mediatype This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 188 Devonfw Guide v2.4.0 As an example, the next could be a valid application.properties configuration file, for an application in which, you want an async process with a core pool size of 20, and a timeout of 10 seconds, returning with a status of 500 (internal server error) and a response in json format: devonfw.async.corePoolSize=20 devonfw.async.timeout.milliseconds=10000 devonfw.async.timeout.status=500 devonfw.async.timeout.mediatype=application/json devonfw.async.timeout.responseContent={"response":[{"message":"error", "cause":"time out"}]} This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 189 Devonfw Guide v2.4.0 Chapter 42. The Integration Module 42.1. Introduction Within the Enterprise architecture the integration (or Enterprise Integration) is the field focused on communication between systems: system interconnection, electronic data interchange, product data exchange, distributed computing environments, etc. So the integration defines and provides an infrastructure to allow the communication between different applications or systems in a reliable way. The scope of Integration groups a wide range of communication methods, the systems can communicate by file transfer, sharing a data base, by remote procedure invocation or by messaging. The approach of the Integration Module implementation is based on the latter, using a solution based on channels and message queues to allow the exchange of information between applications. 42.2. Stack of technologies The Integration Module is a Java module based on Spring Integration solution and internally is implemented using the Spring integration Java dsl reference. In the core of all those technologies the implementation relies on the Java Message Service (JMS) as the messaging standard to create, send, receive and read messages. • Java Message Service: JMS allows applications to exchange messages using reliable and loosely coupled communication. To achieve this, the communication is done through message queues so the different applications (message server, and message clients) don’t know the actual addresses of each other, so they work in an emitter/subscriber manner. To manage those message queues, that are "out" of the applications but should be accessible, the solution provided by Devon relies on a message broker that will be in charge of messages infrastructure. • Apache Active MQ: The channels, queues and messages that the solution is based on, need an external infrastructure to be supported. The Devonfw implementation relies on Apache Active MQ as the message broker to manage the messages and queues that the different applications will use as communication channel. Active MQ is an open source infrastructure that is one of the most popular messaging servers and provides fully support for JMS. • Spring Integration extends the Spring framework to support the Enterprise Integration (system interconnection, electronic data interchange, product data exchange and distributed computing environments). Spring Integration enables lightweight messaging within Spring-based applications providing a simple model for building enterprise integration solutions while maintaining the separation of concerns. Among its features Spring Integration provides JMS support through Channel Adapters for receiving JMS messages. • The Java DSL extension for Spring Integration provides a set of convenient Builders and a fluent API to configure Spring Integration message flows from Spring configuration classes. It allows the creation of channel/queues and the message flow in applications start up time, so users can avoid the standard Spring Integration XML configuration (although it can also be used along side the DSL definitions). This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 190 Devonfw Guide v2.4.0 42.3. Installing Apache Active MQ 42.3.1. How to get and install the message broker If you want to be ready to a quick test of the module you will need an Active MQ broker. To do so you can download the Active MQ message broker from the official site. After the download is over, extract the zip folder, open a command line window and go to the bin folder inside the just created apache-activemq-{version} folder. your\location\apache-activemq-5.14.3>cd bin And start the server from the bin folder your\location\apache-activemq-5.14.3\bin>activemq start Now open a browser and access to url http://localhost:8161/admin, fill the access login with admin for both user and password. After logging in, you will have access to all the infrastructure of the message server This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 191 Devonfw Guide v2.4.0 We are installing the Active MQ server in our local machine only for test or example NOTE purposes. If you want further configuration details please visit the official documentation or ask your IT department. 42.4. Using Apache Active MQ with Docker Active MQ can also be run with Docker, you will need to: • Create a folder on your machine for a shared volume with the container (c:/Users/docker/activemq on the example below) • Create an activemq.xml configuration file (c:/Users/docker/activemq/conf/activemq.xml on the example below) • Run the following command docker run --rm -p 61616:61616 -p 8161:8161 -v c:/Users/docker/activemq/conf:/etc/activemq/conf -v c:/Users/docker/activemq/data:/var/activemq/data rmohr/activemq:5.14.3-alpine For the activemq.xml file, you can use the following content (extracted from sample folder on activemq distribution) This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 192 Devonfw Guide v2.4.0 42.4.1. Module connection configuration The Integration module provides a default connection configuration for Active MQ broker through the following properties spring.activemq.broker-url=tcp://localhost:61616 spring.activemq.user=admin spring.activemq.password=admin If you have changed the Active MQ configuration remember to overwrite the affected properties in the application.properties of your project. 42.5. Integration module details 42.5.1. Adding the starter to a project To access the functionality of the Integration module, you will need to include its starter in your project’s pom.xml. Verify that the version matches the last available version of the module. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 193 Devonfw Guide v2.4.0 com.devonfw.starter devonfw-integration-starter${devonfw.version}

42.5.2. Injecting the module
After adding the dependency, in order to start using the module inject it using the @Inject
annotation

import com.devonfw.module.integration.common.api.Integration;
...
@Inject
private Integration integration;
We will see the detailed usage of the module in the upcoming sections.

42.5.3. Default channels
Based on the mentioned stack of technologies, the Devonfw Integration module provides three
communication channels pre-configured and ready to be used out-of-the-box. The user will only
need to enable it through the module configuration.
Simple message channel
This is the most basic communication channel. In this case in one side is an application (emitter)
that sends messages to a specific queue in the message broker. In the other side a second
application (subscriber) is subscribed to that channel, which means that polls the message broker in
a defined interval of time to ask for new messages in that particular queue.

The subscriber application doesn’t provide a response, only consumes the messages.
To configure your application to use this default channel you only need to edit the
For emitter applications set the one-direction.emitter property to true:

194

Devonfw Guide v2.4.0

devonfw.integration.one-direction.emitter=true
If your application acts as subscriber set the property one-direction.subscriber to _true:

devonfw.integration.one-direction.subscriber=true
Doing this, when running your app the related Beans will be loaded automatically and the
communication channel and its related queue will be also created.
We will see more details of the simple message channel configuration further.
In this second approach the message flow is completed in two directions. In this case, instead of
talking about an emitter and subscriber systems, we should rather talk about a request/replay
channel. There will exist a communication between two clients, in which the first one will send a
message and wait for a response from the second one. So both sides are emitters and subscriber.

To configure your application to use this default channel, as we explained in the previous section,
you only need to edit the application.properties of your Spring project adding in this case the
property

or

devonfw.integration.request-

For emitter/subscriber applications set the request-reply.emitter property to true:

If your application acts as subscriber/emitter set the property request-reply.subscriber to true:

Doing this, same as in the previous case, when running your app the related Beans will be loaded
automatically and the communication channel and its related queue will be also created.
We also will see more details of the simple message channel configuration further.

195

Devonfw Guide v2.4.0

For the cases where the previous request/reply communication has to be asynchronous the module
provides a default async communication channel.
To configure your application to use this asynchronous channel, as in the previous cases, you only
need to enable the corresponding properties into the application.properties file of your project.
For emitter/subscriber applications set the request-reply-async.emitter property to true:

Otherwise, if your application is the subscriber/emitter, set the property request-reply.subscriber to
true:

We will show the complete configuration of this default channel in upcoming sections.

42.5.4. Usage of the default channels
How to use the default simple channel
As we previously mentioned the Integration module provides a simple communication channel
where in one side one emitter application will send a message and in the other side other subscriber
To achieve that in our applications we only need to configure the corresponding properties to
create the channel and its related queue.
Default configuration
The default configuration properties for this channel, provided by default with the Integration
module, are:

devonfw.integration.one-direction.emitter=false
devonfw.integration.one-direction.subscriber=false
devonfw.integration.one-direction.channelname=1d.Channel
devonfw.integration.one-direction.queuename=1d.queue
devonfw.integration.one-direction.poller.rate=5000
• emitter: if your app is going to send messages through this channel to the related queue.
• subscriber if your app is going to subscribe to the channel to read the messages of the queue.
• channelname: the name for the channel.
• queuename: the name for the channel queue.
• poller.rate: in case of subscriber applications this is the interval to poll the message broker for

196

Devonfw Guide v2.4.0

new messages.
If you want to customize these properties you can overwrite them in the application.properties of
Emitter application configuration
As we already mentioned the Emitter applications must enable the emitter property so you must

devonfw.integration.one-direction.emitter=true
Optionally,

you

can

edit

the

name

for

the

channel

and

for

the

queue

using

the

devonfw.integration.one-direction.channelname and devonfw.integration.one-direction.queuename
properties.
Emitter application example
After you have added the module dependency you can start using the module injecting it in your
app. Lets see how to send a simple message through that default simple channel.
NOTE

In order to make the example run properly remember that we will need an Active
MQ instance running to provide support to the channels and queues.

In our sender application we only need to call the send method of the integration object and provide
a message content

import com.devonfw.module.integration.common.api.Integration;
public class MyEmitterApp{
@Inject
private Integration integration;
public void sendSimpleMessage(){
this.integration.send("hello world");
}
}
Running the application will result into a message sent to the Integration module default simple
channel with name 1d.Channel and to the queue 1d.queue (or the names you provided through
configuration properties). So if now we go to the Active MQ web client we will see in the Queues
section that we have a new queue created with one message as pending messages, no consumers (as
we still don’t have any subscriber to this channel/queue) and no dequeued messages.

197

Devonfw Guide v2.4.0

Clicking on the queue name shows us the pending messages details

And clicking again on the message ID takes us to the message view where we can see more details
like the message content

With this we have finished the out flow for the Integration module default simple channel. Lets see
now how to read that message we have sent using a different application.
Subscriber application configuration

198

Devonfw Guide v2.4.0

For subscriber applications you must enable the channel through the corresponding property in the

devonfw.integration.one-direction.subscriber=true
In case of subscriber applications you can also configure the interval of time to make the requests to
the message broker for new messages. To do so you can add the property devonfw.integration.onedirection.poller.rate to your application.properties file and provide a milliseconds amount as
property value. If you don’t overwrite this property its default value is 5000 (5 seconds).
As in the emitter case, you can edit the name for both the channel and the queue
(devonfw.integration.one-direction.channelname and devonfw.integration.one-direction.queuename
properties) but have into account that these names must match between the emitter and the
subscriber applications in order to perform the communication.
Subscriber application example
As in the case of emitter application you have to add the module dependency and inject the module.
Once that is done we can subscribe our application to the channel/queue to start receiving messages
from the Integration module default simple channel.

import com.devonfw.module.integration.common.api.Integration;
public class MySubscriberApp{
@Inject
private Integration integration;
@Inject
private SubscriptionHandler subscriptionHandler;
this.integration.subscribe(this.subscriptionHandler);
}
}
In this case we provide to the subscribe method a Subscription Handler to manage what we want to
do with each message. For the example we have implemented a basic subscription handler. To
create your own Subscription Handler you only need to create a class, annotate it with @Component
and implement the SubscriptionHandler interface. Lets see our SubscriptionHandlerImp

199

Devonfw Guide v2.4.0

@Component
public class SubscriptionHandlerImp implements SubscriptionHandler {
public void handleMessage(Message message) throws MessagingException {
System.out.println("***********************************");
System.out.println("***********************************");
}
}
As you can see we are only showing, through console, the message content which we access
through the getPayload() method. Now running the application we get the output

***********************************
MESSAGE IS: Hello world
***********************************
And going back to the Active MQ web client we can see the changes in the Queues section

The first you should note is that now the Number of Consumers is 1 as we have subscribed an
application to the channel. Then the Pending Messages has changed to 0 and the Messages Dequeued
has increased to 1.
At this point we have finished the example for the in flow of the Integration module default simple
channel. Now you have the whole picture of how a simple integration channel works with Devonfw
Integration module and Active MQ server.
How to use the default request-reply channel
With the Integration module a ready to be used request-reply channel is provided by default. This
channel will allow us to communicate systems sending and receiving messages in both sides. A first

200

Devonfw Guide v2.4.0

application will send a message and wait for a response, while a second application will receive the
message sent by the first one and send back the response that the other app is waiting for.
To achieve that in our applications we only need to configure the corresponding properties to
create the channel and its related queue.
The default configuration properties for this channel, provided by default with the Integration
module, are:

• emitter: if your app is going to send and then receive messages through this channel.
• subscriber if your app is going to receive and then send back messages using this channel.
• channelname: the name for the channel.
• queuename: the name for the channel queue.
• receivetimeout: in case of send + receive applications this is the maximum amount of
milliseconds to receive a response from "the other side" of the channel. If this time is exceeded a
timeout Exception will be thrown.
If you want to customize these properties you can overwrite them in the application.properties of
your project, as we are going to see below.
To enable the sending of messages through this channel you must set the request-reply.emitter
property to true in the application.properties of our project.

Optionally,

you

can

edit

the

name

for

the

channel

and

for

the

queue

using

the

properties. As we just mentioned, the timeout for the response can be edited adding the
milliseconds value. By default the timeout is 5000 (5 seconds).
After you have added the module dependency you need to inject it. Lets see how to send and

201

Devonfw Guide v2.4.0

In order to make the example run properly remember that we will need an Active

NOTE

MQ instance running to provide support to the channels and queues.

In our sender-receiver application we only need to call the sendAndReceive method of the
integration object and provide a message content

import com.devonfw.module.integration.common.api.Integration;
public class MyFirstApp{
@Inject
private Integration integration;
public void myMethod(){
System.out.println("Response:" + response);
}
}
If now we run the application we would get a timeout exception as there is no one ready to provide
a response within the defined timeout limit (5 seconds). So first, lets prepare our other-side
application.
In this application we need to enable the request-reply.subscriber property so, in the
applications.property file of our project, we must set to true that property.

You can also edit the name for both the channel and the queue (devonfw.integration.requestreply.channelname and devonfw.integration.request-reply.queuename properties). But, as mentioned
in previous section, have into account that these names must match between the sernder-receiver
and the receiver-sender applications, in order to perform the communication.
As in the case of sender-receiver application, you have to add the module dependency and inject the
module. Once that is done we can subscribe our application to the channel/queue to start receiving
messages and sending responses from/to the Integration module default request-reply channel.

202

Devonfw Guide v2.4.0

import com.devonfw.module.integration.common.api.Integration;
public class MySecondApp{
@Inject
private Integration integration;
@Inject
private RequestHandler requestHandler;
public void myMethod(){
}
}
In this case we provide to the subscribeAndReply method an Request Handler to manage the
responses to each message. For the example we have implemented a basic Request Handler, to
create your own one you only need to create a class and implement the RequestHandler interface.
Lets see our RequestHandlerImp

@Component
public class RequestHandlerImp implements RequestHandler {
@Override
public Object handleMessage(Message m) {
System.out.println("***********************************");
System.out.println("***********************************");
}
}
As you can see we are simply printing the original message received, using the getPayload()
At this point we can run that second application and see what happens through the Active MQ web
client.

203

Devonfw Guide v2.4.0

The above image shows that the channel and queue for our request-reply channel have been
created automatically and in the Number of Consumers you can see that 1 that refers to our
application.
Now we can run the first application, as at this point we already have the second application ready
to reply to the first one requests.
The output in the second application is as expected

***********************************
MESSAGE IS: Hello
***********************************
While the output in the first app is

Response:Hello World
If we check out again the Active MQ web client we can see that we still have one consumer (the
second application) but now we have also one Message Enqueued and one Message Dequeued.

We have finished the demonstration for the default Request-Reply channel provided by the

204

Devonfw Guide v2.4.0

Integration module. Now we are going to see how to achieve the same but in an asynchronous way
using the third default channel provided by the module: the request-reply-async channel.
How to use the default asynchronous request-reply channel
The usage of this default channel, provided also by default within the Integration module, is the
same than for previous channels, and specially regarding the default request-reply channel
explained in the previous section. Anyway let´s briefly show the basics about how to configure and
use the asynchronous channel.
Default properties for asynchronous channel

The properties are the same as in the simple request-reply channel.
The application that is going to trigger the communication flow, sending a first message, must
enable the request-reply-async.emitter property, setting true as value.

In the configuration of this application we can also define the timeout for the response. If exceeded,
the process will be stopped and a timeout exception will be thrown. This can be controlled with the
In the other side, the application that is subscribed to the channel and is going to receive the
messages and reply to them, must have the property request-reply-async.subscriber defined as
true.

After you have added the module dependency you need to inject it. Lets see how to send and
NOTE

In order to make the example run properly remember that we will need an Active
MQ instance running to provide support to the channels and queues.

In our sender-receiver application we only need to call the sendAndReceiveAsync method of the
integration object and provide a message content. As we are creating an asynchronous process we
will use the Java Future to handle the response. We will not complicate the example with too many
details of Future’s use so the code will look like the following

205

Devonfw Guide v2.4.0

import com.devonfw.module.integration.common.api.Integration;
public class MyFirstApp{
@Inject
private Integration integration;
public void myMethod() throws InterruptedException, ExecutionException{
System.out.println("Message sent.");
while (!response.isDone()) {
// things that you can do in parallel while waiting for the response
System.out.println("Waiting...");
}
System.out.println("ASYNC RESPONSE: " + response.get());
}
}
If now we run the application we would get a timeout exception as there is no one ready to provide
a response within the defined timeout limit (5 seconds). So first, lets prepare our other-side
application.
As in the previous application, you have to add the module dependency and inject the module.
Once that is done we can subscribe our application to the channel/queue (with the subscribeAsync
method) to start receiving asynchronously messages and sending responses from/to the Integration

import com.devonfw.module.integration.common.api.Integration;
public class MySecondApp{
@Inject
private Integration integration;
@Inject
private RequestAsyncHandler RequestAsyncHandler;
public void myMethod(){
this.integration.subscribeAsync(this.RequestAsyncHandler);
}
}

206

Devonfw Guide v2.4.0

In this case we provide to the subscribeAsync method an Request Async Handler to manage the
responses to each message. For the example we have implemented a very simple Request Async
Handler that blocks the process during 3 seconds to simulate a long process. To create your own
Request Async Handler you only need to create a class, annotate it with @Component and implement
the RequestAsyncHandler interface. Lets see our RequestAsyncHandlerImp.

@Component
public class RequestAsyncHandlerImp implements RequestAsyncHandler {
@Override
public Object handleMessage(Message m) {
System.out.println("***********************************");
System.out.println("***********************************");
try {
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
As you can see we are simply printing the original message received, using the getPayload()
method, and after the delay of 3 seconds, it returns a reply adding "World" to the original message.
Now we can run that second application, the channel and its async.queue will be automatically
created in the Active MQ broker and the new consumer (our second app) will be subscribed to that
channel.

If now we run the first application the output is

207

Devonfw Guide v2.4.0

Message Sent.
[...]
Waiting...
Waiting...
Waiting...
ASYNC RESPONSE: Hello World
With this we have completed the example about the default asynchronous channel for the requestreply flow of the Devonfw Integration module.
Also here ends the content about the pre-configured part of the module. Next we will see how to
create, programmatically, new channels and queues.

42.5.5. Creating new channels
The Devonfw Integration module provides the option of creating new channels programmatically.
The user can generate new channels and send and receive messages defining every step in Java
code, without the necessity of configure anything.
Types of channels that can be created
The types are the same than the default channels. The user will be able to create
• simple channels: one app sends a message, other app receives the message.
• request-reply channels: a first app sends a message, a second app receives the message and
sends a response, the first app receives the response.
• asynchronous request-reply channels: Same as the previous channel but with asynchronous
behaviour.
Creating and using a new simple channel
After you have added the module dependency and injected ityou only need to call the
createChannel method and provide a name for the channel and a name for the related queue.

208

Devonfw Guide v2.4.0

import com.devonfw.module.integration.common.api.Integration;
public class MySenderApp{
@Inject
private Integration integration;
public void sendSimpleMessage(){
IntegrationChannel myChannel = this.integration.createChannel("my-channel", "myqueue");
Boolean sent = myChannel.send("Hello");
if (sent) System.out.println("message successfully sent");
}
}
Then, in the subscriber application, after adding the module dependency and the injection of it, we
only need to use the subscribeTo method and provide the name for the channel and the queue (that
must match the provided names in the first application) and the Subscription Handler to manage
the received messages. For more details about the Subscription Handler check out the subscriber
application example section.

import com.devonfw.module.integration.common.api.Integration;
public class MySubscriberApp{
@Inject
private Integration integration;
@Inject
private SubscriptionHandler subscriptionHandler;
this.integration.subscribeTo("my-channel", "my-queue", this.subscriptionHandler);
}
}
By default, the interval for polling the channel is 5000 (5 seconds) and can be changed through
property devonfw.integration.default.poller.rate in application.properties file. In addition, you
can define that value when creating the channel passing the milliseconds timeout as a parameter

this.integration.subscribeTo("my-channel", "my-queue", this.subscriptionHandler,
10000);

209

Devonfw Guide v2.4.0

Creating and using a new request-reply channel
In the app that is going to start the flow, after adding the module dependency and injected it, you
only need to call the createRequestReplyChannel method and provide a name for the channel, a
name for the related queue and, this part is slightly different from the rest of module
implementation, you need to provide a Response Handler that will manage the received response,
as we do with subscriber applications.

import com.devonfw.module.integration.common.api.Integration;
public class MyFirstApp{
@Inject
private Integration integration;
@Inject
private ResponseHandler responseHandler;
public void startCommunication(){
IntegrationChannel myChannel = this.integration.createRequestReplyChannel("mychannel", "my-queue", this.responseHandler);
Boolean sent = myChannel.send("Hello");
if (sent) System.out.println("message successfully sent");
}
}
The ResponseHandler provided in the example above is similar than explained previously in this
Integration module chapter (subscriber application example), except that it must be a void method.

public class ResponseHandlerimp implements ResponseHandler {
@Override
public void handleMessage(Message m) {
System.out.println("***********************************");
System.out.println("***********************************");
}
}
With this code we will create the channel/queue infrastructure, send the message and provide a
handler for the response. Now we need to define the second side of the flow to receive the message

210

Devonfw Guide v2.4.0

The

timeout

for

the

response

can

be

configured

through

property

devonfw.integration.default.receivetimeout in application.properties file, by default is set to 5000
(5 seconds).
You can also configure it when creating the channel passing the timeout as a parameter

IntegrationChannel myChannel = this.integration.createRequestReplyChannel("mychannel", "my-queue", this.responseHandler, 10000);
In a second application, after adding the module dependency and the injection of it, we only need
to use the subscribeAndReplyTo method and provide the name for the channel and the queue (that
must match the names provided in the first app) and the Request Handler to manage the received
messages. The implementation is the same as the one described in the receiver application example
section. So our sample code will look like

import com.devonfw.module.integration.common.api.Integration;
public class MySecondApp{
@Inject
private Integration integration;
@Inject
private RequestHandler requestHandler;
public void startCommunication(){
requestHandler);
}
}

NOTE

Remember that if you run the first app before the subscriber app is running you
will probably get a timeout exception.

Creating and using a new asynchronous request-reply channel
To create that type of channels the implementation is exactly the same than in the previous section.
So in this section we are going to show only the code differences.
The first app will use the method createAsyncRequestReplyChannel to create the channel, the rest is
the same

IntegrationChannel demoAsyncChannel =

211

Devonfw Guide v2.4.0

You can define your own values for the ThreadPoolExecutor’s core pool size and response timeout

the

properties

devonfw.integration.default.poolsize

and

value.
However, you can also define those values when creating the channel

IntegrationChannel demoAsyncChannel =
In the second app you can subscribe to the channel with the method subscribeAndReplyAsyncTo
and providing the names for the channel and queue (that must match with the names provided in
the first application), and an Request Async Handler to manage the messages and provide a reply.

import com.devonfw.module.integration.common.api.Integration;
public class MySecondApp{
@Inject
private Integration integration;
@Inject
private RequestAsyncHandler requestAsyncHandler;
public void startCommunication(){
this.RequestAsyncHandler);
}
}
The implementation for the Request Async Handler is explained here.

42.5.6. Sending headers with the message
The Integration module also allows to send headers alongside the message content. To do so you can
use the methods provided by the module that accept a Map as parameter for headers.
You can create the message headers using a Java Map object

212

Devonfw Guide v2.4.0

Each send method provided with the module accepts a Map object as parameter for the headers, so
you can send it alongside the message content
• default simple channel: integration.send("Hello", headers)
• new created channels: new_channel.send("Hello", headers)

213

Devonfw Guide v2.4.0

Chapter 43. Microservices in Devonfw
43.1. Introduction
The Microservices architecture is an approach for application development based on a series of
small services grouped under a business domain. Each individual service runs autonomously and
communicating with each other through their APIs. That independence between the different
services allows to manage (upgrade, fix, deploy, etc.) each one without affecting the rest of the
system’s services. In addition to that the microservices architecture allows to scale specific services
when facing an increment of the requests, so the applications based on microservices are more
flexible and stable, and can be adapted quickly to demand changes.
However, this new approach, developing apps based on microservices, presents some downsides.
To address those, Devonfw microservices approach is based on Spring Cloud Netflix, that provides
all the main components for microservices integrated within Spring Boot context.
Let’s see the main challenges when working with microservices:
• Having the applications divided in different services we will need a component (router) to
redirect each request to the related microservice. These redirection rules must implement
filters to guarantee a proper functionality.
• In order to manage correctly the routing process, the application will also need a catalog with
all the microservices and its details: IPs and ports of each of the deployed instances of each
microservice, the state of each instance and some other related information. This catalog is
called Service Discovery.
• With all the information of the Service Discovery the application will need to calculate and select
between all the available instances of a microservice which is the suitable one. This will be
figured out by the library Client Side Load Balancer.
• The different microservices will be likely interconnected with each other, that means that in
case of failure of one of the microservices involved in a process, the application must
implement a mechanism to avoid the error propagation through the rest of the services and
provide an alternative as a process result. To solve this, the pattern Circuit Breaker can be
implemented in the calls between microservices.
• As we have mentioned, the microservices will exchange calls and information with each other
so our applications will need to provide a secured context to avoid not allowed operations or
intrusions. In addition, since microservices must be able to operate in an isolated way, it is not
recommended to maintain a session. To meet this need without using Spring sessions, a tokenbased authentication is used that exchanges information using the json web token (JWT)
protocol.
In addition to all of this we will find other issues related to this particular architecture that we will
address fitting the requirements of each project.
• Distributed data bases: each instance of a microservice should have only one data base.
• Centralized logs: each instance of a microservice creates a log and a trace that should be
centralized to allow an easier way to read all that information.

214

Devonfw Guide v2.4.0

• Centralized configuration: each microservice has its own configuration, so our applications
should group all those configurations in only one place to ease the configuration management.
• Automatized deployments: as we are managing several components (microservices, catalogs,
balancers, etc.) the deployment should be automatized to avoid errors and ease this process.

43.2. Microservices schema
In the following schema we can see an overview of the structure of components in a Devon
application based on the Spring Cloud Netflix solution for microservices.

Let’s explain each component

43.2.1. Service Discovery - Eureka
Eureka is a server to register and locate the microservices. The main function for Eureka is to
register the different instances of the microservices, its location, its state and other metadata.
It works in a simple way, during the start of each microservice, this communicates with the Eureka
server to notify its availability and to send the metadata. The microservice will continue to notify
its status to the Eureka server every 30 seconds (default time on Eureka server properties). This
value can be changed in the configuration of the component.
If after 3 periods, Eureka does not receive notification of any of the microservices, it will be
considered as unavailable and will eliminate its registration.
In addition, it serves as a catalog to locate a specific microservice when routing a request to it.

43.2.2. Circuit Breaker - Hystrix
Hystrix is a library that implements the Circuit Breaker pattern. Its main functionality is to improve
the reliability of the system, isolating the entry points of the microservices, preventing the
cascading failure from the lower levels of the application all the way up to the user.
In addition to that, it allows developers to provide a fallback in case of error. Hystrix manages the
requests to a service, and in case that the microservice doesn’t response, allows to implement an

215

Devonfw Guide v2.4.0

alternative to the request.

43.2.3. Client Side Load Balancer - Ribbon
Ribbon is a library designed as client side load balancer. Its main feature is to integrate with Eureka
to discover the instances of the microservices and their metadata. In that way the Ribbon is able to
calculate which of the available instances of a microservice is the most appropriate for the client,
when facing a request.

43.2.4. REST Client - Feign
Feign is a REST client to make calls to other microservices. The strength of Feign is that it integrates
seamlessly with Ribbon and Hystrix, and its implementation is through annotations, which greatly
facilitates this task to the developer.
Using annotations, Spring-cloud generates, automatically, a fully configured REST client.

43.2.5. Router and Filter - Zuul
Zuul is the entry point of the apps based on Spring-cloud microservices. It allows dynamic routing,
load balancing, monitoring and securing of requests. By default Zuul uses Ribbon to locate, through
Eureka, the instances of the microservice that it wants to invoke and sends the requests within a
Hystrix Command, taking advantage of its functionality.

43.3. How to create microservices in Devonfw?
Follow the instructions in the link below to set up Devonfw distribution
Next, install Devonfw modules and dependencies

43.3.1. Step 1: Open the console
Open the Devonfw console by executing the batch file console.bat from the Devonfw distribution. It
is a pre-configured console which automatically uses the software and configuration provided by
the Devonfw distribution.

43.3.2. Step 2: Change the directory
Run the following command in the console to change directory to Devonfw module

cd workspaces\examples\devon

43.3.3. Step 3: Install
To install modules and dependencies, you need to execute the following command:

216

Devonfw Guide v2.4.0

mvn --projects
bom,modules/microservices/microservices,modules/microservices/microservicearchetype,modules/microservices/microservice-infra-archetype --also-make install

NOTE

In case installation fails, try running the command again as it is often due to hitch
in the network.

Now, you can use the Microservices archetype given below to create Microservices.
In order to generate microservices in a Devonfw project we can choose between two approaches:
• generate a new OASP4J application and implement one by one all the needed components
(based on Spring Cloud).
• generate a new OASP4J application through the custom microservice archetype included in the
Devonfw distributions.
That second approach, using the Devonfw microservices archetype, will generate automatically all
the basic structure and components to start developing the microservices based application.

43.4. Devonfw archetypes
To simplify starting with projects based on microservices, Devonfw includes two archetypes to
generate pre-configured projects that include all the basic components of the Spring Cloud
implementation.
• archetypes-microservices-infra: generates a project with the needed infrastructure services to
manage microservices. Includes the Eureka service, Zuul service and the authentication service.
• archetypes-microservices: generates a simple project pre-configured to work as a
microservice.

43.5. Create New Microservices infrastructure
application
To generate a new microservices infrastructure application through the Devonfw archetype you
only need to open a Devonfw console (console.bat script) and follow the same steps described in
getting started creating new devonfw oasp4j application. But, instead of using the standard
archetype, we must provide the special infrastructure archetype archetype-microservice-infra.
Remember to provide your own values for DgroupId, DartifactId, Dversion and Dpackage
parameters, Also provide the -DarchetypeVersion with latest value:

mvn -DarchetypeVersion=2.4.0 -DarchetypeGroupId=com.devonfw.microservices
-DarchetypeArtifactId=microservices-infra-archetype archetype:generate -DgroupId
=com.capgemini -DartifactId=sampleinfra -Dversion=0.1-SNAPSHOT -Dpackage
=com.capgemini.sampleinfra

217

Devonfw Guide v2.4.0

Once the Maven command has finished an application with the following modules should be
created:

43.5.1. service-eureka module
This module contains the needed classes and configuration to start a Eureka server.
This service runs by default on port 8761 although ti can be changed in the application.properties
file of the project.

43.5.2. service-zuul module
This module contains all the needed classes and configuration to start a Zuul server, that will be in
charge of the routing and filter of the requests.
This service by default runs on port 8081 but, as we already mentioned, it can be changed through
the file application.properties of the project.

43.5.3. service-auth module
This module runs an authentication and authorization mock microservice that allows to generate a
security token to make calls to the rest of microservices. This module is only providing a basic
structure, the security measures must be implemented fitting the requirements of each project
(authentication through DB, SSO, LDAP, OAuth,…)
This service runs by default on port 9999, although, as in previous services, it can be edited in the
application.properties file.

43.6. Create New Microservices Application
To generate a new microservice project through the Devonfw archetype, as in previous archetype
example, you can follow the instructions explained in getting started creating new devonfw oasp4j
application. But, instead of using the standard archetype, we must provide the special
microservices archetype archetype-microservices. Open a Devonfw console (console.bat script) and
launch a Maven command like the following (provide your own values for DgroupId, DartifactId,
Dversion and Dpackage parameters, also provide the -DarchetypeVersion with latest value):

mvn -DarchetypeVersion=2.4.0 -DarchetypeGroupId=com.devonfw.microservices
-DarchetypeArtifactId=microservices-archetype archetype:generate -DgroupId
=com.capgemini -DartifactId=sampleapp1 -Dversion=0.1-SNAPSHOT -Dpackage
=com.capgemini.sampleapp1

218

Devonfw Guide v2.4.0

That command generates a simple application containing the source code for the microservice. By
default, the pom.xml includes the devon-microservices module, that contains the security
configuration, jwt interceptors, Hystrix, Ribbon and FeignClient configuration and some properties
common to all microservices.
The created microservice runs by default on port 9001 and has the context-path with the same
name than the project. This parameters can be changed through the 'application.properties' file of
the project.

43.7. How to use microservices in Devonfw
In the following sections we are going to provide some patterns to manage microservices in
Devonfw using the archetype, alongside the options that each of the available modules offer.

43.7.1. Eureka service
We are going to review the general options for the Eureka service. If you are interested in getting
more details you can visit the official site for Spring Cloud Eureka clients.
To create an Eureka server you only need to create a new Spring Boot application and add the
@EnableEurekaServer to the main class.
NOTE

The provided archetype archetype-microservices-infra already provides that
annotated class.

@Configuration
@EnableEurekaServer
@EnableAutoConfiguration
@SpringBootApplication
public class EurekaBootApp {
public static void main(String[] args) {
new SpringApplicationBuilder(EurekaBootApp.class).web(true).run(args);
}
}
The basic properties that must be configured for Eureka server are:
• port: in which port the service will run. The default port is the 8761 and you have to keep in
mind that the connection to this port is specially critical as all the microservices must be able to
connect to this host:port. Remember that Eureka generates and manages the microservices
catalog, so its crucial to allow the microservices to register in this component.
• url: which URL manages as area.

219

Devonfw Guide v2.4.0

eureka.instance.hostname=localhost
eureka.instance.port=8761
server.port=${eureka.instance.port} eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${eureka.insta nce.port}/eureka/ The way to connect a microservice to Eureka server is really simple. You only will need to specify the host:port where the server is located and annotate the Spring Boot class with @EnableMicroservices annotation. NOTE Instead of using that @EnableMicroservices annotation, you can use the equivalent Spring annotations @EnableDiscoveryClient or @EnableEurekaClient. @Configuration @EnableMicroservices @SpringBootApplication public class MicroserviceBootApp { public static void main(String[] args) { SpringApplication.run(MicroserviceBootApp.class, args); } } eureka.instance.hostname=localhost eureka.instance.port=8761 eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${eureka.insta nce.port}/eureka/ With this the application will register automatically in Eureka and will be validated each 30 seconds. This value can be changed editing the property eureka.instance.leaseRenewalIntervalInSeconds in application.properties file. It must be taken into account that each Eureka client will maintain a cache of Eureka records to avoid calling the service every time it is necessary to access another microservice. This cache is reloaded every 30 seconds, this value can also be edited through property eureka.client.registryFetchIntervalSeconds in application.properties file. 43.7.2. Zuul service We are going to show an overview to the options of the Zuul service, if you want to know more details about this particular service visit the official site of Spring Cloud. Zuul is the component in charge for router and filtering the requests to the microservices system. It works as a gateway that, through a rule engine, redirects the requests to the suitable microservice. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 220 Devonfw Guide v2.4.0 In addition, it can be used as a security filter as it can implement PRE-Filters and POST-Filters. To create a basic Zuul server you only need to create a new Spring Boot application and add the @EnableZuulProxy annotation. @EnableAutoConfiguration @EnableEurekaClient @EnableZuulProxy @SpringBootApplication public class ZuulBootApp { public static void main(String[] args) { SpringApplication.run(ZuulBootApp.class, args); } } To allow Zuul to redirect the requests we need to connect Zuul with the previously created Eureka service, to allow him to register and access to the catalog of microservices created by Eureka. Also, if we are going to use the Zuul service from a web browser, we must configure the CORS filter to allow connections from any source. This is really easy to implement by adding the following Java Bean to our ZuulBootApp class: @Bean public CorsFilter corsFilter() { final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); final CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.addAllowedOrigin("*"); config.addAllowedHeader("*"); config.addAllowedMethod("OPTIONS"); config.addAllowedMethod("HEAD"); config.addAllowedMethod("GET"); config.addAllowedMethod("PUT"); config.addAllowedMethod("POST"); config.addAllowedMethod("DELETE"); config.addAllowedMethod("PATCH"); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } To configure the Zuul service we need to define a series of properties that we will describe below: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 221 Devonfw Guide v2.4.0 server.port=8081 spring.application.name=zuulserver eureka.instance.hostname=localhost eureka.instance.port=8761 eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${eureka.insta nce.port}/eureka/ microservices.context-path=/demo zuul.routes.security.path=${microservices.context-path}/services/rest/security/**
zuul.routes.security.serviceId=AUTH
zuul.routes.security.stripPrefix=false
zuul.routes.login.path=${microservices.context-path}/services/rest/login zuul.routes.login.serviceId=AUTH zuul.routes.login.stripPrefix=false zuul.ignoredServices='*' zuul.sensitive-headers= ribbon.eureka.enabled=true hystrix.command.default.execution.timeout.enabled=false • server.port: Is the port where the Zuul service is listening. • spring.application.name: The name of the service the will be sent to Eureka. • eureka.*: The properties for the register of the Eureka client. • zuul.routes.XXXXX: The configuration of a concrete route. • zuul.routes.XXXXX.path: The path used for a redirection. • zuul.routes.XXXXX.serviceId: ID of the service where the request will be redirected. It must match the property spring.application.name in the microservice. • zuul.routes.XXXXX.stripPrefix: by default set to false. With this property we configure if the part of the route that has matched the request must be cutted out. i.e., if the path is /sample/services/rest/foomanagement/∗∗ and the property is set to true it will redirect to the microservice but it will only send the path **, the root /sample/services/rest/foomanagement/ will be removed. • zuul.ignoredServices: Configures which services without result in the routes, must be ignored. • zuul.sensitive-headers: Configures which headers must be ignored. This property must be set to empty, otherwise Zuul will ignore security authorization headers and the json web token will not work. • ribbon.eureka.enabled: Configures if the Ribbon should be used to route the requests. • hystrix.command.default.execution.timeout.enabled: Enables or disables the timeout parameter This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 222 Devonfw Guide v2.4.0 to consider a microservices as unavailable. By default the value for this property is 1 second. Any request that takes more than this will be consider failed. By default in the archetype this property is disabled. Having an Eureka client activated, the Zuul service will refresh its content every 30 seconds, so a just registered service may still have not been cached in Zuul. On the contrary, if a service is unavailable, 3 cycles of 30 seconds must pass before Eureka sets its register as dead, and other 30 seconds for Zuul to refresh its cache. 43.7.3. Security, Authentication and authorization The most commonly used authentication in micro-service environments is authentication based on json web tokens, since the server does not need to store any type of user information (stateless) and therefore favors the scalability of the microservices. The service-auth module is useful only if the authentication and authorization needs to be done by a remote service (e.g. to have a common IMPORTANT auth. service to be used by several microservices). Otherwise, the autentication and authorization can happen in the main application, that will perform the authentication and will generate the JWT. Security in the monolith application In this case, the main microservice or application will perform the authentication and generate the JWT, without using service-auth. It works as follows: • The user is authenticated in our application, either through a user / password access, or through a third provider. • This authentication request is launched against the Zuul server which will redirect it to an instance of the microservice. • The microservice will check the user, retrieve their roles and metadata and generate two tokens: one with user access information and another needed to refresh the access token. This information will be returned to the client. • The client is now able to call the microservice, adding the authorization token to the header of the request. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 223 Devonfw Guide v2.4.0 Security in external service (service-auth) It works as follows: • The user is authenticated in our application, either through a user / password access, or through a third provider. • This authentication request is launched against the Zuul server which will redirect it to an instance of the Auth microservice. • The Auth microservice will check the user, retrieve their roles and metadata and generate two tokens: one with user access information and another needed to refresh the access token. This information will be returned to the client. The service-auth service is already prepared to listen to the /login path and generate the two mentioned tokens. To do so we can use the JsonWebTokenUtility class that is implemented in Devonfw UserDetailsJsonWebTokenAbstract clientTo = new UserDetailsJsonWebTokenTo(); clientTo.setId(1L); clientTo.setUsername("demo"); clientTo.setRoles(new ArrayList<>(Arrays.asList("DEMO"))); clientTo.setExpirationDate(buildExpirationDate(this.expirationTime * 60 * 1000L)); return new ResponseEntity<>(new JwtHeaderTo(this.jsonWebTokenUtility .createJsonWebTokenAccess(clientTo), this.jsonWebTokenUtility.createJsonWebTokenRefresh(clientTo), this.expirationTime * 60 * 1000L), HttpStatus.OK); This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 224 Devonfw Guide v2.4.0 In our example you can make a POST request to: NOTE http://localhost:8081/service-auth/services/rest/login HEADER BODY Content-Type : application/json { "j_username" : "xxx", "j_password" : "xxx"} This will generate a response like the following { "accessToken": "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJkZW1vIiwiZmlyc3ROYW1lIjoiZGVtbyIsImxhc3ROYW1lIjoiZGVt byIsImV4cCI6MTQ4Nzg3NTAyMSwicm9sZXMiOlsiREVNTyJdfQ.aEdJWEpyvRlO8nF_rpSMSM7NXjRIyeJF425 HRt8imCTsq4iGiWbmi1FFZ6pydMwKjd-Uw1-ZGf2WF58qjWc4xg", "refreshToken": "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJkZW1vIiwiZmlyc3ROYW1lIjoiZGVtbyIsImxhc3ROYW1lIjoiZGVt byIsImV4cCI6MTQ4Nzg3NTAyMSwicm9sZXMiOlsiUkVGUkVTSF9KV1QiXX0.YtK8Bh07Oh1GTsyTK36YHxkGniyiTlxnazZXi8tT-RtUxxW8We8cdiYJn6tw0RoFkOyr1F5EzvkGyU0HNoLyw", "expirationTime": 900000, "accessHeaderName": "Authorization", "refreshHeaderName": "Authorization-Refresh" } The client now should store, in the header defined in accessHeaderName, the token included as accessToken. By default, when using devon4sencha, this functionality is already implemented. When using service-auth (or any other external authorization service), we must secure not only the communication between the Client and Zuul, but IMPORTANT also between Zuul and the service-auth. There is very sensitive information being sent (username and password) between the different services that anyone could read if the channel is not properly secured. When configuring the service-auth module is very important to have into account the following aspects: • The expiration date of the token can be configured in the properties file with the property jwt.expirationTime (will appear in minutes). • The key for the token generation can be configured also in the properties file using the property jwt.encodedKey which will have a Base64 encoded value. • The roles inserted in the token should be the list of the access roles of the user. Doing this we avoid that each microservice has to look for the roles that belong to a profile. • If you want to use a specific UserDetails for the project, with new fields, you must extend the behavior as explained in here. From now on, the client will be able to make calls to the microservices, sending the access token in the header of the request. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 225 Devonfw Guide v2.4.0 Once the request reaches the microservice, the app must validate the token and register the user in the security context. These operations will be automatic as long as the microservice has enabled the security inherited from the JsonWebTokenSecurityConfig class. This is done using the following code: @Configuration @EnableWebSecurity public class WebSecurityConfig extends JsonWebTokenSecurityConfig { @Override public JsonWebTokenUtility getJsonWebTokenUtility() { return new JsonWebTokenUtility(); } @Override protected void setupAuthorization(HttpSecurity http) throws Exception { http.authorizeRequests() // authenticate all other requests .anyRequest().authenticated(); } } In addition, Devonfw has already implemented the needed interceptors and filters to resend the security header each time that a microservice calls other microservice of the ecosystem. When validating the token, it is also checked its expiration date, so it is highly recommended that the client refresh from time to time the token, in order to update its expiration date. This is done by launching a request to /refresh_jwt within the service-auth module and sending both the access token and the refresh token in the header. When using devon4sencha that requests are automated by the framework. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 226 Devonfw Guide v2.4.0 If for any reason an attempt is made to access a business operation without having a valid token, or without sufficient role level permission to execute that operation, the microservice response will be Forbidden. 43.8. How to modify the UserDetails information In order to modify the UserDetails information we will need to accomplish two steps: modify the authentication service to generate the authentication token with the custom attributes embedded, and modify the pre-authentication filter of the microservices to convert the token into an Object with the custom attributes available. 43.8.1. Modify the authentication service to generate a new token We must modify the service-auth that is in charge of logging the user and generate the security token. The first thing to do is to create a UserDetails class that contains the required attributes and custom attributes. In the code sample we will call this class UserDetailsJsonWebTokenCustomTo, and must either implement the generic UserDetailsJsonWebTokenAbstract interface or extend it from the current UserDetailsJsonWebTokenTo class, since the services are prepared to work with it. In the example, we will add two new attributes firstName and lastName. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 227 Devonfw Guide v2.4.0 public class UserDetailsJsonWebTokenCustomTo extends UserDetailsJsonWebTokenTo { private String firstName; private String lastName; public String getFirstName() { return this.firstName; } public String getLastName() { return this.lastName; } public void setFirstName(String firstName) { this.firstName = firstName; } public void setLastName(String lastName) { this.lastName = lastName; } } In case that the UserDetailsJsonWebTokenAbstract interface is implemented, in addition to the new attributes the rest of the interface must be implemented. The next step would be to override the component that performs the conversions Token→UserDetails and UserDetails→Token. This component is the JsonWebTokenUtility, so you should create a new class that extends from this, in the example we will call it JsonWebTokenUtilityCustom. In this new class, you must overwrite the only two methods that are allowed to perform the conversions, to add writing and reading operations for the new custom attributes. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 228 Devonfw Guide v2.4.0 public class JsonWebTokenUtilityCustom extends JsonWebTokenUtility { @Override protected UserDetailsJsonWebTokenAbstract addCustomPropertiesClaimsToUserDetails (Claims claims) { UserDetailsJsonWebTokenCustomTo userDetails = new UserDetailsJsonWebTokenCustomTo(); userDetails.setFirstName(claims.get("firstName", String.class)); userDetails.setLastName(claims.get("lastName", String.class)); return userDetails; } @Override protected void addCustomPropertiesUserDetailsToJwt(UserDetailsJsonWebTokenAbstract authTokenDetailsDTO, JwtBuilder jBuilder) { UserDetailsJsonWebTokenCustomTo userDetails = (UserDetailsJsonWebTokenCustomTo) authTokenDetailsDTO; jBuilder.claim("firtName", userDetails.getFirstName()); jBuilder.claim("lastName", userDetails.getLastName()); } } Now you should enable that new converter to replace the default one. In the WebSecurityConfig class you must change the related @Bean to start using this new class @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { ... @Bean public JsonWebTokenUtility getJsonWebTokenUtility() { return new JsonWebTokenUtilityCustom(); } ... } Finally, in the login process the new attributes should be filled in when creating the user. In our example in the class SecuritymanagementRestServiceImpl. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 229 Devonfw Guide v2.4.0 UserDetailsJsonWebTokenCustomTo clientTo = new UserDetailsJsonWebTokenCustomTo(); clientTo.setId(1L); clientTo.setUsername("demo"); clientTo.setRoles(new ArrayList<>(Arrays.asList("DEMO"))); clientTo.setExpirationDate(buildExpirationDate(this.expirationTime * 60 * 1000L)); clientTo.setFirstName("firstName"); clientTo.setLastName("lastName"); return new ResponseEntity<>(new JwtHeaderTo(this.jsonWebTokenUtility .createJsonWebTokenAccess(clientTo), this.jsonWebTokenUtility.createJsonWebTokenRefresh(clientTo), // this.expirationTime * 60 * 1000L), HttpStatus.OK); 43.8.2. Modify the pre-authentication filter to read the new token Once a token with custom attributes has been obtained, the steps to read it and put it in the security context are very simple. The changes shown in this point should be reproduced in those microservices where you want to use the new custom attributes. The steps to follow are those: • Create a UserDetailsJsonWebTokenCustomTo class that contains the new attributes, as was done in the previous section. The ideal would be to reuse the same class. • Create a JsonWebTokenUtilityCustom class that extends the implementation of the token generator, just as it was done in the previous section. Again, the ideal would be to reuse the same class. • Configure the creation of this new @Bean in the WebSecurityConfig class just like in the previous section. With these three steps you can use the new security object with the custom attributes. One way to use it could be as follows: UserDetailsJsonWebToken principal = (UserDetailsJsonWebToken) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UserDetailsJsonWebTokenCustomTo userDetails = (UserDetailsJsonWebTokenCustomTo) principal.getUserDetailsJsonWebTokenAbstract(); userDetails.getFirstName(); 43.9. How to start with a microservice Once the microservice has been created through its archetype, you need to have a series of points in mind to configure it correctly: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 230 Devonfw Guide v2.4.0 • The microservice must have the microservices starter in its pom.xml configuration to be able to use the interceptors and the generic configuration. com.devonfw.starter devonfw-microservices-starter${devonfw.version}

• It should be annotated in its initial class with @EnableMicroservices, this will activate the
annotations for Eureka client, CircuitBreaker and the client Feign. All of this is configured in the
properties file.
• This is a bootified application so in the pom.xml file you will have to define which one is the boot
class.
• You must consider the boot configuration: port and context-path. In development, each
microservice must have a different port, to avoid colliding with other microservices, while the
context-path is recommended to be the same, to simplify the Zuul configurations and calls
between microservices.
• You can use @RolesAllowed annotations in the services methods to secure them, as long as the
Web security inherited from JsonWebTokenSecurityConfig has been enabled, since it is the
responsible for putting the UserDetails generated from the token into the security context.
• All microservices must share the security key to encrypt and decrypt the token. And, specially, it
should be the same as the service-auth, which will be responsible for generating the initial
token.
• In the Zuul module, the routes must be well configured to be able to route certain URLs to the
new created microservices. So, if we have added a sampleapp1 with server.contextpath=/sampleapp1 we will need to map that service in the Zuul’s application.properties file

zuul.routes.sampleapp1.path=/sampleapp1/services/rest/**
zuul.routes.sampleapp1.serviceId=sampleapp1
zuul.routes.sampleapp1.stripPrefix=false
The rest will be treated as if it were a normal Web application, which exposes some services
through a REST API.

43.10. Calls between microservices
In order to invoke a microservice manually, you would need to implement the following steps:
• Obtain the instances of the microservice you want to invoke.
• Choose which of all instances is the most optimal for the client.
• Retrieve the security token from the source request.

231

Devonfw Guide v2.4.0

• Create a REST client that invokes the instance by passing the generated security token.
• Intercept the response in case it causes an error, to avoid a cascade propagation.
Thanks to the combination of Feign, Hystrix, Ribbon, Eureka and Devonfw it is possible to make a
call to another microservice in a declarative, very simple and almost automatic way.
You only need to create an interface with the methods that need to be invoked. This interface must
be annotated with @FeignClient and each of the methods created must have a path and a method in
the @RequestMapping annotation. An example interface might be as follows:

@FeignClient(value = "foo")
public interface FooClient {
@RequestMapping(method = RequestMethod.GET, value = "/${server.contextpath}/services/rest/foomanagement/v1/foo") FooMessageTo foo(); } It is important to highlight the following aspects: • The @FeignClient annotation comes along with the name of the microservice to be invoked. The correct and optimal would be to use the name of the microservice, but it is also possible to launch the request to the Zuul server. In the latter case it would be the server itself that would perform the load balancing and self-discovery of the most appropriate microservice, but have in mind that, doing this, the proxy server is also unnecessarily overloaded with unnecessary requests. • The @RequestMapping annotation must have the same method and path as expected on target, otherwise the request will be thrown and no response will be found. • The input and output parameters will be mapped to json, so they may not be exactly the same classes in both destination and source. It will depend on how you want to send and retrieve the information. Once the interface is created and annotated, in order to use the calls, it would be enough to inject the component into the object from which we want to use it and invoke any of its methods. Spring Cloud will automatically generate the required bean. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 232 Devonfw Guide v2.4.0 ... @Inject FooClient fooClient; public FooMessageTo ivokeFooClient() { return this.fooClient.foo(); } ... With these two annotations, almost all the functionality is covered automatically: search in Eureka, choice of the best instance through Ribbon, registration of the token and creation of the REST client. Only would be necessary to control the response in case of failure. The idea is to allow, in case of failure or fall of the invoked microservice, from the origin of the invocation is executed an alternative plan. This is as simple as activating the fallback in the @FeignClient annotation and assigning a class that will be invoked in case the REST client response fails. @FeignClient(value = "foo", fallback = FooClientHystrixFallback.class) public interface FooClient { @RequestMapping(method = RequestMethod.GET, value = "/${server.contextpath}/services/rest/foomanagement/v1/foo")
FooMessageTo foo();
}
Finally, you will need to create a class annotated with @Component that implements the interface of
the Feign client. Within this implementation you can add the desired functionality in case the
invocation to the REST client fails.

@Component
public class FooClientHystrixFallback implements FooClient {
@Override
public FooMessageTo foo() {
return new FooMessageTo("Fail Message");
}
}

233

Devonfw Guide v2.4.0

Chapter 44. Compose for Redis module
44.1. Introduction
Redis is an open-source, blazingly fast, key/value low maintenance store. Compose's platform gives
you a configuration pre-tuned for high availability and locked down with additional security
features. The component will manage the service connection and the main methods to manage the
key/values on the storage.

44.2. Include Compose for Redis in a Devon project
The module provides you a connection to a compose for redis service, and a bunch of predefined
operations for managing the objects inside the service, for your Devon applications. To implement
the module in a Devon project, you must follow these steps:

Include the starter in your pom.xml, verifying that the version matches the last available version of
the module

com.devonfw.starter
devonfw-compose-redis-starter
{devonfw.version} 44.2.2. Step 2: Properties configuration In order to use the module for managing the cache service, it is necessary to define some connection parameters in the application.properties file in the project. # Compose for Redis module params devon.redis.service.name = compose-for-redis devon.redis.uri = redis://admin:RFAAVWWDZSXXXXX@sl-eu-lon-2-portal.1.dblayer.com:16552 devon.redis.runTests.enable = false • The parameter devon.redis.service.name is used to lookup the service in a cloud environment from the environment variable information VCAP_SERVICES. Default value compose-for-redis. • The parameter devon.redis.uri is used to indicate the service’s uri in case we need to connect to the service outside of a cloud environment. There is no default value. • The parameter devon.redis.runTests.enable is used for enabling the test execution. By default this execution is disabled. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 234 Devonfw Guide v2.4.0 • When the parameter devon.redis.service.name is indicated, and the application is running on a cloud environment, the parameter devon.redis.uri will be never used. • The paramenter devon.redis.uri is used when there is no value for the NOTE paramenter devon.redis.service.name or when the application is not running on a cloud environment. • In case the test execution is enabled (true) the service’s uri (devon.redis.uri) must be indicated because the test execution context will be outside a cloud environment. 44.3. Basic implementation 44.3.1. The injection of the module To access the module functionalities, you need to inject the module in a private property, it can be done using the @Inject annotation public class MyClass { @Inject LettuceManagement lettuceManagement; [...] } The lettuceManagement object will give us access to all the module functionalities described in the following section. 44.4. Available operations This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 235 Devonfw Guide v2.4.0 /** * Set a hash entry * @param hashName hash name * @param key key * @param value value * @return true if the entry has been set, false otherwise */ boolean setHashEntry(String hashName, String key, String value); /** * Get a hash entry * @param hashName hash name * @param key key * @return value */ String getHashEntry(String hashName, String key); /** * Get a hash map * @param hashName hash name * @return map */ Map getHash(String hashName); /** * Check if a Hash Entry exists * @param hashName hash name * @param key key * @return true if it exists, false otherwise */ Boolean hashEntryExists(String hashName, String key); /** * Set a hash map * @param hashName The name for the map * @param map The map object to be persisted on Redis * @return simple-string-reply */ String setHash(String hashName, Map map); /** * Delete hash map entries * @param hashName The name for the map * @param fields Field names to be deleted * @return True if all the given fields has been deleted, false otherwise */ Boolean deleteHashEntries(String hashName, String... fields); This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 236 Devonfw Guide v2.4.0 Chapter 45. Cookbook 45.1. Advanced Topics This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 237 Devonfw Guide v2.4.0 Chapter 46. Devon Security Layer Security is todays most important cross-cutting concern of an application and an enterprise ITlandscape. We seriously care about security and give you detailed guides to prevent pitfalls, vulnerabilities, and other disasters. While many mistakes can be avoided by following our guidelines you still have to consider security and think about it in your design and implementation. The security guides provided by this document will not automatically prevent you from any harm, but they may give you hints and best practices already used in different software products. 46.1. Authentication Definition: Authentication is the verification that somebody interacting with the system is the actual subject for whom he claims to be. The one authenticated is properly called subject or principal. However, for simplicity we use the common term user even though it may not be a human (e.g. in case of a service call from an external system). To prove his authenticity the user provides some secret called credentials. The most simple form of credentials is a password. Please never implement your own authentication mechanism or credential store. You have to be aware of implicit demands such as salting and hashing credentials, password life-cycle with recovery, expiry, and renewal including email notification NOTE confirmation tokens, central password policies, etc. This is the domain of access managers and identity management systems. In a business context you will typically already find a system for this purpose that you have to integrate (e.g. via LDAP). oasp4j uses Spring Security as a framework for authentication purposes. In-memory authentication for an user is configured using Spring security. Refer the overriden configureGlobal method of AbstractWebSecurityConfig.java of OASP4J for an implementation of In-memory authentication. @SuppressWarnings("javadoc") @Inject public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication().withUser("user").password("password").roles("USER"); } 46.1.1. Mechanisms This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 238 Devonfw Guide v2.4.0 Basic Http-Basic authentication can be easily implemented using BasicAuthenticationFilter: Form Login For a form login the spring security implementation might look like this: .formLogin().successHandler(new SimpleUrlAuthenticationSuccessHandler ()).defaultSuccessUrl("/") .failureUrl("/login.html?error").loginProcessingUrl( "/j_spring_security_login").usernameParameter("username") .passwordParameter("password").and() .logout().logoutSuccessUrl("/login.html") The interesting part is, that there is a login-processing-url, which should be adressed to handle the internal spring security authentication and similarly there is a logout-url, which has to be called to logout a user. 46.1.2. Preserve original request anchors after form login redirect Spring Security will automatically redirect any unauthorized access to the defined login-page. After sucuessful login, the user will be redirected to the original requested URL. The only pitfall is, that anchors in the request URL will not be transmitted to server and thus cannot be restored after successful login. Therefore the oasp4j-security module provides the RetainAnchorFilter, which is able to inject javascript code to the source page and to the target page of any redirection. Using javascript this filter is able to retrieve the requested anchors and store them into a cookie. Heading the target URL this cookie will be used to restore the original anchors again. To enable this mechanism you have to integrate the RetainAnchorFilter as follows: First, declare the filter with: • storeUrlPattern a regular expression matching the URL, where anchors should be stored • restoreUrlPattern a regular expression matching the URL, where anchors should be restored • cookieName the name of the cookie to save the anchors in the intermediate time This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 239 Devonfw Guide v2.4.0 @Bean public RetainAnchorFilter retainAnchorFilter() { RetainAnchorFilter retainAnchorFilter = new RetainAnchorFilter(); // first [^/]+ part describes host name and possibly port, second [^/]+ is the application name retainAnchorFilter.setStoreUrlPattern("http://[^/]+/[^/]+/login.*"); retainAnchorFilter.setRestoreUrlPattern("http://[^/]+/[^/]+/.*"); retainAnchorFilter.setCookieName("TARGETANCHOR"); return retainAnchorFilter; } Second, register the filter as first filter in the request filter chain. You might want to use the before="FIRST" or after="FIRST" attribute if you have multiple request filters, which should be run before the default filters. Listing 17. simple Spring Security filter insertion .addFilterAfter(FIRST, RetainAnchorFilter.class) Nevertheless, the oasp4j follows a different approach. The simple interface of Spring Security for inserting custom filters as stated above is driven by a relative alignment of the different filters been executed. You relatively can insert custom filters before or after existing ones and also at the beginning or at the end. You might easily see, that the real filter chain will get more and more invisible. Thus the oasp4j follows the default ordering of the Spring Security filter chain, such that it gets more transparent for any developer, which filters will be executed in which order and at which position a new custom filter may be inserted. This documentation depends on Spring Security v4.2.3.RELEASE: • http://docs.spring.io/spring-security/site/docs/4.2.3.BUILD-SNAPSHOT/reference/htmlsingle/#nscustom-filters These lists will be maintained each release, which will include a Spring Security upgrade. Thus first, we will not loose any changes from the possibly updated default filter chain of Spring Security. Second, due to the absolute declaration of the filter order, you might not get any strange behavior in your system after upgrading to a new version of Spring Security. 46.1.3. Users vs. Systems If we are talking about authentication we have to distinguish two forms of principals: • human users • autonomous systems While e.g. a Kerberos/SPNEGO Single-Sign-On makes sense for human users it is pointless for authenticating autonomous systems. So always keep this in mind when you design your This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 240 Devonfw Guide v2.4.0 authentication mechanisms and separate access for human users from access for systems. 46.2. Authorization Definition: Authorization is the verification that an authenticated user is allowed to perform the operation he intends to invoke. 46.2.1. Clarification of terms For clarification we also want to give a common understanding of related terms that have no unique definition and consistent usage in the wild. Table 4. Security terms related to authorization Term Meaning and comment Permission A permission is an object that allows a principal to perform an operation in the system. This permission can be granted (give) or revoked (taken away). Sometimes people also use the term right what is actually wrong as a right (such as the right to be free) can not be revoked. Group We use the term group in this context for an object that contains permissions. A group may also contain other groups. Then the group represents the set of all recursively contained permissions. Role We consider a role as a specific form of group that also contains permissions. A role identifies a specific function of a principal. A user can act in a role. For simple scenarios a principal has a single role associated. In more complex situations a principal can have multiple roles but has only one active role at a time that he can choose out of his assigned roles. For KISS it is sometimes sufficient to avoid this by creating multiple accounts for the few users with multiple roles. Otherwise at least avoid switching roles at runtime in clients as this may cause problems with related states. Simply restart the client with the new role as parameter in case the user wants to switch his role. Access Control Any permission, group, role, etc., which declares a control for access management. 46.3. Suggestions on the access model The access model provided by oasp4j-security follows this suggestions: • Each Access Control (permission, group, role, …) is uniquely identified by a human readable string. • We create a unique permission for each use-case. • We define groups that combine permissions to typical and useful sets for the users. • We define roles as specific groups as required by our business demands. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 241 Devonfw Guide v2.4.0 • We allow to associate users with a list of Access Controls. • For authorization of an implemented use case we determine the required permission. Furthermore, we determine the current user and verify that the required permission is contained in the tree spanned by all his associated Access Controls. If the user does not have the permission we throw a security exception and thus abort the operation and transaction. • We try to avoid negative permissions, that is a user has no permission by default but only those granted to him additively permit him for executing use cases. • Technically we consider permissions as a secret of the application. Administrators shall not fiddle with individual permissions but grant them via groups. So the access management provides a list of strings identifying the Access Controls of a user. The individual application itself contains these Access Controls in a structured way, whereas each group forms a permission tree. 46.3.1. oasp4j-security The OASP4J applications provide a ready to use module oasp4j-security that is based on springsecurity and makes your life a lot easier. Figure 2. OASP4J Security Model The diagram shows the model of oasp4j-security that separates two different aspects: • The Indentity- and Access-Management is provided by according products and typically already available in the enterprise landscape (e.g. an active directory). It provides a hierarchy of primary access control objects (roles and groups) of a user. An administrator can grant and revoke permissions (indirectly) via this way. • The application security is using oasp4j-security and defines a hierarchy of secondary access control objects (groups and permissions) in the file access-control-schema.xml (see example from sample app). This hierarchy defines the application internal access control schema that should be an implementation secret of the application. Only the top-level access control objects are public and define the interface to map from the primary to secondary access control objects. This mapping is simply done by using the same names for access control objects to match. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 242 Devonfw Guide v2.4.0 Access Control Schema The oasp4j-security module provides a simple and efficient way to define permissions and roles. The file access-control-schema.xml is used to define the mapping from groups to permissions. The general terms discussed above can be mapped to the implementation as follows: Table 5. General security terms related to oasp4j access control schema Term oasp4j-security implementation Comment Permission AccessControlPermission Group AccessControlGroup When considering different levels of groups of different meanings, declare type attribute, e.g. as "group". Role AccessControlGroup With type="role". Access Control AccessControl Super type that represents a tree of AccessControlGroups and AccessControlPermissions. If a principal "has" a AccessControl he also "has" all AccessControls with according permissions in the spanned sub-tree. Listing 18. Example access-control-schema.xml Barkeeper ... This example access-control-schema.xml declares • a group named ReadMasterData, OfferManagement_GetOffer which grants four different permissions, e.g., • a group named Waiter, which This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 243 Devonfw Guide v2.4.0 ◦ also grants all permissions from the group Barkeeper ◦ in addition grants the permission TableManagement_ChangeTable ◦ is marked to be a role for further application needs. The oasp4j-security module automatically validates the schema configuration and will throw an exception if invalid. The permissions and roles defined in access-control-schema.xml are loaded using implementation of AccessControlSchemaProvider interface as show below. InputStream inputStream = this.accessControlSchema.getInputStream()) { AccessControlSchema schema = this.accessControlSchemaMapper.read(inputStream); Group permissions are collected that principal is part of and is used to decide access to application resources as shown below Set authorities = new HashSet<>(); Collection accessControlIds = this.principalAccessControlProvider .getAccessControlIds(principal); Set accessControlSet = new HashSet<>(); for (String id : accessControlIds) { boolean success = this.accessControlProvider.collectAccessControls(id, accessControlSet); if (!success) { LOG.warn("Undefined access control {}.", id); } } for (AccessControl accessControl : accessControlSet) { authorities.add(new AccessControlGrantedAuthority(accessControl)); } Configuration on URL level The authorization (in terms of Spring security "access management") can be enabled seperately for different url patterns, the request will be matched against. The order of these url patterns is essential as the first matching pattern will declare the access restriction for the incoming request (see access attribute). Here an example: Listing 19. Extensive example of authorization on URL level .authorizeRequests() .antMatchers("/login", "/home").permitAll() .antMatchers("/admin/**").hasRole("ADMIN") .anyRequest().authenticated() This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 244 Devonfw Guide v2.4.0 Configuration on Java Method level As state of the art oasp4j will focus on role-based authorization to cope with authorization for executing use case of an application. We will use the JSR250 annotations, mainly @RolesAllowed, for authorizing method calls against the permissions defined in the annotation body. This has to be done for each use-case method in logic layer. Here is an example: @RolesAllowed(PermissionConstants.FIND_TABLE) public TableEto findTable(Long id) { return getBeanMapper().map(getTableDao().findOne(id), TableEto.class); } Now this method findTable can only be called if the user that is logged-in has the permission FIND_TABLE. More in detail, imagine that you have two types of users in your app: customers and admins. So you want to allow both of them to see your products but only admins can create new ones. In the Devonfw based apps ( JSR250 annotations) the way you should proceed to achieve that would be 1 - Define the roles in the access-control-schema.xml file (usually located on /src/main/resources/config/app/security) ... Customer ... As you can see we have created two roles Customer and Admin. The Customer can SeeProducts and the Admin inherits permissions from Customer, so he can also SeeProducts, and in addition to that we have defined an new permission CreateProduct to allow only him to create new products. 2 - Is recommended, although optional, to use an intermediate class to manage the permission terminology to avoid errors. So we could create a class PermissionConstants and store there the This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 245 Devonfw Guide v2.4.0 permission names. You can use Cobigen to easily generate this class. public abstract class PermissionConstants { public static final String SEE_PRODUCTS = "SeeProducts"; public static final String CREATE_PRODUCT = "CreateProduct"; ... } 3 - Finally, in our theoretical Productmanagement use case, at implementation level ( src/main/java/my/devonfw/app/productmanagement/logic/impl/ProductmanagementImpl.java ) , we can define the permissions for each method using the @RolesAllowed annotation public class ProductmanagementImpl extends AbstractComponentFacade implements Productmanagement { @Override @RolesAllowed(PermissionConstants.SEE_PRODUCTS) public ProductEto findProduct(Long id) { ProductEntity product = getProductDao().load(id); return getBeanMapper().map(product, ProductEto.class); } @Override @RolesAllowed(PermissionConstants.CREATE_PRODUCT) public ProductEto saveProduct(ProductEto product) { Objects.requireNonNull(product, "product"); ProductEntity persistedProduct = getProductDao().save(getBeanMapper().map(product, ProductEntity.class)); return getBeanMapper().map(persistedProduct, ProductEto.class); } } At this point, with these three simple steps, the regular customers can see the products but not create new ones, while admins can do both operations. Check Data-based Permissions Currently, we have no best practices and reference implementations to apply permission based access on an application’s data. Nevertheless, this is a very important topic due to the high standards of data privacy & protection especially in germany. We will further investigate this topic This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 246 Devonfw Guide v2.4.0 and we will adress it in one of the next releases. For further tracking have a look at issue #125. 46.3.2. Spring Security APIs and their usage in OASP4j UsersDetailsService UserDetailsService is a core interface used to load user-specific data. It has loadUserByUsername() method to find a user entity and can be overridden to provide custom implementation. For further reading follow the Spring Security documentation here BaseUserDetailsService This is a custom implementation of Spring’s UserDetailsService. It overrides loadUserByUsername() method and returns UserDetails with user data and authorities. This implementation of UserDetailsService is further used in AbstractWebSecurityConfig. AbstractWebSecurityConfig This class extends Spring Security’s class WebSecurityConfigurerAdapter and overrides configure() method. Here BaseUserDetailsService is set in Spring HttpSecurity that configures web based security for http requests. 46.4. Vulnerabilities and Protection Independent from classical authentication and authorization mechanisms there are many common pitfalls that can lead to vulnerabilities and security issues in your application such as XSS, CSRF, SQL-injection, log-forging, etc. A good source of information about this is the OWASP. We address these common threats individually in security sections of our technological guides as a concrete solution to prevent an attack typically depends on the according technology. The following table illustrates common threats and contains links to the solutions and protection-mechanisms provided by the OASP: Table 6. Security threats and protection-mechanisms Thread Protection Link to details A1 Injection validate input, escape output, use proper frameworks data-access-layer guide A2 Broken Authentication and Session Management encrypt all channels, use a central identity management with strong password-policy Authentication A3 XSS prevent injection (see A1) for HTML, JavaScript and CSS and understand same-origin-policy client-layer A4 Insecure Direct Object References Using direct object references (IDs) only with appropriate authorization logic-layer A5 Security Misconfiguration Use OASP application template and guides to avoid application template This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 247 Devonfw Guide v2.4.0 Thread Protection Link to details A6 Sensitive Data Exposure Use secured exception facade, design your data model accordingly REST exception handling A7 Missing Function Level Access Control Ensure proper authorization for Method authorization all use-cases, use @DenyAll als default to enforce A8 CSRF secure mutable service operations with an explicit CSRF security token sent in HTTP header and verified on the server A9 Using Components with Known Vulnerabilities subscribe to security CVE newsletter newsletters, recheck products and their versions continuously, use OASP dependency management A10 Unvalidated Redirects and Forwards Avoid using redirects and forwards, in case you need them do a security audit on the solution. OASP proposes to use richclients (SPA/RIA). We only use redirects for login in a safe way. Log-Forging Escape newlines in log messages logging security service-layer security Tool for testing your web application against vulnerabilities: OWASP Zed Attack Proxy Project 1. Easy to Install 2. Supports Different types of Fuzzer Based Tests 3. Details Results Reports 4. Convenient to carry out Test on Staging environment This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 248 Devonfw Guide v2.4.0 Chapter 47. Data-Access Layer The data-access layer is responsible for all outgoing connections to access and process data. This is mainly about accessing data from a persistent data-store but also about invoking external services. 47.1. Persistence For mapping java objects to a relational database we use the Java Persistence API (JPA). As JPA implementation we recommend to use hibernate. For general documentation about JPA and hibernate follow the links above as we will not replicate the documentation. Here you will only find guidelines and examples about how we recommend to use it properly. The following examples show how to map the data of a database to an entity. 47.1.1. Entity Entities are part of the persistence layer and contain the actual data. They are POJOs (Plain Old Java Objects) on which the relational data of a database is mapped and vice versa. The mapping is configured via JPA annotations (javax.persistence). Usually an entity class corresponds to a table of a database and a property to a column of that table. A persistent entity instance then represents a row of the database table. A Simple Entity The following listing shows a simple example: @Entity @Table(name="TEXTMESSAGE") public class MessageEntity extends AbstractPersistenceEntity { private String text; public String getText() { return this.text; } public void setText(String text) { this.text = text; } } The @Entity annotation defines that instances of this class will be entities which can be stored in the database. The @Table annotation is optional and can be used to define the name of the corresponding table in the database. If it is not specified, the simple name of the entity class is used instead. In order to specify how to map the attributes to columns we annotate the corresponding getter methods (technically also private field annotation is also possible but approaches can not be This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 249 Devonfw Guide v2.4.0 mixed). The @Id annotation specifies that a property should be used as primary key. @Entity @Table(name="EMPLOYEE") public class Employee { private Long id; ... @Id @GeneratedValue(strategy = GenerationType.AUTO) public Long getId() { return this.id; } ... } With the help of the @Column annotation it is possible to define the name of the column that an attribute is mapped to as well as other aspects such as nullable or unique. If no column name is specified, the name of the property is used as default. @Column(name="DESCRIPTION", nullable=false, length=512) public String getDescription() { return description; } Note that every entity class needs a constructor with public or protected visibility that does not have any arguments. Moreover, neither the class nor its getters and setters may be final. Entities should be simple POJOs and not contain business logic. Entities and Datatypes Standard datatypes like Integer, BigDecimal, String, etc. are mapped automatically by JPA. Custom datatypes are mapped as serialized BLOB by default what is typically undesired. In order to map atomic custom datatypes (implementations of SimpleDatatype) we implement an AttributeConverter. Here is a simple example: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 250 Devonfw Guide v2.4.0 @Converter(autoApply = true) public class MoneyAttributeConverter implements AttributeConverter { public BigDecimal convertToDatabaseColumn(Money attribute) { return attribute.getValue(); } public Money convertToEntityAttribute(BigDecimal dbData) { return new Money(dbData); } } The annotation @Converter is detected by the JPA vendor if the annotated class is in the packages to scan (see beans-jpa.xml). Further, autoApply = true implies that the converter is automatically used for all properties of the handled datatype. Therefore all entities with properties of that datatype will automatically be mapped properly (in our example Money is mapped as BigDecimal). In case you have a composite datatype that you need to map to multiple columns the JPA does not offer a real solution. As a workaround you can use a bean instead of a real datatype and declare it as @Embeddable. If you are using hibernate you can implement CompositeUserType. Via the @TypeDef annotation it can be registered to hibernate. If you want to annotate the CompositeUserType implementation itself you also need another annotation (e.g. MappedSuperclass although not technically correct) so it is found by the scan. Enumerations By default JPA maps Enums via their ordinal. Therefore the database will only contain the ordinals (0, 1, 2, etc.) so inside the database you can not easily understand their meaning. Using @Enumerated with EnumType.STRING allows to map the enum values to their name (Enum.name()). Both approaches are fragile when it comes to code changes and refactorings (if you change the order of the enum values or rename them) after being in production with your application. If you want to avoid this and get a robust mapping you can define a dedicated string in each enum value for database representation that you keep untouched. Then you treat the enum just like any other custom datatype. BLOB If binary or character large objects (BLOB/CLOB) should be used to store the value of an attribute, e.g. to store an icon, the @Lob annotation should be used as shown in the following listing: @Lob public byte[] getIcon() { return this.icon; } This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 251 Devonfw Guide v2.4.0 Using a byte array will cause problems if BLOBs get large because the entire WARNING BLOB is loaded into the RAM of the server and has to be processed by the garbage collector. For larger BLOBs the type Blob and streaming should be used. public Blob getAttachment() { return this.attachment; } Date and Time To store date and time related values, the @Temporal annotation can be used as shown in the listing below: @Temporal(TemporalType.TIMESTAMP) public java.util.Date getStart() { return start; } Until Java8 the java data type java.util.Date (or Jodatime) has to be used. TemporalType defines the granularity. In this case, a precision of nanoseconds is used. If this granularity is not wanted, TemporalType.DATE can be used instead, which only has a granularity of milliseconds. Mixing these two granularities can cause problems when comparing one value to another. This is why we only use TemporalType.TIMESTAMP. Primary Keys We only use simple Long values as primary keys (IDs). By default it is auto generated (@GeneratedValue(strategy=GenerationType.AUTO)). This is already provided by the class io.oasp.module.jpa.persistence.api.AbstractPersistenceEntity that you can extend. In case you have business oriented keys (often as String), you can define an additional property for it and declare it as unique (@Column(unique=true)). 47.1.2. Data Access Object Data Access Objects (DAOs) are part of the persistence layer. They are responsible for a specific entity and should be named Dao[Impl]. The DAO offers the so called CRUD-functionalities (create, retrieve, update, delete) for the corresponding entity. Additionally a DAO may offer advanced operations such as query or locking methods. DAO Interface For each DAO there is an interface named Dao that defines the API. For CRUD support and common naming we derive it from the interface io.oasp.module.jpa.persistence.api.Dao: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 252 Devonfw Guide v2.4.0 public interface MyEntityDao extends Dao { List findByCriteria(MyEntitySearchCriteria criteria); } As you can see, the interface Dao has a type parameter for the entity class. All CRUD operations are only inherited so you only have to declare the additional methods. DAO Implementation Implementing a DAO is quite simple. We crate a class named DaoImpl that extends io.oasp.module.jpa.persistence.base.AbstractDao and implements your Dao interface: public class MyEntityDaoImpl extends AbstractDao implements MyEntityDao { public List findByCriteria(MyEntitySearchCriteria criteria) { TypedQuery query = createQuery(criteria, getEntityManager()); return query.getResultList(); } ... } As you can see AbstractDao already implements the CRUD operations so you only have to implement the additional methods that you have declared in your Dao interface. In the DAO implementation you can use the method getEntityManager() to access the EntityManager from the JPA. You will need the EntityManager to create and execute queries. 47.1.3. Queries The Java Persistence API (JPA) defines its own query language, the java persistence query language (JPQL), which is similar to SQL but operates on entities and their attributes instead of tables and columns. Static Queries The OASP4J advises to specify all queries in one mapping file called orm.xml (located in src/main/resources/META-INF directory). You can add the new queries to this file: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 253 Devonfw Guide v2.4.0 ... ... To avoid redundant occurrences of the query name (get.staff.member.by.login) we define the constants for each named query: package io.oasp.gastronomy.restaurant.general.common.api.constants; public class NamedQueries { ... public static final String GET_STAFF_MEMBER_BY_LOGIN = "get.staff.member.by.login"; ... } Note that changing the name of the java constant (GET_STAFF_MEMBER_BY_LOGIN) can be easily done with refactoring (in Eclipse right click over the property and select Refactor > Rename). Further you can trace where the query is used by searching the references of the constant. The following listing shows how to use this query in class StaffMemberDaoImpl (remember that we must adapt StaffMemberDao). We will have a StaffMemberDao like the following: public interface StaffMemberDao extends ApplicationDao, MasterDataDao { StaffMemberEntity findByLogin(String login); ... } And the implementation of the interface would be: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 254 Devonfw Guide v2.4.0 public class StaffMemberDaoImpl extends ApplicationMasterDataDaoImpl< StaffMemberEntity> implements StaffMemberDao { ... @Override public StaffMemberEntity findByLogin(String login) { TypedQuery query = getEntityManager().createNamedQuery(NamedQueries.GET_STAFF_MEMBER_BY_LOGIN, StaffMemberEntity.class); query.setParameter("login", login); return query.getSingleResult(); } ... } The EntityManager contains a method called createNamedQuery(String), which takes as parameter the name of the query and creates a new query object. The parameters of the query have to be set using the setParameter(String, Object) method. Using the createQuery(String) method, which takes as parameter the query (a string NOTE that already contains the parameters), is not allowed as this makes the application vulnerable to SQL injection attacks. When the method getResultList() is invoked, the query is executed and the result is delivered as list. As an alternative, there is a method called getSingleResult(), which returns the entity if the query returned exactly one and throws an exception otherwise. Using Queries to Avoid Bidirectional Relationship With the usage of queries it is possible to avoid bidirectional relationships, which have some disadvantages (see relationships). So for example to get all WorkingTime's for a specific StaffMember without having an attribute in the StaffMember's class that stores these WorkingTime's, the following query is needed: The method looks as follows (extract of class WorkingTimeDaoImpl): This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 255 Devonfw Guide v2.4.0 public List getWorkingTimesForStaffMember(StaffMember staffMember) { Query query = getEntityManager().createNamedQuery(NamedQueries .WORKING_TIMES_SEARCH_BY_STAFFMEMBER); query.setParameter("staffMember", staffMember); return query.getResultList(); } Do not forget to adapt the WorkingTimeDao interface and the NamedQueries class accordingly. To get a more detailed description of how to create queries using JPQL, please have a look here or here. Dynamic Queries For dynamic queries we recommend to use QueryDSL. It allows to implement queries in a powerful but readable and type-safe way (unlike Criteria API). If you already know JPQL you will quickly be able to read and write QueryDSL code. It feels like JPQL but implemented in Java instead of plain text. Please be aware that code-generation can be painful especially with large teams. We therefore recommend to use QueryDSL without code-generation. Here is an example from our sample application: public List findOrders(OrderSearchCriteriaTo criteria) { OrderEntity order = Alias.alias(OrderEntity.class); EntityPathBase alias = Alias.(order);
JPAQuery query = new JPAQuery(getEntityManager()).from(alias);
Long tableId = criteria.getTableId();
if (tableId != null) {
query.where(Alias.$(order.getTableId()).eq(tableId)); } OrderState state = criteria.getState(); if (state != null) { query.where(Alias.$(order.getState()).eq(state));
}
applyCriteria(criteria, query);
return query.list(alias);
}

Using Wildcards
For flexible queries it is often required to allow wildcards (especially in dynamic queries). While
users intuitively expect glob syntax the SQL and JPQL standards work different. Therefore a
mapping is required (see here).

256

Devonfw Guide v2.4.0

Pagination
The OASP4J provides the method findPaginated in AbstractGenericDao that executes a given query
(for now only QueryDSL is supported) with pagination parameters based on SearchCriteriaTo. So all
you need to do is derive your individual search criteria objects from SearchCriteriaTo, prepare a
QueryDSL-query with the needed custom search criterias, and call findPaginated. Here is an
example from our sample application:

@Override
public PaginatedListTo findOrders(OrderSearchCriteriaTo criteria) {
OrderEntity order = Alias.alias(OrderEntity.class);
EntityPathBase alias = $(order); JPAQuery query = new JPAQuery(getEntityManager()).from(alias); Long tableId = criteria.getTableId(); if (tableId != null) { query.where($(order.getTableId()).eq(tableId));
}
OrderState state = criteria.getState();
if (state != null) {
query.where($(order.getState()).eq(state)); } return findPaginated(criteria, query, alias); } Then the query allows pagination (SearchCriteriaTo.getPagination().setSize(Integer)) to the by number setting of hits pagination.size per page and pagination.page (SearchCriteriaTo.getPagination().setPage(int)) to the desired page. If you allow the client to specify pagination.size, it is recommended to limit this value on the server side (SearchCriteriaTo.limitMaximumPageSize(int)) to prevent performance problems or DOS-attacks. If you need to also return the total number of hits available, you can set SearchCriteria.getPagination().setTotal(boolean) to true. Pagination example For the table entity we can make a search request by accessing the REST endpoint with pagination support like in the following examples: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 257 Devonfw Guide v2.4.0 POST oasp4j-sample-server/services/rest/tablemanagement/v1/table/search { "pagination": { "size":2, "total":true } } //Response { "pagination": { "size": 2, "page": 1, "total": 11 }, "result": [ { "id": 101, "modificationCounter": 1, "revision": null, "waiterId": null, "number": 1, "state": "OCCUPIED" }, { "id": 102, "modificationCounter": 1, "revision": null, "waiterId": null, "number": 2, "state": "FREE" } ] } NOTE as we are requesting with the total property set to true the server responds with the total count of rows for the query. For retrieving a concrete page, we provide the page attribute with the desired value. Here we also left out the total property so the server doesn’t incur on the effort to calculate it: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 258 Devonfw Guide v2.4.0 POST oasp4j-sample-server/services/rest/tablemanagement/v1/table/search { "pagination": { "size":2, "page":2 } } //Response { "pagination": { "size": 2, "page": 2, "total": null }, "result": [ { "id": 103, "modificationCounter": 1, "revision": null, "waiterId": null, "number": 3, "state": "FREE" }, { "id": 104, "modificationCounter": 1, "revision": null, "waiterId": null, "number": 4, "state": "FREE" } ] } Query Meta-Parameters Queries can have meta-parameters and the OASP4J currently provides support for timeout. OASP4J provides the method applyCriteria in AbstractGenericDao that applies meta-parameters to a query based on SearchCriteriaTo. If you already use the pagination support (see above), you do not need to call applyCriteria manually, as it is called internally by findPaginated. 47.1.4. Relationships n:1 and 1:1 Relationships Entities often do not exist independently but are in some relation to each other. For example, for every period of time one of the StaffMember’s of the restaurant example has worked, which is This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 259 Devonfw Guide v2.4.0 represented by the class WorkingTime, there is a relationship to this StaffMember. The following listing shows how this can be modeled using JPA: ... @Entity public class WorkingTime { ... private StaffMember staffMember; @ManyToOne @JoinColumn(name="STAFFMEMBER") public StaffMember getStaffMember() { return staffMember; } public void setStaffMember(StaffMember staffMember) { this.staffMember = staffMember; } } To represent the relationship, an attribute of the type of the corresponding entity class that is referenced has been introduced. The relationship is a n:1 relationship, because every WorkingTime belongs to exactly one StaffMember, but a StaffMember usually worked more often than once. This is why the @ManyToOne annotation is used here. For 1:1 relationships the @OneToOne annotation can be used which works basically the same way. To be able to save information about the relation in the database, an additional column in the corresponding table of WorkingTime is needed which contains the primary key of the referenced StaffMember. With the name element of the @JoinColumn annotation it is possible to specify the name of this column. 1:n and n:m Relationships The relationship of the example listed above is currently an unidirectional one, as there is a getter method for retrieving the StaffMember from the WorkingTime object, but not vice versa. To make it a bidirectional one, the following code has to be added to StaffMember: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 260 Devonfw Guide v2.4.0 private Set workingTimes; @OneToMany(mappedBy="staffMember") public Set getWorkingTimes() { return workingTimes; } public void setWorkingTimes(Set workingTimes) { this.workingTimes = workingTimes; } To make the relationship bidirectional, the tables in the database do not have to be changed. Instead the column that corresponds to the attribute staffMember in class WorkingTime is used, which is specified by the mappedBy element of the @OneToMany annotation. Hibernate will search for corresponding WorkingTime objects automatically when a StaffMember is loaded. The problem with bidirectional relationships is that if a WorkingTime object is added to the set or list workingTimes in StaffMember, this does not have any effect in the database unless the staffMember attribute of that WorkingTime object is set. That is why the OASP4J advises not to use bidirectional relationships but to use queries instead. How to do this is shown here. If a bidirectional relationship should be used nevertheless, appropiate add and remove methods must be used. For 1:n and n:m relations, the OASP4J demands that (unordered) Sets and no other collection types are used, as shown in the listing above. The only exception is whenever an ordering is really needed, (sorted) lists can be used. For example, if WorkingTime objects should be sorted by their start time, this could be done like this: private List workingTimes; @OneToMany(mappedBy = "staffMember") @OrderBy("startTime asc") public List getWorkingTimes() { return workingTimes; } public void setWorkingTimes(List workingTimes) { this.workingTimes = workingTimes; } The value of the @OrderBy annotation consists of an attribute name of the class followed by asc (ascending) or desc (descending). To store information about a n:m relationship, a separate table has to be used, as one column cannot store several values (at least if the database schema is in first normal form). In the example application, in the case of the Bill and the orderPositions the relation between them could be modeled as follows: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 261 Devonfw Guide v2.4.0 private List orderPositions; @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "Bill_OrderPosition", joinColumns = { @JoinColumn(name = "Bill_id") }) public List getOrderPositions() { return this.orderPositions; } public void setOrderPositions(List orderPositions) { this.orderPositions = orderPositions; } Information about the relation is stored in a table called BILL_ORDERPOSITION that has to have two columns, one for referencing the Bill (bill_id), the other one for referencing the Order (orderpositions_id). Note that the @JoinTable annotation is not needed in this case because a separate table is the default solution here (same for n:m relations) unless there is a mappedBy element specified. For 1:n relationships this solution has the disadvantage that more joins (in the database system) are needed to get a Bill with all the Order’s it refers to. This might have a negative impact on performance so that the solution to store a reference to the Bill row/entity in the Order’s table is probably the better solution in most cases. Note that bidirectional n:m relationships are not allowed for applications based on the OASP4J. Instead a third entity has to be introduced, which "represents" the relationship (it has two n:1 relationships). Eager vs. Lazy Loading Using JPA/Hibernate it is possible to use either lazy or eager loading. Eager loading means that for entities retrieved from the database, other entities that are referenced by these entities are also retrieved, whereas lazy loading means that this is only done when they are actually needed, i.e. when the corresponding getter method is invoked. Application based on the OASP4J must use lazy loading per default. Projects generated with the project generator are already configured so that this is actually the case. For some entities it might be beneficial if eager loading is used. For example if every time a Bill is processed, the Order entities it refers to are needed, eager loading can be used as shown in the following listing: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 262 Devonfw Guide v2.4.0 @OneToMany(fetch = FetchType.EAGER) @JoinTable public Set getOrders() { return orders; } This can be done with all four types of relationships (annotations: @OneToOne, @ManyToOne, @OneToMany, @ManyToOne). Cascading Relationships It is not only possible to specify what happens if an entity is loaded that has some relationship to other entities (see above), but also if an entity is for example persisted or deleted. By default, nothing is done in these situations. This can be changed by using the cascade element of the annotation that specifies the relation type (@OneToOne, @ManyToOne, @OneToMany, @ManyToOne). For example, if a StaffMember is persisted, all its WorkingTime's should be persisted and if the same applies for deletions (and some other situations, for example if an entity is reloaded from the database, which can be done using the refresh(Object) method of an EntityManager), this can be done as shown in the following listing: @OneToMany(mappedBy = "staffMember", cascade=CascadeType.ALL) public Set getWorkingTime() { return workingTime; } There are several CascadeTypes, e.g. to specify that a "cascading behavior" should only be used if an entity is persisted (CascadeType.PERSIST) or deleted (CascadeType.REMOVE), see here for more information. 47.1.5. Embeddable An embeddable Object is a way to implement relationships between entities, but with a mapping in which both entities are in the same database table. If these entities are often needed together, this is a good way to speed up database operations, as only one access to a table is needed to retrieve both entities. Suppose the restaurant example application has to be extended in a way that it is possible to store information about the addresses of StaffMember's, this can be done with a new Address class: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 263 Devonfw Guide v2.4.0 ... @Embeddable public class Address { private String street; private String number; private Integer zipCode; private String city; @Column(name="STREETNUMBER") public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } ... // other getter and setter methods, equals, hashCode } This class looks a bit like an entity class, apart from the fact that the @Embeddable annotation is used instead of the @Entity annotation and no primary key is needed here. In addition to that the methods equals(Object) and hashCode() need to be implemented as this is required by Hibernate (it is not required for entities because they can be unambiguously identified by their primary key). For some hints on how to implement the hashCode() method please have a look here. Using the address in the StaffMember entity class can be done as shown in the following listing: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 264 Devonfw Guide v2.4.0 ... @Entity @Table(name = "StaffMember") public class StaffMemberEntity implements StaffMember { ... private Address address; ... @Embedded public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } } The @Embedded annotation needs to be used for embedded attributes. Note that if in all columns in the StaffMember's table that belong to the Address embeddable the values are null, the Address will be null when retrieving the StaffMember entity from the database. This has to be considered when implementing the application core to avoid NullPointerException's. Moreover, if the database tables are created automatically by Hibernate and a primitive data type is used in the embeddable (in the example this would be the case if int is used instead of Integer as data type for the zipCode), there will be a not null constraint on the corresponding column (reason: a primitive data type can never be null in java, so hibernate always introduces a not null constraint). This constraint would be violated if one tries to insert a StaffMember without an Address object (this might be considered as a bug in Hibernate). Another way to realize the one table mapping are Hibernate UserType’s, as described here. 47.1.6. Inheritance Just like normal java classes, entity classes can inherit from others. The only difference is that you need to specify how to map a subtype hierarchy to database tables. The Java Persistence API (JPA) offers three ways to do this: • One table per hierarchy: This table contains all columns needed to store all types of entities in the hierarchy. If a column is not needed for an entity because of its type, there is a null value in this column. An additional column is introduced, which denotes the type of the entity (called "dtype" which is of type varchar and stores the class name). • One table per concrete class. For each concrete entity class there is a table in the database that can store such an entity with all its attributes. An entity is only saved in the table corresponding to its most concrete type. To get all entities of a type that has subtypes, joins are needed. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 265 Devonfw Guide v2.4.0 • One table per subclass: In this case there is a table for every entity class (this includes abstract classes), which contains all columns needed to store an entity of that class apart from those that are already included in the table of the supertype. Additionally there is a primary key column in every table. To get an entity of a class that is a subclass of another one, joins are needed. Every of the three approaches has its advantages and drawbacks, which are discussed in detail here. In most cases, the first one should be used, because it is usually the fastest way to do the mapping, as no joins are needed when retrieving entities and persisting a new entity or updating one only affects one table. Moreover it is rather simple and easy to understand. One major disadvantage is that the first approach could lead to a table with a lot of null values, which might have a negative impact on the database size. The following listings show how to create a class hierarchy among entity classes for the class FoodDrink and its subclass Drink: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 266 Devonfw Guide v2.4.0 ... @Entity @Inheritance(strategy=InheritanceType.SINGLE_TABLE) public abstract class FoodDrink { private long id; private String description; private byte[] picture; private long version; @Id @Column(name = "ID") @GeneratedValue(generator = "SEQ_GEN") @SequenceGenerator(name = "SEQ_GEN", sequenceName = "SEQ_FOODDRINK") public long getId() { return this.id; } public void setId(long id) { this.id = id; } ... } ... @Entity public class Drink extends FoodDrink { private boolean alcoholic; public boolean isAlcoholic() { return alcoholic; } public void setAlcoholic(boolean alcoholic) { this.alcoholic = alcoholic; } } To specify how to map the class hierarchy, the @Inheritance annotation is used. Its element strategy defines which type of mapping is used and can have the following values: • InheritanceType.SINGLE_TABLE (= one table per hierarchy). This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 267 Devonfw Guide v2.4.0 • InheritanceType.TABLE_PER_CLASS (= one table per concrete class). • InheritanceType.JOINED (= one table per subclass, joined tables). As a best practice we advise you to avoid deep class hierarchies among entity classes (unless they reduce complexity). 47.1.7. Concurrency Control The concurrency control defines the way concurrent access to the same data of a database is handled. When several users (or threads of application servers) are concurrently accessing a database, anomalies may happen, e.g. a transaction is able to see changes from another transaction although that one did not jet commit these changes. Most of these anomalies are automatically prevented by the database system, depending on the isolation level (property hibernate.connection.isolation in the jpa.xml, see here). A remaining anomaly is when two stakeholders concurrently access a record, do some changes and write them back to the database. The JPA addresses this with different locking strategies (see here or here). As a best practice we are using optimistic locking for regular end-user services (OLTP) and pessimistic locking for batches. Optimistic Locking The class io.oasp.module.jpa.persistence.api.AbstractPersistenceEntity already provides optimistic locking via a modificationCounter with the @Version annotation. Therefore JPA takes care of optimistic locking for you. When entities are transferred to clients, modified and sent back for update you need to ensure the modificationCounter is part of the game. If you follow our guides about transfer-objects and services this will also work out of the box. You only have to care about two things: • How to deal with optimistic locking in relationships? Assume an entity A contains a collection of B entities. Should there be a locking conflict if one user modifies an instance of A while another user in parallel modifies an instance of B that is contained in the other instance? To take influence besides placing collections take a look at GenericDao.forceIncrementModificationCounter. • What should happen in the UI if an OptimisticLockException occurred? According to KISS our recommendation is that the user gets an error displayed that tells him to do his change again on the recent data. Try to design your system and the work processing in a way to keep such conflicts rare. Pessimistic Locking For back-end services and especially for batches optimistic locking is not suitable. A human user shall not cause a large batch process to fail because he was editing the same entity. Therefore such use-cases use pessimistic locking what gives them a kind of priority over the human users. In your DAO implementation you can provide methods that do pessimistic locking via EntityManager operations that take a LockModeType. Here is a simple example: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 268 Devonfw Guide v2.4.0 getEntityManager().lock(entity, LockModeType.READ); When using the lock(Object, LockModeType) method with LockModeType.READ, Hibernate will issue a select … for update. This means that no one else can update the entity (see here for more information on the statement). If LockModeType.WRITE is specified, Hibernate issues a select … for update nowait instead, which has the same meaning as the statement above, but if there is already a lock, the program will not wait for this lock to be released. Instead, an exception is raised. Use one of the types if you want to modify the entity later on, for read only access no lock is required. As you might have noticed, the behavior of Hibernate deviates from what one would expect by looking at the LockModeType (especially LockModeType.READ should not cause a select … for update to be issued). The framework actually deviates from what is specified in the JPA for unknown reasons. 47.1.8. Database Auditing See auditing guide. 47.1.9. Testing Entities and DAOs See testing guide. 47.1.10. Summary of principles We strongly recommend these principles: • Use the JPA where ever possible and use vendor (Hibernate) specific features only for situations when JPA does not provide a solution. In the latter case consider first if you really need the feature. • Create your entities as simple POJOs and use JPA to annotate the getters in order to define the mapping. • Keep your entities simple and avoid putting advanced logic into entity methods. 47.2. Database Configuration The configuration for Spring and Hibernate is already provided by OASP4J in our sample application and the application template. So you only need to worry about a few things to customize. 47.2.1. Database System and Access For different Database Configuration we only need to give input to archetype at time of project creation which DB you need to configure i.e. if you are using command line tool for generating project you need to add dbtype (h2|postgresql|mysql|mariadb|oracle|hana|db2) This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 269 Devonfw Guide v2.4.0 mvn -DarchetypeVersion= -DarchetypeGroupId=io.oasp.java.templates -DarchetypeArtifactId=oasp4j-template-server archetype:generate -DgroupId = -DartifactId= -Dversion= -Dpackage= -DdbType= Dependencies Dependency for database in pom.xml file will be added automatically. For e.g if we are configuring mysql database in our application the below dependency will be there in your pom.xml file. MySQL: mysql mysql-connector-java Note: this driver should NOT be used in a production environment because of license issues. See down for an alternative. Database configuration Database configuration will be automatically generated in src/resources/config/application.properties_ file. Update the required values accordingly. MySQL: server.port=8081 server.context-path=/ spring.datasource.url=jdbc:mysql://address=(protocol=tcp)(host=localhost)(port=3306)/< db> spring.datasource.password= # Enable JSON pretty printing spring.jackson.serialization.INDENT_OUTPUT=true # Flyway for Database Setup and Migrations flyway.enabled=true flyway.clean-on-validation-error=true Database configuration will be automatically generated in src/resources/application.properties_ file. Update the required values accordingly. MySQL: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 270 Devonfw Guide v2.4.0 spring.application.name=myapplication server.context-path=/ security.expose.error.details=false security.cors.enabled=false spring.jpa.hibernate.ddl-auto=validate # Datasource for accessing the database spring.jpa.database=mysql spring.datasource.username=mysqluser spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming .ImplicitNamingStrategyJpaCompliantImpl spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming .PhysicalNamingStrategyStandardImpl spring.batch.job.enabled=false # Flyway for Database Setup and Migrations flyway.locations=classpath:db/migration,classpath:db/type/mysql Update database script files Devonfw has flyway configured. Flyway is an open-source database migration tool.It strongly favors simplicity and convention over configuration.Flyway will search for script files for corresponding database. It will parse the script files and create or update corresponding tables in a database. Generally, DDL Script file is db/type/mysql/V0002_Create_RevInfo.sql present And at other location script db/type/database/V000. files are present at For e.g location _db/migration/1.0/. Make sure script files are error free. We can set customized location for migration scripts. We need to add _flyway.locations property in application.properties. For example flyway.locations=classpath:db/migration,classpath:db/migration/mysql Here we can mention classpath or filesystems path. You can see more examples of database configurations here 47.2.2. Database Migration See database migration guide. 47.3. Data-Access Layer Security 47.3.1. SQL injection A common security threat is SQL-injection. Never build queries with string concatenation or your code might be vulnerable as in the following example: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 271 Devonfw Guide v2.4.0 String query = "Select op from OrderPosition op where op.comment = " + userInput; return getEntityManager().createQuery(query).getResultList(); Via the parameter userInput an attacker can inject SQL (JPQL) and execute arbitrary statements in the database causing extreme damage. In order to prevent such injections you have to strictly follow our rules for queries: Use named queries for static queries and QueryDSL for dynamic queries. Please also consult the SQL Injection Prevention Cheat Sheet. 47.3.2. Limited Permissions for Application We suggest that you operate your application with a database user that has limited permissions so he can not modify the SQL schema (e.g. drop tables). For initializing the schema (DDL) or to do schema migrations use a separate user that is not used by the application itself. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 272 Devonfw Guide v2.4.0 Chapter 48. Set up and maintain database schemas with Flyway Flyway is an open-source database migration tool. It strongly favors simplicity and convention over configuration. 48.1. Why use flyway Consider, you have an application (a piece of software) and a database. Great! And this could be all you need. But in most of the projects, this simple view of the world very quickly translates into this: Then, you not only have to deal with one copy of the environment, but with several other. This presents a number of challenges to maintain the databases across various environments. Many projects still rely on manually applied SQL scripts. And sometimes not even that (a quick SQL statement here or there to fix a problem). And soon many questions arise: • What state is the database in on this machine? • Has this script already been applied or not? • Has the quick fix in production been applied in test afterwards? • How do you set up a new database instance? Most often the answer to above questions is: We don’t know. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 273 Devonfw Guide v2.4.0 Database migration (where the flyway comes into the picture) is the great way to regain control over the above turmoil. They allow you to: • Recreate a database from scratch • Make it clear at all times what state a database is in • Migrate in a deterministic way from your current version of the database to a newer one. 48.2. How it works for setting up the database and maintaining it To know which state your database is in, Flyway relies on a special metadata table for all its internal bookkeeping. 1. When you point Flyway to an empty database, it will try to locate its metadata table. 2. As the database is empty, Flyway won’t find it and will create it instead. Now, you have a database with a single empty table called SCHEMA_VERSION by default. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 274 Devonfw Guide v2.4.0 3. Immediately, Flyway will begin to scan the file system or the classpath of the application for migrations. They can be written in either SQL or Java. 4. The migrations are then sorted based on their version number and applied in order. As each migration gets applied, the metadata table is updated accordingly. With the metadata and the initial state in place, you can now talk about migrating to newer versions. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 275 Devonfw Guide v2.4.0 Flyway will once again scan the filesystem or the classpath of the application for migrations. The migrations are checked against the metadata table. If their version number is lower or equal to the one of the versions marked as current, they are ignored. And that’s it! No matter when the need to evolve the database arises, whether structure (DDL) or reference data (DML), simply create a new migration with a version number higher than the current one. The next time Flyway starts, it will find it and upgrade the database accordingly. A typical metadata table looks like below: 48.3. How to set up a database Flyway can be used as a standalone or integrated tool via its API to make sure the database migration takes place on startup. To enable auto migration on startup (not recommended for production environment), set the following property in the application.properties file. database.migration.auto = true It is set to false by default via application-default.properties and shall be done explicitly in production environments. In a development environment, it is set to true in order to simplify development. This is also recommended for regular test environments. If you want to use Flyway, set the following property in any case to prevent Hibernate from making changes in the database (pre-configured by default of OASP4J): database.hibernate.hbm2ddl.auto=validate If you want flyway to clear the database before applying the migrations (all data will be deleted), set the following property (default is false): database.migration.clean = true New database migrations are added to src/main/resources/db/migrations. They can be SQL based or Java based and follow below naming convention: V__ (e.g.: V0003__Add_new_table.sql). For new SQL based migrations, stick to the following conventions: • properties in camelCase • tables in UPPERCASE This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 276 Devonfw Guide v2.4.0 • ID properties with underscore (e.g. table_id) • constraints all UPPERCASE with • PK_{table} for primary key • FK_{sourcetable}2{target} for foreign keys • UC_{table}_{property} for unique constraints • Inserts always with the same order of properties in blocks for each table • Insert properties always starting with id, modificationCounter, [dtype, ] … For example, look at the sample script (migration) shown below: -- *** Staffmemeber *** CREATE TABLE STAFFMEMBER( id BIGINT NOT NULL, modificationCounter INTEGER NOT NULL, firstname VARCHAR(255), lastname VARCHAR(255), login VARCHAR(255), role INTEGER ); It is also possible to use Flyway for test data. To do so, place your test data migrations in src/main/resources/db/test-data/ and set property database.migration.testdata = true Then, Flyway scans the additional location for migrations and applies all in the order specified by their version. If migrations V_01… and V_02… exist and a test data migration should be applied, in between, you can name it V_01_1_…_. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 277 Devonfw Guide v2.4.0 Chapter 49. Logic Layer The logic layer is the heart of the application and contains the main business logic. According to the OAPS4J business architecture we should divide an application into business components. The component part assigned to the logic layer contains the functional use-cases the business component is responsible for. For further understanding consult the application architecture. 49.1. Component Interface A component may consist of several use cases but is only accessed by the next higher layer or other components through one interface, i.e. by using one Spring bean. If the implementation of the component interface gets too complex it is recommended to further sub-divide it in separate use-case-interfaces to be aggregated in the main component interface. This suits for better maintainability. First we create an interface that contains the method(s) with the business operation documented with JavaDoc. The API of the use cases has to be business oriented. This means that all parameters and return types of a use case method have to be business transfer-objects, datatypes (String, Integer, MyCustomerNumber, etc.), or collections of these. The API may only access objects from other business components in the (transitive) dependencies of the declaring business component. Here is an example of a use case interface: public interface StaffManagement { StaffMemberEto getStaffMemberByLogin(String login); StaffMemberEto getStaffMember(Long id); ... } 49.2. Component Implementation The implementation of the use case typically needs access to the persistent data. This is done by injecting the corresponding DAO. For the principle data sovereignty only DAOs of the same business component may be accessed directly from the use case. For accessing data from other components the use case has to use the corresponding component interface. Further it shall not expose persistent entities from the persistence layer and has to map them to transfer objects. Within a use-case implementation, entities are mapped via a BeanMapper to persistent entities. Let’s take a quick look at some of the StaffManagement methods: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 278 Devonfw Guide v2.4.0 package io.oasp.gastronomy.restaurant.staffmanagement.logic.impl; public class StaffManagementImpl extends AbstractComponentFacade implements StaffManagement { public StaffMemberEto getStaffMemberByLogin(String login) { StaffMemberEntity staffMember = getStaffMemberDao().searchByLogin(login); return getBeanMapper().map(staffMember, StaffMemberEto.class); } public StaffMemberEto getStaffMember(Long id) { StaffMemberEntity staffMember = getStaffMemberDao().find(id); return getBeanMapper().map(staffMember, StaffMemberEto.class); } } As you can see, provided entities are mapped to corresponding business objects (here StaffMemberEto.class). These business objects are simple POJOs (Plain Old Java Objects) and stored in: ....api.. The mapping process of these entities and the declaration of the AbstractLayerImpl class are described here. For every business object there has to be a mapping entry in the src/main/resources/config/app/common/dozer-mapping.xml file. For example, the mapping entry of a TableEto to a Table looks like this: io.oasp.gastronomy.restaurant.tablemanagement.logic.api.TableEto io.oasp.gastronomy.restaurant.tablemanagement.persistence.api.entity.Table Below, a class diagram illustrating the pattern is shown (here: the StaffManagement business component): This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 279 Devonfw Guide v2.4.0 As the picture above illustrates, the necessary DAO entity to access the database is provided by an abstract class. Use Cases that need access to this DAO entity, have to extend that abstract class. Needed dependencies (in this case the staffMemberDao) are resolved by Spring, see here. For the validation (e.g. to check if all needed attributes of the StaffMember have been set) either Java code or Drools, a business rule management system, can be used. 49.3. Passing Parameters Among Components Entities have to be detached for the reasons of data sovereignty, if entities are passed among components or layers (to service layer). For further details see Bean-Mapping. Therefore we are using transfer-objects (TO) with the same attributes as the entity that is persisted. The packages are: Persistence Entities ....persistence.api.entity Transfer Objects(TOs) ....logic.api This mapping is a simple copy process. So changes out of the scope of the owning component to any TO do not directly affect the persistent entity. 49.4. Logic Layer Security The logic layer is the heart of the application. It is also responsible for authorization and hence security is important here. 49.4.1. Direct Object References A security threat are Insecure Direct Object References. This simply gives you two options: • avoid direct object references at all This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 280 Devonfw Guide v2.4.0 • ensure that direct object references are secure Especially when using REST, direct object references via technical IDs are common sense. This implies that you have a proper authorization in place. This is especially tricky when your authorization does not only rely on the type of the data and according static permissions but also on the data itself. Vulnerabilities for this threat can easily happen by design flaws and inadvertence. Here an example from our sample application: Listing 20. TablemanagementImpl.java @RolesAllowed(PermissionConstants.FIND_TABLE) public TableEto findTable(Long id) { return getBeanMapper().map(getTableDao().findOne(id), TableEto.class); } We have a generic use-case to manage Tables. In the first place it makes sense to write a generic REST service to load and save these Tables. However, the permission to read or even update such Table depend on the business object hosting the Table. Therefore such a generic REST service would open the door for this OWASP A4 vulnerability. To solve this in a secure way you need individual services for each hosting business object. There you have to check permissions based on the parent business object. In this example the ID of the Table would be the direct object reference and the ID of the business object would be the indirect object reference. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 281 Devonfw Guide v2.4.0 Chapter 50. Service Layer The service layer is responsible to expose functionality of the logical layer to external consumers over a network via technical protocols. 50.1. Types of Services If you want to create a service please distinguish the following types of services: • External Services are used for communication between different companies, vendors, or partners. • Internal Services are used for communication between different applications in the same application landscape of the same vendor. ◦ Back-end Services are internal services between Java back-ends typically with different release and deployment cycles (otherwise if not Java consider this as external service). ◦ JS-Client Services are internal services provided by the Java back-end for JavaScript clients (GUI). ◦ Java-Client Services are internal services provided by the Java back-end for for a native Java client (JavaFx, EclipseRcp, etc.). The choices for technology and protocols will depend on the type of service. Therefore the following table gives a guideline for aspects according to the service types. These aspects are described below. Table 7. Aspects according to service-type Aspect External Service Back-end Service JS-Client Service Java-Client Service Versioning required required not required not required Interoperability mandatory not required implicit not required recommended Protocol SOAP or REST REST REST+JSON REST 50.2. Versioning For services consumed by other applications we use versioning to prevent incompatibilities between applications when deploying updates. This is done by the following conventions: • We define a version number and prefixed with v for version (e.g. v1). • If we support previous versions we use that version numbers as part of the Java package defining the service API (e.g. com.foo.application.component.service.api.v1) • We use the version number as part of the service name in the remote URL (e.g. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 282 Devonfw Guide v2.4.0 https://application.foo.com/services/rest/component/v1/resource) • Whenever we need to change the API of a service in an incompatible we increment the version (e.g. v2) as an isolated copy of the previous version of the service. In the implementation of different versions of the same service we can place compatibility code and delegate to the same unversioned use-case of the logic layer whenever possible. • For maintenance and simplicity we avoid keeping more than one previous version. 50.3. Interoperability For services that are consumed by clients with different technology interoperability is required. This is addressed by selecting the right protocol following protocol-specific best practices and following our considerations especially simplicity. 50.4. Protocol For services there are different protocols. Those relevant for and recommended by Devonfw are listed in the following sections with examples how to implement them in Java. 50.4.1. REST REST is not a technology and certainly is not a standard of some kind. It is merely an architectural style that chalks down how to write a web service in a certain way. REST stands for REpresentational State Transfer. REST is web standards based architecture and uses HTTP Protocol. It revolves around resource where every component is a resource and a resource is accessed by a common interface using HTTP standard methods. In REST architecture, a REST Server simply provides access to resources and REST client accesses and modifies the resources. Here each resource is identified by URIs/ global IDs. REST uses various representations to represent a resource like text, JSON, XML. JSON is the most popular one. Principal protagonist of a REST architecture is a "Resource" which can be uniquely identified by an Uniform Resource Identifier or URI. State of a resource at any given point of time is represented by a document and is called Representation of resource. The client can update the state of resource by transferring the representation along with the request. The new representation is now returned to client along with the response. The representation contains the information in formats like html, xml, JSON etc that is accepted by the resource. The resource which adheres to rules of REST architecture is called a RESTful resource and web service that adheres to this rule are called RESTful web service. 50.4.2. SOAP SOAP is a common protocol that is rather complex and heavy. It allows to build inter-operable and well specified services (see WSDL). SOAP is transport neutral what is not only an advantage. We strongly recommend to use HTTPS transport and ignore additional complex standards like WSSecurity and use established HTTP-Standards such as RFC2617 (and RFC5280). This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 283 Devonfw Guide v2.4.0 50.5. Details on Rest Service For a general introduction consult the wikipedia. For CRUD operations in REST we distinguish between collection and element URIs: • A collection URI is build from the rest service URI by appending the name of a collection. This is typically the name of an entity. Such URI identifies the entire collection of all elements of this type. Example: https://mydomain.com/myapp/services/rest/mycomponent/v1/myentity • An element URI is build from a collection URI by appending an element ID. It identifies a single element (entity) within the collection. Example: https://mydomain.com/myapp/services/rest/mycomponent/v1/myentity/42 50.5.1. Representation of Resource A resource in REST is similar Object in Object Oriented Programming or similar to Entity in a Database. Once a resource is identified then its representation is to be decided using a standard format so that server can send the resource in above said format and client can understand the same format. For example, in RESTful Web Services ," User " is a resource which is represented using following XML format: 1 Mahesh Teacher Same resource can be represented in JSON format: { "id":1, "name":"Mahesh", "profession":"Teacher" } 50.5.2. Characteristics of good Representation In REST, there is no restriction on the format of a resource representation. A client can ask for JSON representation where as another client may ask for XML representation of same resource to the server and so on. It is responsibility of the REST server to pass the client the resource in the format that client understands. Following are important points to be considered while designing a This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 284 Devonfw Guide v2.4.0 representation format of a resource in a RESTful web services. Understandability: Both Server and Client should be able to understand and utilize the representation format of the resource. Completeness: Format should be able to represent a resource completely. For example, a resource can contain another resource. Format should be able to represent simple as well as complex structures of resources. Linkablity: A resource can have a linkage to another resource, a format should be able to handles such situations. 50.5.3. RESTful Web Services-Messages RESTful web services make use of HTTP protocol as a medium of communication between client and server. A client sends a message in form of a HTTP Request and server responds in form of a HTTP Response. This technique is terms as Messaging. These messages contain message data and metadata that is information about message itself. Let’s have a look on HTTP Request and HTTP Response messages for HTTP 1.1. A HTTP Request has five major parts: • Verb- Indicate HTTP methods such as GET, POST etc. • URI- Contains the URI, Uniform Resource Identifier to identify the resource on server • HTTP Version- Indicate HTTP version, for example HTTP v1.1 . • Request Header- Contains metadata for the HTTP Request message as key-value pairs. For example, client ( or browser) type, format supported by client, format of message body, cache settings etc. • Request Body- Message content or Resource representation. HTTP RESPONSE A HTTP Response has four major parts: • Status/Response Code- Indicate Server status for the requested resource. For example 404 means resource not found and 200 means response is ok. • HTTP Version- Indicate HTTP version, for example HTTP v1.1 . • Response Header- Contains metadata for the HTTP Response message as key-value pairs. For example, content length, content type, response date, server type etc. • Response Body- Response message content or Resource representation. 50.5.4. Constructing a standard URI Addressing refers to locating a resource or multiple resources lying on the server. It is analogous to locate a postal address of a person. Each resource in REST architecture is identified by its URI, Uniform Resource Identifier. A URI is of This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 285 Devonfw Guide v2.4.0 following format: ://// Purpose of an URI is to locate a resource(s) on the server hosting the web service. Another important attribute of a request is VERB which identifies the operation to be performed on the resource. For example, in RESTful Web Services - First Application tutorial, URI is http://localhost:8080/UserManagement/rest/UserService/users and VERB is GET. Following are important points to be considered while designing a URI: • Use Plural Noun - Use plural noun to define resources. For example, we’ve used users to identify users as a resource. • Avoid using spaces - Use underscore(_) or hyphen(-) when using a long resource name, for example, use authorized_users instead of authorized%20users. • Use lowercase letters - Although URI is case-insensitive, it is good practice to keep url in lower case letters only. • Maintain Backward Compatibility - As Web Service is a public service, a URI once made public should always be available. In case, URI gets updated, redirect the older URI to new URI using HTTP Status code, 300. • Use HTTP Verb - Always use HTTP Verb like GET, PUT, and DELETE to do the operations on the resource. It is not good to use operations names in URI. The "pure" REST architecture style is more suitable for creating "scalable" systems on the open web. But for usual business applications its complexity outweight its benefits, therefore the Devonfw proposes a more "pragmatic" approach to REST services. 50.5.5. HTTP Methods On the next table we compare the main differences between the "canonical" REST approach (or RESTful) and the Devonfw proposal. Table 8. Usage of HTTP methods HTTP Method RESTful Meaning Devonfw GET http://localhost:8080/ UserManagement/rest/ UserService/users/1 Get User of Id 1 (Read Only)(Read Only) Read a single element. PUT http://localhost:8080/ Not used UserManagement/rest/ UserService/users/2 Insert User with Id 2 (Idempotent) This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 286 Devonfw Guide v2.4.0 HTTP Method RESTful Meaning Devonfw POST http://localhost:8080/ UserManagement/rest/ UserService/users/2 Update User with Id 2 (N/A) Create or update an element in the collection. Search on an entity (parametrized post body) Bulk deletion. DELETE http://localhost:8080/ Delete an entity. UserManagement/rest/ UserService/users/1 Delete User Delete an entiry collection with Id 1 (Idempotent) (typically not supported) Please consider these guidelines and rationales: * We use POST on the collection URL for both create and update operations on an entity. This avoids pointless discussions in distinctions between PUT and POST and what to do if a "creation" contains an ID or if an "update" is missing the ID property. * Hence, we do NOT use PUT but always use POST for write operations. As we always have a technical ID for each entity, we can simply distinguish create and update by the presence of the ID property. Here are important points to be considered: • GET operations are read only and are safe. • PUT and DELETE operations are idempotent means their result will always same no matter how many times these operations are invoked. • PUT and POST operation are nearly same with the difference lying only in the result where PUT operation is idempotent and POST operation can cause different result. 50.5.6. JAX-RS For implementing REST services we use the JAX-RS standard. As an implementation we recommend CXF. JAX-RS stands for JAVA API for RESTful Web Services. JAX-RS is a JAVA based programming language API and specification to provide support for created RESTful Webservices. JAX-RS makes heavy use of annotations available from Java to simplify development of JAVA based web services creation and deployment. It also provides supports for creating clients for RESTful web services. • Specifications S.no Annotation Description 1 @Path Relative path of the resource class/method. 2 @GET HTTP Get request, used to fetch resource. 3 @PUT HTTP PUT request, used to create resource. 4 @POST HTTP POST request, used to create/update resource. 5 @DELETE HTTP DELETE request, used to delete resource. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 287 Devonfw Guide v2.4.0 S.no Annotation Description 6 @HEAD HTTP HEAD request, used to get status of method availability. 7 @Produces States the HTTP Response generated by web service, for example application/xml,text/html,applic ation/json etc. 8 @Consumes States the HTTP Request type, for example application/xwww-form-urlencoded to accept form data in HTTP body during POST request. 9 @PathParam Binds the parameter passed to method to a value in path. 10 @QueryParam Binds the parameter passed to method to a query parameter in path. 11 @MatrixParam Binds the parameter passed to method to a HTTP matrix parameter in path. 12 @HeaderParam Binds the parameter passed to method to a HTTP header. 13 @CookieParam Binds the parameter passed to method to a Cookie. 14 @FormParam Binds the parameter passed to method to a form value. 15 @DefaultValue Assigns a default value to a parameter passed to method. 16 @Context Context of the resource for example HTTPRequest as a context. Following are the commonly used annotations to map a resource as a web service resource. If you want to know more about why we have chosen these options see this. For JSON bindings we use Jackson while XML binding works out-of-the-box with JAXB. To implement a service you simply write a regular class and use JAX-RS annotations to annotate methods that shall be exposed as REST operations. Here is a simple example: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 288 Devonfw Guide v2.4.0 @Path("/tablemanagement") @Named("TableManagementRestService") public class TableManagementRestServiceImpl implements RestService { // ... @Produces(MediaType.APPLICATION_JSON) @GET @Path("/table/{id}/") @RolesAllowed(PermissionConstant.GET_TABLES) public TableBo getTable(@PathParam("id") String id) throws RestServiceException { Long idAsLong; if (id == null) throw new BadRequestException("missing id"); try { idAsLong = Long.parseLong(id); } catch (NumberFormatException e) { throw new RestServiceException("id is not a number"); } catch (NotFoundException e) { throw new RestServiceException("table not found"); } return this.tableManagement.getTable(idAsLong); } // ... } Here we can see a REST service for the business component tablemanagement. The method getTable can be accessed via HTTP GET (see @GET) under the URL path tablemanagement/table/{id} (see @Path annotations) where {id} is the ID of the requested table and will be extracted from the URL and provided as parameter id to the method getTable. It will return its result (TableBo) as JSON (see @Produces). As you can see it delegates to the logic component tableManagement that contains the actual business logic while the service itself only contains mapping code and general input validation. Further you can see the @RolesAllowed for security. The REST service implementation is a regular CDI bean that can use dependency injection. With JAX-RS it is important to make sure that each service method is annotated with NOTE the proper HTTP method (@GET,@POST,etc.) to avoid unnecessary debugging. So you should take care not to forget to specify one of these annotations. 50.5.7. JAX-RS Configuration Starting from CXF 3.0.0 it is possible to enable the auto-discovery of JAX-RS roots and providers thus avoiding having to specify each service bean in the beans-service.xml file. When the jaxrs server is instantiated all the scanned root and provider beans (beans annotated with javax.ws.rs.Path and javax.ws.rs.ext.Provider) are configured. The xml configuration still allows us to specify the root path for all endpoints. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 289 Devonfw Guide v2.4.0 50.5.8. HTTP Status Codes Further we define how to use the HTTP status codes for REST services properly. In general the 4xx codes correspond to an error on the client side and the 5xx codes to an error on the server side. Table 9. Usage of HTTP status codes HTTP Code Meaning Response Comment 200 OK requested result Result of successful GET 204 No Content none Result of successful POST, DELETE, or PUT (void return) 400 Bad Request error details The HTTP request is invalid (parse error, validation failed) 401 Unauthorized none (security) Authentication failed 403 Forbidden none (security) Authorization failed 404 Not found none Either the service URL is wrong or the requested resource does not exist 500 Server Error error code, UUID Internal server error occurred (used for all technical exceptions) For more details about REST service design please consult the RESTful cookbook. 50.5.9. REST Exception Handling For exceptions a service needs to have an exception facade that catches all exceptions and handles them by writing proper log messages and mapping them to a HTTP response with an according HTTP status code. Therefore the OASP4J provides a generic solution via RestServiceExceptionFacade. You need to follow the exception guide so that it works out of the box because the facade needs to be able to distinguish between business and technical exceptions. You need to configure it in your beans-service.xml as following: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 290 Devonfw Guide v2.4.0 Now your service may throw exceptions but the facade will automatically handle them for you. 50.5.10. Metadata OASP4J has support for the following metadata in REST service invocations: Name Description Further information Correlation ID A unique identifier to associate different requests belonging to the same session / action Logging guide Validation errors Standardized format for a service to communicate validation errors to the client Server-side validation is documented in the Validation guide. The protocol to communicate these validation errors to the client is discussed at https://github.com/oasp/oasp4j/ issues/218 Pagination Standardized format for a service to offer paginated access to a list of entities Server-side support for pagination is documented in the Data-Access Layer Guide. 50.5.11. Recommendations for REST requests and responses The OASP4J proposes, for simplicity, a deviation from the REST common pattern: • Using POST for updates (instead of PUT) • Using the payload for addressing resources on POST (instead of identifier on the URL) • Using parametrized POST for searches This use of REST will lead to simpler code both on client and on server. We discuss this use on the next points. REST services are called via HTTP(S) URIs. As we mentioned at the beginning we distinguish between collection and element URIs: • A collection URI is build from the rest service URI by appending the name of a collection. This is typically the name of an entity. Such URI identifies the entire collection of all elements of this type. Example: https://mydomain.com/myapp/services/rest/mycomponent/myentity • An element URI is build from a collection URI by appending an element ID. It identifies a single element (entity) within the collection. Example: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 291 Devonfw Guide v2.4.0 https://mydomain.com/myapp/services/rest/mycomponent/myentity/42 The following table specifies how to use the HTTP methods (verbs) for collection and element URIs properly (see wikipedia). For general design considerations beyond this documentation see the API Design eBook. 50.5.12. Unparameterized loading of a single resource • HTTP Method: GET • URL example: /products/123 For loading of a single resource, embed the identifier of the resource in the URL (for example /products/123). The response contains the resource in JSON format, using a JSON object at the top-level, for example: { "name": "Steak", "color": "brown" } 50.5.13. Unparameterized loading of a collection of resources • HTTP Method: GET • URL example: /products For loading of a collection of resources, make sure that the size of the collection can never exceed a reasonable maximum size. For parameterized loading (searching, pagination), see below. The response contains the collection in JSON format, using a JSON object at the top-level, and the actual collection underneath a result key, for example: { "result": [ { "name": "Steak", "color": "brown" }, { "name": "Broccoli", "color": "green" } ] } This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 292 Devonfw Guide v2.4.0 Avoid returning JSON arrays at the top-level, to prevent CSRF attacks (see https://www.owasp.org/index.php/OWASP_AJAX_Security_Guidelines) 50.5.14. Saving a resource • HTTP Method: POST • URL example: /products The resource will be passed via JSON in the request body. If updating an existing resource, include the resource’s identifier in the JSON and not in the URL, in order to avoid ambiguity. If saving was successful, an empty HTTP 204 response is generated. If saving was unsuccessful, refer below for the format to return errors to the client. 50.5.15. Parameterized loading of a resource • HTTP Method: POST • URL example: /products/search In order to differentiate from an unparameterized load, a special subpath (for example search) is introduced. The parameters are passed via JSON in the request body. An example of a simple, paginated search would be: { "status": "OPEN", "pagination": { "page": 2, "size": 25 } } The response contains the requested page of the collection in JSON format, using a JSON object at the top-level, the actual page underneath a result key, and additional pagination information underneath a pagination key, for example: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 293 Devonfw Guide v2.4.0 { "pagination": { "page": 2, "size": 25, "total": null }, "result": [ { "name": "Steak", "color": "brown" }, { "name": "Broccoli", "color": "green" } ] } Compare the code needed on server side to accept this request: @Path("/order") @POST public List findOrders(OrderSearchCriteriaTo criteria) { return this.salesManagement.findOrderCtos(criteria); } With the equivalent code required if doing it the REST way by issuing a GET request: @Path("/order") @GET public List findOrders(@Context UriInfo info) { RequestParameters parameters = RequestParameters.fromQuery(info); OrderSearchCriteriaTo criteria = new OrderSearchCriteriaTo(); criteria.setTableId(parameters.get("tableId", Long.class, false)); criteria.setState(parameters.get("state", OrderState.class, false)); return this.salesManagement.findOrderCtos(criteria); } 50.5.16. Pagination details The client can choose to request a count of the total size of the collection, for example to calculate the total number of available pages. It does so, by specifying the pagination.total property with a value of true. The service is free to honour this request. If it chooses to do so, it returns the total count as the pagination.total property in the response. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 294 Devonfw Guide v2.4.0 50.5.17. Deletion of a resource • HTTP Method: DELETE • URL example: /products/123 For deletion of a single resource, embed the identifier of the resource in the URL (for example /products/123). 50.5.18. Error results The general format for returning an error to the client is as follows: { "message": "A human-readable message describing the error", "code": "A code identifying the concrete error", "uuid": "An identifier (generally the correlation id) to help identify corresponding requests in logs" } If the error is caused by a failed validation of the entity, the above format is extended to also include the list of individual validation errors: { "message": "A human-readable message describing the error", "code": "A code identifying the concrete error", "uuid": "An identifier (generally the correlation id) to help identify corresponding requests in logs", "errors": { "property failing validation": [ "First error message on this property", "Second error message on this property" ], // .... } } 50.5.19. REST Media Types The payload of a REST service can be in any format as REST by itself does not specify this. The most established ones that the OASP4J recommends are XML and JSON. Follow these links for further details and guidance how to use them properly. JAX-RS and CXF properly support these formats (MediaType.APPLICATION_JSON and MediaType.APPLICATION_XML can be specified for @Produces or @Consumes). Try to decide for a single format for all services if possible and NEVER mix different formats in a service. In order to use JSON via Jackson with CXF you need to register the factory in your beansservice.xml and make CXF use it as following: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 295 Devonfw Guide v2.4.0 50.5.20. REST Testing For testing REST services in general consult the testing guide. For manual testing REST services there are browser plugins: • Firefox: httprequester (or poster) • Chrome: postman (advanced-rest-client) 50.6. Details of SOAP 50.6.1. JAX-WS For building web-services with Java we use the JAX-WS standard. There are two approaches: • code first • contract first Here is an example in case you define a code-first service. We define a regular interface to define the API of the service and annotate it with JAX-WS annotations: @WebService public interface TablemanagmentWebService { @WebMethod @WebResult(name = "message") TableEto getTable(@WebParam(name = "id") String id); } And here is a simple implementation of the service: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 296 Devonfw Guide v2.4.0 @Named("TablemanagementWebService") @WebService(endpointInterface = "io.oasp.gastronomy.restaurant.tablemanagement.service.api.ws.TablemanagmentWebService ") public class TablemanagementWebServiceImpl implements TablemanagmentWebService { private Tablemanagement tableManagement; @Override public TableEto getTable(String id) { return this.tableManagement.findTable(id); } Finally we have to register our service implementation in the spring configuration file beansservice.xml: The implementor attribute references an existing bean with the ID TablemanagementWebService that corresponds to the @Named annotation of our implementation (see dependency injection guide). The address attribute defines the URL path of the service. SOAP Custom Mapping In order to map custom datatypes or other types that do not follow the Java bean conventions, you need to write adapters for JAXB (see XML). SOAP Testing For testing SOAP services in general consult the testing guide. For testing SOAP services manually we strongly recommend SoapUI. 50.7. Service Considerations The term service is quite generic and therefore easily misunderstood. It is a unit exposing coherent functionality via a well-defined interface over a network. For the design of a service we consider the following aspects: • self-contained The entire API of the service shall be self-contained and have no dependencies on other parts of the application (other services, implementations, etc.). • idem-potent E.g. creation of the same master-data entity has no effect (no error) • loosely coupled This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 297 Devonfw Guide v2.4.0 Service consumers have minimum knowledge and dependencies on the service provider. • normalized complete, no redundancy, minimal • coarse-grained Service provides rather large operations (save entire entity or set of entities rather than individual attributes) • atomic Process individual entities (for processing large sets of data use a batch instead of a service) • simplicity avoid polymorphism, RPC methods with unique name per signature and no overloading, avoid attachments (consider separate download service), etc. 50.8. Security Your services are the major entry point to your application. Hence security considerations are important here. 50.8.1. CSRF A common security threat is CSRF for REST services. Therefore all REST operations that are performing modifications (PUT, POST, DELETE, etc. - all except GET) have to be secured against CSRF attacks. In OASP4J we are using spring-security that already solves CSRF token generation and verification. The integration is part of the application template as well as the sample-application. For testing in development environment the CSRF protection can be disabled using the JVM option -DCsrfDisabled=true when starting the application. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 298 Devonfw Guide v2.4.0 Chapter 51. Web Services (JAX WS) 51.1. What are Web Services? A Web service can be defined by following ways: • a client-server application or an application component for the communication. • a method of communication between the two devices over network. • a software system for interoperable machine to machine communication. • a collection of standards or protocols for exchanging information between the two devices or an application. In the above figure, java, .net or PHP applications can communicate with other applications through the web service over the network. For example, a java application can interact with Java, .Net and PHP applications. Hence, the web service is a language independent way of communication. There are mainly two types of web services: 1. SOAP web services. 2. RESTful web services. Next section describes more about JAX-WS (SOAP based) web services. 51.2. Why use Web Services? Exposing the Existing Function on the network A web service is a unit of managed code that can be remotely invoked using HTTP, that is, it can be activated using HTTP requests. Web services allow you to expose the functionality of your existing code over the network. Once it is exposed on the network, other applications can use the functionality of your program. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 299 Devonfw Guide v2.4.0 Interoperability Web services allow various applications to talk to each other and share data and services among themselves. Other applications can also use the web services. For example, a VB or .NET application can talk to Java web services and vice versa. Web services are used to make the application, platform and technology independent. Standardized Protocol Web services use standardized industry standard protocol for the communication. All the four layers (Service Transport, XML Messaging, Service Description, and Service Discovery layers) use well-defined protocols in the web services protocol stack. This standardization of protocol stack gives the business many advantages, such as a wide range of choices, reduction in the cost due to competition, and increase in the quality. Low Cost of Communication Web services use SOAP over HTTP protocol, so you can use your existing low-cost internet for implementing web services. This solution is much less costly compared to the proprietary solutions like EDI/B2B. Besides SOAP over HTTP, web services can also be implemented on the other reliable transport mechanisms like FTP. 51.3. Characteristics of Web Services XML-Based Web Services uses XML at data representation and data transportation layers. Using XML, eliminates any networking, operating system, or platform binding. Web Services based applications are highly interoperable application at their core level. Loosely Coupled A consumer of a web service is not tied to that web service directly. The web service interface can change over time without compromising the client’s ability to interact with the service. A tightly coupled system implies that the client and server logic are closely tied to one another, implying that if one interface changes, the other must be updated. Adopting a loosely coupled architecture tends to make software systems more manageable and allows simpler integration between different systems. Coarse-Grained Object-oriented technologies such as Java expose their services through individual methods. An individual method is a too fine operation to provide any useful capability at a corporate level. Building a Java program from scratch requires the creation of several fine-grained methods that are then composed into a coarse-grained service that is consumed by either a client or the other service. Businesses and the interfaces that they expose should be coarse-grained. Web services technology provides a natural way of defining coarse-grained services that access the right amount of business logic. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 300 Devonfw Guide v2.4.0 Ability to be Synchronous or Asynchronous Synchronicity refers to the binding of the client to the execution of the service. In synchronous invocations, the client blocks and waits for the service to complete its operation before continuing. Asynchronous operations allow a client to invoke a service and then execute other functions. Asynchronous clients retrieve their results at a later point in time, while synchronous clients receive their results, when the service has completed. Asynchronous capability is a key factor in enabling loosely coupled systems. 51.4. Components of SOAP based Web Service There are three major web service components: 1. SOAP 2. WSDL 3. UDDI SOAP SOAP is an acronym for Simple Object Access Protocol. SOAP is an XML-based protocol for accessing web services. SOAP is a W3C recommendation for communication between applications. SOAP is XML based, so it is platform independent and language independent. In other words, it can be used with Java, .Net or PHP language on any platform. WSDL WSDL is an acronym for Web Services Description Language. WSDL is an xml document containing information about web services such as method name, method parameter and how to access it. WSDL is a part of UDDI. It acts as an interface between web service applications. WSDL is pronounced as "wiz-dull". UDDI UDDI is an acronym for Universal Description, Discovery and Integration. UDDI is an XML based framework for describing, discovering and integrating web services. UDDI is a directory of web service interfaces described by WSDL, containing information about web services. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 301 Devonfw Guide v2.4.0 51.5. Apache CXF and JAX WS CXF implements the JAX-WS APIs which makes building web services easier. JAX-WS encompasses many different areas: • Generating WSDL from Java classes and generating Java classes from WSDL • Provider API which allows you to create simple messaging receiving server endpoints • Dispatch API which allows you to send raw XML messages to server endpoints • Spring integration • It supports Restful services too In devonfw, Apache CXF implementation of JAX WS is used. 51.6. Creation of Web Service using Apache CXF Developing the service This can be done in two ways: code-first and contract-first. The code-first approach is used below: Here is an example in case you define a code-first service. Create a regular interface to define the API of the service and annotate it with JAX-WS annotations: @WebService public interface TablemanagmentWebService { @WebMethod @WebResult(name = "message") TableEto getTable(@WebParam(name = "id") String id); } And here is a simple implementation of the service: @Named("TablemanagementWebService") @WebService(endpointInterface = "io.oasp.gastronomy.restaurant.tablemanagement.service.api.ws.TablemanagmentWebService ") public class TablemanagementWebServiceImpl implements TablemanagmentWebService { private Tablemanagement tableManagement; @Override public TableEto getTable(String id) { return this.tableManagement.findTable(id); } This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 302 Devonfw Guide v2.4.0 If you look at the above interface, you can tell that it is a normal Java interface with the exception of three annotations: • @WebService – Specifies that the JWS file implements a web service turning a normal POJO into a web service. In the above case, the annotation is placed right above the interface definition and it notifies that TablemanagmentWebService is not a normal interface rather an web service interface or SEI. • @WebMethod – This annotation is optional and is mainly used to provide a name attribute to the public method in WSDL. • @WebResult - The @WebResult annotation allows you to specify the properties of the generated wsdl:part that is generated for the method’s return value. • @WebParam - The @WebParam annotation is defined by the javax.jws.WebParam interface. It is placed on the parameters on the methods defined in the SEI. The @WebParam annotation allows you to specify the direction of the parameter, if the parameter will be placed in the SOAP header, and other properties of the generated wsdl:part. The @WebService annotation on the implementation class lets CXF know which interface to use when creating WSDL. In this case, it’s simply our TablemanagmentWebService interface. Finally, you have to register the service implementation in the spring in this @Configuration -annotated Class. Here, the CXF and end point is initialized. So, the @Configuration-annotated Class that is ServiceConfiguration.java can be found within the sample app in src/main/java/io.oasp.gastronomy.restaurant/general/configuration of xxx-core project. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 303 Devonfw Guide v2.4.0 @Configuration @EnableWs @ImportResource({ "classpath:META-INF/cxf/cxf.xml" /* , "classpath:META-INF/cxf/cxfservlet.xml" */ }) public class ServiceConfiguration extends WsConfigurerAdapter { @Bean(name = "cxf") public SpringBus springBus() { return new SpringBus(); } @Bean public ServletRegistrationBean servletRegistrationBean() { CXFServlet cxfServlet = new CXFServlet(); ServletRegistrationBean servletRegistration = new ServletRegistrationBean (cxfServlet, URL_PATH_SERVICES + "/*"); return servletRegistration; } // BEGIN ARCHETYPE SKIP @Bean public Endpoint tableManagement() { EndpointImpl endpoint = new EndpointImpl(springBus(), new TablemanagementWebServiceImpl()); endpoint.publish("/TablemanagementWebService"); return endpoint; } // END ARCHETYPE SKIP } You can see the beans SpringBus and ServletRegistrationBean inside the @Configuration-Class. You need to configure it to return an instance of org.apache.cxf.jaxws.EndpointImpl, which later will be forwarded to the SpringBus and the implementor via constructor-arg: Furthermore, you have to use the publish method of the org.apache.cxf.jaxws.EndpointImpl to define the last part of the WebService-URI. Now, if you are fire up the sample application with SpringBoot, opening a browser and hit below URL where the web service is hosted: http://localhost:8081/oasp4j-sample-server/services/ You should see our TablemanagementService beneath "Available SOAP services" including all available web service methods. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 304 Devonfw Guide v2.4.0 51.7. Soap Custom Mapping In order to map custom datatypes or other types that do not follow the Java bean conventions, you need to write the adapters for JAXB XML). 51.8. SOAP Testing For testing SOAP services manually, it is strongly recommended to use SoapUI. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 305 Devonfw Guide v2.4.0 Chapter 52. Batch Layer We understand batch processing as bulk-oriented, non-interactive, typically long running execution of tasks. For simplicity we use the term batch or batch job for such tasks in the following documentation. OASP4J uses Spring Batch as batch framework. This guide explains how Spring Batch is used in OASP4J applications. Please note that it is not yet, fully consistent concerning batches with the sample application. You should adhere to this guide for now. 52.1. Batch architecture In this chapter we will describe the overall architecture (especially concerning layering) and how to administer batches. 52.1.1. Layering Batches are implemented in the batch layer. The batch layer is responsible for batch processes, whereas the business logic is implemented in the logic layer. Compared to the service layer you may understand the batch layer just as a different way of accessing the business logic. From a component point of view each batch is implemented as a subcomponent in the corresponding business component. The business component is defined by the business architecture. Let’s make an example for that. The sample application implements a batch for exporting bills. This bill-export-batch belongs to the sales management business component. So the bill-export-batch is implemented in the following package: .salesmanagement.batch.impl.billexport.* Batches should invoke use cases in the logic layer for doing their work. Only "batch specific" technical aspects should be implemented in the batch layer. Example: For a batch, which imports product data from a CSV file this means that all code for actually reading and parsing the CSV input file is implemented in the batch layer. The batch calls the use case "create product" in the logic layer for actually creating the products for each line read from the CSV input file. 52.1.2. Accessing data access layer In practice it is not always appropriate to create use cases for every bit of work a batch should do. Instead, the data access layer can be used directly. An example for that is a typical batch for data This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 306 Devonfw Guide v2.4.0 retention which deletes out-of-time data. Often deleting out-dated data is done by invoking a single SQL statement. It is appropriate to implement that SQL in a DAO method and call this method directly from the batch. But be careful that this pattern is a simplification which could lead to business logic cluttered in different layers which reduces maintainability of your application. It is a typical design decision you have to take when designing your specific batches. 52.1.3. Batch administration and execution Starting and Stopping Batches Spring Batch provides a simple command line API for execution and parameterization of batches, the CommandLineJobRunner. However, it is not yet fully compatible with Spring Boot. For those using Spring Boot OASP4J provides the SpringBootBatchCommandLine with similar functionalities. Both execute batches as a "simple" standalone process (instantiating a new JVM and creating a new ApplicationContext). Starting a Batch Job For starting a batch job, the following parameters are required: jobPath The location of the JavaConfig classes (usually annotated with @Configuration or @SpringBootApplication) and/or XML files that will be used to create an ApplicationContext. The CommandLineJobRunner only accepts one class/file, which must contain everything needed to run a job (potentially by referencing other classes/files), the SpringBootBatchCommandLine, however, expects that there are two paths given: one for the general batch setup and one for the XML file containing the batch job to be executed. There is an example of a general batch setup for Spring Boot in the samples/core (oasp4j-samplecore) project, class SpringBootBatchApp, which also imports the general configuration class introduced in the chapter on the general configuration. Note that SpringBootBatchApp deactivates the evaluation of annotations used for authorization, especially the @RolesAllowed annotation. You should of course make sure that only authorized users can start batches, but once the batch is started there is usually no need to check any authorization. jobName The name of the job to be run. All arguments after the job name are considered to be job parameters and must be in the format of 'name=value': Example for the CommandLineJobRunner: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 307 Devonfw Guide v2.4.0 java org.springframework.batch.core.launch.support.CommandLineJobRunner classpath:config/app/batch/beans-billexport.xml billExportJob -outputFile=file:out.csv date(date)=2015/12/20 Example for the SpringBootBatchCommandLine: java io.oasp.module.batch.common.base.SpringBootBatchCommandLine io.oasp.gastronomy.restaurant.SpringBootBatchApp classpath:config/app/batch/beansbillexport.xml billExportJob -outputFile=file:out.csv date(date)=2015/12/20 The date parameter will be explained in the section on parameters. Note that when a batch is started with the same parameters as a previous execution of the same batch job, the new execution is considered a restart, see restarts for further details. Parameters starting with a "-" are ignored when deciding whether an execution is a restart or not (so called non identifying parameters). When trying to restart a batch that was already complete, there will either be an exception (message: "A job instance already exists and is complete for parameters={…}. If you want to run this job again, change the parameters.") or the batch will simply do nothing (might happen when no or only non identifying parameters are set; in this case the console log contains the following message for every step: "Step already complete or not restartable, so no action to execute: …"). Stopping a Job The command line option to stop a running execution is as follows: java org.springframework.batch.core.launch.support.CommandLineJobRunner classpath:config/app/batch/beans-billexport.xml –stop billExportJob or java io.oasp.module.batch.common.base.SpringBootBatchCommandLine io.oasp.gastronomy.restaurant.SpringBootBatchApp classpath:config/app/batch/beansbillexport.xml billExportJob –stop Note that the job is not shutdown immediately, but might actually take some time to stop. Scheduling In real world scheduling of batches is not as simple as it first might look like. • Multiple batches have to be executed in order to achieve complex tasks. If one of those batches fails the further execution has to be stopped and operations should be notified for example. • Input files or those created by batches have to be copied from one node to another. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 308 Devonfw Guide v2.4.0 • Scheduling batch executing could get complex easily (quarterly jobs, run job on first workday of a month, …) For Devonfw we propose the batches themselves should not mess around with details of batch administration. Likewise your application should not do so. Batch administration should be externalized to a dedicated batch administration service or scheduler. This service could be a complex product or a simple tool like cron. We propose Rundeck as an open source job scheduler. This gives full control to operations to choose the solution which fits best into existing administration procedures. 52.2. Implementation In this chapter we will describe how to properly setup and implement batches. 52.2.1. Main Challenges At a first glimpse, implementing batches is much like implementing a backend for client processing. There are, however, some points at which batches have to be implemented totally different. This is especially true if large data volumes are to be processed. The most important points are: Transaction handling For processing request made by clients there is usually one transaction for each request. If anything goes wrong, the transaction is rolled back and all changes are reverted. A naive approach for batches would be to execute a whole batch in one single transaction so that if anything goes wrong, all changes are reverted and the batch could start from scratch. For processing large amounts of data, this is technically not feasible, because the database system would have to be able to undo every action made within this transaction. And the space for storing the undo information needed for this (the so called "undo tablespace") is usually quite limited. So there is a need of short running transactions. To help programmers to do so, Spring Batch offers the so called chunk processing which will be explained here. Restarting Batches In client processing mode, when an exception occurs, the transaction is rolled back and there is no need to worry about data inconsistences. This is not true for batches however, due to the fact that you usually can’t have just one transaction. When an unexpected error occurs and the batch aborts, the system is in a state where the data is partly processed and party not and there needs to be some sort of plan how to continue from there. Even if a batch was perfectly reliable, there might be errors that are not under the control of the application, e.g. lost connection to the database, so that there is always a need for being able to This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 309 Devonfw Guide v2.4.0 restart. The section on restarts describes how to design a batch that is restartable. What’s important is that a programmer has to invest some time upfront for a batch to be able to restart after aborts. Exception handling in Batches The problem with exception handling is that e.g. a single record can cause a whole batch to fail and many records will remain unprocessed. In contrast to this, in client processing mode when processing fails this usually affects only one user. To prevent this situation, Spring Batch allows to skip data when certain exceptions occur. However, the feature should not be misused in a way that you just skip all exceptions independently of their cause. So when implementing a batch, you should think about what exceptional situations might occur and how to deal with that and weather it is okay to skip those exceptions or not. When an unexpected exception occurs, the batch should still fail so that this exception is not ignored but its causes are analyzed. Another way of handling exceptions in batches is retrying: Simply try to process the data once more and hope that everything works well this time. This approach often works for database problems, e.g. timeouts. The section on exception handling explains skipping and retrying in more detail. Note that exceptions are another reason why you should not execution a whole batch in one transaction. If anything goes wrong, you could either rollback the transaction and start the batch from scratch or you could manually revert all relevant changes. Both are not very good solutions. Performance issues In client processing mode, optimizing throughput (and response times) is an important topic as well, of course. However, a performance that is still considered okay for client processing might be problematic for batches as these usually have to process large volumes of data and the time for their execution is usually quite limited (batches are often executed at night when no one is using the application). An example: If processing the data of one person takes a second, this is usually still considered OK for client processing (even though performance could be better). However if a batch has to process the data of 100.000 persons in one night and is not executed with multiple threads, this takes roughly 28 hours, which is by far too much. The section on performance contains some tips how to deal with performance problems. 52.2.2. Setup Database Spring Batch needs some meta data tables for monitoring batch executions and for restoring state This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 310 Devonfw Guide v2.4.0 for restarts. Detailed description about needed tables, sequences and indexes can be found in Spring Batch - Reference Documentation: Appendix B. Meta-Data Schema. It is not recommended to add additional meta data tables, because this easily leads to inconsistencies with what is stored in those tables maintained by Spring Batch. You should rather try to extract all needed information out of the standard tables in case the standard API (especially JobRepository and JobExplorer, see below) does not fit your needs. Failure information BATCH_JOB_EXECUTION.EXIT_MESSAGE and BATCH_STEP_EXECUTION.EXIT_MESSAGE store a detailed description of how the job exited. In the case of failure, this might include as much of the stack trace as is possible. BATCH_STEP_EXECUTION_CONTEXT.SHORT_CONTEXT stores a stringified version of the step’s ExecutionContext (see saving and restoring state, the rest is stored in a BLOB if needed). The default length of those columns in the sample schema scripts is 2500. It is good to increase the length of those columns as far as the database allows it to make it easier to find out which exception failed a batch (not every exception causes a failure, see exception handling). Some JDBC drivers cast CLOBs to string automatically. If this is the case, you can use CLOBs instead. General Configuration For configuring batches, we recommend not to use annotations (would not work very well for batches) or JavaConfig, but XML, because this makes the whole batch configuration more transparent, as its structure and implementing beans are immediately visible. Moreover the Spring Batch documentation focuses rather on XML based configurations than on JavaConfig. For explanations on how these XML files are build in general, have a look at the spring documentation. There is, however, some general configuration needed for all batches, for which we use JavaConfig, as it is also used for the setup of all other layers. You can find an example of such a configuration in the example application (oasp4j-samples-core project): BeansBatchConfig. In this section, we will explain the most important parts of this class. The jobRepository is used to update the meta data tables. The database type can optionally be set on the jobRepository for correctly handling database specific things using the setDatabaseType method. Possible values are oracle, mysql, postgres, … If the size of all three columns, which per default have a length limitation of 2500, has been increased as proposed here, the property maxVarCharLength should be adjusted accordingly using the corresponding setter method in order to actually utilize the additional space. The jobExplorer offers methods for reading from the meta data tables in addition to those methods provided by the jobRepository, e.g. getting the last executions of a batch. The jobLauncher is used to actually start batches. We use our own implementation (JobLauncherWithAdditionalRestartCapabilities) here, which can This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 311 Devonfw Guide v2.4.0 be found in the module modules/batch (oasp4j-batch). It enables a special form of restarting a batch ("restart from scratch", see the section on restarts for further details). The jobRegistry is basically a map, which contains all batch jobs. It is filled by the bean of type JobRegistryBeanPostProcessor automatically. A JobParametersIncremeter (bean "incrementer") can be used to generate unique parameters, see restarts and parameters for further details. It should be configured manually for each batch job, see example batch below, otherwise exceptions might occur when starting batches. 52.2.3. Example-Batch As already mentioned, every batch job consists of one or more batch steps, which internally either use chunk processing or tasklet based processing. Our bill export batch job consists of the following to steps: 1. Read all (not processed) bills from the database, mark them as processed (additional attribute) and write them into a CSV file (to be further processed by other systems). This step is implemented using chunk processing (see chunk processing). 2. Delete all bill from the database which are marked as processed. This step is implemented in a tasklet (see tasklet based processing). Note that you could also delete the bills directly. However, for being able to demonstrate tasklet based processing, we have created a separate step here. Also note that in real systems you would usually create a backup of data as important as bills, which is not done here. The beans-billexport.xml (located in src/main/resources/config/app/batch) has to look like this to implement the batch. Note that you might not fully understand this example by now, but you should after reading the whole chapter on batches. As you can see, there is a job element (billExportJob), which contains the two step elements This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 313 Devonfw Guide v2.4.0 (createCsvFile and deleteBills). Note that for every step you have to explicitly specify which step comes next (using the next attribute), unless it is the last step. The step elements always contains a tasklet element, even if chunk processing is used. The transaction-attributes element is especially used to set timeouts of transactions (in seconds). Note that there is usually more than one transaction per step (see below). What follows is either a chunk element with ItemReader, ItemProcessor, ItemWriter and a commit interval (see chunk processing) or the tasklet element contains a reference to a tasklet. In the example above the ItemReader unprocessedBillsReader always reads 1000 ids of unprocessed bills (via a DAO) and returns them one after another. The ItemProcessor processedMarker reads the corresponding bills from the database (see chunk processing why we do not read them directly in the ItemReader) and marks them as processed. The ItemWriter csvFileWriter (see below on how this writer is configured) writes them to a CSV file. The path of this file is provided as batch parameter ("outputFile"). The tasklet billsDeleter deletes all processed bills (10.000 in one transaction). The chunkLoggingListener, which is also used in the example above, can be utilized for all chunk steps to log exceptions together with the items where these exceptions occurred (see listeners for further details on listeners). It’s implementation can be found in the module modules/batch. Note that classes used for items have to have an appropriate toString() method in order for this listener to be useful. 52.2.4. Restarts A batch execution is considered a restart, if it was run already (with the same parameters) and there was a (non skippable) failure or the batch has been stopped. There are basically two ways how to do a restart: • Undo all changes and restart from scratch. • Restore the state of that batch at the time the error occurred and continue processing. The first approach has two major disadvantages: One is that depending on what the batch does, reverting all of its changes can get quite complex. And you easily end up having implemented a batch that is restartable, but not if it fails in the wrong step. The second disadvantage is that if a batch runs for several hours and then it fails it has to start all over again. And as the time for executing batches is usually quite limited, this can be problematic. If reverting all changes is as easy as deleting all files in a given directory or something like that and the expected duration for an execution of the batch is rather short, you might consider the option of always starting at the beginning, otherwise you shouldn’t. Spring Batch supports implementing the second option. Per default, if a batch is restarted with the same parameters as a previous execution of this batch, then this new execution continues processing at the step where the last execution was stopped or failed. If the last execution was already complete, an exception is raised. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 314 Devonfw Guide v2.4.0 The step itself has to be implemented in a way so that it can restore its internal state, which is the main drawback of this second option. However, there are 'standard implementations' that are capable of doing so and these can easily be adapted to your needs. They are introduced in the section on chunk processing. For telling Spring Batch to always restart a batch at the very beginning even though there has been an execution of this batch with the same parameters already, set the restartable attribute of the Job element to false. Per default, setting this attribute to false means that the batch is not restartable (i.e. it cannot be started with the same parameters once more). It would raise an error if there was attempt to do so, so that it cannot be restarted where it left off. We use our own JobLauncher (JobLauncherWithAdditionalRestartCapabilities) as described in the section on the general configuration to modify this behavior so that those batches are always restarted from the first step on by adding an extra parameter (instead of raising an exception), so that you do not have to take care of that yourself. So don’t think of a batch marked with restartable="false" as a batch that is not restartable (as most people would probably assume just looking at the attribute) but as a batch that restarts always from the first step on. Note that if a batch is restartable by restoring its internal state, it might not work correctly if the batch is started with different parameters after it failed, which usually comes down to the same thing as restating it from scratch. So the batch has to be restarted and complete successfully before executing the next regular 'run'. When scheduling batches, you should make that sure. 52.2.5. Chunk Processing Chunk processing is item based processing. Items can be bills, persons or whatever needs to be processed. Those items are grouped into chunks of a fixed size and all items within such a chunk are processed in one transaction. There is not one transaction for every single (small) item because there would be too many commits which degrades performance. All items of a chunk are read by an ItemReader (e.g. from a file or from database), processed by an ItemProcessor (e.g. modified or converted) and written out as a whole by an ItemWriter (e.g. to a file or to database). The size of a chunk is also called commit interval. Careful when choosing a large chunk size: When a skip or retry occurs for a single item (see exception handling), the current transaction has to be rolled back and all items of the chunk have to be reprocessed. This is especially a problem when skips and retries occur more often and results in long runtimes. The most important advantages of chunk processing are: • good trade-off between size and number of transactions (configurable via commit size) • transaction timeouts that do not have to be adapted for larger amounts of data that needs to be processed (as there is always one transaction for a fixed number of items) • an exception handling that is more fain-grained than aborting/restarting the whole batch (item based skipping and retrying, see exception handling) This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 315 Devonfw Guide v2.4.0 • logging items where exceptions occurred (which makes failure analysis much more easy) Note that you could actually achieve similar results using tasklets as described below. However, you would have to write many lines of additional code whereas you get these advantages out of the box using chunk processing (logging exceptions and items where these exceptions occurred is an extension, see example batch). Also note that items should not be too "big". For example, one might consider processing all bills within one month as one item. However, doing so you would not have those advantages any more. For instance, you would have larger transactions, as there are usually quite a lot of bills per month or payment method and if an exception occurs, you would not know which bill actually caused the exception. Additionally you would lose control of commit size, since one commit would comprsie many bills hard coded and you cannot choose smaller chuncks. Nevertheless, there are sometimes situations where you cannot further "divide" items, e.g. when these are needed for one single call to an external system (e.g. for creating a PDF of all bills within a certain month, if PDFs are created by an external system). In this case you should do as much of the processing as possible on the basis of "small" items and then add an extra step to do what cannot be done based on these "small" items. ItemReader A reader has to implement the ItemReader interface, which has the following method: public T read() throws Exception; T is a type parameter of the ItemReader interface to be replaced with the type of items to be read. The method returns all items (one at a time) that need to be processed or null if there are no more items. If an exception occurs during read, Spring Batch cannot tell with item caused the exception (as it has not been read jet). That is why a reader should contain as little processing logic as possible, minimizing the potential for failures. Caching Per default, all items read by an ItemReader are cached by Spring Batch. This is useful because when a skippable exception occurs during processing of a chunk, all items (or at least those, that did not cause the exception) have to be reprocessed. These items are not read twice but taken from the cache then. This is often necessary, because if a reader saves it current state in member variables (e.g. the current position within a list of items) or uses some sort of cursor, these will be updated already and the next calls of the read method would deliver the next items already and not those that have to be reprocessed. However this also means that when the items read by an ItemReader are entities, these might be detached, because these might have been read in a different transaction. In some standard This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 316 Devonfw Guide v2.4.0 implementations Spring Batch even manually detaches entities in ItemReaders. In case these entities are to be modified it is a good practice that the ItemReader only reads IDs and the ItemProcessor loads the entities for these IDs to avoid the problem. Reading from Transactional Queues In case the reader reads from a transactional queue (e.g. using JMS), you must not use caching, because then an item might get processed twice: Once from cache and once from queue to where it has been returned after the rollback. To achieve this, set reader-transactional-queue="true" in the chunk element in the step definition. Moreover the equals and hashCode methods of the class used for items have to be appropriately implemented for Spring Batch to be able to identify items that were processed before unsuccessfully (causing a rollback and thereby returning them to the queue). Otherwise the batch might be caught in an infinite loop trying to process the same item over and over again (e.g. when the item is about to be skipped, see exception handling). Reading from the Database When selecting data from a database, there is usually some sort of cursor used. One challenge is to make this cursor not participate in the chunk’s transaction, because it would be closed after the first chunk. We will show how to use JDBC based cursors for ItemReader’s in later releases of this documentation. For JPA/JPQL based queries, cursors cannot be used, because JPA does not know of the concept of a cursor. Instead it supports pagination as introduced in the chapter on the data access layer, which can be used for this purpose as well. Note that pagination requires the result set to be sorted in an unambiguous order to work reliably. The order itself is irrelevant as long it does not change (you can e.g. sort the entities by their primary key). ItemReader’s using pagination should inherit from the AbstractPagingItemReader, which already provides most of the needed functionality. It manages the internal state, i.e. the current position, which can be correctly restored after a restart (when using an unambiguous order for the result set). Classes inheriting from AbstractPagingItemReader must implement two methods. The method doReadPage() performs the actual read of a page. The result is not returned (return type is void) but used to replace the content of the 'results' instance variable (type: List). Due to our layering concept and the persistence layer being the only place where accesses to the database should take place, you should not directly execute a query in this method, but call a DAO, which itself executes the query (using pagination). AbstractPagingItemReader provides methods for finding out the current position: use getPage() for the current page and getPageSize() for the (max.) page size. These values should be passed to the DAO as parameters. Note that the AbstractPagingItemReader starts counting pages from zero, This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 317 Devonfw Guide v2.4.0 whereas the PaginationTo used for pagination (retrieved by calling SearchCriteriaTo.getPagination()) starts counting from one, which is why you always have to increment the page number by one. The second method is doJumpToPage(int), which usually only requires an empty implementation. Furthermore, you need to set the property pageSize, which specifies how many items should be read at once. A page size that is as big as the commit interval usually results in the best performance. The approach of using pagination for ItemReader’s should not be used when items (usually entities) are added or removed or modified by the batch step itself or in parallel with the execution of the batch step so that the order changes, e.g. by other batches or due to operations started by clients (i.e. if the batch is executed in online mode). In this case there might be items processed twice or not processed at all. Be aware that due to hibernates Hi/Lo-Algorithm newer entities could get lower IDs than existing IDs and you probably will not process all entities if you rely on strict ID monotony! A simple solution for such scenarios would be to introduce a new flag 'processed' for the entities read if that is an option (as it is also done in the example batch). The query should be rewritten then so that only unprocessed items are read (additionally limiting the result set size to the number of items to be processed in the current chunk, but not more). Note that most of the standard implementations provided by Spring Batch do not fit to the layering approach in OASP4J applications, as these mostly require direct access to an EntityManager or a JDBC connection for example. You should think twice when using them an break the layering concept. Reading from Files For reading simply structured files, e.g. for those in which every line corresponds to an item to be processed by the batch, the FlatFileItemReader can be used. It requires two properties to be set: The first one the LineMapper (property lineMapper), which is used to convert a line (i.e. a String) to an item. It is a very simple interface which will not be discussed in more detail here. The second one is the resource, which is actually the file to be read. When set in the XML, it is sufficient to specify the path with a "file:" in front of it if it is a normal file from the file system. In addition to that, the property linesToSkip (integer) can be set to skip headers for example. For reading more than one line before for creating an item a RecordSeparatorPolicy can be used, which will not be discussed in more detail here, too. Per default, all lines starting with a '#' will be considered to be a comment, which can be changed by changing the comment property (string array). The encoding property can be used to set the encoding. A FlatFileItemReader can restore its state after restarts. For reading XML files, you can use the StaxEventItemReader (StAX is an alternative to DOM and SAX), which will not be discussed in further detail here. In case the standard implementations introduced here do not fit your needs, you will need to implement your own ItemReader. If this ItemReader has some internal state (usually stored in member variables), which needs to be restored in case of restarts, see the section on saving and This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 318 Devonfw Guide v2.4.0 restoring state for information on how to do this. ItemProcessor A processor must implement the ItemProcessor interface, which has the following method: public O process(I item) throws Exception; As you can see, there are two type parameters involved: one for the type of items received from the ItemReader and one for the type of items passed to the ItemWriter. These can be the same. If an item has been selected by the ItemReader, but there is no need to further process this item (i.e. it should not be passed to the ItemWriter), the ItemProcessor can return null instead of an item. Strictly interpreting chunk processing, the ItemProcessor should not modify anything but should only give instructions to the ItemWriter how to do modifications. For entities however this is not really practical and as it requires no special logic in case of rollbacks/restarts (as all modifications are transactional), it is usually OK to modify them directly. In contrast to this, performing accesses to files or calling external systems should only be done in ItemReader’s/ItemWriter’s and the code needed for properly handling failures (restarts for example) should be encapsulated there. It is usually a good practice to make ItemProcessor’s stateless, as the process method might be called more than once for one item (see the section on ItemReader’s why). If your ItemProcessor really needs to have some internal state, see saving and restoring state on how to save and restore the state for restarts. Do not forget to implement use cases instead of implementing everything directly in the ItemProcessor if the processing logic gets more complex. ItemWriter A writer has to implement the ItemWriter interface, which has the following method: public void write(List items) Exception; This method is called at the end of each chunk with a list of all (processed) items. It is not called once for every item, because it is often more efficient doing 'bulk writes', e.g. when writing to files. Note that is method might also be called more than once for one item (see the section on ItemReader’s why). At the end of the write method, there should always be a flush. When writing to files, this should be obvious, because when a chunks completes, it is expected that all changes are already there in case of restarts, which is not true if these changes were only buffered but have not been written out. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 319 Devonfw Guide v2.4.0 When modifying the database, the flush method on the EntityManager should be called, too (via a DAO), because otherwise there might be changes not written out jet and therefore constraints were not checked jet. This can be problematic, because Spring Batch considers all exceptions that occur during commit as critical, which is why these exceptions cannot be skipped. You should be careful using deferred constraints for the same reason. Writing to Database or Transactional Queues All changes made which are transactional can be conducted directly, there is no special logic needed for restarts, because these changes are applied if and only if the chunk succeeds. Writing to Files For writing simply structured files, the FlatFileItemWriter can be used. Similar to the FlatFileItemReader it requires the resource (i.e. the file) and a LineAggregator (property lineAggregator; instead of the lineMapper) to be set. There are various properties that can be used of which we will only present the most important ones here. As with the FlatFileItemReader, the encoding property is used to set the encoding. A FlatFileHeaderCallback (property headerCallback) can be used to write a header. The FlatFileItemWriter can restore its state correctly after restarts. In case the files contains too many line (written out in chunks that did not complete successfully), these lines are removed before continuing execution. For writing XML files, you can use the StaxEventItemWriter, which will not be discussed in further detail here. Just as with ItemReader’s and ItemProcessor’s: In case your ItemWriter has some internal state this state is not managed by a standard implementation, see saving and restoring state on how to make your implementation restartable (restart by restoring the internal state). 52.2.6. Saving and Restoring State For saving and restoring (in case of restarts) state, e.g. saving and restoring values of member variables, the ItemStream interface should be implemented by the ItemReader/ItemProcessor/ItemWriter, which has the following methods: public void open(ExecutionContext executionContext) throws ItemStreamException; public void update(ExecutionContext executionContext) throws ItemStreamException; public void close() throws ItemStreamException; The open method is always called before the actual processing starts for the current step and can be used to restore state when restarting. The ExecutionContext passed in as parameter is basically a map to be used to retrieve values set before the failure. The method containsKey(String) can be used to check if a value for a given key is set. If it is not set, this might be because the current batch execution is no restart or no value has been set before the failure. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 320 Devonfw Guide v2.4.0 There are several getter methods for actually retrieving a value for a given key: get(String) for objects (must be serializable), getInt(String), getLong(String), getDouble(String) and getString(String). These values will be the same as after the subsequent call to the update method after the last chunk that completed successfully. Note that if you update the ExecutionContext outside of the update method (e.g. in the read method of an ItemReader), it might contain values set in chunks that did not finish successfully after restarts, which is why you should not do that. So the update method is the right place to update the current state. It is called after each chunk (and before and after each step). For setting values, there are several put methods: put(String, Object), putInt(String, int), putLong(String, long), putDouble(String, double) and putString(String, String). You can choose keys (String) freely as long as these are unique within the current step. Note that when a skip occurs, the update method is sometimes but not always called, so you should design your code in a way that it can deal with both situations. The close method is usually not needed. Do not misuse the ItemStream interface for purposes other than storing/restoring state. For instance, do not use the update method for flushing, because you will not have the chance to properly handle failure (e.g. skipping). For opening or closing a file handle, you should rather use a StepExecutionListener as introduced in the section on listeners. The state can also be restored in the beforeStep(ExecutionListener) method (instead of the open method). Note that when a batch that always starts from scratch (i.e. the restartable attribute has been set to false for the batch job) is restarted, the ExecutionContext will not contain any state from the previous (failed) execution, so there is no use in storing the state in this case and usually no need to, of course, because the batch will start all over again. 52.2.7. Tasklet based Processing Tasklets are the alternative to chunk processing. In the section on chunk processing we already mentioned the advantages of chunk processing as compared to tasklets. However, if only very few data needs to be processed (within one transaction) or if you need to do some sort of bulk operation (e.g. deleting all records from a database table), where the currently processed item does not matter and it is unlikely that a 'fain grained' exception handling will be needed, tasklets might still be considered an option. Note that for the latter use case you should still use more than one transaction, which is possible when using tasklets, too. Tasklets have to implement the interface with the same name, which has the following method: public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception; This method might be called several times. Every call is executed inside a new transaction automatically. If processing is not finished yet and the execute method should be called once more, just use RepeatStatus.CONTINUABLE as return value and RepeatStatus.FINISHED otherwise. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 321 Devonfw Guide v2.4.0 The StepContribution parameter can be used to set how many items have been processed manually (which is done automatically using chunk processing), there is, however, usually no need to do so. The ChunkContext is similar to the ExecutionContext, but is only used within one chunk. If there is a retry in chunk processing, the same context should be used (with the same state that this context had when the exception occurred). Note that tasklets serve as the basis for chunk processing internally. For chunk processing there is a Spring Batch internal tasklet, which has an execute method that is called for every chunk and itself calls ItemReader, ItemProcessor and ItemWriter. That is the reason why a StepContribution and a ChunkContext are passed to tasklets as parameters, even though they are more useful in chunk processing. Moreover this is also the reason why you have to use the tasklet element in the XML even though you want to specify a step that uses chunk processing (see the example batch). 52.2.8. Exception Handling As already mentioned, in chunk processing you can configure a step so that items are skipped or retried when certain exceptions occur. If reties are exhausted (per default, there is no retry) and the exception that occurred cannot be skipped (per default, no exception can be skipped), the batch will fail (i.e. stop executing). In tasklet based processing this cannot be done, the only chance is to implement the needed logic yourself. Skipping Before skipping items you should think about what to do if a skip occurs. If a skip occurs, the exception will be logged in the server log. However if no one evaluates those logs on a regular basis and informs those who are affected further actions need to take place when implementing the batch. Implement the SkipListener interface to be informed when a skip occurs. For example, you could store a notification or send a message to someone. For skips that occurred in ItemReader’s there is no information available about the item that was skipped (as it has not been read jet) which is why there should be as little processing logic as possible in an ItemReader. It might also be a reason why you might want to forbid to skip exceptions that might occur in readers. Do not try to catch skipped exceptions and write something into the database in a new transaction (e.g. a notification) instead of using a SkipListener, because a skipped item might be processed more than once before actually being skipped (for example, if a skippable exception is thrown during a call of an ItemWriter, Spring Batch does not know which item of the current chunk actually caused the exception and therefore has to retry each item separately in order to know which item actually caused the exception). Skippable exception classes can be specified as shown below: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 322 Devonfw Guide v2.4.0 ... The attribute skip-limit, which has to be set in case there is any skippable exception class configured, is used to set how many items should be skipped at most. It is useful to avoid situations where very many items are skipped but the batch still completes successfully and no one notices this situation. Skippable exception classes are specified by their fully qualified name (e.g. java.lang.Exception), each of such class set in its own include element as shown above. Subclasses of such classes are also skipped. To programmatically decide whether to skip an exception or not, you can set a skip policy as shown below: The skip policy (here mySkipPolicy) has to be a bean that implements the interface SkipPolicy with the following method: public boolean shouldSkip(java.lang.Throwable t, int skipCount) throws SkipLimitExceededException To skip the exception and continue processing, just return true and otherwise false. The parameter skipCount can be used for a skip limit. A SkipLimitExceededException should be thrown if there should be thrown if there should be no more skips. Note that this method is sometimes called with a skipCount less than zero to test if an exception is skippable in general. When a SkipPolicy is set, the attribute skip-limit and element skippable-exception-classes are ignored. You could of course skip every exception (using java.lang.Exception as skippable exception class). This is, however, not a good practice as it might easily result in an error in the code that is ignored as the batch still completes successfully and everything seems to be fine. Instead, you should think about what kind of exceptions might actually occur, what to do if they occur and if it is OK to skip them. If an unexpected exception occurs, it is usually better to fail the batch execution and analyze the cause of the exception before restarting the batch. Exceptions that can occur in ItemWriter’s that write something to file should not be skipped unless the ItemWriter can properly deal with that. Otherwise there might be data written out even though This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 323 Devonfw Guide v2.4.0 the according item is skipped, because operations in the file systems are not transactional. Another situation where skips can be problematic is when calls to external interfaces are being made and these calls change something "on the other side", as these calls are usually not transactional. So be careful using skips here, too. Retrying For some types of exceptions, processing should be retried independently of weather the exception can be skipped or would otherwise fail the batch execution. For example, if there was a database timeout, this might be because there were too many requests at the time the chunk was processed. And it is not unlikely that retrying to successfully complete the chunk would succeed. There are, of course, also exceptions where retrying does not make much sense. E.g. exceptions caused by the business logic should be deterministic and therefore retrying does not make much sense in this case. Nevertheless, retrying every exception results in longer runtime but should in general be considered OK if you do not know which exceptions might occur or do not have the time to think about it. Retryable exception classes can be set similarly to setting skippable exception classes: ... The retry-limit attribute specifies how many times one individual item can be retried, as long as the exception thrown is "retryable". As with skippable exception classes, retryable exception classes are set in include elements and their subclasses are retried, too. To programmatically decide, whether to retry an exception or not, you can use a RetryPolicy, which is not covered in more detail here. Note that even if no retry is configured, an item might nevertheless be processed more than once. This is because if a skippable exception occurs in a chunk, all items of the chunk that did not cause the exception have to reprocessed, which is done in a separate transaction for every item, as the transaction in which these items were processed in the first place was rolled back. And even if the exception is not skippable, there is no guarantee that Spring Batch will not attempt to reprocess each item separately. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 324 Devonfw Guide v2.4.0 52.2.9. Listeners Spring Batch provides various listeners for various events to be notified about. For every listener there is an interface which can either be implemented by an ItemReader, ItemProcessor, ItemWriter or Tasklet or by a separate listener class, which can be registered for a step like this: .... ... The most commonly use listener is probably the StepExecutionListener, which has methods that are called before and after the execution of the step. It can be utilized e.g. for opening and closing files. The following example shows how to use the listener: public class MyListener implements StepExecutionListener { public void beforeStep(StepExecution stepExecution) { // take actions before processing of the step starts } public ExitStatus afterStep(StepExecution stepExecution) { try { // take actions after processing is finished } catch (Exception e) { stepExecution.addFailureException(e); stepExecution.setStatus(BatchStatus.FAILED); return ExitStatus.FAILED.addExitDescription(e); } return null; } } In the afterStep(StepExecution) method, you can check the outcome of the batch execution (completed, failed, stopped etc.) checking the ExitStatus, which can be accessed via StepExecution#getExitStatus(). You can even modify the ExitStatus by returning a new ExitStatus, This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 325 Devonfw Guide v2.4.0 which is something we will not discuss in further detail here. If you do not want to modify the ExitStatus, just return null. Throwing an exception in this method has no effect. If you want to fail the whole batch in case an exception occurs, you have to do an exception handling as shown above. This does not apply to the beforeStep method. For other types of listeners (among others the SkipListener mentioned already) see Spring Batch Reference Documentation - 5. Configuring a Step - Intercepting Step Execution. Note that exception handling for listeners is often a problem, because exceptions are mostly ignored, which is not always documented very well. If an important part of a batch is implemented in listener methods, you should always test what happens when exceptions occur. Or you might think about not implementing important things in listeners … If you want an exception to fail the whole batch, you can always wrap it in a FatalStepExecutionException, which will stop the execution. 52.2.10. Parameters The section on starting and stopping batches already showed how to start a batch with parameters. One way to get access to the values set is using the StepExecutionListener introduced in the section on listeners like this: public void beforeStep(StepExecution stepExecution) { String parameterValue = stepExecution.getJobExecution().getJobParameters(). getString("parameterKey"); } There are getter methods for strings, doubles, longs and dates. Note that when set via the CommandLineJobRunner or SpringBootBatchCommandLine, all parameters will be of type string unless the type is specified in brackets after the parameter key, e.g. processUntil(date)=2015/12/31. The parameter key here is "processUntil". Another way is to inject values. In order for this to work, the bean has to have step scope, which means there is a new object created for every execution of a batch step. It works like this: There has to be an appropriate setter method for the parameter of course. As already mentioned in the section on restarts, a batch that successfully completed with a certain set of parameters cannot be started once more with the same parameters as this would be considered a restart, which is not necessary, because the batch was already finished. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 326 Devonfw Guide v2.4.0 So using no parameters for a batch would mean that it can be started until it completes successfully once, which usually does not make much sense. As batches are usually not executed more than once a day, we purpose introducing a general "date" parameter (without time) for all batch executions. It is advisable to add the date parameter automatically in the JobLauncher if it has not been set manually, which can be done as shown below: private static final String DATE_PARAMETER = "date"; ... if (jobParameters.getDate("DATE_PARAMETER") == null) { Date dateWithoutTime = new Date(); Calendar cal = Calendar.getInstance(); cal.setTime(dateWithoutTime); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); dateWithoutTime = cal.getTime(); jobParameters = new JobParametersBuilder(jobParameters).addDate( DATE_PARAMETER, dateWithoutTime).toJobParameters(); ... // using the jobParametersIncrementer as shown above } Keep in mind that you might need to set the date parameter explicitly for restarts. Also note that automatically setting the date parameter can be problematic if a batch is sometimes started before and sometimes after midnight, which might result in a batch not being executed (as it has already been executed with the same parameters), so at least for productive systems you should always set it explicitly. The date parameters can also be useful for controlling the business logic, e.g. a batch can process all data that was created until the current date (as set in the date parameter), thereby giving a chance to control how much is actually processed. If your batch has to run more than once a day you could easily adapte the concept for timestamps. If you are using an external batch scheduler, they often provide a counter for the execution and you might automatically pass this instead of the date parameter. 52.2.11. Performance Tuning Most important for performance are of course the algorithms that you write and how fast (and scalable) these are, which is the same as for client processing. Apart from that, the performance of batches is usually closely related to the performance of the database system. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 327 Devonfw Guide v2.4.0 If you are retrieving information from the database, you can have one complex query executed in the ItemReader (via a DAO) retrieving all the information needed for the current set of items, or you can execute further queries in the ItemProcessor (or ItemWriter) on a per item basis to retrieve further information. The first approach is usually by far more performant, because there is an overhead for every query being executed and this approach results in less queries being executed. Note that there is a tradeoff between performance and maintainability here. If you put everything into the query executed by an ItemReader, this query can get quite complex. Using cursors instead of pagination as described in the section on ItemReaders can result in a better performance for the same reason: When using a cursor, the query is only executed once, when using pagination, the query is usually executed once per chunk. You could of course manually cache items, however this easily leads to a high memory consumption. Further possibilities for optimizations are query (plan) optimization and adding missing database indexes. 52.2.12. Testing This section covers how to unit and integration test in detail. Therefore we focus here on testing batches. In order for the unit test to run a batch job the unit test class must extends the AbstractSpringBatchIntegrationTest class. Two annotations are used to load the job’s ApplicationContext: @RunWith(SpringJUnit4ClassRunner.class): Indicates that the class should use Spring's JUnit facilities @SpringApplicationConfiguration(classes = {...}, locations = {...}): Indicates which JavaConfig classes (attribute 'classes') and/or XML files (attribute 'locations') contain the ApplicationContext. Use @ContextConfiguration(...) if Spring Boot is not used. @RunWith(SpringJUnit4ClassRunner.class) @DirtiesContext(classMode = ClassMode.AFTER_CLASS) @ActiveProfiles("db-plain") public abstract class AbstractSpringBatchIntegrationTest {..} @SpringApplicationConfiguration(classes= { SpringBootBatchApp.class }, locations = { "classpath:config/app/batch/beans-productimport.xml" }) public class ProductImportJobTest extends AbstractSpringBatchIntegrationTest {..} Testing Batch Jobs For testing the complete run of a batch job from beginning to end involves following steps: Set up a test condition, This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 328 Devonfw Guide v2.4.0 execute the job, Verify the end result. The test method below begins by setting up the database with test data. The test then launches the Job using the launchJob() method. The launchJob() method is provided by the JobLauncherTestUtils class. Also provided by the utils class is launchJob(JobParameters), which allows the test to give particular parameters. The launchJob() method returns the JobExecution object which is useful for asserting particular information about the Job run. In the case below, the test verifies that the Job ended with ExitStatus "COMPLETED". @SpringApplicationConfiguration(classes= { SpringBootBatchApp.class }, locations = { "classpath:config/app/batch/beans-productimport.xml" }) public class ProductImportJobTest extends AbstractSpringBatchIntegrationTest { @Inject private Job billExportJob; @Test public void shouldExportBills() throws Exception { JobExecution jobExecution = getJobLauncherTestUtils(this.billExportJob). launchJob(); assertThat(jobExecution.getExitStatus()).isEqualTo(ExitStatus.COMPLETED); } } Note that when using the launchJob() method, the batch execution will never be considered as a restart (i.e. it will always start from scratch). This is achieved by adding a unique (random) parameter. This is not true for the method launchJob(JobParameters) however, which will result in an exception if the test is executed twice or a batch is executed in two different tests with the same parameters. We will add methods for appropriately handling this situation in future releases of Devonfw. Until then you can help yourself by using the method getUniqueJobParameters() and then add all required parameters to those parameters returned by the method (as shown in the section on parameters). Also note that even if skips occurred, the ExitStatus is still COMPLETED. That is one reason why you should always checks weather the batch did what it was supposed to do or not. Testing Individual Steps For complex batch jobs individual steps can be tested. For example to test a createCsvFile, run just that particular Step. This approach allows for more targeted tests by allowing the test to set up data for just that step and to validate its results directly. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 329 Devonfw Guide v2.4.0 JobExecution jobExecution = getJobLauncherTestUtils(this.billExportJob).launchStep ("createCsvFile"); Validating Output Files When a batch job writes to the database, it is easy to query the database to verify the output. To facilitate the verification of output files Spring Batch provides the class AssertFile. The method assertFileEquals takes two File objects and asserts, line by line, that the two files have the same content. Therefore, it is possible to create a file with the expected output and to compare it to the actual result: private static final String EXPECTED_FILE = "classpath:expected.csv"; private static final String OUTPUT_FILE = " file:./temp/output.csv"; AssertFile.assertFileEquals(new FileSystemResource(EXPECTED_FILE), new FileSystemResource(OUTPUT_FILE)); Testing Restarts Simulating an exception at an arbitrary method in the code can be done relatively easy using AspectJ. Afterwards you should restart the batch and check if the outcome is still correct. Note that when using the launchJob() method, the batch is always started from the beginning (as already mentioned). Use the launchJob(JobParameters) instead with the same parameters for the initial (failing) execution and for the restart. Test your code thoroughly. There should be at least one restart test for every step of the batch job. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 330 Devonfw Guide v2.4.0 Chapter 53. Integrating Spring Data in OASP4J 53.1. Introduction This chapter specifies a possible solution for integrating the Spring Data module in devonfw. It is possible to have some services using the Spring Data module while some services use JPA/Hibernate. To integrate Spring Data in devonfw, there can be two approaches as stated below: 1. Create a new module for spring data 2. Integrate Spring Data in an existing core project The more feasible approach will be option 2 i.e to integrate it into an existing core project instead of creating a new module. Below are the reasons for not preferring a creation of a different module supporting spring data: • It does not follow KISS (Keep it simple, stupid) principle. In the existing structure of sample application, Entity classes along with some abstract implementation classes are included in oasp4j-samples-core project. You need to refer these classes while implementing spring-data. If you try to refer it in different module, it will become complex to compare it to the first approach. • If you integrate Spring Data in oasp4j, you need to annotate SpringBootApp.java class with @Enablejparepositories. If you create a different module, it will not be possible, as SpringBootApp class is in the core module. 53.2. Existing Structure of Data Access Layer Consider TableEntity as an example. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 331 Devonfw Guide v2.4.0 53.3. Structure of Data Access Layer with Spring Data This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 332 Devonfw Guide v2.4.0 Below are the steps, to integrate Spring Data in the data access layer: • Add dependency for Spring Data in pom.xml of oasp4j-samples-core project org.springframework.boot spring-boot-starter-data-jpa • Create Spring data Repository - Create interface which extends spring data repositories such as CRUDRepository or PagingAndSortingRepository and annotate it with @Repository annotation. Spring data have repositories such as CRUDRepository which provide the default CRUD functionality. @Repository Public interface TableRepo extends CrudRepository{ } • Create the class, annotate it with @Component annotation and autowire spring data repository created above. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 333 Devonfw Guide v2.4.0 @Component public class RegistrationBean { @Inject private TableRepo tableRepo; /** * The constructor. */ public RegistrationBean() { } /** * @return tableRepo */ public TableRepo getTableRepo() { return this.tableRepo; } /** * @param tableRepo the tableRepo to set */ public void setTableRepo(TableRepo tableRepo) { this.tableRepo = tableRepo; } } • Here, you are ready to test the functionality. Create a test class to test above changes. @SpringApplicationConfiguration(classes = { SpringBootApp.class }) @WebAppConfiguration @EnableJpaRepositories(basePackages = { "io.oasp.gastronomy.restaurant.tablemanagement.dataaccess.api.repo" }) @ComponentScan(basePackages = { "io.oasp.gastronomy.restaurant.tablemanagement.dataaccess.api.dao" }) public class TestClass extends ComponentTest { @Inject RegistrationBean registrationBean; /** * @return registerationBean */ public RegistrationBean getRegisterationBean() { This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 334 Devonfw Guide v2.4.0 return this.registrationBean; } /** * @param registerationBean the registerationBean to set */ public void setRegisterationBean(RegistrationBean registerationBean) { this.registrationBean = registerationBean; } /** * @param args */ @Test public void saveTable() { TableEntity table = new TableEntity(); table.setId(106L); table.setModificationCounter(1); table.setNumber(6L); table.setState(TableState.FREE); table.setWaiterId(2L); System.out .println("TableRepo instance *************************************************** " + getRegisterationBean()); TableEntity entity = getRegisterationBean().getTableRepo().save(table); System.out.println("entity id " + entity); } } Note: If you get DataIntegrityViolationExceptions while saving an object in a database, modify the script to auto_increment column id. The database should be able to auto increment column id as you have @GeneratedValue annotation in ApplicationPersistenceEntity. • Modify SpringBootApp.java class to scan the JPA repositories. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 335 Devonfw Guide v2.4.0 @SpringBootApplication(exclude = { EndpointAutoConfiguration.class }) @EntityScan(basePackages = { "io.oasp.gastronomy.restaurant" }, basePackageClasses = { AdvancedRevisionEntity.class }) @EnableGlobalMethodSecurity(securedEnabled = true) public class SpringBootApp { /** * Entry point for spring-boot based app * * @param args - arguments */ public static void main(String[] args) { SpringApplication.run(SpringBootApp.class, args); } } The above example shows how you can implement default functionalities. If you want to add custom functionalities, then you need to add custom repository and provide its implementation class. Also, you need to modify TableRepo to extend the custom repository. Below are the steps. Make note that, this is in continuation with previous example: Add custom repository as below in a repo package itself: public interface TableRepoCustom { /** * @param number * @return */ List findByTableState(int number); } • Create an implementation class for the above custom repository in a repo package itself. You have not annotated repository with any annotation, still Spring data will consider it as a custom repository. This is because spring data scan the repository package to search for any class and if it found one, then spring data consider it as a custom repository. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 336 Devonfw Guide v2.4.0 public class TableRepoImpl implements TableRepoCustom { @PersistenceContext private EntityManager entityManager; /** * {@inheritDoc} */ @Override public List findByTableState(int state) { String query = "select table from TableEntity table where table.state= " + state; System.out.println("Query " + query); List tableList = this.entityManager.createQuery(query). getResultList(); return tableList; } } • Modify test class to include above functionality This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 337 Devonfw Guide v2.4.0 @SpringApplicationConfiguration(classes = { SpringBootApp.class }) @WebAppConfiguration @EnableJpaRepositories(basePackages = { "io.oasp.gastronomy.restaurant.tablemanagement.dataaccess.api.repo" }) @ComponentScan(basePackages = { "io.oasp.gastronomy.restaurant.tablemanagement.dataaccess.api.dao" }) public class TestClass extends ComponentTest { @Inject RegistrationBean registrationBean; /** * @return registerationBean */ public RegistrationBean getRegisterationBean() { return this.registrationBean; } /** * @param registerationBean the registerationBean to set */ public void setRegisterationBean(RegistrationBean registerationBean) { this.registrationBean = registerationBean; } /** * @param args */ @Test public void saveTable() { TableEntity table = new TableEntity(); table.setId(106L); table.setModificationCounter(1); table.setNumber(6L); table.setState(TableState.FREE); table.setWaiterId(2L); System.out .println("TableRepo instance *************************************************** " + getRegisterationBean()); TableEntity entity = getRegisterationBean().getTableRepo().save(table); System.out.println("entity id " + entity); } @Test public void testFindByTableState() { List tableList = getRegisterationBean().getTableRepoImpl().findByTableState(0); System.out.println("tableList size ***************************** " + tableList.size()); } } With custom repository, you can implement functionality such as getrevisionHistory(). Additionally, spring data support @Query annotatio and derived query. Here, samples are attached for 2 entities This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 338 Devonfw Guide v2.4.0 (DrinkEntity, TableEntity) which are later implemented with spring data. 53.4. Query Creation in Spring Data JPA Below are the ways to create a query in Spring Data JPA: • Query creation by method names: List findByEmailAddressAndLastname(String emailAddress, String lastname); Above method is equivalent to the below query: select u from User u where u.emailAddress = ?1 and u.lastname = ?2 This is explained in the next section. • Using JPA Named Queries Example: @NamedQuery(name = "Drink.nonalcholic", query = "select drink from DrinkEntity drink where drink.alcoholic=false") • Using @Query annotation @Query(name = "table.query1", value = "select table from TableEntity table where table.state= :#{#criteria.state}") public Page findTablesDummy(@Param("criteria") TableSearchCriteriaTo criteria, Pageable pageable); Include above method in repository i.e TableRepo. • Native Queries - This Queries can be created using @Query annotation and setting nativeQuery=true • Similar to the criteria, you have Predicate from QueryDsl. This is explained in below section. 53.5. Query creation by method names Consider tablemanagement as an example. First, you will create a TableEntity class with attribute number, waiterId and state. To test query creation by method names, you will create new method findByState(TableState state) in TableRepo. This method will find table based on TableState provided. Follow below steps: • Create TableEntity class as below: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 339 Devonfw Guide v2.4.0 @Entity // Table is a reserved word in SQL/RDBMS and can not be used as table name @javax.persistence.Table(name = "RestaurantTable") public class TableEntity extends ApplicationPersistenceEntity implements Table { private static final long serialVersionUID = 1L; private Long number; private Long waiterId; private TableState state; @Override @Column(unique = true) public Long getNumber() { return this.number; } @Override public void setNumber(Long number) { this.number = number; } @Override @Column(name = "waiter_id") public Long getWaiterId() { return this.waiterId; } @Override public void setWaiterId(Long waiterId) { this.waiterId = waiterId; } @Override public TableState getState() { return this.state; } @Override public void setState(TableState state) { this.state = state; } } • In TableRepo create findByState(TableState state) method as below: @Repository public interface TableRepo extends JpaRepository, TableRepoCustom { // Query Creation By method names List findByState(TableState state); } • You will have RegistrationBean class as shown in the previous example. Now, you are ready to test the method findByState(TableState state). In test class, include below test method: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 340 Devonfw Guide v2.4.0 @Test public void testFindTableByState() { List tableList = getRegisterationBean().getTableRepo().findByState (TableState.FREE); System.out.println("tableList size " + tableList.size()); } 53.6. Implementing Query with QueryDsl Like the JPA Criteria API, it uses a Java 6 annotation processor to generate meta-model objects and produces a much more approachable API. Another good thing about the project is that, it not only has the support for JPA but also allows querying Hibernate, JDO, Lucene, JDBC and even plain collections. • To start with QueryDsl add below plugin in a pom.xml: com.mysema.maven apt-maven-plugin 1.1.1 generate-sources process com.mysema.query.apt.jpa.JPAAnnotationProcessor • Execute mvn clean install on the project. This will create special query classes e.g for DrinkEntity class generated will be QDrinkEntity. • To execute Querydsl predicates, you simply let your repository extend QueryDslPredicateExecutor Example: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 341 Devonfw Guide v2.4.0 @Repository public interface DrinkRepo extends JpaRepository, QueryDslPredicateExecutor, DrinkRepoCustom { /** * {@inheritDoc} */ @Override S save(S entity); } • You will have registrationBean class, which have above repository autowired in it. • Create test class and below method. @Test public void testFindNonAlcoholicDrinks() { QDrinkEntity drinkEntityEqu = QDrinkEntity.drinkEntity; BooleanExpression drink = drinkEntityEqu.alcoholic.isFalse(); List drinkList = (List) getDrinkEntityRegistrationBean().getDrinkRepo().findAll(drink); for (DrinkEntity drink1 : drinkList) { System.out.println("drink id " + drink1.getId() + " description: " + drink1 .getDescription()); } } This will return list of drink entities which are nonalcoholic. 53.7. Paging and Sorting Support • For Paging and Sorting support in Spring Data JPA, you should implement PagingAndSortingRepository. Create an interface as shown below: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 342 Devonfw Guide v2.4.0 @Repository public interface TableRepo extends JpaRepository, TableRepoCustom { /** * {@inheritDoc} */ @Override S save(S table); TableEntity findByNumber(long number); /** * {@inheritDoc} */ @Override Page findAll(Pageable pageable); @Query(name = "table.query", value = "select table from TableEntity table where table.state= ?1") Page findByTableState1(TableState state, Pageable pageable); } • Create test method as below: @Test public void testFindTableByState1() { PageRequest pageRequest = new PageRequest(0, 2, Direction.DESC, "state"); Page pageEntity = getRegisterationBean().getTableRepo().findByTableState1(TableState.FREE, pageRequest); List tableList = pageEntity.getContent(); for (TableEntity table : tableList) { System.out.println("Table details: " + table.getId() + " , " + table. getWaiterId() + " , " + table.getState()); } } In the above example, you are extending JpaRepository which in turn extends PagingAndSortingRepository. So, you will get paging and sorting functionality. For Paging and Sorting support, you need to pass Pageable as method Parameter. PageRequest pageRequest = new PageRequest(0, 2, Direction.DESC, "state"); //Here 0 - indicate page number. //2 - object on a page //Direction Desc or ASC- Sorting sequence Desc or Asc //State - this is a property based on which query gets sorted This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 343 Devonfw Guide v2.4.0 For creating pageRequest object, you have different constructors available as below: PageRequest(int PageRequest(int PageRequest(int PageRequest(int page,int size) page,int size,int sort) page,int size,Direction direction) page, int size, Direction direction, String... properties) 53.8. References https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/ http://docs.spring.io/spring-data/jpa/docs/1.4.1.RELEASE/reference/html/jpa.repositories.html http://javabeat.net/spring-data-jpa-querydsl-integration/ This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 344 Devonfw Guide v2.4.0 Chapter 54. WebSocket WebSocket is a protocol providing full-duplex communication channels over a single TCP connection. WebSocket is designed to be implemented in web browsers and web servers, but it can be used by any client or server application. The WebSocket Protocol is an independent TCP-based protocol. Its only relationship to HTTP is that its handshake is interpreted by HTTP servers as an Upgrade request. The WebSocket protocol makes more interaction between a browser and a website possible, facilitating the real-time data transfer from and to the server. For more information you can visit wikipedia. 54.1. WebSocket configuration In Devonfw a websocket endpoint is configured within the business package as a Spring configuration class. We can find an example in the salesmanagement use case of the OASP4J example application. package io.oasp.gastronomy.restaurant.salesmanagement.websocket.config; ... @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/topic"); config.setApplicationDestinationPrefixes("/sample"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/websocket/positions").withSockJS(); } ... } The @Configuration annotation indicates that this is a Spring configuration class. The @EnableWebSocketMessageBroker annotation makes Spring Boot registering this endpoint and enables the WebSocket support. The AbstractWebSocketMessageBrokerConfigurer implements the WebSocketMessageBrokeConfigurer interface that allows to customize the imported configuration. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 345 Devonfw Guide v2.4.0 The method configureMessageBroker() WebSocketMessageBrokerConfigurer to configure overrides the the message default broker. It method starts by in calling enableSimpleBroker() to enable a simple memory-based message broker to carry the messages back to the client on destinations prefixed with "/topic". The setApplicationDestinationPrefixes defines the application name using which browser and server will communicate over WebSocket. The registerStompEndpoints registers the "/websocket/positions" endpoint and enables SockJS fallback options so that alternative messaging options may be used if WebSocket is not available. SockJS is a Javascript library which provides websocket like object for browsers and provides cross browser compatibility and support for STOMP protocol. SockJS works in the way that we need to provide URL to connect with message broker and then get the stomp client to communicate. STOMP is Streaming Text Oriented Messaging Protocol. A STOMP client communicates to a message broker which supports STOMP protocol. STOMP uses different commands like connect, send, subscribe, disconnect, etc. to communicate. You can see a complete example of how to build an interactive web application using WebSocket here This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 346 Devonfw Guide v2.4.0 Chapter 55. File Upload and Download Apache CXF is an open source services framework. CXF helps you to build and develop services using front-end programming APIs, like JAX-WS and JAX-RS. 55.1. File download from CXF org.apache.cxf provides the option to download files of different MIME (Multipurpose Internet Mail Extensions) types. Example: In JAX-RS, annotate the service method with @Produces("application/octet-stream"). You can define an interface for the service and annotate it with JAX-RS annotations: public interface DownloadService { @SuppressWarnings("javadoc") @Produces("application/octet-stream") @GET @Path("/downloadFile") public Response getDownloadableFile() throws SQLException, IOException; } And here is a simple implementation of the service: @Override public Response getDownloadableFile() throws SQLException, IOException { // FILE_PATH - specifies the location of the file in the file system. File file = new File("FILE_PATH"); ResponseBuilder response = Response.ok((Object) file); // FILENAME.FILE_EXTENSION - specifies the filename and its extension after download. response.header("Content-Disposition", "attachment; filename=FILENAME.FILE_EXTENSION"); return response.build(); } In the above code snippet, a file object is constructed by supplying path of the file to be downloaded. ResponseBuilder is created from this file object and finally the response is built from ResponseBuilder object. Following table explains the annotations used for the download functionality: Table 10. Annotations used for download functionality Annotation Description This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 347 Devonfw Guide v2.4.0 @Produces It is used to specify the MIME media type that can be produced and sent back to the client. @GET It is a request method designator defined by JAXRS and corresponds to the similarly named HTTP method. @PATH This identifies the URI path template to which the resource responds, and is specified at the class level of a resource. 55.1.1. Produces Annotation @Produces annotation is used to specify the MIME media type that can be produced and sent back to the client. If @Produces is applied at the class level, all the methods in a resource produce the specified MIME type by default. If applied at the method level, the annotation overrides any @Produces annotation applied at the class level. If no methods in a resource are able to produce the MIME type in a client request, the JAX-RS runtime sends back an HTTP "406 Not Acceptable" error. The value of @Produces is an array of String of MIME types. For example: @Produces({"image/jpeg,image/png"}) The following example shows how to apply @Produces at both, the class and method levels: @Path("/myResource") @Produces("text/plain") public class SomeResource { @GET public String doGetAsPlainText() { ... } @GET @Produces("text/html") public String doGetAsHtml() { ... } } 55.2. File upload from CXF org.apache.cxf provides option to upload files of different MIME (Multipurpose Internet Mail Extensions) type. Example: In JAX-RS, annotate the service method with @Consumes("multipart/mixed"). You can define an interface for the service and annotate it with JAX-RS annotations: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 348 Devonfw Guide v2.4.0 import javax.ws.rs.core.MediaType; public interface UploadService { @SuppressWarnings("javadoc") @Consumes(MediaType.MULTIPART_FORM_DATA) @GET @Path("/uploadFile") public Response uploadFile(List attachments) throws SQLException, IOException; } And here is a simple implementation of the service: Add annotation @Transactional at class level and inject UcManageBinaryObject class object. @Transactional public class implClassName{ @Inject private UcManageBinaryObject ucManageBinaryObject; /** * @return ucManageBinaryObject */ public UcManageBinaryObject getUcManageBinaryObject() { return this.ucManageBinaryObject; } } Web Service method implementation: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 349 Devonfw Guide v2.4.0 @POST @Consumes ("multipart/mixed") // use @Consumes(MediaType.MULTIPART_FORM_DATA) if request contains multi-part form data and import //javax.ws.rs.core.MediaType; @Path ("/upload") public Response uploadFile(List attachments) { BinaryObjectEto binaryObject = new BinaryObjectEto(); Blob blob = null; for (Attachment attachment: attachments) { DataHandler handler = attachment.getDataHandler(); try { InputStream stream = handler.getInputStream(); OutputStream outputStream = new ByteArrayOutputStream(); IOUtils.copy(stream, outputStream); byte[] byteArray = outputStream.toString().getBytes(); if (byteArray != null && byteArray.length != 0) { blob = new SerialBlob(byteArray); getUcManageBinaryObject().saveBinaryObject(blob, binaryObject); } } catch (SQLException e) { throw new SQLException(e.getMessage(), e); } catch (IOException e) { throw new IOException(e.getMessage(), e); } } return Response.ok("file uploaded").build(); } In the above code snippet, uploaded attachments are iterated and InputStream for each attachment is extracted. Each InputStream is converted into the byte array and a Blob object is created out of it. The Blob object is saved into the database by calling saveBinaryObject(blob, binaryObject). Following table explains the annotations used for upload functionality: Table 11. Annotations used for upload functionality Annotation Description @Consumes It is used to specify MIME media types that can be accepted, or consumed, from the client. @GET It is a request method designator defined by JAXRS and corresponds to the similarly named HTTP method. @PATH This identifies the URI path template to which the resource responds, and is specified at the class level of a resource. 55.2.1. Consumes Annotation The @Consumes annotation is used to specify MIME media types that can be accepted, or This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 350 Devonfw Guide v2.4.0 consumed, from the client. If @Consumes is applied at the class level, all the response methods accept the specified MIME type by default. If applied at the method level, @Consumes overrides any @Consumes annotation applied at the class level. If a resource is unable to consume the MIME type of a client request, the JAX-RS runtime sends back an HTTP 415 ("Unsupported Media Type") error. The value of @Consumes is an array of String of acceptable MIME types. For example: @Consumes({"text/plain,text/html"}) The following example shows how to apply @Consumes at both, the class and method levels: @Path("/myResource") @Consumes("multipart/related") public class SomeResource { @POST public String doPost(MimeMultipart mimeMultipartData) { ... } @POST @Consumes("application/x-www-form-urlencoded") public String doPost2(FormURLEncodedProperties formData) { ... } } 55.3. MIME Types MIME stands for "Multipurpose Internet Mail Extensions". It is a way of identifying files on the Internet, according to their nature and format. For example, using the "Content-type" header value defined in an HTTP response, the browser can open the file with the proper extension/plugin. For more information, visit: http://www.freeformatter.com/mime-types-list.html This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 351 Devonfw Guide v2.4.0 Chapter 56. Docker 56.1. Introduction Docker is an open source project to wrap, ship and run software using containers. The docker containers run independently of the hardware or platform you are using, and the application wrapped doesn’t require to be built on a specific language or framework. That makes them great building blocks for deploying and scaling network applications like web servers, databases, mail servers with a small amount of effort. 56.2. Install Docker on Windows If you do not use Windows 10, you can not run Docker directly on your machine. Rather, you need a virtual machine, with a specific Linux distribution on it. Docker offers a ready to use toolbox that automates this process. In order to run a virtual machine, your machine needs virtualization enabled. This can be done in your BIOS settings. For a Lenovo T450, this works as shown below. This will work similar for other models: • Shut down your computer • Turn on your computer and keep hitting the button F1 • You will hear some kind of beep and the BIOS screen will pop up • Navigate to the Security tab, then Virtualization and hit enter • Enable Intel ® Virtualization Technology and hit F10 and confirm the changes by hitting 'Y' option • Boot your machine Now, install Docker Toolbox (full installation is recommended). When the installation is done, start Docker Quickstart Terminal to see if everything works. As soon as you see some whale and a command prompt, you can try the following command: docker run hello-world. This will download and run a container from docker hub and print some message, if this was successful. 56.3. Use Docker manually As docker (boot2docker) runs on virtualized machine, you may need to share folders with this virtual machine (VM). Start Oracle VM VirtualBox. You should see a machine called default. If you have started the Docker Quickstart Terminal prior to this, then the default should be running. In order to add a shared folder, you have to shut down the machine. You can do this by right clicking on default, close, power off. Then, click on default and go to Settings, Shared Folders. On the right side, you can click on Adds new shared folder. In the folder path, go to Other and navigate to the folder on your machine that you wish to share with the VM (e.g. c:\dockershare) and type a name (e.g. dockershare). Toggle Auto-mount and Make Permanent and save the changes. Now, you can either start the VM out of Oracle VM VirtualBox, or again start the Docker Quickstart Terminal. It is advised to select the second option, as you can keep your keyboard layout in the language of the host machine. If you have chosen to start the Quickstart Terminal, then ssh into the VM with This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 352 Devonfw Guide v2.4.0 docker-machine ssh default. From the Linux VM, you can execute docker commands, as docker is installed on that distribution. Now, create a folder which contains the shared files and mount the shared folder: sudo mkdir /dockershare sudo mount -t vboxsf dockershare /dockershare cd /dockershare/ If there are files in this folder on your Windows host, you can see them with ls. To manually create a container with the sample application, copy a bootable oasp4j-sample-server.war in the shared folder. Docker uses Dockerfile to create images, which can be started as containers. Therefore, create a file called Dockerfile (no ending) in the shared folder, with the following content: FROM java:openjdk-7-jre COPY /oasp4j-sample-server.war ../oasp4j-sample-server.war EXPOSE 8080 This will use a java 7 image as a base, which gets downloaded from the public Docker repository and copy the application in the root folder of the VM. When the container is started, Docker exposes the port 8080 towards the VM. Now, go back to the Quickstart Terminal. In the shared folder, you can type docker build –t java7 . (Don’t forget the full stop) to build an image with the above Dockerfile. You can now run the container with docker run -v /dev/urandom:/dev/random –p 8080:8080 java7 java –jar oasp-sample-server.war. The –p 8080:8080 maps the port of the boot2docker to the port 8080 of the VM. The command -v /dev/urandom:/dev/random specifies where to get random numbers. Utilizing urandom drastically speeds up the boot process of a container in a docker environment. When the application is started, you can connect with the IP of the VM. You can find out the IP of the VM by typing docker-machine ip in a console of your host machine. In the above case, IP is 192.168.99.100, so the sample app can be accessed in a browser of the Windows host with 192.168.99.100:8080. To list all running containers types, use docker ps command. In order to stop a running container, one can use docker stop nameOfContainer. 56.4. Fabric8io plugin Various maven plugins enable the dockerziation within the process of building the application. In the devon sample server, this is shown using the fabric8io/docker-maven-plugin, integrated as an extra profile called docker. Executing mvn -Pdocker install • Executes the build process, • Grabs the bootable war file, • Builds an image based on the configuration in the server/pom.xml and the server/src/main/docker/assembly.xml, • Deploys the image depending on the environment variables e.g. To a local instance of docker, • Starts the application, test if it is reachable through an URL and finally This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 353 Devonfw Guide v2.4.0 • Stops the container. In detail, if you want to test this on your local machine, you need to have docker installed as mentioned above. In the console, type docker-machine env to see the IP and other properties of your VM. To set the environment variables, so that the plugin deploys the application into your local docker instance, type the last line of that output (something like eval$("C:\Program Files\Docker
Toolbox\docker-machine.exe" env)). Now, navigate to your workspace, where the project is located.
In the devon/sample folder, you can now execute mvn -Pdocker install and see how the process
starts. If you only want to build the container, you can navigate into /devon/sample/server and
execute mvn -Pdocker package docker:build or start the container with mvn -Pdocker docker:start.

56.5. Docker tips
56.5.1. Run WAR file on Tomcat
If you have a WAR file and want to run on Tomcat without installing you can use docker and mount
the WAR file under the /usr/local/tomcat/webapps/yourapp.war.
docker
run
--name
tomcat
-it
-p
8080:8080
path_to_your/app.war:/usr/local/tomcat/webapps/app.war tomcat:8-alpine

--rm

-v

c:/

The format for Docker for Windows is c:/ whereas if using Docker Toolbox with Virtualbox you
should use /c/Users/

354

Devonfw Guide v2.4.0

Chapter 57. Production Line for Beginners
(Practical Guide)
57.1. Production Line description
The Production Line is a set of tools that will allow us to trigger some actions like testing, packaging,
building, storage, etc. This will help us to improve our development having a quick and real-time
feedback about how good is our work according to some pre-set rules.

57.2. Jenkins
Jenkins will allow us to create those processes that will evaluate, test, package, build or whatever
action we might need. Ideally, those processes should be triggered every time we push some
changes to the repository of the project we want to pass through the Production Line.

57.2.1. Jobs
A Jenkins job will execute a single and specific action. For example, we want to ng build an
Angular2 project. We could do it by creating a Jenkins job that fetches the repository and builds
what’s on it.

The way of creating it consists in add the URL of the location of the repository we’re working on. In
this case is the Production Line’s Gerrit, but it could equally be Git or SVN (necessary little bit of
further work with SVN, but valid as well).

Then, add to our Job all the tools we need to use the commands (necessary to have them previously
installed in our instance either as a general tool or as a custom tool), in that case we’d need NodeJS
and Angular-CLI:

355

Devonfw Guide v2.4.0

Finally, it’s time to set up our script, which is the main part of the Jenkins Job. In that script we tell
our Job to, for example, install (via NodeJS) all the dependencies of the project on the PL’s
workspace, build the project (via Angular-CLI) as a distributable set of files and at the end, serve it
to an external server (for example, why not?) via ssh connection, and perform some Docker actions
some credentials for it.

If everything is correctly set up, we should build our Jenkins Job and wait for it to give us some
feedback.

57.2.2. Pipelines
Let’s thing about pipelines as a set of jobs. Using pipelines will give us good feedback about each
process of the chain as well.

A pipeline will mostly consist in a Jenkins script (wrote in Groovy) that will define every single task
or process that we want to trigger for our project. We’ll define each process as a stage and one
could be used, for example, to trigger project’s tests, another one to build or package the project,
and another one to deploy this built project to somewhere.

356

Devonfw Guide v2.4.0

There is a helpful guide to write some pipeline commands. Just press "Pipeline Syntax", below the
script box.

57.3. Nexus
57.3.1. Upload artifacts to a Nexus repository
This process seems quite interesting as a final stage of our pipeline’s script. It makes sense to trigger
project’s tests, then build and package the application and then publish it to a repository where
someone could download it and use it, or just have it as a backup. This process will be defined from
our project’s pom.xml file. There is a section called  that will tell Maven
where to upload the created project’s artifact.

Having this, we’ll store two artifacts on the Nexus repositories every time we package our project
on a Production Line job or pipeline. In case of this pipeline, we build an Angular2 project as a
JS/HTML project. After we store it on Nexus, we can always download it and publish it wherever we
want.

357

Devonfw Guide v2.4.0

Surely there will be some cases on we will need to store something on a Nexus repository we’ve
created before. For example, for Sencha projects, we need to retrieve the license if we want to
compile or build the project. We will follow the next steps:
• 1. Get the project from our repository (Gerrit/GitLab/GitHub…)
• 2. Get the license from Nexus (ext.zip)
• 3. Follow with every extJS command we need
• 4. …
So, the question is: How we upload a file on a Nexus repository of the PL? There is an API to deal
with it:

358

Devonfw Guide v2.4.0

This command below will upload the file ext.zip on the nexus repository with the url http://urlto-your-nexus-repository using the credentials myusername/mypassword.

359

Devonfw Guide v2.4.0

Chapter 58. Production Line
58.1. Introduction
The continuous delivery is a software engineering approach in which teams produce software in
short cycles, ensuring that the software can be reliably released at any time. It aims at building,
testing, and releasing software faster and more frequently. The approach helps to reduce the cost,
time, and risk of delivering the changes, by allowing more incremental updates into the
applications in production.
Continuous delivery treats the notion of a deployment pipeline based on a mistake-proofing
approach: a set of validations through which a piece of software must pass on its way to release.
Code is compiled if necessary and then packaged by a build server every time a change is
committed to a source control repository, then tested by a number of different techniques (possibly
including manual testing) before it can be marked as releasable.

58.2. Continuous Delivery benefits
The practices at the heart of continuous delivery help us achieve several important benefits:
• Low risk releases. The primary goal of continuous delivery is to make software deployments
painless, low-risk events that can be performed at any time, on demand. With continuous
delivery can be relatively straightforward to achieve zero-downtime deployments that are
undetectable to users.
• Faster time to market. It’s not uncommon for the integration and the test/fix phase of the
traditional phased software delivery lifecycle to consume weeks or even months. When teams
work together to automate the build and deployment, environment provisioning, and
regression testing process, developers can incorporate integration and regression testing into
their daily work and these phases will be completely removed. It is always preferred to avoid

360

Devonfw Guide v2.4.0

the large amounts of re-work that plague the phased approach.
• Higher quality. When developers have automated tools that discover regressions within
minutes, teams are free to focus on their efforts on user research and higher level testing.
• Lower costs. By investing in build, test, deployment and environment automation, the cost of
making and delivering incremental changes to software is substantially reduced. Moreover,
many of the fixed costs associated with the release process are eliminated.
• Better products. Continuous delivery makes possible to get feedback from users throughout
the delivery lifecycle based on working software. This means we can avoid the 2/3 of the
features we build that deliver zero or negative value to our businesses.
• Happier teams. By removing the low-value painful activities associated with software delivery,
we can focus on what we care about most—continuously delighting our users.

58.3. The Production Line
The Production Line is the set of methods and tools to facilitate the implementation of the
continuous delivery methodology in a Devon project, covering all the phases involved in the
application development cycle from requirements to testing and hand-off to the client.
Created to make the easier inclusion of all the continuous delivery tools in a project, the Production
Line will be automated and provisioned within few hours from the moment a project starts,
providing access to the set of tools over the Production Line interface on http://devon.s2eu.capgemini.com.
After logging in, the tools can be accessed over a drop down menu (called Services) in the top menu
bar.

58.3.1. Prerequisites
To implement the Production Line in your project, you only need:
• A Production Line instance.
• A Remote Linux host for deployment.

58.4. Devonfw Continuous Delivery infrastructure
58.4.1. Tools
Git
Git is a version control system, that helps a software team to manage the changes in a source code
over time. Version control software keeps the track of every modification in the code. It allows
restoring the state to the earlier versions of the code or working on the parallel features of the
software using branches.

361

Devonfw Guide v2.4.0

Gerrit
Gerrit is a code collaboration tool. It hosts the Git repository and extends available functionality. It
implements the voting protocol, allowing automated code review by software tools as well as
manual acceptance by a reviewer.
Gerrit can be placed in between the repository and the user’s code push request to provide the
ability to discuss a change before submitting.
Jenkins
Jenkins is an automation engine with a great plugin ecosystem to support the majority of the tools
surrounding continuous integration, automated testing or continuous delivery. It provides tools for
scheduling and automating the whole build process for the Devonfw apps managing the trigger and
build processes.
SonarQube
SonarQube is a tool for continuous inspection of code quality, preventing redundancies, complexity
and aiming to approach to the code conventions and good practices. It performs static code analysis
and allows gathering reports of the various tests performed on the application. Provides a single
point with web GUI, where developers can check the test results.
Maven
Maven is a build automation tool used primarily for the Java projects. Was originally created to
achieve a clear definition of how to build an ANT project. Over the time, thanks to the community
support and its plugin system, it evolved into a fully functional JAVA project management system.
Within the continuous integration, the build process of the Devonfw applications is executed
through Maven and only initiated by Jenkins.
Nexus
Nexus is a repository providing centralized storage place for the JAVA artifacts – JAR / WAR files
containing built applications.
Tomcat
Tomcat is an open-source Java Servlet Container that implements several Java EE specifications,
including Java Servlet, JavaServer Pages (JSP), Java EL, and WebSocket, and provides a "pure Java"
HTTP web server environment in which Java code can run.
Docker
Docker is a lightweight virtualization software allowing wrapping the applications into containers –
running images with all the prerequisites and dependencies needed for the application to run. By
letting go of the operating system burden, through the usage of the underlying host operating
system, Docker containers can be started almost instantly. Additionally, Docker provides a set of
tools that support management of the containers, hosting image repositories and many others.

362

Devonfw Guide v2.4.0

58.4.2. Schema
The continuous delivery concept is applied in the context of Devonfw apps with Jenkins as the core
of the process and the rest of tools surrounding it.
The following schema shows the infrastructure of the tools used for the Devonfw Continuous
Integration and their relations.

• A change in the project’s git repository is registered (commit, push).
• Jenkins, as we just mentioned the core of continuous integration, gets triggered by that changes.
• Then, it builds and tests the project using Maven
• The resulting artifacts can be either deployed to a Nexus repository or to an app container
(Docker, Tomcat).
• During the integration process a SonarQube instance manages the project’s source quality.
If some of these stages fails or doesn’t fit few requirements, all the process can be frozen until a
solution is included in the content of the project. Once this happens, complete process will start
again.

58.5. Production Line implementation for Devonfw
projects
58.5.1. Continuous Delivery Pipeline
While preparing the process of the automated build and testing, a good practice is to organize the
development processes of the project in the form of the pipeline, that provides a clear view of its
stages. This pipeline is reflected in Jenkins job stages and facilitates organization and issue
identification.
Below, you can find the continuous delivery pipeline used in a basic Devonfw app:

363

Devonfw Guide v2.4.0

1. Code commits into source version control tool, triggers the Jenkins job. Alternatively, it can be
triggered manually.
2. The environment is prepared for the deployment – the prerequisites are checked and
provisioned if not met.
3. Code is being built using Maven. During the build, the code checking tests are executed.
4. When the tests are finished successfully, the artifact and Docker ready image are sent to the
repository, ready to be deployed in the staging environment.
5. When the environment is ready, Jenkins automatically deploys image from the repository.
6. After the application deployment, automatic tests are executed for the verification of the actual
version on the test instance.
7. After the whole process, the environment is cleared, releasing hardware resources for the next
run.
In terms of the tools, the previous schema could be represented as

Using the Pipeline plugin, it is possible to implement the Continuous Delivery pipeline as a
Jenkinsfile, so the Jenkins job definition is treated as another piece of code checked into source
control. The Jenkins jobs are each of the runnable tasks that are controlled or monitored by
Jenkins.
This approach allows easy scalability and replicability of Jenkins implementation.
So, thanks to the Production Line the continuous delivery methodology can be included as part of
the development of a Devonfw project achieving reliable releases, faster time to market, higher
quality, lower costs and ultimately better products.

364

Devonfw Guide v2.4.0

Chapter 59. Devon in Bluemix
59.1. Introduction
59.1.1. Platform as a service
Platform as a service (PaaS) is a category of cloud computing services that provides a platform
allowing customers to develop, run, and manage applications without the complexity of building
and maintaining the infrastructure typically associated with developing and launching an app.

59.1.2. Cloud Foundry
Cloud Foundry is an open source, multi cloud application platform as a service (PaaS) governed by
the Cloud Foundry Foundation, a 501(c)(6) organization.
Cloud Foundry supports the full lifecycle, from initial development, through all testing stages, to
deployment. It is therefore well-suited to the continuous delivery strategy. Users have access to one
or more spaces, which typically correspond to a lifecycle stage. For example, an application ready
for QA testing might be pushed (deployed) to its project’s QA space. Different users can be restricted
to different spaces with different access permissions in each.

59.1.3. Bluemix
IBM Bluemix is a cloud PaaS developed by IBM. It supports several programming languages and
services as well as integrated DevOps to build, run, deploy and manage applications on the cloud.
Bluemix is based on Cloud Foundry open technology and runs on SoftLayer infrastructure. Bluemix
supports several programming languages, including Java, Node.js, Go, PHP, Swift, Python, Ruby
Sinatra, Ruby on Rails and can be extended to support other languages such as Scala through the
use of buildpacks.

59.2. Bluemix Services
The Services dashboard provides access to the Bluemix services available from IBM® and thirdparty providers. These include Watson, Internet of Things, Analytics, Mobile, and DevOps services.
See more about Bluemix Services here.

59.2.1. VCAP services
The VCAP_SERVICES environment variable is a JSON object that contains information that you can
use to interact with a service instance in Bluemix. The information includes service instance name,
credential, and connection URL to the service instance. These values are populated into the
VCAP_SERVICES environment variable when your application is bound to a service instance in
Bluemix.
The value of the VCAP_SERVICES environment variable is available only when you bind a service
instance to your application. You can view the application environment variables by using the
following command:

365

Devonfw Guide v2.4.0

cf env APP_NAME

59.2.2. VCAP services on Devon
A simple example below demonstrates how to work with VCAP services in Devon. In this scenario, a
dashDB database service is configured in the Devon application. So, you can obtain the database
credentials from VCAP service.
The JSON that Bluemix provides us with the VAP_SERVICE variable is the next:

{
"dashDB": {
"name": "service-instance-name",
"label": "dashDB",
"plan": "Entry",
"credentials": {
"port": 50000,
"db": "BLUDB",
"host": "23.246.206.254",
"https_url": "https://23.246.206.254:8443",
"hostname": "23.246.206.254",
"jdbcurl": "jdbc:db2://23.246.206.254:50000/BLUDB",
"ssljdbcurl": "jdbc:db2://23.246.206.254:50001/BLUDB:sslConnection=true;",
"uri": "db2://***:***@23.246.206.254:50000/BLUDB",
"dsn":
"DATABASE=BLUDB;HOSTNAME=23.246.206.254;PORT=50000;PROTOCOL=TCPIP;UID=***;PWD=***;",
"ssldsn":
"DATABASE=BLUDB;HOSTNAME=23.246.206.254;PORT=50001;PROTOCOL=TCPIP;UID=***;PWD=***;Secu
rity=SSL;"
}
}
}

NOTE

You can find more details about dashDB service connection and VAP_SERVICE here.

Now, in the Devon application, you can simply create a configuration class and configure the
DataSource:

366

Devonfw Guide v2.4.0

@Configuration
public class BeanJPAConfiguration {
public String url;
public String driverClassName = "com.ibm.db2.jcc.DB2Driver";
@Bean
@Primary
public DataSource dataSource() {
String VCAP_SERVICES = System.getenv("VCAP_SERVICES");
if (VCAP_SERVICES != null) {
setDataSourceProperties(VCAP_SERVICES);
}
return DataSourceBuilder.create()
url(this.url).
driverClassName(this.driverClassName).
build();
}
public void setDataSourceProperties(String VCAP_SERVICES) {
JSONObject jsonObj = new JSONObject(VCAP_SERVICES);
JSONArray jsonArray;
if (jsonObj.has("dashDB")) {
jsonArray = jsonObj.getJSONArray("dashDB");
// Transform the JSONArray to JSONObject because JSONArray can't find by string
key
jsonObj = jsonArray.toJSONObject(new JSONArray().put("dashDB"));
jsonObj = jsonObj.getJSONObject("dashDB");
}
if (jsonObj.has("credentials")) {
jsonObj = jsonObj.getJSONObject("credentials");
if (jsonObj != null) {
if (jsonObj.has("jdbcurl")) {
this.url = jsonObj.getString("jdbcurl");
} else if (jsonObj.has("ssljdbcurl")) {
this.url = jsonObj.getString("ssljdbcurl");
}
}
}
}
}

367

Devonfw Guide v2.4.0

As you can see, the check is applied to make sure that if VCAP_SERVICES exist. So, if it doesn’t exist,
you can configure the other database, throw an error, etc.
This is a simple way to use Bluemix services, you can see Spring Cloud Foundry too.
In the example, you are learning how to obtain the credentials of the database. If
NOTE

you want to know how to configure a DB2/dashDB database, you can see more
details here.

59.3. Logs in Bluemix
59.3.1. Devon logging
Devon uses OASP logging module as a logging system. The module uses SLF4J API and the Logback
implementation and the OASP wiki contains an excellent entry explaining its configuration.
By default, the logging system uses the following configuration:

In the above configuration, each log level is written in its own file.
Each appender has its own XML configuration file that can be found on:
NOTE

https://github.com/oasp/oasp4j/tree/develop/modules/logging/src/main/resources/io/
oasp/logging/logback.

368

Devonfw Guide v2.4.0

59.3.2. Bluemix logging
Bluemix does not allow users to navigate through any log files and it uses its own tool to look into
the logs. The Bluemix environment has a console to show the logs. Due to this fact, the default
configuration for the Devon logging system is not appropriate for the environments like Bluemix.
To adapt the logging system, Devon users are required to make certain changes in the default
configuration. The file appenders have no sense in this environment, so they must be removed. The
following example, could be a valid configuration:

The above configuration is intended to write all the logs equal or superior to DEBUG level in the
Bluemix console.

369

Devonfw Guide v2.4.0

Chapter 60. Configuring and Running
Bootified WAR
60.1. Steps
1. Integrate Server application with client(Angular/Sencha) application as per the instructions
given below.
◦ https://github.com/oasp/oasp4js/wiki/tutorial-jspacking-angular-client
◦ https://github.com/devonfw/devon-guide/wiki/getting-started-deployment-on-tomcat
2. Comment out the following configuration in BaseWebSecurityConfig.java

SimpleUrlAuthenticationSuccessHandler()).defaultSuccessUrl("/")
1. Exclude static content from core module by adding following line to build section of pom.xml

static/*.html
1. Add redirection to client application from server context by adding following method to

@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView getJSClientHome() {
return new ModelAndView("redirect:/jsclient/index.html");
}
1. Allow

access

to

application

context

root

by

"/"

to

unsecured

URLs

in

BaseWebSecurityConfig.java.
2. Replace targetPath in Server module build with code below

static/jsclient
1. Set

Application

context

path

to

core/src/main/resources/application.properties

oasp4j-sample-server
and

in

/oasp4j-sample/oasp4j-sample-

core/src/test/resources/config/application.properties like below

370

Devonfw Guide v2.4.0

server.context-path=/oasp4j-sample-server
1. Access application on following link.

>http://localhost:8080/oasp4j-sample-server

371

Devonfw Guide v2.4.0

Chapter 61. Deployment on Wildfly
Following describes the steps to install and configure Wilfly 10.x
2. Place the extracted folder under {DISTRIBUTION_PATH}/software folder.
3. Run console.bat under {DISTRIBUTION_PATH}.
4. Navigate to path {DISTRIBUTION_PATH}/software/{extracted wildfly folder}/bin
5. Run standalone.bat file.

It will start the administration console at http://localhost:9990/console. If the administration console
Administration console is up and running at http://localhost:9990/console/App.html#home

• Click on start (highlighted in below screenshot)

372

Devonfw Guide v2.4.0

• Click on Add button (highlighted in below screenshot)

• Choose .war file for the deployment.

373

Devonfw Guide v2.4.0

• Successful deployment

Execute below mentioned steps to create .war file :
• start

a

new

oasp4j

project

from

-DarchetypeGroupId=io.oasp.java.templates
archetype:generate

the

template

mvn

-DarchetypeVersion=2.5.0

-DarchetypeArtifactId=oasp4j-template-server

-DgroupId=io.oasp.application

-DartifactId=sampleProj

-Dversion=0.1

-SNAPSHOT -Dpackage=io.oasp.application.sampleProj
• On SpringBootApp.java
◦ Remove the exclude=xxxx option on the @SpringBootApplication annotation

374

Devonfw Guide v2.4.0

◦ Remove the @EnableGlobalMethodSecurity annotation
• Remove the SpringBootBatchApp file.
• In pom.xml

org.apache.cxf
cxf-spring-boot-starter-jaxrs
3.1.10

javax.xml.ws
jaxws-api
2.2.11

javax.xml.ws
jaxws-api
2.2.11

Remove below dependency:

org.springframework.boot
spring-boot-starter-actuator

-->
Modify below dependency:

375

Devonfw Guide v2.4.0

org.springframework.boot
spring-boot-starter-web

org.apache.tomcat.embed
tomcat-embed-websocket

• create a file for a new rest controller

package io.oasp.application.sampleapp.general.service.impl.rest;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.springframework.stereotype.Component;
@Component
@Path("/hello")
public class HelloWorldEndpoint {
@GET
@Path("/world")
@Produces(MediaType.TEXT_PLAIN)
public String test() {
return "Hello world!";
}
}
• server logback.xml comment non console loggers:

• Build your application with command "mvn clean install" and create the .war file and deploy it
to wildfly 10 Administration console and start the application. You can hit the URL based on the
context root of your project which is deployed on Wildfly.

376

Devonfw Guide v2.4.0

• To test the webservice created on server, hit the URL http://localhost:8080/sampleProj-server0.0.1-SNAPSHOT/services/rest/hello/world on browser.

377

Devonfw Guide v2.4.0

Chapter 62. Deploy oasp4j application to
WebSphere Liberty
Following describes the steps to install and configure WebSphere Liberty in Eclipse Neon that is
provided as part of the Devonfw distribution and also details the steps to deploy oasp4j sample
application created from oasp4j maven archetype template onto WebSphere Liberty.

62.1. Setup and Configure WebSphere Liberty
Launch Eclipse Neon from devonfw distribution and
• Go to Help → Eclipse MarketPlace and search for ‘WebSphere Liberty’ and hit ‘Enter’. In the
results screen, select ‘IBM WebSphere Application Server Liberty Developer Tools’ and click
‘install’ button.
(16.0.0.4) with Java EE 7 Web Profile'.
• To add new Websphere Liberty server, Go to ‘Servers’ View in eclipse and click on ‘New’ and in
the screen that is displayed (see below), please select 'WebSphere Application Server Liberty' as
the server type and click 'Next' button.

• In the screen that is displayed (see below), assuming that WebSphere Liberty is installed at
'D:\Users\vkiran\D_Backup\softwares\WS-Liberty\wlp' , click the radio button 'Choose an existing
installation', and browse the Path 'D:\Users\vkiran\D_Backup\softwares\WS-Liberty\wlp' and
click 'Next' button.

378

Devonfw Guide v2.4.0

• In the screen that is displayed (see below), select the 'server type' as 'Stand-alone server' and
enter server name in the text box 'Server name' and click 'Finish' button.

• Create a sample oasp4j application by referring the link . Alternatively, you can run the
following command inside devonfw distribution.
• Assuming distribution is at D:\Devon2.1.0\Devon-dist_2.1.0, run the command console.bat inside

379

Devonfw Guide v2.4.0

this directory and then run the following command

mvn -DarchetypeVersion=2.5.0 -DarchetypeGroupId=io.oasp.java.templates
-DarchetypeArtifactId=oasp4j-template-server archetype:generate -DgroupId
=io.oasp.application -DartifactId=libertyTest -Dversion=0.1-SNAPSHOT -Dpackage
=io.oasp.application.libertyTest
• Add the following entries in the pom.xml of the server module just before the closing project tag

...

com.sun.xml.bind
jaxb-impl
provided

com.sun.xml.bind
jaxb-core
provided

org.springframework.boot
spring-boot-starter-tomcat
provided

org.apache.tomcat
tomcat-jdbc
provided

org.apache.tomcat.embed
tomcat-embed-el
provided

javax.ws.rs
javax.ws.rs-api
provided

xml-apis
xml-apis
provided

380

Devonfw Guide v2.4.0

org.hibernate.javax.persistence
hibernate-jpa-2.1-api
provided

javax.annotation
javax.annotation-api
provided

javax.inject
javax.inject
provided

...
• In …../general/service/impl/config/WebConfig.java, please remove the import statement 'import
org.apache.catalina.filters.SetCharacterEncodingFilter;'

and

the

import

'import

in

…

org.springframework.web.filter.CharacterEncodingFilter;'
• Code

inside

method

../general/service/impl/config/WebConfig.java

setCharacterEncodingFilter
should

be

changed

accordingly

to

use

org.apache.catalina.filters.SetCharacterEncodingFilter as follows :

....
....
CharacterEncodingFilter setCharacterEncodingFilter = new CharacterEncodingFilter();
setCharacterEncodingFilter.setEncoding("UTF-8");
setCharacterEncodingFilter.setForceEncoding(false);
....
....
• Create an empty file flyway.location inside the directory core\src\main\resources\db\migration\
• Do ‘mvn clean install’ of the complete project
• Open server.xml of Websphere Liberty and add the following features,

381

Devonfw Guide v2.4.0

...

webProfile-7.0
localConnector-1.0
jaxb-2.2
jaxws-2.2

...
• Deploy the war file on to the Websphere Liberty Profile and start the server.

• Once the application is published on to WebSphere Liberty, application url is logged in the
Websphere console. Use this url and launch the application in browser.

382

Devonfw Guide v2.4.0

Chapter 63. Deployment on WebLogic
Make the following changes to OASP4J Server-client application for deployment on WebLogic:

63.1. Steps
1. Create

an

empty

weblogic.xml

file

under

directory

oasp4j/samples/server/src/main/webapp/WEB-INF/ and paste the code below into it. weblogic.xml
set application deployment configurations for WebLogic server.

1. Define OSP4J application libraries and resources that should be given preference over
WebLogic server libraries in weblogic.xml under prefer-application-packages.

org.slf4j
org.jboss.logging.*
javax.ws.rs.*
com.fasterxml.jackson.*
org.apache.neethi.*

org/jboss/logging/Logger.class
META-INF/services/*

1. To avoid multiple logging libraries exclude logback-classic dependency from spring-boot-starterweb

383

Devonfw Guide v2.4.0

org.springframework.boot
spring-boot-starter-web

org.springframework.boot
spring-boot-starter-tomcat

org.springframework.boot
spring-boot-starter-validation

ch.qos.logback
logback-classic

1. Resolve unique REST method error on WebLogic by changing the @Path as given below.
◦ Change URL from @Path("/bill/{billId}/payment") to @Path("/bill/{billId}/paymentdata") for
method

doPayment(@PathParam("billId")

long

billId,

PaymentData

paymentData)

in

SalesmanagementRestService.java.
◦ Change

URL

from

@Path("/table/")

to

@Path("/createtable/")

for

method

TableEto

createTable(TableEto table) in TablemanagementRestService.java

63.2. Sencha
1. Enable CORS.
◦ Set corsEnabled to true in BaseWebSecurityConfig.
◦ Set

security.cors.enabled

to

true

in

/oasp4j-sample-

core/src/main/resources/application.properties.
2. Change server details in ExtSample/app/Config.js accroding to WebLogic server.

63.3. Packaging
1. Package your application by Executing the command below from within oasp4j/samples project

> mvn clean package -P-embedded,jsclient

384

Devonfw Guide v2.4.0

Chapter 64. Cobigen advanced use cases:
SOAP and nested data
64.1. Introduction
In Devonfw we have a server-side code generator called Cobigen. Cobigen is capable of creating
CRUD code from an entity or generate the content of the class that defines the user permissions.
Cobigen is distributed in the Devonfw distribution as an Eclipse plugin, and is available to all
Devonfw developers. If you want to go deeper in Cobigen you can visit the documentation of the
here. Previous versions of CobiGen is able to generate code for REST services only.Now it is possible
to generate SOAP services code with it.
There are two usecases available in CobiGen:
1. CobiGen with SOAP functionality (without nested data)
2. CobiGen with SOAP functionality with nested data

64.2. CobiGen with SOAP functionality (without nested
data)
To generate SOAP services with CobiGen follow below steps:
• If you are using cobigen for first time, setup CobiGen following instructions on
https://github.com/devonfw/devon-guide/wiki/getting-started-Cobigen#preparing-cobigen-forfirst-use .
• Clone latest code of CobiGen tool from https://github.com/devonfw/tools-cobigen.git
• Import ${workspace_path}\tools-cobigen\cobigen-templates\templates-oasp4j code in eclipse. • Once Cobigen is setup, we can start generating code. • To generate SOAP code (without nested data) you need to select below options as shown in figure: • Once above options are selected cobigen will generate code for SOAP services as well. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 385 Devonfw Guide v2.4.0 • To check if code is actually generated, check service package, it should have soap package inside it. Also in soap services which are generated will do CRUD operations on respective entity and will return ETO classes. 64.3. CobiGen with nested data What is nested data? Consider there are 3 or more entities which are interrelated with each other; we will generate a code which will return this relationship along with attributes in it i.e currently cobigen services return ETO classes, we have enhanced CobiGen to return CTO classes (ETO + relationship). Steps to generate code with CobiGen are as below: • If you are using cobigen for first time , setup cobigen following instructions on https://github.com/devonfw/devon-guide/wiki/getting-started-Cobigen#preparing-cobigen-forfirst-use . • Clone latest code of CobiGen tool from https://github.com/devonfw/tools-cobigen.git • Import${workspace_path}\tools-cobigen\cobigen-templates\templates-oasp4j code in eclipse.
• Once Cobigen is setup, we can start generating code.
• To generate SOAP code with nested data you need to select below options as shown in figure:

Here we need to select option with nested prefix.
• Once above options are selected cobigen will generate code for SOAP services as well.
• To check if code is actually generated, check service package, it should have soap package inside
it. Also in soap services which are generated will do CRUD operations on respective entity and
you will have one method returning CTO class of that entity.

386

Devonfw Guide v2.4.0

Chapter 65. Compatibility guide for JAVA and
TOMCAT
65.1. What this guide contains
As, we have migrated to eclipse neon, which mandatorily uses Java8, if any project or user wants to
use JAVA7, this guide will assist to do so. We also have integrated TOMCAT8 with the Devon
distribution and oasp4j IDE. Therefore, it also describes the steps required to use Tomcat7 with the
distributions.

65.2. Using JAVA7

65.2.2. Change installed jre in eclipse
When you open the eclipse, follow the below steps to change installed jre preferences:
1. Go to Preferences

2. Go to Installed Jres

387

Devonfw Guide v2.4.0

3. Browse for java7

4. Apply changes

After following the above instructions, you can import projects or create new ones, and build using
java7.

388

Devonfw Guide v2.4.0

65.3. Using java8
One can use distribution as is, and there is no extra configuration needed for java8.

65.4. Using Tomcat8
As mentioned earlier in the guide, distribution comes with Tomcat8 by default, so no changes are
required to run the applications with tomcat8.

65.5. Using Tomcat7 for deploying

65.6. Linux and Windows Compatibility
So, the above mentioned steps on java7 and tomcat7 compatibility, apply to devonfw distributions
of Windows OS as well as Linux.
Linux and Windows distribution works by default on JAVA8 and TOMCAT8.

389

Devonfw Guide v2.4.0

Chapter 66. Dockerfile for the maven based
spring.io projects
66.1. Overview
Docker containers are created by using [base] images. An image can be basic, with nothing but the
operating-system fundamentals, or it can consist of a sophisticated pre-built application stack ready
for launch.
When building your images with docker, each action taken (i.e. a command executed such as aptget install) forms a new layer on top of the previous one. These base images then can be used to
create new containers.
In this wiki, we will see about automating this process as much as possible, as well as demonstrate
the best practices and methods to make most of docker and containers via Dockerfiles: scripts to
build containers, step-by-step, layer-by-layer, automatically from a source (base) image.

66.1.1. Docker in Brief
The docker project offers higher-level tools which work together, built on top of some Linux kernel
features. The goal is to help developers and system administrators port applications. - with all of
their dependencies conjointly - and get them running across systems and machines headache free.
Docker achieves this by creating safe, LXC (i.e. Linux Containers) based environments for
applications called “docker containers”. These containers are created using docker images, which
can be built either by executing commands manually or automatically through Dockerfiles.

66.1.2. Dockerfiles
Each Dockerfile is a script, composed of various commands (instructions) and arguments listed
successively to automatically perform actions on a base image in order to create (or form) a new
one. They are used for organizing things and greatly help with deployments by simplifying the
process start-to-finish.
Dockerfiles begin with defining an image FROM which the build process starts. Followed by various
other methods, commands and arguments (or conditions), in return, provide a new image which is
to be used for creating docker containers.

66.2. Dockerfile Commands
Dockerfile consists of two kind of main line blocks: comments and commands + arguments.

66.2.1. FROM
The FROM command initializes a new build stage and sets the Base Image for subsequent

390

Devonfw Guide v2.4.0

# Usage: FROM [image name]
FROM ubuntu

ADD Commands Copy a file from the host into the container.

# Usage: ADD [source directory or URL] [destination directory]

66.2.3. CMD
This command set default commands to be executed, or passed to the ENTRYPOINT.

# Usage 1: CMD application "argument", "argument", ..
CMD "echo" "Hello docker!"

66.2.4. RUN
The RUN command will execute any commands in a new layer on top of the current image and
commit the results. The resulting committed image will be used for the next step in the Dockerfile.

# Usage: RUN [command]
RUN mvn install

66.2.5. WORKDIR
The WORKDIR directive is used to set where the command defined with CMD is to be executed.

# Usage: WORKDIR /path
WORKDIR ~/
MAINTAINER The MAINTAINER set the author / owner data of the Dockerfile.

# Usage: MAINTAINER [name]

66.2.6. EXPOSE
Expose a port to outside.

391

Devonfw Guide v2.4.0

# Usage: EXPOSE [port]
EXPOSE 8080

66.2.7. VOLUME
The VOLUME command is used to enable access from your container to a directory on the host
machine (i.e. mounting it). Example:

# Usage: VOLUME ["/dir_1", "/dir_2" ..]
VOLUME ["/my_files"]

66.3. Multi-stage builds
In Docker, one of the main issues is the size of the final image. It’s not uncommon to end up with
images over 1 GB even for simple Java applications. Since version 17.05 of Docker, it’s possible to
have multiple builds in a single Dockerfile, and to access the output of the previous build into the
current one. Those are called [multi-stage builds](https://docs.docker.com/engine/userguide/engimage/multistage-build/). The final image will be based on the last build stage.
Let’s imagine the code is hosted on GitHub, and that it’s based on Maven. Build stages would be as
follows:
• Clone the code from GitHub.
• Copy the folder from the previous stage; build the app with Maven.
• Copy the JAR/WAR from the previous stage; run it with java -JAR/WAR .

66.3.1. 1. Create the Dockerfile
This docker build file can be used for building any oasp4j web app with the following features:
• The source code is hosted on GitHub.
• The build tool is Maven.
• The resulting output is an executable JAR/WAR file.
File: Dockerfile

392

Devonfw Guide v2.4.0

# Stage 1. Git clone
FROM alpine/git AS clone
ARG url
WORKDIR /app
RUN git clone ${url} # Stage 2. Maven build FROM maven:3.5-jdk-8-alpine AS build ARG project WORKDIR /app COPY --from=clone /app/${project} /app
RUN mvn install
# Stage 3. Run Spring Boot
FROM openjdk:8-jre-alpine
ARG artifactId
ARG version
ENV artifact ${artifactId}-server-bootified.war WORKDIR /app COPY --from=build /app/server/target/${artifact} /app
EXPOSE 8080
ENTRYPOINT ["sh", "-c"]
CMD ["java -jar ${artifact}"] Note that url, project, artifactId and version are arguments that must be passed on the command line. artifact must be set as an environment variable with ENV, so it is persisted in the final app image and can be used at runtime by java. 66.3.2. 2. Build the image The Spring Boot app image can now be built using the following command-line. Please change the parameters as per your project, e.g.:$ docker build --build-arg url=https://github.com/username/java-getting-started.git
--build-arg project=java-getting-started --build-arg artifactId=java-getting-started
--build-arg version=1.0 -t java-getting-started .

66.3.3. 3. Run a new container
Run the image built with the previous command:

$docker run -d -p 8090:8080 java-getting-started This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 393 Devonfw Guide v2.4.0 66.3.4. Example The next example shows how to create a Dockerfile to build and run a container running the server from [My Thai Star](https://github.com/oasp/my-thai-star) application. Rather than using arguments like in the previous example, the data (git repo url, project name, …) is set directly into the Dockerfile. 66.3.5. Sample Dockerfile File Name: Dockerfile # 1. Clone the project code FROM alpine/git AS clone WORKDIR /app RUN git clone https://github.com/Himanshu122798/mtsj.git # 2. Copy the project folder from the previous build stage and build the app with maven FROM maven:3.5-jdk-8-alpine AS build WORKDIR /app COPY --from=clone /app/mtsj /app RUN mvn install #3. Copy the war file from the previous build stage and run the app with java FROM openjdk:8-jre-alpine WORKDIR /app COPY --from=build /app/server/target/mtsj-server-bootified.war /app EXPOSE 8080 ENTRYPOINT ["sh", "-c"] CMD ["java -jar mtsj-server-bootified.war"] 66.3.6. Build the Docker image Build the Docker image from the same folder as Dockerfile using this command (including the dot .)$ docker build -t mtsj .
Where the option -t mtsj is used to tag the image name.

66.3.7. Run the container
Use this command to run the Spring Boot application.

394

Devonfw Guide v2.4.0

$docker run --name mtsj0 -p 8090:8080 mtsj Where the options: • --name mtsj0 specifies the container name • -p 8090:8080 maps the port 8080 of the container to 8090 The command docker ps lists all the running containers: λ docker ps CONTAINER ID STATUS fb0c6836838b Up 43 seconds IMAGE COMMAND PORTS NAMES mtsj "sh -c 'java -jar ..." 0.0.0.0:8090->8080/tcp mtsj0 CREATED 44 seconds ago The application is now running on http://localhost:8090/mythaistar/. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 395 Devonfw Guide v2.4.0 Chapter 67. Introduction to generatorjhipster-DevonModule JHipster (2) is a code generator based on Yeoman generators (1). Its default generator generatorjhipster generates a specific JHipster structure. The purpose of generator-jhipster-DevonModule is to generate the structure and files of a typical OASP4j project. It is therefore equivalent to the standard OASP4j application template based Cobige code generation. This module was made in order to comply with strong requirements (especially from the French BU) to use jHipster for code generation. Note: the term module refers to the generator-jhipster-DevonModule, our generator. The term project refers to our project where we want to generate files, i.e. a Devon project. 67.1. Requirements • Node & Npm (https://nodejs.org/en/download/, fyi: https://www.npmjs.com/get-npm) • Yarn (https://yarnpkg.com/lang/en/docs/install/) [npm install –global yarn] 67.2. Download and install the generator-jhipsterDevonModule Clone generator-jhipster-DevonModule from git: http://gitlab-val.es.capgemini.com/gitlab/ADCenter/ jhipster-devon You can save the project in any folder you want. Use a cmd as admin and navigate (“cd”) into your folder where you cloned the module. Execute both commands after each other: yarn install yarn link After yarn link JHipster will print “You can now run yarn link "generator-jhipster-DevonModule" in the projects where you want to use this module and it will be used instead”. Note: Yarn link registers the generator-module in yarn. The module only needs to be linked (registered) once. There is no need to reinstall or relink the module when you update it (i.e. through git pull). This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 396 Devonfw Guide v2.4.0 67.3. Registering the generator-jhipster-DevonModule in your project As the generator-jhipster-DevonModule is registered now, it can be used everywhere. This means, in any folder – for example the folder which stores a Devon project. To use the generator-jhipsterDevonModule, use a cmd and navigate (“cd”) into your project folder. Execute yarn link generator-jhipster-DevonModule Note: you can type this or copy it from the JHipster message you got after linking the module. 67.4. Generate files As the generator-jhipster-DevonModule is now connected to the project, it can be used for generating files. Execute (replacing [YourEntityName] by your own entity name) yo jhipster-DevonModule:entity <> 67.4.1. Some questions need to be answered now: ? Enter the package name. The package name must be the same as the devon project >> enter your package name: i.e. com.cap.devonproject ? Do you want to add a field to your entity? (Y/n) >> if you want to add a field, press y. If not, n. ? What is the name of your field? >> enter the name of your field: i.e. name (Remember the common rules as lowercase fieldnames) ? What is the type of your field? (Use arrow keys) >> choose the type of your field: i.e. String ? Do you want to add validation rules to your field? (y/N) >> If you want to add some validation to your field, press y. If not, n. ? Which validation rules do you want to add? (Press to select, to toggle all, to inverse selection) >> select the validation rules you want to use. Depends on the rule you get another question to answer accordingly (i.e. minimum length). You get a preview of your entity: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 397 Devonfw Guide v2.4.0 More fields can be added the same way – one after each other. At the end JHipster will print a summary like this: Now all files regarding the entity called Moto are in place and can be used. When generating the same entity a second time, there are 3 options: regenerate, add more fields or remove fields. Depending on the chosen option, more questions (as explained above) will be asked to enter i.e. the new field name. 67.5. Interesting links 1. Yeoman: http://yeoman.io/learning/, https://github.com/yeoman/yo 2. JHipster: http://www.jhipster.tech/ This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 398 Devonfw Guide v2.4.0 Chapter 68. Cookbook OSS Compliance This chapter helps you to gain transparency on OSS usage and reach OSS compliance in your project. 68.1. Preface devonfw, as most Java software, makes strong use of Open Source Software (OSS). It is using about 150 OSS products on the server only and on the client even more. Using a platform like devonfw to develop your own custom solution requires handling contained OSS correctly, i.e acting OSScompliant. Please read the Open Source policy of your company first, e.g. the Capgemini OSS Policy which contains a short, comprehensive and well written explanation on relevant OSS-knowledge. Make sure you: • understand the copyleft effect and its effect in commercial projects • understand the 3 license categories: "permissive", "weak copyleft" and "strong copyleft" • know prominent license types as e.g. "Apache-2.0" or GPL-3.0" and what copyleft-category they are in • are aware that some OSS offer dual/multi-licenses • Understand that OSS libraries often come with sub-dependencies of other OSS carying licenses themselfes To define sufficient OSS compliance measures, contact your IP officer or legal team as early as possible, especially if you develop software for clients. 68.2. Obligations when using OSS If you create a custom solution containing OSS, this in legal sense is a "derived" work. If you distribute your derived work to your business client or any other legal entity in binary packaged form, the license obligations of contained OSS get into effect. Ignoring these leads to a license infringement which can create high damage. To carefully handle these obligations you must: • maintain an OSS inventory (to gain transparency on OSS usage and used licenses) • check license conformity depending on usage/distribution in a commercial scenario • check license compatibility between used OSS-licenses • fulfill obligations defined by the OSS-licenses Obligations need to be checked per license. Frequent obligations are: • deliver the license terms of all used versions of the OSS licenses • not to change any copyright statements or warranty exclusions contained in the used OSS This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 399 Devonfw Guide v2.4.0 components • deliver the source code of the OSS components (e.g. on a data carrier) • when modifying OSS, track any source code modification (including date and name of the employee/company) • display OSS license notice in a user frontend (if any) • other obligations depending on individual license 68.3. Automate OSS handling Carefully judging the OSS usage in your project is a MANUAL activity! However, collecting OSS information and fulfilling license obligations should be automated as much as possible. A prominent professional tool to automate OSS compliance is the commercial software "Blackduck". Unfortunately it is rather expensive - either purchased or used as Saas. The most recommended lightweight tooling is a combination of Maven plugins. We will mainly use the Mojo Maven License Plugin. 68.4. Configure the Mojo Maven License Plugin You can use it from commandline but this will limit the ability to sustainably configure it (shown later). Therefore we add it permanently as a build-plugin to the project parent-pom like this (already contained in OASP-parent-pom): This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 400 Devonfw Guide v2.4.0 org.codehaus.mojo license-maven-plugin 1.14${project.build.directory}/generated-resources
true
true

true

true

Apache-2.0|Apache 2.0
Apache-2.0|The Apache Software License, Version 2.0

utf-8

In the config above there are several settings that help to permanently improve the result of an
automated OSS scan. We explain these now.

Sometimes the licenses of used OSS cannot be resolved automatically. That is not the mistake of the
maven-license-tool, but the mistake of the OSS author who didn’t make the respective licenseinformation properly available.

401

Devonfw Guide v2.4.0

# - ASF 2.0
# - Apache 2
...
...
dom4j--dom4j--1.6.1=BSD 3-Clause
javax.servlet--jstl--1.2=CDDL
...
In case the use of "missing files" is activated, but the THIRD-PARTY.properties-file is not yet existing,
the first run of an "aggregate-add-third-party" goal (see below) will fail. Luckily the license-plugin
just helped us and created the properties-files automatically (in each maven-subproject) and
prefilled it with:
• a list of all detected licenses within the maven project
• all OSS libraries where a license could not be detected automatically.
You now need to fill in missing license information and rerun the plugin.

In case automatically detected licenses proof to be wrong by closer investigation, this wrong
detection can be overwritten. Add a configuration to declare alternative licenses within each

com.sun.mail--javax.mail--1.5.6=Common Development and Distribution License 1.1
This can be also be useful for OSS that provides a multi-license to make a decision which license to
actually choose .

You will see that many prominent licenses come in all sorts of notations, e.g. Apache-2.0 as: "Apache
2" or "ASL-2.0" or "The Apache License, Version 2.0". The Mojo Maven License Plugin allows to
harmonize different forms of a license-naming like this:

402

Devonfw Guide v2.4.0

Apache-2.0|Apache 2.0
Apache-2.0|The Apache Software License, Version 2.0

License-names will be harmonized in the OSS report to one common term. We propose to
harmonize to short-license-IDs defined by the SPDX standard.

For a quick initial judgement of OSS license situation run the following maven command from
commandline:

$mvn license:license-list You receive the summary list of all used OSS licenses on the cmd-out. 68.6. Create an OSS inventory To create an OSS inventory means to report on the overall bill of material of used OSS and corresponding licenses. Within the parent project, run the following maven goal from command line.$ mvn license:aggregate-download-licenses
1. a license.xml that contains all used OSS depenencies (even sub-dependencies) with respective
2. puts all used OSS-license-texts as html files into folder target/generated resources
Carefully validate and judge the outcome of the license list. It is recommended to copy the
license.xml to the project documentation and hand it over to your client. You may also import it
into a spreadsheet to get a better overview.

68.7. Create a THIRD PARTY file
Within Java software it is a common practice to add a "THIRD-PARTY" text file to the distribution.
Contained is a summary-list of all used OSS and respective licenses. This can also be achieved with
Within the parent project, run the following maven goal from command line.

403

Devonfw Guide v2.4.0

$mvn license:aggregate-add-third-party Find the THIRD-PARTY.txt in the folder: target\generated-resources. The goal aggregate-add-thirdparty also profits from configuration as outlined above. 68.8. Download and package OSS SourceCode Some OSS licenses require handing over the OSS source code which is packaged with your custom software to the client the solution is distributed to. It is a good practice to hand over the source code of all used OSS to your client. Collecting all source code can be accomplished by another Maven plugin: Apache Maven Dependency Plugin. It downloads all OSS Source Jars into the folder: \target\sources across the parent and all child maven projects. You configure the plugin like this: org.apache.maven.plugins maven-dependency-plugin 3.0.2 sources false${project.build.directory}/sources

src-dependencies
package

copy-dependencies

You run the plugin from commandline like this:

$mvn dependency:copy-dependencies The plugin provides another goal that also unzips the jars, which is not recommended, since contents get mixed up. Deliver the OSS source jars to your client with the release of your custom solution. This has been This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 404 Devonfw Guide v2.4.0 done physically - e.g. on DVD. 68.9. Handle OSS within CI-process To automate OSS handling in the regular build-process (which is not recommended to start with) you may declare the following executions and goals in your maven-configuration: ... aggregate-add-third-party generate-resources aggregate-add-third-party aggregate-download-licenses generate-resources aggregate-download-licenses Note that the build may fail in case the OSS information was not complete. Check the build-output to understand and resolve the issue - like e.g. add missing license information in the "missing file". This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 405 Devonfw Guide v2.4.0 Chapter 69. Topical Guides for Service Layers This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 406 Devonfw Guide v2.4.0 Chapter 70. OASP4J 70.1. A Closer Look This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 407 Devonfw Guide v2.4.0 Chapter 71. Creating New OASP4J Application In this chapter we are going to show how to create a new Oasp4j application from scratch. The app that we are going to create is Jump the Queue, a simple app to avoid the queue in an event by registering a visitor and obtaining an access code. You can read all the details of the app in the Jump the Queue Design page. 71.1. Getting Devonfw distribution The recommended way to start developing Oasp4j applications is to get the latest Devonfw distribution to have a ready-to-start development environment. You can download Devonfw distributions from TeamForge. Once the download is done extract the zip file and you will get the distribution’s content as shown below: 71.2. Initialize the distribution Before creating our first project you must initialize the distribution. To do so execute the scripts This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 408 Devonfw Guide v2.4.0 create-or-update-workspace.bat and then update-all-workspaces.bat Now you should see the conf directory and the Eclipse launchers. NOTE Learn more about Devonfw initialization here 71.3. Create the Server Project First, in the workspaces directory of the distribution create a new folder jumpthequeue and go into it workspaces>mkdir jumpthequeue workspaces> cd jumpthequeue We are going to generate the new Oasp4j project using Devcon. To launch the tool run the 'console.bat' script, in the new opened command line window execute the command devcon -g You should see the Devcon's GUI. Select oasp4j and create Then we only need to define our server app path (for the location of the app select our just created jumpthequeue directory), name, groupid, package, version and dbtype(h2|postgresql|mysql|mariadb|oracle|hana|db2). Finally click on Start button. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 409 Devonfw Guide v2.4.0 Once you see the BUILD SUCCESS info message your new app is ready. You can also create new projects: NOTE • manually from command line see how • from Eclipse see how 71.4. Import and Run the Application As last step we can import the project we just created into the Eclipse IDE provided with Devonfw distribution. Although our new Oasp4j based app is still empty we are going to show how to run it with Spring Boot simply to check that everything is ok. We could use the eclipse-main.bat or the eclipse-examples.bat launchers (that you should see on your distribution’s root directory) but we are going to create a new Eclipse launcher related to our new project. To do it launch again the script update-all-workspaces.bat After the process is done you should see a new eclipse-jumpthequeue.bat launcher. Execute it and a new Eclipse instance should be opened. Now import our new project with File > Import. Select Maven/Existing Maven Projects This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 410 Devonfw Guide v2.4.0 Browse for the jumpthequeue project This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 411 Devonfw Guide v2.4.0 Click Finish and wait while the dependencies of the project are resolved to complete the import process. Now let’s change the server context path of our application. Open /jumpthequeue- core/src/main/resources/config/application.properties and set the server.context-path property to /jumpthequeue server.context-path=/jumpthequeue You can also change the port where the application will be available with the NOTE property server.port Finally, using Spring Boot features (that provides us with an embedded Tomcat), we can run the app in an easy way. Look for the SpringBootApp.java class and click right button and select Run As > Java Application. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 412 Devonfw Guide v2.4.0 If everything is ok you will see a messages in the Console window like INFO [main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8081 (http) INFO [main] com.cap.jumpthequeue.SpringBootApp : Started SpringBootApp in 16.978 seconds (JVM running for 17.895) The app will be available at 'http://localhost:8081/jumpthequeue' This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 413 Devonfw Guide v2.4.0 NOTE You are redirected to the login screen because, by default, the new Oasp4j applications provide a basic security set up. 71.5. What is generated? Creating Oasp4j based apps, we get the following main features out-of-the-box: • Maven project with api project, core project and server project: ◦ api project for the common API ◦ core project for the app implementation ◦ server project ready to package the app for the deployment • Data base ready environment with an h2 instance • Data model schema • Mock data schema • Data base version control with Flyway This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 414 Devonfw Guide v2.4.0 • Bean mapper ready • Cxf services pre-configuration This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 415 Devonfw Guide v2.4.0 • Basic security enabled (based on Spring Security) • Unit test support and model This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 416 Devonfw Guide v2.4.0 This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 417 Devonfw Guide v2.4.0 Chapter 72. OASP4J Application Structure 72.1. The OASP4J project Using the Oasp4j approach for the Java back-end project, we will have a structure of a main Maven project formed by two sub-projects: In the core project we will store all the logic and functionality of the application. The server project configures the packaging of the application. 72.2. The components In early chapters we have mentioned that the Oasp4j applications should be divided in different components that will provide the functionality for the different features of the application. Following the naming convention [Target]management being the Target the main entity that we want to manage. The components, as part of the logic of the app, are located in the core project of the app. In the case of My Thai Star we need to show the different available dishes, we need to manage the booking and the orders and we need to create new users. So the application will be divided in the following components: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 418 Devonfw Guide v2.4.0 72.3. The component structure (layers) Each component of the app is internally divided following the three-layer architecture (service, logic and dataaccess) that Oasp4j proposes. So we will have three different packages to order our component’s elements: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 419 Devonfw Guide v2.4.0 Chapter 73. OASP4J Architecture OASP4J provides a solution for industrialized web apps based on components and a three-layers architecture. A component is a package that contains the services and logic related to one feature of the app. Each component will be divided in three layers: service, logic and dataacess. • Service Layer: will expose the REST api to exchange information with the client applications. • Logic Layer: the layer in charge of hosting the business logic of the application. • Data Access Layer: the layer to communicate with the data base. Finally the Oasp4j applications provide a general package to locate the cross-cutting functionalities such as security, logging or exception handling. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 420 Devonfw Guide v2.4.0 Chapter 74. OASP4J Components 74.1. Overview When working with Oasp4j the recommended approach for the design of the applications is the Component Oriented Design. Each component will represent a significant part (or feature) of our application related to CRUD operations. Internally, the components will be divided in three layers (service, logic, and dataaccess) and will communicate in two directions: service with database or, in the logic layer, a component with other component. 74.1.1. Principles The benefits of dividing our application in components are: • Separation of concerns. • Reusability. • Avoid redundant code. • Information hiding. • Self contained, descriptive and stable component APIs. • Data consistency, a component is responsible for its data and changes to this data shall only happen via the component. 74.1.2. Naming In Oasp4j, as a convention, we will name our components with the name of the target entity This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 421 Devonfw Guide v2.4.0 followed by management term. Foomanagement 74.2. OASP4J Component example My Thai Star is an application of a restaurant that allows booking tables, and order different dishes so the main Oasp4j components are: • dishmanagement: This component will manage the dishes information retrieving it from the db and serving it to the client. It also could be used to create new menus. • bookingmanagement: Manages the booking part of the application. With this component the users (anonymous/logged in) can create new reservations or cancel an existing reservation. The users with waiter role can see all scheduled reservations. • ordermanagement: This component handles the process to order dishes (related to reservations). A user (as a host or as a guest) can create orders (that contain dishes) or cancel an existing one. The users with waiter role can see all ordered orders. • usermanagement: Takes care of the User Profile management, allowing to create and update the data profiles. Apart from that components we will have other packages for the cross-cutting concerns: • general: is a package that stores the common elements or configurations of the app, like security, cxf services or object mapping configurations. • imagemanagement: in case of functionalities that will be used in several components, instead of duplicate the functionality (code) we can extract it to a component that the other components will consume. In the case of the images, as both dishmanagement and usermanagement components are going to need to manage images, this imagecomponent will be used for that purpose. • mailservice: with this service we will provide the functionality for sending email notifications. This is a shared service between different app components such as bookingmanagement or ordercomponent. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 422 Devonfw Guide v2.4.0 74.2.1. OASP4j Component Structure The component will be formed by one package for each one of the three layers that are defined by the Oasp4j architecture: service, logic and dataaccess. • Service Layer: will expose the REST api to exchange information with client applications. • Logic Layer: the layer in charge of hosting the business logic of the application. • Data Access Layer: the layer to communicate with the data base. Apart from that the components will have a fourth package common.api to store the common elements that will be used by the different layers of the component. This is the place will contain common interfaces, constants, exceptions or enums. 74.2.2. OASP4j Component Core As we mentioned earlier, each component will be related to a functionality and this functionality will be represented in code by an Entity that will define all the properties needed to wrap the logic of that feature. This Entity, that represents the core of the component, will be located in the dataaccess.api package. The naming convention in Oasp4j for these entities is [Target]Entity The 'Target' should match the name of the related table in the data base, although this is not mandatory. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 423 Devonfw Guide v2.4.0 Basically an Entity is simply a POJO that will be mapped to a table in the data base, and that reflects each table column with a suitable property. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 424 Devonfw Guide v2.4.0 Chapter 75. OASP4J Component Layers As we already mentioned in the introduction to Oasp4j the components of our Java backend apps will be divided in three layers: service, logic and dataaccess. • Service Layer: will contain the REST services to exchange information with the client applications. • Logic Layer: the layer in charge of hosting the logic of the application (validations, authorization control, business logic, etc.). • Data Access Layer: the layer to communicate with the data base. 75.1. Layers implementation Following the Oasp4j recommendations for Dependency Injection in MyThaiStar’s layers we will find: • Separation of API and implementation: Inside each layer we will separate the elements in different packages: api and impl. The api will store the interface with the methods definition and inside the impl we will store the class that implements the interface. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 425 Devonfw Guide v2.4.0 • Usage of JSR330: The Java standard set of annotations for dependency injection (@Named, @Inject, @PostConstruct, @PreDestroy, etc.) provides us with all the needed annotations to define our beans and inject them. @Named public class MyBeanImpl implements MyBean { @Inject private MyOtherBean myOtherBean; @PostConstruct public void init() { // initialization if required (otherwise omit this method) } @PreDestroy public void dispose() { // shutdown bean, free resources if required (otherwise omit this method) } } 75.1.1. Communication between layers The communication between layers is solved using the described Dependency Injection pattern, based on Spring and the Java standards: java.inject (JSR330) combined with JSR250. Service layer - Logic layer This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 426 Devonfw Guide v2.4.0 import javax.inject.Inject; import javax.inject.Named; import io.oasp.application.mtsj.bookingmanagement.logic.api.Bookingmanagement; @Named("BookingmanagementRestService") public class BookingmanagementRestServiceImpl implements BookingmanagementRestService { @Inject private Bookingmanagement bookingmanagement; @Override public BookingCto getBooking(long id) { return this.bookingmanagement.findBooking(id); } ... } Logic layer - Data Access layer import javax.inject.Inject; import javax.inject.Named; import io.oasp.application.mtsj.bookingmanagement.dataaccess.api.dao.BookingDao; @Named public class BookingmanagementImpl extends AbstractComponentFacade implements Bookingmanagement { @Inject private BookingDao bookingDao; @Override public boolean deleteBooking(Long bookingId) { BookingEntity booking = this.bookingDao.find(bookingId); this.bookingDao.delete(booking); return true; } ... } This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 427 Devonfw Guide v2.4.0 75.1.2. Service layer As we mentioned at the beginning, the Service layer is where the services of our application (REST or SOAP) will be located. In Oasp4j applications the default implementation for web services is based on Apache CXF, a services framework for Java apps that supports web service standards like SOAP (implementing JAX-WS) and REST services (JAX-RS). In this tutorial we are going to focus only in the REST implementation of the services. Service definition The services definition is done by the service interface located in the service.api.rest package. In the boooking component of My Thai Star application we can see a service definition statement like the following @Path("/bookingmanagement/v1") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public interface BookingmanagementRestService { @GET @Path("/booking/{id}/") public BookingCto getBooking(@PathParam("id") long id); ... } JAX-RS annotations: • @Path: defines the common path for all the resources of the service. • @Consumes and @Produces: declares the type of data that the service expects to receive from the client and the type of data that will return to the client as response. • @GET: annotation for HTTP get method. • @Path: the path definition for the getBooking resource. • @PathParam: annotation to configure the id received in the url as a parameter. Service implementation The service implementation is a class located in the service.impl.rest package that implements the previous defined interface. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 428 Devonfw Guide v2.4.0 @Named("BookingmanagementRestService") public class BookingmanagementRestServiceImpl implements BookingmanagementRestService { @Inject private Bookingmanagement bookingmanagement; @Override public BookingCto getBooking(long id) { return this.bookingmanagement.findBooking(id); } ... } As you can see this layer simply delegates in the logic layer to resolve the app requirements regarding business logic. 75.1.3. Logic layer In this layer we will store all the custom implementations to resolve the requirements of our applications. Including: • business logic. • Delegation of the transaction management to Spring framework. • object mappings. • validations. • authorizations. Within the logic layer we must avoid including code related to services or data access, we must delegate those tasks in the suitable layer. Logic layer definition As in the service layer, the logic implementation will be defined by an interface located in a logic.api package. public interface Bookingmanagement { BookingCto findBooking(Long id); ... } This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 429 Devonfw Guide v2.4.0 Logic layer implementation In a logic.impl package a Impl class will implement the interface of the previous section. @Named @Transactional public class BookingmanagementImpl extends AbstractComponentFacade implements Bookingmanagement { /** * Logger instance. */ private static final Logger LOG = LoggerFactory.getLogger(BookingmanagementImpl .class); /** * @see #getBookingDao() */ @Inject private BookingDao bookingDao; /** * The constructor. */ public BookingmanagementImpl() { super(); } @Override public BookingCto findBooking(Long id) { LOG.debug("Get Booking with id {} from database.", id); BookingEntity entity = getBookingDao().findOne(id); BookingCto cto = new BookingCto(); cto.setBooking(getBeanMapper().map(entity, BookingEto.class)); cto.setOrder(getBeanMapper().map(entity.getOrder(), OrderEto.class)); cto.setInvitedGuests(getBeanMapper().mapList(entity.getInvitedGuests(), InvitedGuestEto.class)); cto.setOrders(getBeanMapper().mapList(entity.getOrders(), OrderEto.class)); return cto; } public BookingDao getBookingDao() { return this.bookingDao; } ... } This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 430 Devonfw Guide v2.4.0 In the above My Thai Star logic layer example we can see: • business logic and/or object mappings. • Delegation of the transaction management through the Spring’s @Transactional annotation. 75.1.4. Transfer objects In the code examples of the logic layer section you may have seen a BookingCto object. This is one of the Transfer Objects defined in Oasp4j to be used as transfer data element between layers. Main benefits of using TO’s: • Avoid inconsistent data (when entities are sent across the app changes tend to take place in multiple places). • Define how much data to transfer (relations lead to transferring too much data). • Hide internal details. In Oasp4j we can find two different _Transfer Objects: 75.1.5. Entity Transfer Object (ETO) • Same data-properties as entity. • No relations to other entities. • Simple and solid mapping. 75.1.6. Composite Transfer Object(CTO) • No data-properties at all. • Only relations to other TOs. • 1:1 as reference, else Collection(List) of TOs. • Easy to manually map reusing ETO’s and CTO’s. 75.2. Data Access layer The third, and last, layer of the Oasp4j architecture is the one responsible for store all the code related to connection and access to data base. For mapping java objects to the data base Oasp4j use the Java Persistence API(JPA). And as JPA implementation Oasp4j use hibernate. Apart from the Entities of the component, in the dataaccess layer we are going to find the same elements that we saw in the other layers: definition (an interface) and implementation (a class that implements that interface). However, in this layer the implementation is slightly different, the [Target]DaoImpl extends general.dataaccess.base.dao.ApplicationDaoImpl that provides us (through io.oasp.module.jpa) with This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 431 Devonfw Guide v2.4.0 the basic implementation dataaccess methods: save(Entity), findOne(id), findAll(ids), delete(id), etc. Because of that, in the [Target]DaoImpl implementation of the layer we only need to add the custom methods that are not implemented yet. Following the My Thai Star component example (bookingmanagement) we will find only the paginated findBookings implementation. Data Access layer definition public interface BookingDao extends ApplicationDao { PaginatedListTo findBookings(BookingSearchCriteriaTo criteria); } Data Access layer implementation @Named public class BookingDaoImpl extends ApplicationDaoImpl implements BookingDao { @Override public PaginatedListTo findBookings(BookingSearchCriteriaTo criteria) { BookingEntity booking = Alias.alias(BookingEntity.class); EntityPathBase alias = Alias.$(booking);
JPAQuery query = new JPAQuery(getEntityManager()).from(alias);
...
}
}
The implementation of the findBookings uses queryDSL to manage the dynamic queries.

75.2.3. Layers of the Jump the Queue application
All the above sections describe the main elements of the layers of the Oasp4j components. If you
have completed the exercise of the previous chapter you may have noticed that all those
components are already created for us by Cobigen.
Take a look to our application structure

432

Devonfw Guide v2.4.0

visitor component
• 1. definition for dataaccess layer.
• 2. the entity that we created to be used by Cobigen to generate the component structure.
• 3. implementation of dataaccess layer
• 4. Transfer Objects located in the logic layer.
• 5. definition of the logic layer.
• 6. implementation of the logic layer.
• 7. definition of the rest service of the component.
• 8. implementation of the rest service.
For the access code component you will find a similar structure.
So, as you can see, our components have all the layers defined and implemented following the
Oasp4j principles.
Using Cobigen we have created a complete and functional Oasp4j application without the necessity
of any manual implementation.
Let’s see the application running and let’s try to use the REST service to save a new visitor.

433

Devonfw Guide v2.4.0

75.2.4. Jump the Queue running
As we already mentioned, for this tutorial we are using Postman for Chrome, but you can use any
other similar tool to test your API.
First, open your Jump the Queue project in Eclipse and run the app (right click over the
SpringBootApp.java class > Run as > Java application)
Simple call
If you remember we added some mock data to have some visitors info available, let’s try to retrieve
a visitor’s information using our visitormanagement service.
Call the service (GET) http://localhost:8081/jumpthequeue/services/rest/visitormanagement/v1/
visitor/1 to obtain the data of the visitor with id 1.

Instead of receiving the visitor’s data we get a response with the login form. This is because the
Oasp4j applications, by default, implements the Spring Security so we would need to log in to access
to the services.
To ease the example we are going to "open" the application to avoid the security filter and we are
going to enable the CORS filter to allow requests from clients (Angular).
In the file general/service/impl/config/BaseWebSecurityConfig.java:
• change property corsEnabled to true

434

Devonfw Guide v2.4.0

@Value("${security.cors.enabled}") boolean corsEnabled = true; • edit the configure(HttpSecurity http) method to allow access to the app to any request and add the cors filter: @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests().anyRequest().permitAll().and().csrf().disable(); if (this.corsEnabled) { http.addFilterBefore(getCorsFilter(), CsrfFilter.class); } } Finally in the file /jumpthequeue-core/src/main/resources/application.properties set security.cors.enabled to true security.cors.enabled=true Now run again the app and try again the same call. We should obtain the data of the visitor Paginated response Cobigen has created for us a complete services related to our entities so we can access to a paginated list of the visitors without any extra implementation. We are going to use the following service visitormanagement/service/api/rest/VisitormanagementRestService.java defined This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). in 435 Devonfw Guide v2.4.0 @Path("/visitor/search") @POST public PaginatedListTo findVisitorsByPost(VisitorSearchCriteriaTo searchCriteriaTo); The service definition states that we will need to provide a Search Criteria Transfer Object. This object will work as a filter for the search as you can see in visitormanagement/dataaccess/impl/dao/VisitorDaoImpl.java in findVisitors method. If the Search Criteria is empty we will retrieve all the visitors, in other case the result will be filtered. Call (POST) http://localhost:8081/jumpthequeue/services/rest/visitormanagement/v1/visitor/search in the body we need to define the Searc Criteria object, that will be empty in this case {} NOTE You can see the definition of the SearchCriteriaTo visitormanagement/logic/api/to/VisitorSearchCriteriaTo.java in The result will be something like If we want to filter the results we can define a criteria object in the body. Instead of previous empty criteria, if we provide an object like This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 436 Devonfw Guide v2.4.0 { "name": "Jason" } we will filter the results to find only visitors with name Jason. If now we repeat the request the result will be We could customize the filter editing the visitormanagement/dataaccess/impl/dao/VisitorDaoImpl.java class. Saving a visitor To fit the requirements of the related user story we need to register a visitor and return an access code. By default Cobigen has generated for us all the CRUD operations related to the visitor entity, so we already are able to save a visitor in our database without extra implementation. To delegate in Spring to manage the transactions we only need to add the @Transactional (org.springframework.transaction.annotation.Transactional) annotation to our logic layer implementations. Since Devonfw 2.2.0 Cobigen adds this annotation automatically, so we don’t need to do it manually. Check your logic implementation classes and add the annotation in case it is not present. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 437 Devonfw Guide v2.4.0 @Named @Transactional public class VisitormanagementImpl extends AbstractComponentFacade implements Visitormanagement { ... } See the service definition in our visitor component (visitormanagement/service/api/rest/VisitormanagementRestService.java) @Path("/visitormanagement/v1") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public interface VisitormanagementRestService { ... @POST @Path("/visitor/") public VisitorEto saveVisitor(VisitorEto visitor); } To save a visitor we only need to use the REST resource /services/rest/visitormanagement/v1/visitor and provide in the body the visitor definition for the VisitorEto. So, call (POST) http://localhost:8081/jumpthequeue/services/rest/visitormanagement/v1/visitor providing in the body a Visitor object like { "name": "Mary", "email": "mary@mail.com", "phone": "1234567" } NOTE You can see the definition visitormanagement/logic/api/to/VisitorEto.java for VisitorEto in We will get a result like the following This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 438 Devonfw Guide v2.4.0 In the body of the response we can see the default content for a successful service response: the data of the new visitor. This is the default implementation when saving a new entity with Oasp4j applications. However, the Jump the Queue design defines that the response must provide the access code created for the user, so we will need to change the logic of our application to fit this requirement. In the next chapter we will see how we can customize the code generated by Cobigen to adapt it to our necessities. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 439 Devonfw Guide v2.4.0 Chapter 76. OASP4J and Spring Boot Configuration An application needs to be configured in order to allow internal setup such as CDI (Context and Dependency Injection), but also to allow externalized configuration of a deployed package (e.g. integration into runtime environment). Using Spring Boot, you can rely on a comprehensive configuration approach following a "convention over configuration" pattern. The Spring Boot guide adds on to this by detailed instructions and best-practices to deal with configurations. In general, the kinds of configuration can be distinguished as explained in the following sections: • Internal Application configuration maintained by developers • Externalized Environment configuration maintained by the operators • Externalized Business configuration maintained by business administrators 76.1. Internal Application Configuration The application configuration contains all the internal settings and the wiring of the application (bean wiring, database mappings, etc.) and is maintained by the application developers at development time. Usually, there is a main configuration registered with the main Spring Boot App, but differing configurations to support automated test of the application can be defined using profiles (not detailed in this guide). web.xml is referred as a place for all the web related configurations, but now it is no more used to configure the web app. It is empty. Therefore, let’s discuss how you can configure the Devon based on Spring Boot applications! 76.1.1. Standard beans configuration For basic bean configuration, you can rely on spring boot, mainly using the configuration classes and occasionally xml-configuration files. Following are some of the key principles to understand Spring Boot auto-configuration features: • Spring Boot auto-configuration attempts to automatically configure your Spring application based on the jar dependencies and annotated components found in your source code. • Auto-configuration is noninvasive, at any point you can start to define your own configuration to replace specific parts of the auto-configuration by redefining your identically named bean. Beans are configured via annotations at the java-class (@Component, @Bean, @Named, etc.). These beans will be known when wiring the application at runtime. The required component scan is already auto-enabled within the main SpringBootApp. For beans that needs separate configuration for any reason, additional Configuration Classes (using annotation @Configuration) can be used and will be automatically evaluated during application startup. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 440 Devonfw Guide v2.4.0 Lets see how you can customize your own Configuration Class Step 1: Creating the Configuration Class In Devon, the Configuration Classes reside in the folder src/main/general/configuration/ and it’s recommended to include the new Configuration Classes here. This is just to keep a clear structure of projects. In fact, you can include the Configuration Classes anywhere in the project, it is the responsibility of the Spring Boot to scan the application. Therefore, to create your Configuration Class, for example src/main/general/configuration/MyConfigurationClass.java This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 441 Devonfw Guide v2.4.0 @Configuration public class MyConfigurationClass{ public int propertie1; public String propertie2; public float propertie3; private MyBean myBean; @Bean public MyBean myBean() { this.myBean= new MyBean(propertie1,propertie2,propertie3); return this.myBean; } } With the annotation @Configuration Spring Boot will manage your class as a configuration class. Step 2: Including properties In the last step, you have a Configuration Class that configures a simple bean (MyBean). Let’s see how you can set values as per your configuration. There are several ways of setting the parameter’s value: • Initialize the variable in the code. (...) public int propertie1 = 0; public String propertie2 = "0"; public float propertie3 = 0.0f; (...) Obviously, this is the simplest way to define the parameters, but isn’t a good practice. Therefore, it is recommended to use some of the following ways to define the values in configuration. • Using @Value annotation and application.properties file First, you need to define the properties in the application.properties file. You can define the properties in /main/resources/application.properties or in /main/resources/config/application.properties. If you are running the server with the embedded Tomcat of the application, you can use both the files, but if you are deploying the application on an external Tomcat, you need to define your properties in the first one. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 442 Devonfw Guide v2.4.0 Learn more about how to run the application here. mybean.property1=0 mybean.property2=0 mybean.property3=0.0f Finally, you can access the defined properties in the code using the @Value annotation: (...) @Value("${mybean.property1}")
public int property1;
@Value("${mybean.property2}") public String property2; @Value("${mybean.property3}")
public float property3;
(...)
• Using @ConfigurationProperties annotation and application.properties file

443

Devonfw Guide v2.4.0

@Configuration
@ConfigurationProperties(prefix = "mybean")
public class MyConfigurationClass{
public int
property1;
public String property2;
public float property3;
//WE NEED TO IMPLEMENT THE GETTERS AND SETTERS OF THE VARIABLES
}
Now, Spring Boot maps the variables to the value of the properties under the prefix "mybean".
Therefore, you just need to include these in the application.properties file as you did in the @Value
example.

76.1.2. XML-based beans configuration
It is still possible and allowed to provide (bean-) the configurations using xml, though not
recommended. These configuration files are no more bundled via a main xml config file but loaded
individually from their respective owners, e.g. for unit-tests:

@SpringApplicationConfiguration(classes = { SpringBootApp.class }, locations = {
"classpath:/config/app/batch/beans-productimport.xml" })
public class ProductImportJobTest extends AbstractSpringBatchIntegrationTest {
...
Configuration XML-files reside in an adequately named sub-folder of:
src/main/resources/app

76.1.3. Batch configuration
In the directory src/main/resources/config/app/batch, you can place the configuration file for the
batch jobs. Each file within this directory represents one batch job.

76.1.4. WebSocket configuration
A websocket endpoint is configured within the business package as a Spring configuration class.
The annotation @EnableWebSocketMessageBroker makes Spring Boot registering this endpoint.

package io.oasp.gastronomy.restaurant.salesmanagement.websocket.config;
...
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
...

444

Devonfw Guide v2.4.0

76.2. Externalized Configuration
Externalized configuration is provided separately in a deployment package and can be maintained
undisturbed by redeployments.

76.2.1. Environment Configuration
The environment configuration contains the configuration parameters (typically port numbers,
environments. These are under the control of the operators responsible for the application.
The environment configuration is maintained in the application.properties files, defining various
properties. These properties are explained in the corresponding configuration sections of the
guides for each topic:
• persistence configuration
• service configuration
• logging guide
There are two properties files exist within the example server:
• src/main/resources/application.properties provides a default configuration - bundled and
deployed with the application package. It further acts as a template to derive a tailored minimal
environment-specific configuration.
• src/main/resources/config/application.properties provides the additional properties only
required at development time (for all local deployment scenarios). This property file is excluded
from all packaging.
The location of the tailored application.properties file after deployment depends on the deployment
strategy:
• standalone runnable Spring Boot App using embedded tomcat: place a tailored copy of
application.properties into installpath/config/
• dedicated tomcat (one tomcat per app): place a tailored copy of application.properties into
tomcat/lib/config
• tomcat serving a number of apps (requires expanding the wars): place a tailored copy of
application.properties into the tomcat/webapps//WEB-INF/classes/config
In this application.properties, only define the minimum properties that are environment specific
and inherit everything else from the bundled src/main/resources/application.properties. In any
case, make sure that the class loader will find the file.
Also, assure that the properties are thoroughly documented by providing a comment to each
property. This inline documentation is most valuable for your operations department.

The business configuration contains all business configuration values of the application, which can

445

Devonfw Guide v2.4.0

be edited by administrators through the GUI. The business configuration values are stored in the
database in key/value pairs.
The database table business_configuration has the following columns:
• ID
• Property name
• Property type (Boolean, Integer, String)
• Property value
• Description
According to the entries in the above table, the administrative GUI shows a generic form to change
business configuration. The hierarchy of the properties determines the place in the GUI, so the GUI
bundles the properties from the same hierarchy level and name. Boolean values are shown as
checkboxes, integer and string values as text fields. The properties are read and saved in a typed
form, an error is raised if you try to save a string in an integer property, for example.
Following base layout is recommended for the hierarchical business configuration:
component.[subcomponent].[subcomponent].propertyname

446

Devonfw Guide v2.4.0

Chapter 77. OASP4J Validations
For validations, OASP4J includes the Hibernate Validator as one of the available libraries in the
pom.xml file

org.hibernate
hibernate-validator

Hibernate Validator allow us to check the values by adding annotations in our Java classes.

77.1. My Thai Star Validations
In My Thai Star app, you can find validations for some fields that we receive from the client.
The main part of the inputs from the client is related to the booking process. The client needs to
provide: name, comment, bookingDate, email and assistants.

@NotNull
private String name;
...
private String comment;
@NotNull
@Future
private Timestamp bookingDate;
...
@NotNull
@EmailExtended
private String email;
...
@Min(value = 1, message = "Assistants must be greater than 0")
@Digits(integer = 2, fraction = 0)
private Integer assistants;
• @NotNull checks that the field is not null before saving in the database.
• @Future, for dates, checks that the provided date is not in the past.
• @Min declares a minimum value for an integer.
• @Digits checks the format of an integer.

447

Devonfw Guide v2.4.0

• @Email is the standard validator for email accounts. In this case the standard validator is not
checking the domain of the email, so for My Thai Star we added a custom validator called
'@EmailExtended'

that

is

defined

in

a

new

'general/common/api/validation/EmailExtended.java class. In the next section we will see it in
more detail.

In Jump the Queue app, we have some inputs from the client so let’s add some validations for that
data to avoid errors and ensure the consistency of the information before trying to save to data
base.
When registering a visitor the client provides:
• name: must be not null.
• email: must be not null and must match the format @.
• phone: must be not null and must match a sequence of numbers and spaces.

77.2.1. Name Validation
As we have just mentioned the name of the visitor must be not null, to do so Hibernate Validator
provides us with the already mentioned @NotNull annotation (javax.validation.constraints.NotNull).
We are going to add the annotation in the 'visitormanagement/dataaccess/api/VisitorEntity.java' just
before the field name

@NotNull
private String name;
Run the app with Eclipse and, using Postman or a similar tool, call the register resource (POST)
http://localhost:8081/jumpthequeue/services/rest/visitormanagement/v1/register providing in the
body a visitor object without a name

{
"email": "mary@mail.com",
"phone": "123456789"
}
You will get a ValidationError message regarding the name field

448

Devonfw Guide v2.4.0

77.2.2. Email Validation
In the case of the email, as we have already commented for My Thai Star, using the @Email
annotation for validations will allow to enter emails like something@something. This does not fit the
app requirements, so we need to add a custom email validator.
Add an annotation EmailExtended.java in a new general.common.api.validation package.

449

Devonfw Guide v2.4.0

Listing 21. EmailExtended.java

import
import
import
import
import

java.lang.annotation.Documented;
java.lang.annotation.ElementType;
java.lang.annotation.Retention;
java.lang.annotation.RetentionPolicy;
java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.constraints.Pattern;
import org.hibernate.validator.constraints.Email;
@Email
@Pattern(regexp = ".+@.+\\..+", message = "Email must specify a domain")
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {})
@Documented
public @interface EmailExtended {
Class[] groups() default {};
}
This validator extends the @Email validation with an extra @Pattern that defines a regular
expression that the fields annotated with @EmailExtended must match.
Now we can annotate the email field in with @NotNull and @EmailExtended to fit the app
requirements.

@NotNull
@EmailExtended
private String email;
Then, if we try to register a user with a null email we get the ValidationError with message
"{email=[may not be null]}"

450

Devonfw Guide v2.4.0

And if we provide an email that does not match the expected format we get the related
ValidationError

Finally if we provide a valid email the registration process ends successfully.

77.2.3. Phone Validation
For validating the phone, apart from the @NotNull annotation, we need to use again a custom
validation based on the @Pattern annotation and a regular expression.
We are going to follow the same approach used for EmailExtended validation.

451

Devonfw Guide v2.4.0

Add an annotation Phone.java to the general.common.api.validation package. With the @Pattern
annotation we can define a regular expression to filter phones ("consists of sequence of numbers or
spaces").
Listing 22. Phone.java

import
import
import
import
import

java.lang.annotation.Documented;
java.lang.annotation.ElementType;
java.lang.annotation.Retention;
java.lang.annotation.RetentionPolicy;
java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.constraints.Pattern;
@Pattern(regexp = "[ 0-9]{0,14}$", message = "Phone must be valid") @Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = {}) @Documented public @interface Phone { String message() default "Phone must be well formed"; Class[] groups() default {}; Class[] payload() default {}; } Then we only need to apply the new validation to our phone field in 'visitormanagement/dataaccess/api/VisitorEntity.java' @NotNull @Phone private String phone; As last step we can test our new validation. Call again the service defining a wrong phone, the response should be a ValidationError like the following This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 452 Devonfw Guide v2.4.0 However, if we provide a valid phone the process should end successfully In this chapter we have seen how easy is to add validations in the server side of our Oasp4j applications. In the next chapter we will show how to test our components using Spring Test and Oasp4j's test module. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 453 Devonfw Guide v2.4.0 Chapter 78. Testing with OASP4J Testing our applications is one of the most important parts of the development. The Oasp4j documentation provides a detailed information about the testing principles. In addition to that, in both the DevonfwGuide.pdf (Write Unit Test Cases) and in the Devon documentation you can find information about testing explained, on a more practical way. In this chapter we are going to focus on showing some test examples, and explain briefly how to start testing our Oasp4j apps. 78.1. Testing: My Thai Star In all the Oasp4j projects (based on Maven and Spring) we are going to find a dedicated package for testing. In addition to this the testing part of the project has also its own resources package, so we are going to be able to configure the application properties or other resources to create specific testing scenarios. We should incorporate the unit test as one of the main efforts during the development, considering even approaches like TDD. The tests in our applications should cover a significant amount of functionality, however in this part of the tutorial we are going to focus in the test of our Oasp4j components. As you have seen in the previous snapshot, each component of our application should have a dedicated package for testing in the test package. Inside each testing package we will create the related test classes that should follow the naming convention [Component]Test.java This is because we are going to use Maven to launch the tests of our application and Maven will look for test classes that end with Test word. Testing with Oasp4j means that we have already available Spring Test and Oasp4j test module This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 454 Devonfw Guide v2.4.0 which means that we will find a significant amount of annotations and implementations that are going to provide us with all the needed libraries and tools to create tests in a really simply way. Focusing on the components test means that we are going to test the implementation of the logic layer of our applications. Because of this, you can see in our test structure that our test classes are inside the [component].logic.impl package. If we open one of the test classes we will find something like this @SpringBootTest(classes = SpringBootApp.class) public class DishmanagementTest extends ComponentTest { @Inject private Dishmanagement dishmanagement; @Test public void findAllDishes() { DishSearchCriteriaTo criteria = new DishSearchCriteriaTo(); List categories = new ArrayList<>(); criteria.setCategories(categories); PaginatedListTo result = this.dishmanagement.findDishCtos(criteria); assertThat(result).isNotNull(); } ... } • @SpringBootTest is the Spring Test annotation to load the context of our application. So we will have the application running like in a real situation. • extending the Oasp4j test class ComponentTest will inject in our test class functionalities like Assertions • Spring Test gives us the option for dependency injection, so we are going to be able to @Inject our components to test them. • Finally with the @Test annotation we can declare a test to be executed during testing process. 78.2. Testing: Jump the Queue Now that we have briefly overview we are going to add some tests to our Jump the Queue application. We have a main component for managing visitors, so we are going to create a dedicated package for the component within the general testing package. And inside this new package we are going to add a new test class named VisitormanagementTest.java This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 455 Devonfw Guide v2.4.0 You NOTE can see that we already have some test packages in the src/test/java/com.cap.jumpthequeue.general package. Those tests are from the Oasp4j archetype and we can use them as model for some tests in our apps. In the VisitormanagementTest class we are going to add the annotations to run the app context when executing the tests, extend the ComponentTest class to obtain the assertions and inject our visitormanagement component. import import import import import import javax.inject.Inject; org.junit.Test; org.springframework.boot.test.context.SpringBootTest; com.cap.jumpthequeue.SpringBootApp; com.cap.jumpthequeue.visitormanagement.logic.api.Visitormanagement; io.oasp.module.test.common.base.ComponentTest; @SpringBootTest(classes = SpringBootApp.class) public class VisitormanagementTest extends ComponentTest { @Inject private Visitormanagement visitormanagement; Now we can start adding our first test. In Jump the Queue we have two main functionalities: • register a visitor returning an access code. • list the current visitors. Let’s add a test to check the first one. We are going to create a method called with a descriptive name, registerVisitorTest, and we are going to add to it the @Test annotation. Inside this test we are going to verify the registration process of our app. To do so we only need to call the registerVisitor method of the component and provide a VisitorEto object. After the method is called we are going the check the response of the method to verify that the expected business logic has been executed successfully. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 456 Devonfw Guide v2.4.0 @Test public void registerVisitorTest() { VisitorEto visitor = new VisitorEto(); visitor.setName("Mary"); visitor.setEmail("mary@mail.com"); visitor.setPhone("123456789"); VisitorCto result = this.visitormanagement.registerVisitor(visitor); assertThat(result).isNotNull(); } Have you noticed that the mock data of the test is the same data that we have used NOTE in previous chapters for the manual verification of our services? Exactly, from now on this test will allow us to automate the manual verification process. Now is the moment for running the test. We can do it in several ways but to simplify the example just select the method to be tested, do right click over it and select Run as > JUnit Test NOTE We can also debug our tests using the Debug As > JUnit Test option. The result of the test will be shown in the JUnit tab of Eclipse This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 457 Devonfw Guide v2.4.0 Seems that everything went ok, our register process passes the test. Let’s complete the test checking if the just created user is "Mary" and if the access code has been provided. We can do it simply adding more asserts to check the result object assertThat(result.getVisitor().getName()).isEqualTo("Mary"); assertThat(result.getCode().getCode()).isNotEmpty(); Now running again the test we should obtain the expected result For the second functionality (listing visitors) we can add a new test with a very similar approach. The only difference is that in this case we are going to need to declare a Search Criteria object, that will be empty to retrieve all the visitors. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 458 Devonfw Guide v2.4.0 @Test public void listVisitorsTest() { VisitorSearchCriteriaTo criteria = new VisitorSearchCriteriaTo(); PaginatedListTo result = this.visitormanagement.findVisitorCtos( criteria); assertThat(result).isNotNull(); } To run both tests (all the tests included in the class) we only need to do right click in any part of the class and select Run As > JUnit Test. All the methods annotated with @Test will be checked. 78.3. Additional Test functionalities The Oasp4j test module provide us with some extra functionalities that we can use to create tests in an easier way. Extending ComponentTest class we also have available the doSetUp() and doTearDown() methods, that we can use to initialize and release resources in our test classes. In our Jump the Queue test class we could declare the visitor object in the doSetUp method, so we can use this resource in several test methods instead of declaring it again and again. Doing this our test class would be as follows This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 459 Devonfw Guide v2.4.0 @SpringBootTest(classes = SpringBootApp.class) public class VisitormanagementTest extends ComponentTest { @Inject private Visitormanagement visitormanagement; VisitorEto visitor = new VisitorEto(); VisitorSearchCriteriaTo criteria = new VisitorSearchCriteriaTo(); @Override public void doSetUp() { this.visitor.setName("Mary"); this.visitor.setEmail("mary@mail.com"); this.visitor.setPhone("123456789"); } @Test public void registerVisitorTest() { VisitorCto result = this.visitormanagement.registerVisitor(this.visitor); assertThat(result).isNotNull(); assertThat(result.getVisitor().getName()).isEqualTo("Mary"); assertThat(result.getCode().getCode()).isNotEmpty(); } ... } 78.4. Running the Tests with Maven We can use Maven to automate the testing of our project. To do it we simply need to open the Devonfw console (console.bat script) or a command line with access to Maven and, in the project, execute the command mvn clean test. With this command Maven will scan for classes named with the Test word and will execute all the tests included in these classes. If we do it with Jump the Queue project: D:\Devon-dist\....\jumpthequeue>mvn clean test The result will be similar to this: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 460 Devonfw Guide v2.4.0 We see 5 tests because the Oasp4j archetype provides some default tests. So, apart from our added tests, all the application test are executed. After that we have seen how to create tests in Devonfw, in the next chapter we are going to show how to package and deploy our project. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 461 Devonfw Guide v2.4.0 Chapter 79. OASP4J Deployment As we already mentioned when introducing the oasp4j projects, the apps created with the Oasp4j archetype are going to provide, apart from the core project, a server project that will configure the packaging of the app. In our Jump the Queue app we can verify that we have this server project available So, using Maven, we are going to be able to easily package our app in a .war file to be deployed in an application server like Tomcat (the default server provided in Devonfw). 79.1. The server Project The server project provided in Oasp4j applications is an almost empty Maven project. It only has a pom.xml file that is used to configure the packaging of the core project. Taking a closer look to this pom.xml file we can realize that it only presents a single dependency to the core project. ...${project.groupId}
jumpthequeue-core
${project.version} ... And then it includes the spring-boot-maven-plugin that allows us to package the project in a jar or war archives and run the application "in-place". This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 462 Devonfw Guide v2.4.0 ... org.springframework.boot spring-boot-maven-plugin ... 79.2. Running the app with Maven So thanks to Spring Boot and the spring-boot-maven-plugin we can run our app using Maven. To do so just open a command line with access to Maven (in Devonfw we can do it using the console.bat). And: 1.- As is explained in devon documentation the application.properties used for packaging is /src/main/resources/application.properties. So we need to edit the app properties to access to the app. In /jumpthequeue-core/src/main/resources/application.properties configure the properties server.port=8081 server.context-path=/jumpthequeue 2.- install the jumpthequeue/core project in our local Maven repository D:\Devon-dist\...\jumpthequeue>mvn install 3.- Go to the jumpthequeue/server project and execute the command mvn spring-boot:run D:\Devon-dist\...\jumpthequeue\server>mvn spring-boot:run The app should be launched in the Spring Boot embedded Tomcat server. Wait a few seconds until you see a console message like [L: org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer] [M: Tomcat started on port(s): 8081 (http)] [L: com.cap.jumpthequeue.SpringBootApp] - [M: Started SpringBootApp in 17.908 seconds (JVM running for 19.148)] Now we can try to access to the app resource (GET)http://localhost:8081/jumpthequeue/services/ rest/visitormanagement/v1/visitor/1 we can verify that the app is running fine This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 463 Devonfw Guide v2.4.0 79.3. Packaging the app with Maven In the same way, using Maven we can package our project in a .war file. As in the previous section, open a command line with access to Maven (in Devonfw console.bat script) and execute the command mvn clean package in the project root directory D:\Devon-dist\...\jumpthequeue>mvn clean package The packaging process (compilation, tests and .war file generation) should be launched. Once the process is finished you should see a result like This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 464 Devonfw Guide v2.4.0 [INFO] Building war: D:\Devon-dist\workspaces\jumpthequeue\server\target\jumpthe queue-server-0.0.1-SNAPSHOT.war [INFO] WEB-INF\web.xml already added, skipping [INFO] [INFO] --- spring-boot-maven-plugin:1.5.4.RELEASE:repackage (default) @ jumptheq ueue-server --[INFO] Attaching archive: D:\Devon-dist\workspaces\jumpthequeue\server\target\ jumpthequeue-server-bootified.war, with classifier: bootified [INFO] -----------------------------------------------------------------------[INFO] Reactor Summary: [INFO] [INFO] jumpthequeue ....................................... SUCCESS [ 0.306 s] [INFO] jumpthequeue-core .................................. SUCCESS [ 27.723 s] [INFO] jumpthequeue-server ................................ SUCCESS [ 6.628 s] [INFO] -----------------------------------------------------------------------[INFO] BUILD SUCCESS [INFO] -----------------------------------------------------------------------[INFO] Total time: 35.091 s [INFO] Finished at: 20XX-06-28T10:11:37+02:00 [INFO] Final Memory: 48M/216M [INFO] -----------------------------------------------------------------------The packaging process has created a .war file that has been stored in the jumpthequeue\server\target directory. 79.4. Deploying the war file on Tomcat After packaging the app with Maven, we have the .war file containing our app. In addition, Devonfw provides us with a Tomcat server ready to be used, so let’s deploy the application in that Tomcat server. 1. Go to jumpthequeue\server\target directory. You should find a jumpthequeue-server- {version}.war there. 2. Change the .war name to something easier like jumpthequeue.war 3. Copy the just renamed file to the Tomcat's webapps directory (located in Devondist\software\tomcat\webapps). 4. Go to Devon-dist\software\tomcat\bin directory and execute the startup.bat script to launch Tomcat. A new command window will be opened. Wait until the starting process is finished, you should see a message like Server startup in 31547 ms The app will be available in the url http://localhost:8080/{war-file-name} This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 465 Devonfw Guide v2.4.0 The access to the server is done by default through port 8080. If you want the app to NOTE be available through other port edit it in the D:\Devon- dist\software\tomcat\conf\server.xml file. Now, if we try to access to the app with the simplest resource (GET)http://localhost:8080/ jumpthequeue/services/rest/visitormanagement/v1/visitor/1 we can verify that the app has been successfully deployed on Tomcat This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 466 Devonfw Guide v2.4.0 Chapter 80. OASP4J 80.1. Cookbook This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 467 Devonfw Guide v2.4.0 Chapter 81. OASP4J Project without Database OASP4J application template is made up with database , Restful webservice and security package. If someone don’t want database in his oasp4j template application , he can follow the below steps to run the oasp4j application without database and without having any error. 81.1. Add Property src→main→resources→ application.properties spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAut oConfiguration, org.springframework.boot.autoconfigure.orm.jpa .HibernateJpaAutoConfiguration 81.2. Remove annotation Remove the @Configuration Annotation from the file com.carpool.general.batch.impl.config → BeansBatchConfig @Configuration public class BeansBatchConfig { private JobRepositoryFactoryBean jobRepository; 81.2.1. Remove @name annotation Remove @name from Dao package class , which are related with dao class and from it’s implementation class. 81.2.2. Remove dataaccess package Another option is to disable the database , remove the database package and it’s implementation class from OASP4J template application. 81.2.3. Remove the dependences Remove the all dependency from the pom.xml file , that related with database . This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 468 Devonfw Guide v2.4.0 org.springframework spring-orm Hibernate EntityManager for JPA (implementation) org.hibernate hibernate-entitymanager Database com.h2database h2 org.flywaydb flyway-core hibernate org.hibernate.javax.persistence hibernate-jpa-2.1-api cglib cglib org.hibernate hibernate-validator This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 469 Devonfw Guide v2.4.0 Chapter 82. Create your own Components 82.1. Basics After you have completed your own OASP4J app creation, we are going to create our first app component. Going back to our example application, Jump the Queue, we need to provide two basic functionalities: • register a user (returning an access code). • show the registered queue members. To accomplish that we are going to work over two entities: Visitor and AccessCode. The Visitor will be defined with: name, email and phone. The Access Code will be represented as a code and a date. In addition, we will have to represent the one to one relation between both entities. Now is the moment to decide the components of our app. The complexity of the functionality would allow us to create only one component for managing both entities. But ,in order to clarify the example, we are going to create also two components, one for Visitors and other for Access Codes. However if you feel more comfortable managing both entities in a single component you can also do it in that way. The results will be the same and the only NOTE difference will be related with the structure of the elements and the distribution of the code. 82.1.1. The database In the projects created with the Oasp4j archetype, we already have a complete database schema that we can use as a model to create our own. By default we are going to work over the H2 database engine provided in the Oasp4j applications, although you can use other database alternatives for this exercise. Open the /jumpthequeue-core/src/main/resources/db/migration/h2/V0001__R001_Create_schema.sql and delete all the tables (except BinaryObject and RevInfo, that are used internally by default). This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 470 Devonfw Guide v2.4.0 Visitor table Now we can add our first table Visitor. In the case of Jump the Queue, the visitors will provide: name, email and phone to obtain an access code. So we need to represent that data in our table CREATE TABLE Visitor( id BIGINT NOT NULL AUTO_INCREMENT, modificationCounter INTEGER NOT NULL, name VARCHAR(255), email VARCHAR(255), phone VARCHAR(255), idCode BIGINT, CONSTRAINT PK_Visitor PRIMARY KEY(id) ); • id: the id for each visitor. • modificationCounter: used internally by JPA to take care of the optimistic locking for us. • name: the visitor’s name. • email: the visitor’s email. • phone: the visitor’s phone. • idCode: the relation with the Access Control entity represented with an id. Access Code table As second table we will represent the Access Code that will be formed by the code itself and the related date. CREATE TABLE AccessCode( id BIGINT NOT NULL AUTO_INCREMENT, modificationCounter INTEGER NOT NULL, code VARCHAR(5), dateAndTime TIMESTAMP, idVisitor BIGINT, CONSTRAINT PK_AccessCode PRIMARY KEY(id), CONSTRAINT FK_AccessCode_idVisitor FOREIGN KEY(idVisitor) REFERENCES Visitor(id) ); • id: the id for each code. • modificationCounter: used internally by JPA to take care of the optimistic locking for us. • code: the access code that we are going to provide to the user after registration. • dateAndTime: the date related to the access code. • idVisitor: the relation with the Visitor entity. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 471 Devonfw Guide v2.4.0 Mock data Finally we can provide a certain amount of mock data to start our app. In the /jumpthequeuecore/src/main/resources/db/migration/V0002__R001_Master_data.sql script replace the current inserts with ours INSERT INTO 1, 'Jason', INSERT INTO 1, 'Peter', INSERT (1, 1, INSERT (2, 1, NOTE Visitor (id, modificationCounter, name, email, phone, idCode) VALUES (1, 'jason@mail.com', '123456', 1); Visitor (id, modificationCounter, name, email, phone, idCode) VALUES (2, 'peter@mail.com', '789101', 2); INTO AccessCode (id, modificationCounter, code, dateAndTime, idVisitor) VALUES 'A01', CURRENT_TIMESTAMP + (60 * 60 * 24 * 5), 1); INTO AccessCode (id, modificationCounter, code, dateAndTime, idVisitor) VALUES 'A02', CURRENT_TIMESTAMP + (60 * 60 * 24 * 5), 2); You can delete the scripts V0003R001_Add_blob_table_and_data.sql and V0004R001_Add_batch_tables.sql as we are not going to use them. 82.1.2. The core of the components Now that we have defined the database for our entities is the moment to start creating the code of the related components. We are going to use Cobigen to generate the component structure. That means that, as we already commented, we can generate all the structure and layers starting from a core element: a simple Plain Old Java Object that represents our Entity. So, in order to use Cobigen, we must create our entities in the expected location: MyEntitymanagement.dataaccess.api. Visitor component To implement the component we will need to define a VisitorEntity to connect and manage the data of the Visitor table in the database. The name for this component will be visitormanagement and for the entity VisitorEntity. From the root package of the project create the following packages: - visitormanagement -- dataaccess --- api This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 472 Devonfw Guide v2.4.0 Now create a new java class in the just created visitormanagement.dataaccess.api package This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 473 Devonfw Guide v2.4.0 and call it VisitorEntity In the entity, we are going to add the fields to represent the data model, so our entity should contain: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 474 Devonfw Guide v2.4.0 private String name; private String email; private String phone; private AccessCodeEntity code; We are not adding the id nor the modificationCounter because Cobigen will solve this for us. NOTE The AccessCodeEntity is throwing an error as it is not created yet. We will solve it in next step. Now we need to declare our entity as a JPA entity with @Entity annotation (javax.persistence.Entity) at class level. Also at class level, to map the entity with the database table, we will use the @Table annotation (javax.persistence.Table) defining the name of our already created Visitor table: @Table(name = "Visitor") @Entity @Table(name = "Visitor") public class VisitorEntity Now we have to declare the getters and setters of the fields of our entity. We can do it manually or using Eclipse with the option This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 475 Devonfw Guide v2.4.0 To represent the one to one relation with the Access Control entity we must use the JPA annotations @OneToOne and @JoinColumn in the getCode() method. @OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL) @JoinColumn(name = "idCode") public AccessCodeEntity getCode(){ ... } The result of current implementation for VisitorEntity class is package com.cap.jumpthequeue.visitormanagement.dataaccess.api; import import import import import import javax.persistence.CascadeType; javax.persistence.Entity; javax.persistence.FetchType; javax.persistence.JoinColumn; javax.persistence.OneToOne; javax.persistence.Table; @Entity @Table(name = "Visitor") public class VisitorEntity { private String name; private String email; This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 476 Devonfw Guide v2.4.0 private String phone; private AccessCodeEntity code; /** * @return name */ public String getName() { return this.name; } /** * @param name new value of {@link #getname}. */ public void setName(String name) { this.name = name; } /** * @return email */ public String getEmail() { return this.email; } /** * @param email new value of {@link #getemail}. */ public void setEmail(String email) { this.email = email; } /** * @return phone */ public String getPhone() { return this.phone; } /** * @param phone new value of {@link #getphone}. */ public void setPhone(String phone) { this.phone = phone; This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 477 Devonfw Guide v2.4.0 } /** * @return code */ @OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL) @JoinColumn(name = "idCode") public AccessCodeEntity getCode() { return this.code; } /** * @param code new value of {@link #getcode}. */ public void setCode(AccessCodeEntity code) { this.code = code; } } NOTE The compilation errors related to AccessCodeEntity will be solved when we create the related entity in next step. AccessCode component We are going to repeat the same process for the AccessCode component. So we will end up with the following structure And the content of the AccessCodeEntity before start using Cobigen will be package com.cap.jumpthequeue.accesscodemanagement.dataaccess.api; import java.sql.Timestamp; import javax.persistence.JoinColumn; This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 478 Devonfw Guide v2.4.0 import javax.persistence.OneToOne; import javax.persistence.Temporal; import javax.persistence.TemporalType; import com.cap.jumpthequeue.visitormanagement.dataaccess.api.VisitorEntity; @Entity @Table(name = "AccessCode") public class AccessCodeEntity { private String code; @Temporal(TemporalType.TIMESTAMP) private Timestamp dateAndTime; private VisitorEntity visitor; /** * @return code */ public String getCode() { return this.code; } /** * @param code new value of {@link #getcode}. */ public void setCode(String code) { this.code = code; } /** * @return dateAndTime */ public Timestamp getDateAndTime() { return this.dateAndTime; } /** * @param dateAndTime new value of {@link #getdateAndTime}. */ public void setDateAndTime(Timestamp dateAndTime) { this.dateAndTime = dateAndTime; } /** * @return visitor This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 479 Devonfw Guide v2.4.0 */ @OneToOne @JoinColumn(name = "idVisitor") public VisitorEntity getVisitor() { return this.visitor; } /** * @param visitor new value of {@link #getvisitor}. */ public void setVisitor(VisitorEntity visitor) { this.visitor = visitor; } } With this we have finished preparing the core of our components. Now we can start using Cobigen to generate all the remaining structure (services, layers, dao’s, etc.). NOTE Now we can solve the compilation errors related to AccessCodeEntity in the VisitorEntity.java class. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 480 Devonfw Guide v2.4.0 Chapter 83. Creating Component’s Structure with Cobigen Once we have finished creating the core of our components we could continue creating all the structure and elements manually, but we are going to show how using Cobigen for those tasks we can save a significant amount of time and effort. 83.1. Importing Cobigen templates Before start using Cobigen we need to import into our project the CobiGenTemplates. To do so, we only need to use the Eclipse’s menu File > Import > Existing Projects into Workspace and browse to select the workspaces/main/CobiGen_Templates directory. Then click Finish button and you should have the CobiGenTemplates as a new project in Eclipse’s workspace. 83.2. Cobigen Health Check The first time we use Cobigen is recommended to check the health of the tool. To do so, right-click over an entity and select Health Check This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 481 Devonfw Guide v2.4.0 The next dialogs will show us if there are outdated templates. In that case we can solve it clicking the Update button. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 482 Devonfw Guide v2.4.0 83.3. Visitor component structure To create the whole structure of a component with Cobigen we only need to right-clicking over our component core entity, select Cobigen > Generate This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 483 Devonfw Guide v2.4.0 Now we have to choose which packages we want to generate with the tool. The options are: • CRUD DAO’s: generates the implementation of CRUD operations in the data access layer. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 484 Devonfw Guide v2.4.0 • CRUD REST services: generates a complete service layer with CRUD operations for our entity exposed as a REST service. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 485 Devonfw Guide v2.4.0 • CRUD logic (with use cases): generates the logic layer dividing the implementation in different use cases. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 486 Devonfw Guide v2.4.0 • CRUD logic (all in one): does the same as previous option but with the implementation of the logic layer in only one class instead of different use-cases classes. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 487 Devonfw Guide v2.4.0 • Entity infrastructure: creates the entity main interface and edits (by a merge) the current entity to extend the oasp classes This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 488 Devonfw Guide v2.4.0 • TO’s: generates the related Transfer Objects that we will explain in next chapters of this tutorial This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 489 Devonfw Guide v2.4.0 To generate all the needed functionalities of our component we are going to select the following packages to be generated at the same time Now we can select the fields to be involved (all by default) or directly create all the packages clicking the Finish button. During the process Cobigen will show a message asking us to review some ambiguous references. Click Continue This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 490 Devonfw Guide v2.4.0 Once Cobigen has finished we will check if we need to introduce manual adjustments. In the case of the Visitor component, we have a relation (dependency) with some of the Access Code component elements, that are still not created. We will solve this compilation errors in next step. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 491 Devonfw Guide v2.4.0 83.4. Access Code component structure Now we are going to repeat the same process using Cobigen with our other AccessCode component. Once the process has finished you may see that we need to also adjust manually some imports related to Timestamp type in: • accesscodemanagement/common/api/AccessCode.java • accesscodemanagement/logic/api/to/AccessCodeSearchCriteriaTo.java • accesscodemanagement/dataaccess/impl/dao/AccessCodeDaoImpl.java • accesscodemanagement/logic/api/to/AccessCodeEto.java Solve it manually using the Eclipse helpers and finally go to visitormanagement/logic/api/to/VisitorCto.java and resolve our last compilation error related to AccessCodeEto, that has been already created. 83.5. Run the app If all compilation errors are solved run the app ( SpringBootApp.java right click > Run as > Java application ). The app should be launched without errors. Congratulations you have created your first Oasp4j components. In the next chapter we will explain and show in detail each of the created elements. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 492 Devonfw Guide v2.4.0 Chapter 84. OASP4J Adding Custom Functionality In the previous chapter we have seen that using Cobigen we can generate in a few clicks all the structure and functionality of an Oasp4j component. In this chapter we are going to show how to add custom functionalities in our projects that are out of the scope of the code that Cobigen is able to cover. 84.1. Returning the Access Code The Jump the Queue design defines a User Story in which a visitor can register into an event and obtain an access code to avoid a queue. In our standard implementation of the Jump the queue app we have used Cobigen to generate the components, so we have a default implementation of the services where saving a visitor returns the visitor data as confirmation that the process ended successfully. We are going to create a new service /register, to register a visitor and return both the visitor data and the access code. We also are going to create the logic to generate the access code. 84.1.1. Creating the service To add the new service we need to add the definition to the visitormanagement/service/api/rest/VisitormanagementRestService.java. We are going to create a new /register REST resource bound to a method that we will call registerVisitor. We could also re-write the current saveVisitor method, but for clarity sake we are NOTE generating a new service. @POST @Path("/register/") public VisitorCto registerVisitor(VisitorEto visitor); Then we need to implement the new registerVisitor method in visitormanagement/service/impl/rest/VisitormanagementRestServiceImpl.java class. @Override public VisitorCto registerVisitor(VisitorEto visitor) { return this.visitormanagement.registerVisitor(visitor); } This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 493 Devonfw Guide v2.4.0 The compilation error that we get in this.visitormanagement.registerVisitor is NOTE because the logic for the new functionality is not added yet. We are going to fix it in the next step. 84.1.2. Adding the logic Now we need to create the logic for the new registerVisitor method. To do so, as always, we must add the definition to visitormanagement/logic/api/Visitormanagement.java class VisitorCto registerVisitor(VisitorEto visitor); and then implement the method in visitormanagement/logic/impl/VisitormanagementImpl.java @Override public VisitorCto registerVisitor(VisitorEto visitor) { Objects.requireNonNull(visitor, "visitor"); VisitorEntity visitorEntity = getBeanMapper().map(visitor, VisitorEntity.class); // initialize, validate visitorEntity here if necessary AccessCodeEntity code = new AccessCodeEntity(); code.setCode(this.accesscode.generateCode(new Random(), 3)); code.setDateAndTime(Timestamp.from(Instant.now().plus(1, ChronoUnit.DAYS))); visitorEntity.setCode(code); VisitorEntity savedVisitor = getVisitorDao().save(visitorEntity); VisitorCto cto = new VisitorCto(); cto.setVisitor(getBeanMapper().map(savedVisitor, VisitorEto.class)); cto.setCode(getBeanMapper().map(this.accesscode.findAccessCode(savedVisitor .getCodeId()), AccessCodeEto.class)); return cto; } NOTE we are using java.sql.Timestamp for Timestamp and java.util.Random for Random To solve the compilation errors we need to add the implementation to generate the code in the accesscodemanagement component. The details about the code generation are not important you can implement it with your own preferences. We have created a simple generation code that returns 3 random characters. The definition: Listing 23. Accesscodemanagement.java String generateCode(Random rng, int length); The implementation: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 494 Devonfw Guide v2.4.0 Listing 24. AccesscodemanagementImpl.java: @Override public String generateCode(Random rng, int length) { String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; char[] text = new char[length]; for (int i = 0; i < length; i++) { text[i] = characters.charAt(rng.nextInt(characters.length())); } return new String(text); } and finally we need to inject the accesscode component in the VisitormanagementImpl.java class. @Inject private Accesscodemanagement accesscode; As date and Time we are setting the current date plus one day. Again, that details are not important for the goal of the exercise, do it in other way if you feel more comfortable. In addition to that we are defining a VisitorCto to handle the response. We need to provide the access code in the response. In the previous chapter we talked about the Transfer Objects. In this case the VisitorEto has this defintion: private String name; private String email; private String phone; private Long codeId; So instead of the AccessCode object we only have the id as reference and we can not use this object in the response. Now take a look at the VisitorCto definition: private VisitorEto visitor; private AccessCodeEto code; In this case we have the complete AccessCodeEto object available to be part of the response. For that reason we are returning a VisitorCto object, because it can contain the complete code data in addition to the visitor’s data. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 495 Devonfw Guide v2.4.0 The getBeanMapper().map() is the Oasp4j mapper to automate the mappings to that objects. As last implementation steps we are saving the visitor entity in the database and finally returning the VisitorCto. We are using the default save method, so we don’t need to add any extra implementation to the dataaccess layer. 84.1.3. Testing the new functionality Run the app using Eclipse (SpringBootApp.java > Right click > Java Application). Call our new registration service (POST) http://localhost:8081/jumpthequeue/services/rest/ visitormanagement/v1/register providing in the body a Visitor object again { "name": "Mary", "email": "mary@mail.com", "phone": "1234567" } Now the response includes the Access Code info 84.2. List visitors with their access code For the second user story we need to provide a list with the visitors and their access codes. Right now our app list the visitors but only with the id of the access code. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 496 Devonfw Guide v2.4.0 This is because the service returns a list of VisitorEto (see visitormanagement/service/api/rest/VisitormanagementRestService.java) @Path("/visitor/search") @POST public PaginatedListTo findVisitorsByPost(VisitorSearchCriteriaTo searchCriteriaTo); In the previous section we have talked about the limitation of using the VisitorEto, we have the reference to the access code but not the entire object. So to solve it we can also use the VisitorCto as the object to be listed in the response. 84.2.1. Edit the service We are going to replace the VisitorEto to a VisitorCto in the service response: Listing 25. VisitormanagementRestService.java ... @Path("/visitor/search") @POST public PaginatedListTo findVisitorsByPost(VisitorSearchCriteriaTo searchCriteriaTo); This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 497 Devonfw Guide v2.4.0 Listing 26. VisitormanagementRestServiceImpl.java ... @Override public PaginatedListTo findVisitorsByPost(VisitorSearchCriteriaTo searchCriteriaTo) { return this.visitormanagement.findVisitorCtos(searchCriteriaTo); } 84.2.2. Edit the logic We are going to replace the VisitorEto reference with a VisitorCto Listing 27. Visitormanagement.java ... PaginatedListTo findVisitorCtos(VisitorSearchCriteriaTo criteria); In the implementation we can use the Oasp4j mapper to map the VisitorEntity to VisitorEto and add it to each VisitorCto object. Listing 28. VisitormanagementImpl.java @Override public PaginatedListTo findVisitorCtos(VisitorSearchCriteriaTo criteria) { criteria.limitMaximumPageSize(MAXIMUM_HIT_LIMIT); PaginatedListTo visitors = getVisitorDao().findVisitors(criteria); List ctos = new ArrayList<>(); for (VisitorEntity entity : visitors.getResult()) { VisitorCto cto = new VisitorCto(); cto.setVisitor(getBeanMapper().map(entity, VisitorEto.class)); cto.setCode(this.accesscode.findAccessCode(entity.getId())); ctos.add(cto); } return new PaginatedListTo<>(ctos, visitors.getPagination()); } The method findAccessCode method already returns a AccessCodeEto object, so we don’t need to use the mapper in this case. Testing the changes Now run again the app with Eclipse and try to get the list of visitors, the response should include the access code data This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 498 Devonfw Guide v2.4.0 In this chapter we have seen how easy is extend a Oasp4j application, with few steps you can add new services to your backend app to fit the functional requirements of your projects or edit them to adapt the default implementation to your needs. In the next chapter we will show how easy is to add validations for the data that we receive from the client. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 499 Devonfw Guide v2.4.0 Chapter 85. Spring boot admin Integration with OASP4J Spring Boot Admin is an application to manage and monitor your Spring Boot Applications. The applications register with our Spring Boot Admin Client (via HTTP) or are discovered using Spring Cloud (e.g. Eureka). The UI is just an AngularJs application on top of the Spring Boot Actuator endpoints. 85.1. Configure the Spring boot admin for the OASP4J App. 85.1.1. Setting up Spring boot Admin server To run the spring boot admin. First, you need to setup admin server. To do this create the spring.io project and follow the below steps. Add Spring Boot Admin Server and the UI dependency to the pom.xml file. de.codecentric spring-boot-admin-server 1.5.3 de.codecentric spring-boot-admin-server-ui 1.5.3 Add the Spring Boot Admin Server configuration via adding @EnableAdminServer to your spring boot class. @Configuration @EnableAutoConfiguration @EnableAdminServer public class SpringBootApp{ public static void main(String[] args) { SpringApplication.run(SpringBootAdminApplication.class, args); } } If you want the login for the sever , then add the login depedency . This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 500 Devonfw Guide v2.4.0 de.codecentric spring-boot-admin-server-ui-login 1.5.3 Add the properties to the application.properties file. spring.application.name=Admin-Application server.port=1111 management.security.enabled=false security.user.name=admin security.user.password=admin123 Securing Spring Boot Admin Server Since there are several approaches on solving authentication and authorization in distributed web applications Spring Boot Admin doesn’t ship a default one. If you include the spring-boot-adminserver-ui-login in your dependencies it will provide a login page and a logout button. Add the below configuration. @Configuration public static class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // Page with login form is served as /login.html and does a POST on /login http.formLogin().loginPage("/login.html").loginProcessingUrl("/login"). permitAll(); // The UI does a POST on /logout on logout http.logout().logoutUrl("/logout"); // The ui currently doesn't support csrf http.csrf().disable(); // Requests for the login page and the static assets are allowed http.authorizeRequests() .antMatchers("/login.html", "/**/*.css", "/img/**", "/third-party/**") .permitAll(); // ... and any other request needs to be authorized http.authorizeRequests().antMatchers("/**").authenticated(); // Enable so that the clients can authenticate via HTTP basic for registering http.httpBasic(); } } This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 501 Devonfw Guide v2.4.0 Below is the screenshot of the Admin Server UI. 85.2. Register the client app Spring boot admin gives the monitoring status of multiple [[ spring.io|http://start.spring.io]] application.These applications are registered as the client application to spring boot admin server.You can register the application with the spring-boot-admin-client or use [[ Spring Cloud Discovery|http://projects.spring.io/spring-cloud/spring-cloud.html]] (e.g. Eureka, Consul, …). 85.2.1. Register with spring-boot-admin-starter-client Add spring-boot-admin-starter-client dependency to the pom.xml file de.codecentric spring-boot-admin-starter-client 1.5.3 Enable the SBA Client by configuring the URL of the Spring Boot Admin Server spring.boot.admin.url= http://localhost:8080 management.security.enabled= false 85.2.2. Register with Spring Cloud Discovery If you already use Spring Cloud Discovery for your applications you don’t need the SBA Client. Just make the Spring Boot Admin Server a DiscoveryClient, the rest is done by our AutoConfiguration. The following steps are for using Eureka, but other Spring Cloud Discovery implementations are supported as well. There are examples using [[ Consul |https://github.com/codecentric/spring-boot- This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 502 Devonfw Guide v2.4.0 admin/tree/master/spring-boot-admin-samples/spring-boot-admin-sample-consul/]] Zookeeper and [[ |https://github.com/codecentric/spring-boot-admin/tree/master/spring-boot-admin- samples/spring-boot-admin-sample-zookeeper/]]. Add spring-cloud-starter-eureka dependency to the pom.xml file org.springframework.cloud spring-cloud-starter-eureka Add the Spring Boot Admin Server configuration via adding @EnableDiscoveryClient to your spring boot class @Configuration @EnableAutoConfiguration @EnableDiscoveryClient @EnableAdminServer public class SpringBootApp { /** * Entry point for spring-boot based app * * @param args - arguments */ public static void main(String[] args) { SpringApplication.run(SpringBootApp.class, args); } } Add the properties to the application.properties file eureka.client.serviceUrl.defaultZone=${EUREKA_URI:http://localhost:8180/eureka}
management.security.enabled=false
logging.file=target/${spring.application.name}.log eureka.instance.hostname=localhost eureka.client.register-with-eureka=false eureka.client.fetch-registry=false health.config.enabled=true This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 503 Devonfw Guide v2.4.0 Detailed view of an application is given below. In this view we can see the tail of the log file, metrics, environment variables, log configuration where we can dynamically switch the log levels at the component level, root level or package level and other information. 85.3. Loglevel management For applications using Spring Boot 1.5.x (or later) you can manage loglevels out-of-the-box. For applications using older versions of Spring Boot the loglevel management is only available for Logback. It is accessed via JMX so include Jolokia in your application. In addition you have configure Logback’s JMXConfigurator: Add dependency: org.jolokia jolokia-core Add the logback-spring.xml file in resorce folder: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 504 Devonfw Guide v2.4.0 85.4. Notification Now we will see another feature called notifications from Spring Boot Admin. This will notify the administrators when the application status is DOWN or an application status is coming UP. Spring Boot admin supports the below channels to notify the user. • Email Notifications • Pagerduty Notifications • Hipchat Notifications • Slack Notifications • Let’s Chat Notifications Here, we will configure Slack notifications. Add the below properties to the Spring Boot Admin Server’s application.properties file.To enable Slack notifications you need to add an incoming Webhook under custom integrations on your Slack account and configure it appropriately. spring.boot.admin.notify.slack.enabled=true spring.boot.admin.notify.slack.username=user123 spring.boot.admin.notify.slack.channel=general spring.boot.admin.notify.slack.webhook-url= https://hooks.slack.com/services/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 spring.boot.admin.notify.slack.message="#{application.name} (#{application.id}) is #{to.status}" 85.5. Integrate Spring boot admin with module Please follow the below steps to configure the spring boot admin module to OASP4J app. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 505 Devonfw Guide v2.4.0 85.5.1. Spring boot Admin server Check out the Spring boot Admin server from this repository. 85.5.2. Configure spring boot admin client module to OASP4J sample app Add the dependency in pom.xml file com.capgemini.devonfw.modules devonfw-springbootadminclient 2.4.0 Add the below property to application.properties file and change the values as per the spring boot admin server configuration like admin.url, username, password: eureka.client.serviceUrl.defaultZone=${EUREKA_URI:http://localhost:8180/eureka}
management.security.enabled=false
logging.file=target/${spring.application.name}.log eureka.instance.hostname=localhost eureka.client.register-with-eureka=false eureka.client.fetch-registry=false health.config.enabled=true This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 506 Devonfw Guide v2.4.0 Chapter 86. OASP4Fn 86.1. A Closer Look This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 507 Devonfw Guide v2.4.0 Chapter 87. Creating new OASP4Fn Application In this chapter we are going to build a serverless backend with OASP4Fn. The main objective of this tutorial is to take an initial contact with OASP4Fn and the necessary tools we are going to use in the development, so at the end of it, the user will be enough confident to start developing a new project with OASP4Fn without problems. 87.1. Install tools In this section we’re going to introduce all the necessary tools we’re going to need to start programming and a initial configuration if necessary. 87.1.1. Visual Studio Code Download the installer from the official page and install it. Once installed, the first thing you should do is install the extensions that will help you during the development, to do that follow this steps: 1. Install Settings Sync extension. 2. Open the command palette (Ctrl+Shift+P) and introduce the command: Sync: Download Settings. 3. Provide GIST ID: 3b1d9d60e842f499fc39334a1dd28564. In the case that you are unable to set up the extensions using the method mentioned, you can also use the scripts provided in this repository. 87.1.2. Node.js Go to the node.js official page and download the version you like the most, the LTS or the Current as you wish. 87.1.3. Typescript Let’s install what is going to be the main language during development: TypeScript. This is a ES6 superset that will help us to get a final clean and distributable JavaScript code. This is installed globally with npm, the package manager used to install and create javascript modules in Node.js, that is installed along with Node, so for install typescript you don’t have to install npm explicitly, only run this command: npm install –g typescript 87.1.4. Yarn As npm, Yarn is a package manager, the differences are that Yarn is quite more faster and usable, so we decided to use it to manage the dependencies of OASP4Fn projects. To install it you only have to go to the official installation page and follow the instructions. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 508 Devonfw Guide v2.4.0 Even though, if you feel more confortable with npm, you can remain using npm, there is no problem regarding this point. 87.1.5. Serverless Lastly, we are going to install the serverless framework, that are going to help us deploying our handlers in our provider we have chosen. npm install –g serverless 87.2. Postman Postman is an app that helps you build HTTP requests and send them to a server through any of the HTTP methods. This tool will be useful at the end of the tutorial when we are going to run our handlers locally and send POST http requests to them. 87.3. Starting our project through a template To start with the tutorial we are going to use the oasp4fn application template, so use the following command to clone it in your local machine: git clone https://github.com/oasp/oasp4fn-application-template.git jumpTheQueue Before continue, remember to replace the remote repository, for one that you own: cd jumpTheQueue\ git remote remove origin git remote add origin This template comes with the structure that has to have an OASP4Fn application and the skeleton of some handlers. This handlers are stored on event folders, which we can add or remove adjusting to our needs, so how we only are going to use http events, we are going to access to the cloned folder an remove the s3 folder inside the handlers and test folders: rm handlers\S3\ -r rm test\S3\ -r Only remains to install the base dependencies of our code using yarn, so we only have to run: yarn 87.4. Local database set up The database we are going to use during this tutorial is dynamodb, the noSQL database provided by AWS, which is supported by OASP4Fn. First you have to download and start it following the amazon official documentation, once you have downloaded and started, open the dynamodb shell in this local endpoint: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 509 Devonfw Guide v2.4.0 http://localhost:8000/shell/ And an interactive shell will be opened in your default browser like this: Now we are going to create a table called Queue with the opened shell, to do that write "createTable" in the text pane sited at the left side of the screen and press Ctrl + Space, this will generate a template object specifying the properties that has to be passed to the create function, so we have to modify that object, having at the end something like this: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 510 Devonfw Guide v2.4.0 var params = { TableName: 'Queue', KeySchema: [ // The type of of schema. Must start with a HASH type, with an optional second RANGE. { // Required HASH type attribute AttributeName: 'code', KeyType: 'HASH', } ], AttributeDefinitions: [ // The names and types of all primary and index key attributes only { AttributeName: 'code', AttributeType: 'S', // (S | N | B) for string, number, binary }, // ... more attributes ... ], ProvisionedThroughput: { // required provisioned throughput for the table ReadCapacityUnits: 1, WriteCapacityUnits: 1, } }; dynamodb.createTable(params, function(err, data) { if (err) ppJson(err); // an error occurred else ppJson(data); // successful response }); Finally press Ctrl + Enter, and if we have specified the properties properly an output with table description will be displayed at the left side console: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 511 Devonfw Guide v2.4.0 87.5. AWS credentials Although we are going to use a local instance, the aws-sdk are going to look for credentials for add to the configuration and an error will raise if the credentials are missing, so for that reason we are going to add a credentials file in an .aws folder in our home directory. Said that, first of all create the folder with the following commands: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 512 Devonfw Guide v2.4.0 cd %HOME% #or only 'cd' if you are in a Unix based OS mkdir .aws Once you has created the folder, add a file inside called credentials and write the following: [default] aws_access_key_id = your_key_id aws_secret_access_key = your_secret_key There is not necessary to put real credentials in the file as we are going to work locally in this tutorial, you can leave it as above, without replace your_key_id or your_secret_key, so the sdk will inject the credentials and won’t throw any error, but if you already have credentials, feel free to replace them there, so you have well located for future developments. Finally, it’s worth saying that there are more ways to pass the credentials to the sdk, but this is the best in our case, for more information about credentials take a look on to the official documentation. 87.6. Adding Typings The template we have cloned comes with a declaration types at the root of the handlers folder with typings for AWS lambda service and events, but must add more types for the data we are going to manage, so we are going to export a interface Visitor and a interface Code in our declaration file, that will look like this: export interface Visitor { name: string; email: string; phone: string; } export interface Code { code: string; dateAndTime: number; } 87.7. Start the development Now that we already have finish the set up of our project, we are going to add our handlers based on our design: • One that will add the visitor to the queue • And other to get your position in the queue Both of the handlers will be triggered by http events with a post method, so we should delete the This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 513 Devonfw Guide v2.4.0 rest of the methods than don’t are going to use, both in the handlers and test folders. So once we have done that we are going to modify our initial handler in the template following the next steps: 1. Rename the template handler to register-handler.ts 2. Install the lodash package through yarn add and import it. 3. Import the fn-dynamo adapter. 4. Add our Visitor interface we add to the types.d.ts file. 5. Ser the dynamo adapter to oasp4fn as the database adapter. 6. Specify the configuration to this concrete handler, in this case only the path property is necessary. 7. Rename the handler. 8. Write the logic of our function with the the imported adapter. But before write the logic of our handler, we are going to add some utility function to the utils.ts file at the root of our handlers folder, and export them, so that functions can be exported in our handler: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 514 Devonfw Guide v2.4.0 import * as _ from 'lodash'; import { Visitor } from './types'; const ALPHABET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; export let getRandomCode = (len: number) => { if (!Number.isFinite(len) || len < 1) { throw new TypeError('Invalid code lenght'); } let str = ''; while(len > 0) { str += ALPHABET[_.random(Number.MAX_SAFE_INTEGER) % ALPHABET.length]; --len; } return str; }; export let validateVisitor = (visitor: Visitor) => { let ok = true; _.some(visitor, (value, key) => { switch (key) { case 'phone': ok = /^(\d+\s?)+\d+$/.test(value);
ok = /^(([^<>()\\.,;:\s@"]+(\.[^<>()\\.,;:\s@"]+)*)|(".+"))
@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zAZ]{2,}))$/.test(value); break; } return !ok; }) return ok; }; export let isVisitor = (object: any): object is Visitor => { return 'name' in object && 'phone' in object && 'email' in object; } So the handler that will register the user to the queue will be able to take the visitor information, generata a unique code with the above function package, insert it into our data base, along with the result of the handler, the generated code and the hour to the visit, so the resulting handler will look like this: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 515 Devonfw Guide v2.4.0 import import import import import oasp4fn from '@oasp/oasp4fn'; dynamo from '@oasp/oasp4fn/dist/adapters/fn-dynamo'; { HttpEvent, Context, Visitor } from '../../types'; * as _ from 'lodash'; { getRandomCode, validateVisitor, isVisitor } from '../../utils'; oasp4fn.setDB(dynamo); oasp4fn.config({path: 'register'}); export async function register (event: HttpEvent, context: Context, callback: Function) { try { let visitor = event.body; if(!isVisitor(visitor) || !validateVisitor(visitor)) throw new Error(); let date = new Date(); date.setDate(date.getDate() + 1); let code: string | undefined; while(!code) { let aux = getRandomCode(3); let res = await oasp4fn.table('Queue', aux).promise(); if(!res) code = aux; } let result = { code: code, dateAndTime: Date.parse(date.toDateString())}; await oasp4fn.insert('Queue', _.assign(visitor, result)).promise(); callback(null, result); } catch(err){ callback(new Error('[500] Cannot register the visitor to the queue')); } } The second and last handler for the application will be that which return the full or part of the queue, by passing full or partial information of a visitor or, in case to the full queue, an empty object, so for achieve that we will have to create a new file in the same directory we have the last one, and name it search-handler.ts, next we are going to repeat the 3 to 8 steps, so we will have the next handler: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 516 Devonfw Guide v2.4.0 import oasp4fn from '@oasp/oasp4fn'; import dynamo from '@oasp/oasp4fn/dist/adapters/fn-dynamo'; import { HttpEvent, Context } from '../../types'; oasp4fn.setDB(dynamo); oasp4fn.config({path: 'search'}); export async function search (event: HttpEvent, context: Context, callback: Function) { try { let visitor = event.body; let res = await oasp4fn.table('Queue') .filter(visitor) .promise(); callback(null, res); } catch(err){ callback(new Error('[500] Cannot get the queue')); } } 87.8. Generating the configuration files In this part we are going to learn how to generate the configuration files that we are going to use to build and deploy our handlers. The first step, is to add the configuration in the oasp4fn.config.js file, but how isn’t necessary more configuration than the default one in this tutorial, we are going to remove that file: rm oasp4fn.config.js Finally we can execute the command: yarn fun And is all goes well, two files, serverless.yml and webpack.config.json will be generated and we will see this command line output: 87.9. Build and run your handlers locally To execute our handlers locally we will make use of the serverless-offline plugin, that emulates a local API-gateway that let you build your handlers through webpack and send http requests to This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 517 Devonfw Guide v2.4.0 them, so run: yarn offline To run this command you must have the serverless.yml file generated, and the serverless-offline plugin specified in the plugin section (that is IMPORTANT aucomatically added by the default configuration of OASP4Fn). To search for more information about the serverless plugins, you can dive into the serverless documentation. and you will see the following output: And when the webpack rebuild line appears you can start to send requests to the specified endpoints, so open the postman and create a visitor sending a POST request to the register endpoint: After this, test your other handler, sending a void object with the POST http request, and see how our handler return the visitor inserted: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 518 Devonfw Guide v2.4.0 This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 519 Devonfw Guide v2.4.0 Chapter 88. Application structure OASP4Fn provides Infraestructure as Code. For that reason, the service made with OASP4Fn must follow a specified folder structure, that along with a configuration file will allow the user to avoid having to configure the service manually. /handlers /Http /get handler1.ts handler2.ts … handlerN.ts /post handler1.ts handler2.ts /put … /S3 ... In general every handler should follow the following structure: /{EventName} /{TriggerMethod} {HandlerName}.ts The logic of the application will be stored in a folder, called handlers, inside it there will be a folder for each event used to trigger the handler and inside a folder with the name of the method that triggers the handler. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 520 Devonfw Guide v2.4.0 Chapter 89. Application Program Interface OASP4Fn provides a friendly api, that along with adapters and a sugar kind syntax, allows the user make queries to make use of different kind of cloud services. Our API it’s principally divided in two parts, the first ones are called query starters, which are used to set the a adapters, specify the handlers configuration, and overall, the great part of them return an instance of the OASP4Fn class, which methods will permit the user make queries in the cloud in an isolated way. For more information about the methods check our API documentation. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 521 Devonfw Guide v2.4.0 Chapter 90. Adapters OASP4Fn provides some adapters to use the cloud services supported by the framework. 90.1. Types of adapters The adapters are grouped depending in which kind of service is adapted. In this chapter we gonna describe and explain the different kinds of adapters the framework provides, an list the adapters of each type. 90.1.1. Data Base adapters This type of adapter will let the user connect to a data base service, retrieve items, delete and insert them. See Interface FnDBService. • List of data base adapters: ◦ dynamo: ▪ name: fn-dynamo ▪ import: import dynamo from '@oasp/oasp4fn/dist/adapters/fn-dynamo' 90.1.2. Storage adapters This type of adapter will let the user connect to a storage service, download objects and delete them as binary data. See Interface FnStorageService. • List of storage adapters: ◦ s3: ▪ name: fn-s3 ▪ import: import s3 from '@oasp/oasp4fn/dist/adapters/fn-s3' 90.1.3. Authorization adapters This type of adapter will let the user connect to a authorization service to retrieve the corresponding tokens of a user. See Interface FnAuthService • List of data base adapters: ◦ cognito: ▪ name: fn-cognito ▪ import: import cognito from '@oasp/oasp4fn/dist/adapters/fn-cognito' This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 522 Devonfw Guide v2.4.0 Chapter 91. Application Configuration Furthermore of the specified before, in the file oasp4fn.config.js, it is specified the configuration of the events, deployment information and the runtime environment in which the handlers will run. 91.1. File structure The file will contain an javascript exported object, which will contain specific properties of the future generated serverless.yml: module.exports = { // Serverless properties } There you can insert the properties in a json style format. You can check all the applicable properties at the official reference , all of those can be defined normally except in two special cases: • Provider property: Inside this property are specified the provider and all the relative properties of the associated service which is going to run the functions, but you can only provide the name of the provider, and all the default properties will be added by OASP4Fn in the final serverless.yml: module.exports = { provider: "google" } • Functions property: This property doesn’t have to appear in the configuration file, because this property is extracted from the project structure and generated by OASP4Fn, but there is a possibility to specify a generic configuration of the events that our project has adding a property events, that will contain the configuration to each event: module.exports = { events: { http: { integration: 'lambda', cors: true, authorizer: {arn: 'arn:aws:cognitoidp:us-west-2:836886498299:userpool/us-west-2_1511o0vuo', claims: ['username']} }, s3 : { bucket: 'conveyor-sls' }} } Keep in mind that all the properties specified in the configuration file will IMPORTANT overwrite the default configuration property (if any), and not merge it, so if you want to add an property to the default package you have to add it rewriting the defaults ones (as can be appreciated in the http event above). This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 523 Devonfw Guide v2.4.0 91.2. Handler configuration In the last section we learned how to add the general configuration of our service, but what we do, in the case that we want to add or modify a single property in a handler? (the path of an http endpoint, for example), then we want to add it accros the config property of OASP4Fn: oasp4fn.config({path: 'attachments/{id}'}); That method acts as a dummy function, and should be situated just above the handler function. It will add properties to the handler event property, or delete the default ones or the specified in the oasp4fn.config.js using the [source, typescript]undefined keyword: oasp4fn.config({ path: 'login', authorizer: undefined }); 91.3. Default configuration In this section we’re going to specify the default configuration that OASP4Fn for the different supported providers, which no one is specified, the default used will be the AWS provider. 91.3.1. AWS • provider: ◦ name: 'aws' ◦ runtime: 'node6.10' ◦ region: 'us-west-2' • plugins: ◦ 'serverless-webpack' ◦ 'serverless-offline' • events: ◦ http: ◦ integration: lambda ◦ cors: true ◦ method: ◦ s3: ◦ created: 's3:ObjectCreated:*' / removed: 's3:ObjectRemoved:*' (depending on the folder name) ◦ sns: ◦ topicName: ◦ alexaskill: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 524 Devonfw Guide v2.4.0 ◦ ◦ stream: ◦ type: • iamRoleStatements: ◦ Effect: 'Allow', Action: [ 'dynamodb:*' ], Resource: 'arn:aws:dynamodb:*:*:*' ◦ Effect: 'Allow', Action: [ 's3:*' ], Resource: '*' The iamRoleStatements property in the serverless.yml is inside the provider IMPORTANT property, but must be declared outside it in the configuration file in order to change it, this is made for avoid the possibility of have to change all the provider properties if you want to change some of the iamRoleStatements. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 525 Devonfw Guide v2.4.0 Chapter 92. Command Line Interface OASP4Fn comes with a command line interface that makes use of the infrastructure as code and the configuration to generate the proper files which are going to help the user, testing the application an deploying it. 92.1. Usage The command line interface must be used inside a OASP4Fn project, to use it you only has to type the command with its options (in case you installed it globally): oasp4fn # fun Otherwise you can use it with the package.json script that comes with the template, using npm or yarn: yarn fun The above commands will generate a serverless.yml and a webpack.config.json join with the following output: That command make use of the default configuration, but the usage is the following: oasp4fn [provider] [options] # or: fun [provider] [options] And the supported options are: argument alias description --options -o file with the options for the yml generation --path -p directory where the handlers are stored (handlers by default) This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 526 Devonfw Guide v2.4.0 argument alias description --express -e generates an express app.ts file, for testing purposes --help -h display the help As you can see above, you can check the options by passing the --help flag: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 527 Devonfw Guide v2.4.0 Chapter 93. Testing OASP4Fn Applications In this chapter we are going to learn how to test applications in OASP4Fn using the mocha framework and the chai assertion library. 93.1. Install global dependencies As the title says in this section we’re going to specify the global dependencies that we need to run our tests, that only are the test framework and the typescript interpreter: npm install -g mocha ts-node Yarn can be used instead. yarn global add mocha ts-node No more dependencies are needed, because any OASP4Fn project is started using the oasp4fn application template and the local part of the test dependencies are specified as dev-dependencies at the package.json file. 93.2. Writing the tests First of all, all the handlers, typings and the everytime useful lodash package must be imported in our test files, so the head of our test file will look like this: import import import import import { { { { * HttpEvent, Context, Code } from '../../handlers/types'; register } from '../../handlers/Http/POST/register-handler'; search } from '../../handlers/Http/POST/search-handler'; expect } from 'chai'; as _ from 'lodash'; The next step consists on defining the tests for handler we want to test. For example, for a handler called register the test specifies that it should return an object, with the code and dateAndTime properties: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 528 Devonfw Guide v2.4.0 import import import import { { { * HttpEvent, Context, Code } from '../../handlers/types'; register } from '../../handlers/Http/POST/register-handler'; expect } from 'chai'; as _ from 'lodash'; describe('register', () => { it('The register should return an object, with the code and dateAndTime properties'); }); With yarn test the test is excuted with the following output: This means that there is only one test and it is in a pending state The next step is to call the function and check his behavior. In order to do that, execute yarn test:auto to watch for changes and execute the tests automatically: IMPORTANT It is necessary to have the instance of the database running to pass the tests. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 529 Devonfw Guide v2.4.0 import import import import { { { * HttpEvent, Context, Code } from '../../handlers/types'; register } from '../../handlers/Http/POST/register-handler'; expect } from 'chai'; as _ from 'lodash'; const EVENT = { method: 'POST', path: {}, body: {}, query: {}, headers: {} } let context: Context; describe('register', () => { it('The register should return an object, with the code and dateAndTime properties', (done: Function) => { let event = _.assign({ body: { "name": "John", "email": "somenthing@something.com", "phone": "555566666"}}, EVENT); register(event, context, (err: Error, res: Code) => { try { expect(err).to.be.null; expect(res).to.be.an('object').that.contains.all.keys('code', 'dateAndTime'); done(); } catch(err){ done(err); } }) }); }); Please notice that a HttpEvent is declared and instantiated and only the Context is declared. Context variables are not used inside handlers but event variable are. As test data is inserted into our database, it must be erased after executing the test. In order to do this, a hook can be added that will be executed at the end of our tests, erasing all the data. As it executes an OASP4Fn function, it has to be imported in our file, store the IDs of the inserted data and delete them. For example: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 530 Devonfw Guide v2.4.0 after(async () => { try { if(code) await oasp4fn.delete('Queue', code).promise(); } catch (error) { return Promise.reject(error); } }); The variable code is the code property located in the returned object of the IMPORTANT handler of the previous example register. The result of the callbak would be that code, so it can be used to remove the test data. 93.2.1. Complete example A complete example with the tests of two different handlers below: import import import import import import { HttpEvent, Context, Code } from '../../handlers/types'; { register } from '../../handlers/Http/POST/register-handler'; { search } from '../../handlers/Http/POST/search-handler'; { expect } from 'chai'; * as _ from 'lodash'; oasp4fn from '@oasp/oasp4fn'; const EVENT = { method: 'POST', path: {}, body: {}, query: {}, headers: {} } let context: Context; let code: string; describe('register', () => { it('The register should return an object, with the code and dateAndTime properties', (done: Function) => { let event = _.assign({}, EVENT, { body: { "name": "David", "email": "somenthing@something.com", "phone": "658974145"}}); register(event, context, (err: Error, res: Code) => { try { expect(err).to.be.null; expect(res).to.be.an('object').that.contains.all.keys('code', 'dateAndTime'); code = res.code; done(); This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 531 Devonfw Guide v2.4.0 } catch(err){ done(err); } }) }); }); describe('search', () => { it('The search should return an array with the items of the table Queue', (done: Function) => { search(EVENT, context, (err: Error, res: object[]) => { try { expect(err).to.be.null; expect(res).to.be.an('Array'); res.forEach(obj => { expect(obj).to.be.an('object'); expect(obj).to.contain.all.keys( ['name', 'email', 'phone', 'code', 'dateAndTime'] ); }) done(); } catch(err){ done(err); } }) }); }); after(async () => { try { if(code) await oasp4fn.delete('Queue', code).promise(); } catch (error) { return Promise.reject(error); } }); And a successfull output would look like this: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 532 Devonfw Guide v2.4.0 Chapter 94. OASP4Fn Application Deplyment The deployment is performed by the serverless framework and it is quite simple, since the only command needed to deploy the full service is the following one: sls deploy IMPORTANT This command will fail if the AWS credentials are missing. For more information visit serverless credentials When the deploy is finish with no errors, you will have a command line output with the endpoints and the functions deployed. Note that until now we have been working locally, so if we gonna deploy our handlers and make them work, we should change the endpoint of our services: oasp4fn.setDB(dynamo, {endpoint: 'https://dynamodb.us-west-2.amazonaws.com'}); You can get more information about the deployment in the serverless NOTE documentation, but be aware that OASP4Fn doesn’t support the deployment of a single function. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 533 Devonfw Guide v2.4.0 Chapter 95. Topical Guides for Client Layers This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 534 Devonfw Guide v2.4.0 Chapter 96. OASP4JS 96.1. A Closer Look This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 535 Devonfw Guide v2.4.0 Chapter 97. Creating new OASP4JS Application In this chapter, you are going to see how to build a new OASP4JS from scratch. The proposal of this tutorial is to end having enough knowledge of Angular and the rest of technologies regarding OASP4JS to know how to start developing on it and if you want more advanced and specific functionalities see them on the cookbook. 97.1. End Result is Jump The Queue This mock-up images shows what you are going to have as a result when the tutorial is finished. An app to manage codes assigned to queuers in order to easy the management of the queue, with a code, you can jump positions in queue and know everywhere which is your position. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 536 Devonfw Guide v2.4.0 This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 537 Devonfw Guide v2.4.0 So, hands on it, let’s configure the environment and build this app! 97.2. Installing Global Tools 97.2.1. Visual Code: To install the editor download the installer from the official page and install it. Once installed, the first thing you should do is install the extensions that will help you during the development, to do that follow this steps: 1. Install Settings Sync extension. 2. Open the command palette (Ctrl+Shift+P) and introduce the command: Sync: Download Settings. Provide GIST ID: 3b1d9d60e842f499fc39334a1dd28564. In the case that you are unable to set up the extensions using the method mentioned, you can also use the scripts provided in this repository. 97.2.2. Node.js Go to the node.js official page and download the version you like the most, the LTS or the Current, as you wish. The recommendation is to install the latest version of your election, but keep in mind that to use Angular CLI your version must be at least 8.x and npm 5.x, so if you have a node.js already installed in your computer this is a good moment to check your version and upgrade it if it’s necessary. 97.2.3. TypeScript Let’s install what is going to be the main language during development: TypeScript. This ES6 superset is tightly coupled to the Angular framework and will help us to get a final clean and distributable JavaScript code. This is installed globally with npm, the package manager used to install and create javascript modules in Node.js, that is installed along with Node, so for install typescript you don’t have to install npm explicitly, only run this command: npm install –g typescript 97.2.4. Yarn As npm, Yarn is a package manager, the differences are that Yarn is quite more faster and usable, so we decided to use it to manage the dependencies of Oasp4Js projects. To install it you only have to go to the official installation page and follow the instructions. Even though, if you feel more comfortable with npm, you can remain using npm, there is no problem regarding this point. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 538 Devonfw Guide v2.4.0 97.2.5. Angular/CLI CLI specially built for make Angular projects easier to develop, maintain and deploy, so we are going to make use of it. To install it you have to run this command in your console prompt: npm install –g @angular/cli Then, you should be able to run ng version and this will appear in the console: In addition, you can set Yarn as the default package manager to use with Angular/CLI running this command: ng set --global packageManager=yarn Finally, once all these tools have been installed successfully, you are ready to create a new project. 97.3. Creating Basic Project One of the best reasons to install Angular/CLI is because it has a feature that creates a whole new basic project where you want just running: ng new Where is the name of the project you want to create. In this case, we are going to call it JumpTheQueue. This command will create the basic files and install the dependencies stored This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 539 Devonfw Guide v2.4.0 in package.json Then, if we move to the folder of the project we have just created and open visual code we will have something like this: Finally, it is time to check if the created project works properly. To do this, move to the projects root folder and run: ng serve -o And… it worked: 97.4. Working with Google Material and Covalent Teradata This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 540 Devonfw Guide v2.4.0 97.4.1. Installing Dependencies First, we are going to add Google Material to project dependencies running the following commands: yarn add @angular/material @angular/cdk Then we are going to add animations: yarn add @angular/animations Finally, some material components need gestures support, so we need to add this dependency: yarn add hammerjs That is all regarding Angular/Material. We are now going to install Covalent Teradata dependency: yarn add @covalent/core@2.0.0-beta.1 Now that we have all dependencies we can check in the project’s package.json file if everything has been correctly added: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 541 Devonfw Guide v2.4.0 "dependencies": { "@angular/animations": "6.0.3", "@angular/cdk": "6.2.1", "@angular/common": "6.0.3", "@angular/compiler": "6.0.3", "@angular/core": "6.0.3", "@angular/forms": "6.0.3", "@angular/http": "6.0.3", "@angular/material": "6.2.1", "@angular/platform-browser": "6.0.3", "@angular/platform-browser-dynamic": "6.0.3", "@angular/platform-server": "6.0.3", "@angular/router": "6.0.3", "@angular/service-worker": "6.0.3", "@covalent/core": "^2.0.0-beta.1", "core-js": "^2.4.1", "hammerjs": "^2.0.8", "moment": "^2.20.1", "rxjs": "^6.1.0", "rxjs-compat": "^6.1.0", "zone.js": "^0.8.26" }, 97.4.2. Importing Styles and Modules Now let’s continue to make some config modifications to have all the styles and modules imported to use Material and Teradata: 1. Angular Material and Covalent need the following modules to work: CdkTableModule, BrowserAnimationsModule and every Covalent and Material Module used in the application. So make sure you import them in the imports array inside of app.module.ts. These modules come from @angular/material, @angular/cdk/table, @angular/platform-browser/animations and @covalent/core. 2. Create theme.scss, a file to config themes on the app, we will use one primary color, one secondary, called accent and another one for warning. Also Teradata accepts a foreground and background color. Go to /src into the project and create a file called theme.scss whose content will be like this: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 542 Devonfw Guide v2.4.0 @import '~@angular/material/theming'; @import '~@covalent/core/theming/all-theme'; @include mat-core();$primary: mat-palette($mat-blue, 700);$accent: mat-palette($mat-orange, 800);$warn:
mat-palette($mat-red, 600);$theme: mat-light-theme($primary,$accent, $warn);$foreground: map-get($theme, foreground);$background: map-get($theme, background); @include angular-material-theme($theme);
@include covalent-theme($theme); 3. Now we have to add these styles in angular/CLI config. Go to .angular-cli.json to "styles" array and add theme and Covalent platform.css to make it look like this: "styles": [ "styles.css", "theme.scss", "../node_modules/@covalent/core/common/platform.css" ], With all of this finally done, we are ready to start the development. 97.5. Alternative : ng-seed Another option is to get this basic project structure with all its dependencies and styles already set is to clone the develop-covalent branch of ng-project-seed. Once you have cloned it, move to the project root folder and run a yarn to install all dependencies from package.json. The project serves as an example which also comes with some common functionalities already implemented if you want to use them. In order to make the task easier, we are going to avoid the removal of unused components, so we will use the project created on the previous point to build the app. 97.6. Start the development Now we have a fully functional blank project, all we have to do now is just create the components and services which will compose the application. First, we are going to develop the views of the app, through its components, and then we will create This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 543 Devonfw Guide v2.4.0 the services with the logic, security and back-end connection. 97.6.1. Creating components NOTE Learn more about creating new components in OASP4Js HERE The app consists of 3 main views: • Access • Code viewer • List of the queue To navigate between them we are going to implement routes to the components in order to use Angular Router. To see our progress, move to the root folder of the project and run ng serve this will serve our client app in localhost:4200 and keeps watching for changing, so whenever we modify the code, the app will automatically reload. Root component app.component will be our Root component, so we do not have to create any component yet, we are going to use it to add to the app the elements that will be common no matter in what view we are. NOTE Learn more about the root component in OASP4Js HERE This is the case of a header element, which will be on top of the window and on top of all the components, let’s build it: The first thing to know is about Covalent Layouts because we are going to use it a lot, one for every view component. NOTE Learn more about layouts in OASP4Js HERE As we do not really need nothing more than a header we are going to use the simplest layout: nav view Remember that we need to import in app.module the main app.component and every component of Angular Material and Covalent Teradata we use (i.e. for layouts it is CovalentLayoutModule). Our app.module.ts should have the following content: // Covalent imports import { CovalentLayoutModule, CovalentCommonModule, } from '@covalent/core'; // Material imports This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 544 Devonfw Guide v2.4.0 import { MatCardModule, MatInputModule, MatButtonModule, MatButtonToggleModule, MatIconModule, MatSnackBarModule, MatProgressBarModule, } from '@angular/material'; import { CdkTableModule } from '@angular/cdk/table'; // Angular core imports import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { HttpClientModule } from '@angular/common/http'; import 'hammerjs'; // Application components and services import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, FormsModule, BrowserAnimationsModule, HttpClientModule, MatCardModule, MatInputModule, MatButtonModule, MatButtonToggleModule, MatIconModule, MatProgressBarModule, MatSnackBarModule, CovalentLayoutModule, CovalentCommonModule, ], providers: [], bootstrap: [AppComponent] }) export class AppModule { } NOTE // Angular Material modules we are going to use // Covalent Teradata Layout Module Remember this step because you will have to repeat it for every other component from Teradata you use in your app. Now we can use layouts, so lets use it on app.component.html to make it look like this: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 545 Devonfw Guide v2.4.0 Jump The Queue {{title}} NOTE // Layout tag // Header container // Main content Learn more about toolbars in OASP4Js HERE Once this done, our app should have a header and the "app works!" should remain in the body of the page: To make a step further, we have to modify the body of the Root component because it should be the output of the router, so now it is time to prepare the routing system. First we need to create a component to show as default, that will be our access view, later on we will modify it on it’s section of this tutorial, but for now we just need to have it: stop the ng serve and run ng generate component access. It will add a folder to our project with all the files needed for a component. Now we can move on to the router task again. Run ng serve again to continue the development. Let’s create the module when the Router check for routes to navigate between components. 1. Create a file called app-routing.module.ts and add the following code: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 546 Devonfw Guide v2.4.0 imports... const appRoutes: Routes = [ // Routes string, where Router will check the navigation and its properties. { path: 'access', component: AccessComponent}, // Redirect if url path is /access. { path: '**', redirectTo: '/access', pathMatch: 'full' }]; // Redirect if url path do not match with any other route. @NgModule({ imports: [ RouterModule.forRoot( appRoutes, { enableTracing: true }, // <-- debugging purposes only ), ], exports: [ RouterModule, ], }) export class AppRoutingModule {} // Export of the routing module. Time to add this AppRoutingModule routing module to the app module: ... imports: [ BrowserModule, AppRoutingModule, CovalentLayoutModule, ... NOTE Learn more about routing in OASP4Js HERE Finally, we remove the "{{title}}" from app.component.html and in its place we put a tag. So the final result of our Root component will look like this: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 547 Devonfw Guide v2.4.0 As you can see, now the body content is the html of AccessComponent, this is because we told the Router to redirect to Access when the path is /access, but also, redirect to it as default if any of the other routes match with the path introduced. We will definitely going to modify the header in the future to add some options like log-out but, for the moment, this is all regarding Root Component. AccessComponent As we have already created this component from the section before, let’s move on to building the template of the access view. First, we need to add the Covalent Layout and the card: Access This will add a grey background to the view and a card on top of it with the title: "Access", now that we have the basic structure of the view, let’s add the form with the information to access to our queue number: • Name of the person • Email This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 548 Devonfw Guide v2.4.0 • Telephone number One simple text field, one text field with email validation (and the legal information regarding emails) and a number field. Moreover, we are going to add this image: In order to have it available in the project to show, save it in the following path of the project: /src/assets/images/ and it has been named: jumptheq.png So the final code with the form added will look like this: Access Filling this, I acccept to receive commercial information. This form contains three input container from Material and inside of them, the input with the properties listed above and making all required. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 549 Devonfw Guide v2.4.0 Also, we need to add the button to send the information and redirect to code viewer or show an error if something went wrong in the process, but for the moment, as we neither have another component nor the auth service yet, we will implement the button visually and the validator to disable it if the form is not correct, but not the click event, we will come back later to make this working. NOTE Learn more about forms in OASP4Js HERE This code will give us as a result something similar to this: Now lets continue with the second component: Code viewer. Code viewer component Our first step will be create the component in the exact same way we did with the access component: ng generate component code-viewer and we add the route in the app-routing.module.ts: const appRoutes: Routes = [ { path: 'access', component: AccessComponent}, { path: 'code', component: CodeViewerComponent}, //code-viewer route added { path: '**', redirectTo: '/access', pathMatch: 'full' }]; With two components already created we need to use the router to navigate between them. Following the application flow of events, we are going to add a navigate function to the submit button of our access form button, so when we press it, we will be redirected to our code viewer. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 550 Devonfw Guide v2.4.0 Turning back to access.component.html we have to add this code: // added a ngSubmit event ... // navigation function like access  And the implementation of the code-viewer.component.ts should be something like: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 551 Devonfw Guide v2.4.0 imports... export class CodeViewerComponent implements OnInit { code: string; // declaration of vars used in the template name: string; constructor(private router: Router) { } // instance of Router ngOnInit(): void { this.code = 'Q06'; //This values in the future will be loaded from a service making a call for server information this.name = 'Someone'; } navigateQueue(): void { // this will be filled with the router navigate function when we have created the queue component } Giving this as a result: Finally, we are going to add an icon button to the header to log out, we are not able to log out or to hide the icon yet, we are just letting it prepared for the future when the auth service is implemented. Modify app.component.html div tag as follows: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 552 Devonfw Guide v2.4.0 Jump The Queue //Fill empty space to put the icon in the right of the header If everything goes correctly, you should now have an icon at the right of the header no matter which view you are at. Queue component For our last view component we are going to use a component from Covalent Teradata: the data table. Let’s begin. As always: ng generate component queue-viewer and add a route in app.routes.ts to that component { path: 'queue', component: QueueViewerComponent}, Now we have the component created, let’s take a bit of time to complete navigateQueue() function in code-viewer to point to this new component: navigateQueue(): void { this.router.navigate(['queue']); } Back to our recently created component, it will be quite similar to the 2 others, but in this case, the body of the card will be a data table from covalent. 1. First, import the CovalentDataTableModule in app.module.ts: // Covalent imports import { ... CovalentDataTableModule, } from '@covalent/core'; // Add this line ... @NgModule({ ... imports: [ ... CovalentDataTableModule, ], ... // Add this line 2. Edit the HTML with the new table component: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 553 Devonfw Guide v2.4.0 Queue view: NOTE Learn more about Teradata data tables in OASP4Js HERE What we did here is to create the component by its selector, and give the needed inputs to build the table: columns to display names and establish concordance with the data, and some data to show. Also, a button to return to the code view has been added following the same system as the navigation in code, but pointing to 'code': This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 554 Devonfw Guide v2.4.0 export class QueueViewerComponent implements OnInit { columns: ITdDataTableColumn[] = [ { name: 'code', label: 'Code'}, { name: 'hour', label: 'Hour' }, { name: 'name', label: 'Name'}]; queuers: any[] = [ {code: 'Q04', hour: '14:30', name: 'Elrich'}, {code: 'Q05', hour: '14:40', name: 'Richard'}, {code: 'Q06', hour: '14:50', name: 'Gabin'}, ]; constructor(private router: Router) { } ngOnInit(): void { } navigateCode(): void { this.router.navigate(['code']); } } This will be the result: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 555 Devonfw Guide v2.4.0 97.6.2. Creating services NOTE Learn more about services in OASP4Js HERE At the moment we had developed all the basic structure and workflow of our application templates, but there is still some more work to do regarding security, calls to services and logic functionalities, this will be the objective of this second part of the tutorial. We will use angular/cli to generate our services as we did to create our components. NOTE Learn more about creating new services in OASP4Js HERE Auth service We will start with the security, implementing the service that will store our state and username in the application, this services will have setters and getters of these two properties. This service will be useful to check when the user is logged or not, to show or hide certain elements of the headers and to tell the guard (service that we will do next) if the navigation is permitted or not. To create the service we run: ng generate service shared\authentication\auth. We navigate into this new service and we add this code as described above: import { Injectable } from '@angular/core'; @Injectable() export class AuthService { private logged = false; private user = ''; // state of the user //username of the user public isLogged(): boolean { return this.logged; } public setLogged(login: boolean): void { this.logged = login; } public getUser(): string { return this.user; } public setUser(username: string): void { this.user = username; } } When the access service will be done, it will call for this setters to set them with real information, and when we log off, this information will be removed accordingly. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 556 Devonfw Guide v2.4.0 As an example of use of this information service, we will move to app.component.ts and will add in the constructor the AuthService to inject it and have access to its methods. Now on the template we are going to use and special property from Angular ngIf to show or hide the log-off depending on the state of the session of the user: This property will hide the log-off icon button when the user is not logged and show it when it is logged. NOTE Learn more about authentication in OASP4Js HERE Guard service With AuthService we have a service providing information about the state of the session, so we can now establish a guard checking if the user can pass or not trough the login page. We create it exactly the same way than the AuthService: ng generate service shared\authentication\auth-guard. This service will be a bit different, because we have to implement an interface called CanActivate, which has a method called canActivate returning a boolean, this method will be called when navigating to a specified routes and depending on the return of this implemented method, the navigation will be done or rejected. NOTE Learn more about guards in OASP4Js HERE The code should be as follows: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 557 Devonfw Guide v2.4.0 import { Injectable } from '@angular/core'; import { CanActivate, Router } from '@angular/router'; import { AuthService } from './auth.service'; @Injectable() export class AuthGuardService implements CanActivate { constructor(private authService: AuthService, private router: Router) {} canActivate(): boolean { if (this.authService.isLogged()) { the navigation return true; } // if logged, return true and exit, allowing if (this.router.url === '/') { this.router.navigate(['access']); // if not logged, recheck the navigation to resend to login page in case the user tried to navigate modifying directly the URL in the browser. } return false; // and blocking the navigation. } } Now we have to add them to our app.module.ts providers array: ... providers: [ AuthGuardService, AuthService, ], bootstrap: [AppComponent] ... Finally, we have to specify what routes are secured by this guard, so we move to approuting.module.ts and add the option "canActivate" to the paths to code-viewer and queue-viewer: const appRoutes: Routes = [ { path: 'access', component: AccessComponent}, { path: 'code', component: CodeViewerComponent, canActivate: [AuthGuardService]}, { path: 'queue', component: QueueViewerComponent, canActivate: [AuthGuardService]}, { path: '**', redirectTo: '/access', pathMatch: 'full' }]; If you save all the changes, you will realize you can not go trough access anymore, that is because we need to implement first our login function in the access service, which will change the value in This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 558 Devonfw Guide v2.4.0 AuthService and will let us navigate freely. Access service As we need to have this service in order to access again to our application, this will be the first service to be created. As always, ng generate service access/shared/access will do the job. Also remember to add the service to providers in app module. This service will contain two functions, one for login when the button is pressed and other to log off when the icon button in the header is pressed. This functions will manage to set the values of the session and navigate properly. For now we are going to use a simple if to check if the user credentials are correct, in the future a server will do this for us. export class AccessService { constructor(private auth: AuthService, public snackBar: MatSnackBar, show when an error ocurred private router: Router) { } // Angular Material snackbar component to login(name, email, phone): void { if (name === 'user' && email === 'asd@asd.com' && phone === 123456789) { //check the credentials introduced this.auth.setLogged(true); // if correct, values set and navigation made this.auth.setUser(name); this.router.navigate(['code']); } else { this.snackBar.open('access error', 'OK', { // if incorrect, snackbar with an error message is shown. duration: 2000, }); } } logoff(): void { to false and redirected to access view this.auth.setLogged(false); this.auth.setUser(''); this.router.navigate(['access']); } //remove the values, set logged } This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 559 Devonfw Guide v2.4.0 Now we have to inject this service in our AccessComponent in order to consume it. We inject the dependency into the component and we change our submit function to get the values from the form and to call the service instead of just always redirecting: export class AccessComponent implements OnInit { constructor(private accessService: AccessService) { } ngOnInit(): void { } submitAccess(formValue): void { this.accessService.login(formValue.value.name, formValue.value.email, formValue .value.phone); formValue.reset(); } } This also has to be added to the template in order to pass the parameter into the function: ngSubmit now passes as parameter the ngForm with the values introduced by the user. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 560 Devonfw Guide v2.4.0 Having this working should be enough to have again working our access component and grant access to the code and queue viewer if we introduce the correct credentials and if we do not, the error message would be shown and the navigation not permitted, staying still in the access view. The last thing to do regarding security is to make functional our log-off icon button in the header, we move to app.component.html and add the correspondent (click) event calling for a function, in my case, called "logoff()". mat-icon-button mdTooltip="Log The name has to correspond with the one used in app.component.ts, where we inject AccessService so we can call its logoff function where the one from this components is called: export class AppComponent { constructor(public auth: AuthService, private accesService: AccessService) {} logoff(): void { this.accesService.logoff(); } } Once all of this is finished and saved, we should have all the workflow and navigation of the app working fine. Now it is time to receive the data of the application from a service in order to, in the future, call a server for this information. Code Service First step, as always, create the service in a shared folder inside the component: ng generate service code-viewer/shared/code-viewer. Due to the simplicity of this view, the only purpose of this service is to provide the queue code, which will be generated by the server but, until we connect to it, we have to generate it in the service (imports included here in order to make easier this section): This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 561 Devonfw Guide v2.4.0 import { Injectable } from '@angular/core'; import { Observable, of } from 'rxjs'; @Injectable() export class CodeViewerService { constructor() { } getCode(): Observable { return Observable.of('Q06'); } // later, this will make a call to the server // but, for now, this Observable will do the work } We return an Observable because when we implement calls to the server, we will use Http, and they return observables, so the best way to be prepared to this connection is having a simulation of the return of this Http calls. It is time to inject it in the component and change a bit the variables to show in the template to get their vale from auth and our code-viewer service: export class CodeViewerComponent implements OnInit { code: string; name: string; constructor(private router: Router, private auth: AuthService, private codeService: CodeViewerService) { } ngOnInit(): void { this.codeService.getCode().subscribe((data: string) => { this.code = data; }); this.name = this.auth.getUser(); } navigateQueue(): void { this.router.navigate(['queue']); } } NOTE Learn more about Observables and RxJs in OASP4Js HERE Now if we log in the application, the name we introduce in the form will be the name displayed in the code-viewer view. And the queue code will be the one we set in the service. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 562 Devonfw Guide v2.4.0 Queue service The last element to create in our application, as always: ng generate service queue- viewer/shared/queue-viewer and then add the service in providers at app.module.ts. This service will work the same way code-viewer, it will simulate an observable that returns the data that will be displayed in the data table of Covalent Teradata: Injectable() export class QueueViewerService { queuers: any[]; constructor() { } getQueuers(): Observable { server and return an Observable // later, this will make a call to the this.queuers = [{ code: 'Q04', hour: '14:30', name: 'Elrich' }, { code: 'Q05', hour: '14:40', name: 'Richard' }, { code: 'Q06', hour: '14:50', name: 'Gabin' }]; return Observable.of(this.queuers); // but, for now, this Observable will do the work } } And the queue-viewer.component.ts will be modified the same way: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 563 Devonfw Guide v2.4.0 export class QueueViewerComponent implements OnInit { columns: ITdDataTableColumn[] = [ { name: 'code', label: 'Code'}, { name: 'hour', label: 'Hour' }, { name: 'name', label: 'Name'}]; queuers: any[]; constructor(private router: Router, private queueService: QueueViewerService) { } ngOnInit(): void { this.queueService.getQueuers().subscribe( (data) => { this.queuers = data; }); } navigateCode(): void { this.router.navigate(['code']); } } At the moment, we have a functional application working exclusively with mock data, but we want to connect to a real back-end server to make calls and consume its services to have more realistic data, the way we implemented our components are completely adapted to read mock data or real server data, that is why we use services, to isolate the origin of the logic and the data from the component. Is the code of our services what is going to change, and we will go to see it now. 97.7. Making Calls to Server At this point we are going to assume you have finished the OASP4J configuration and deployment or, at least, you have downloaded the project and have it running locally on localhost:8081. With a real server running and prepared to receive calls from our services, we are going to modify a bit more our application in order to adjust to this new status. 97.7.1. Preparations First, some configurations and modifications must be done to synchronize with how the server works: 1. Now our Authentication.ts should have the parameter "code" along with its getters and setters, which will be the queue code of the user, this has been moved here because this information comes from the register call when we access, not when we load the code view. 2. Completely remove shared service from code-viewer folder, because, at this moment, the only purpose of that folder was to store a service which loads the queue-code of the user, as it is not This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 564 Devonfw Guide v2.4.0 used anymore, this service has no sense and the code-viewer.component now loads its code variable from auth.getCode() function. 3. Create a file called config.ts in app folder, this config will store useful global information, in our case, the basePath to the server, so we can have it in one place and access it from everywhere, and even better, if the url changes, we only need to change it here: export const config: any = { basePath: 'http://localhost:8081/jumpthequeue/services/rest/', }; 97.7.2. Access Services Once done all the preparations, let’s move to acces.service.ts, here we had a simple if to check if the user inputs are what we expected, now we are going to call the server and it will manage all this logic to finally return us the information we need. To call the server, you can import Angular HttpClient class from @angular/common/http, this class is the standard used by angular to make Http calls, so we are going to use it. The register call demands 3 objects: name, email and phone, so we are going to build a post call and send that information to the proper URL of that server service, it will return an observable and we have already worked with them: first we map the result and then we subscribe to have all the response data available, also we implement the error function in case something went wrong. The new register function should be as follows: register(name, email, phone): void { this.http.post(${config.basePath}visitormanagement/v1/register, {name: name, email: email, phone: phone}) .subscribe( (res) => { this.auth.setLogged(true); this.auth.setUser(name); this.auth.setCode(res.code.code); this.router.navigate(['code']); }, (err) => { this.snackBar.open(err.error.message, 'OK', { duration: 5000, }); }); } Important: As we can see in the code the request is mapped with the type any. This is made for this tutorial purposes, but in a real scenario this any should be changed by the correct type (interface or class) that fits with the Http response. As we can see, and mentioned before, our preparations to this server call we have done previously let us avoid changing anything in access component or template, everything should be working only doing that changes. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 565 Devonfw Guide v2.4.0 Our queue-viewer will need some modifications as well, in this case, both component and services will be slightly modified. queue_viewer.service will make a call to the server services as we done in access.service but in this case we are not going to implement a subscription, that will be components task. So getQueuers() should look like this: getQueuers(): Observable { return this.http.post(\${config.basePath} visitormanagement/v1/visitor/search, {}) // the post usually demands some parameters to paginate or make // in this case we do not need nothing to do more } Regarding queue-viewer.component we need to modify the columns to fit with the data received from the server and the template will be modified to use async pipe to subscribe the data directly and a loader to show meanwhile. About the columns, the server sends us the data array composed of two objects: visitor with the queue member information and code with all the code information. As we are using the name of the queuer, the time it is expected to enter and its code, the column code should be like this: columns: ITdDataTableColumn[] = [ { name: 'visitor.name', label: 'Name'}, { name: 'code.dateAndTime', label: 'Hour', format: ( (v: string) => moment(v ).format('LLL') ) }, { name: 'code.code', label: 'Code'}, ]; Additionally, server sends us the date and time as timestamp, so we need to use moment.js to format that data to something readable, to make that, just use the format property from Teradata Covalent columns. Finally, to adapt to async pipe, ngOnInit() now does not subscribe, in its place, we equal the queuers variable directly to the Observable so we can load it using the *ngIf - else structure to show the loading bar from Material and load the queuers in the template: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 566 Devonfw Guide v2.4.0 // load queuers and asign the result to the name queuersList and only show this card if the queuers are loaded Queue view: // Covalent check for column values