Devonfw Guide V2.4.0

User Manual:

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

DownloadDevonfw Guide V2.4.0
Open PDF In BrowserView PDF
Devonfw Guide
v2.4.0
Written by the Devonfw community. This documentation is licensed under the Creative Commons
License (Attribution-NoDerivatives 4.0 International).

Table of Contents
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

Chapter 1. Quick Start with Devonfw

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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:

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.
Find more about devonfw here.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.
To read in details about Devonfw features read here

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

7

Devonfw Guide v2.4.0

Chapter 4. Download and Setup
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

4.2. Download
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
init, learn more here.

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".

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

If your proxy does not require authentication, simply remove the "-Dhttp.proxyUser", "DhttpProxyPassword", "-Dhttps.proxyUser" and "-Dhttps.proxyPassword" parameters.
Eclipse
Open eclipse by executing "eclipse-main.bat".

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

11

Devonfw Guide v2.4.0

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

Switch from "Native" to "Manual"
Enter your proxy configuration

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

12

Devonfw Guide v2.4.0

Thats All!!!

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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:

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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":

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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:

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
First of all, download DynamoDB in order to work with it locally using this link here.
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

Additional step

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:

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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:
username: waiter
password: waiter
as shown below:

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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)
• Login and session management
• 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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
workspace command learn more here

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

17.1.2. Sencha reloading
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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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)

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
command learn more here

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
already available.

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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!
Copyright (c) 2016 Capgemini
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!
Copyright (c) 2016 Capgemini
usage: foo <> [parameters...]
This is only a test module.
Available commands for module: foo
> saySomething: This command is for say something

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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!
Copyright (c) 2016 Capgemini
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!
Copyright (c) 2016 Capgemini
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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

60

Devonfw Guide v2.4.0

D:\devon-dist>devon oasp4j create -p
Hello, this is Devcon!
Copyright (c) 2016 Capgemini
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!
Copyright (c) 2016 Capgemini
devcon v.1.0.0

D:\>devon foo saySomething -message hello -signature John -v
Hello, this is Devcon!
Copyright (c) 2016 Capgemini
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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

61

Devonfw Guide v2.4.0

D:\>devon
Hello, this is Devcon!
Copyright (c) 2016 Capgemini
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!
Copyright (c) 2016 Capgemini
usage: dist <> [parameters...]
Module with general tasks related to the distribution itself
Available commands for module: dist
> install: This command downloads the distribution
> 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!
Copyright (c) 2016 Capgemini
usage: install [-password] [-path] [-type] [-user]
This command downloads the distribution
-password
the password related to the user with permissions to download
the Devon distribution
-path
a location for the Devon distribution download
-type
the type of the distribution, the options are:
'oaspide' to download OASP IDE
'devondist' to download Devon IDE
-user
a user with permissions to download the Devon distribution

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

62

Devonfw Guide v2.4.0

So now you know that the install command of the dist module needs:
• user with permissions to download the distribution.
• the related password.
• the path where the distribution file must to be downloaded.
• 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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

22.1.1. Features and advantages
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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
CheckStyle is a Open Source development tool to help you ensure that your Java code adheres to a
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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
additional Checkstyle violations views.
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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
Checkstyle has finished checking your code please look into your Eclipse Problems View. There
should be some warnings from Checkstyle. This warnings point to the code locations where your
code violates the preconfigured Checks configuration.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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… →
Find Bugs menu entry.

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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/
Analyzing+Source+Code [link].
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:

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

72

Devonfw Guide v2.4.0

Advanced Configuration
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

Or you can go to link http://localhost:9000 and login with admin as id and admin as password and
goto Dashboard.you can see all the statistics of analysis of the configured projects on sonar server.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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):

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

(h2|postgresql|mysql|mariadb|oracle|hana|db2)

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
To read more about the OASP4J application structure, click here.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
privileged access to the event
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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

80

Devonfw Guide v2.4.0

24.4. Model

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

81

Devonfw Guide v2.4.0

⇒ the Event item is not further developed nor implemented

24.4.1. Predicates

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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 >

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

83

Devonfw Guide v2.4.0

type ID :: trivial: Unique Atomic Identifier
type NamedItem :: string
with predicates: notnull, notempty
type EmailAddress :: string
with predicates: isEmailAddress
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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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:

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
command learn more here

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
generating project you need to add dbtype (h2|postgresql|mysql|mariadb|oracle|hana|db2)

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

90

Devonfw Guide v2.4.0

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:

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

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:

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

91

Devonfw Guide v2.4.0


org.mariadb.jdbc
mariadb-java-client
1.5.4

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

spring.datasource.driver-class-name=org.mariadb.jdbc.Driver

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.datasource.username=
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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

92

Devonfw Guide v2.4.0

server.port=8081
server.context-path=/
spring.datasource.url=jdbc:postgresql://localhost:5432/<>
spring.datasource.password=
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.datasource.username=
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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

93

Devonfw Guide v2.4.0

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

server.port=8081
server.context-path=/
spring.datasource.password=
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
db2jcc_license_cisuz
9.7


com.ibm.db2
db2java
9.7

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.datasource.username=
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.password=
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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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:

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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;

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
* {@link TableState#isReserved() reserved}, {@link TableState#isOccupied() occupied}
and may have a
* {@link #getWaiterId() waiter} assigned.
*/
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);
/**
* @return the current {@link TableState state} of this {@link Table}.
*/
TableState getState();
/**
* @param state is the new {@link #getState() state}.
*/
void setState(TableState state);
/**
* @return the {@link
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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

99

Devonfw Guide v2.4.0

package devonfw.tutorial.tablemanagement.common.api.datatype;
/**
* Represents the {@link devonfw.tutorial.tablemanagement.common.api.Table#getState()
state} of a
* {@link devonfw.tutorial.tablemanagement.common.api.Table}.
*/
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);
}
}

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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;
/**
* {@link ApplicationPersistenceEntity Entity} representing a {@link Table} of the
restaurant. A table has a unique
* {@link #getNumber() number} can be {@link TableState#isReserved() reserved}, {@link
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) {

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
about validation in the OASP4J guide about validation

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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 io.oasp.module.jpa.dataaccess.api.MasterDataDao;
import java.util.List;
/**
* {@link ApplicationDao Data Access Object} for {@link TableEntity} entity.
*/
public interface TableDao extends ApplicationDao, MasterDataDao
 {
/**
* Returns a list of free restaurant tables.
*
* @return {@link List} of free restaurant {@link TableEntity}s
*/
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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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:

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.general.dataaccess.base.dao.ApplicationMasterDataDaoImpl;
devonfw.tutorial.tablemanagement.dataaccess.api.TableEntity;
devonfw.tutorial.tablemanagement.dataaccess.api.dao.TableDao;

/**
* Implementation of {@link 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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

105

Devonfw Guide v2.4.0

27.1.4. Step 4: Business logic
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;
/**
* {@link AbstractEto ETO} for {@link Table}.
*/
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;

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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;

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

108

Devonfw Guide v2.4.0

*
* @return {@link List} of all existing restaurant {@link TableEto}s
*/
List findAllTables();
/**
* Returns a list of all existing free restaurant tables.
*
* @return {@link List} of all existing free restaurant {@link TableEto}s
*/
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.general.logic.base.AbstractComponentFacade;
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;
This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

109

Devonfw Guide v2.4.0

/**
* Implementation of {@link Tablemanagement}.
*/
@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)

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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;
}
/**
* @param tableDao the {@link TableDao} to {@link Inject}.
*/
@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,

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
* {@link io.oasp.module.security.common.api.accesscontrol.AccessControlPermission}s.
*
*/
public abstract class PermissionConstants {
/** {@link io.oasp.module.security.common.api.accesscontrol.AccessControlPermission}
to retrieve table. */
public static final String FIND_TABLE = "FindTable";
/** {@link io.oasp.module.security.common.api.accesscontrol.AccessControlPermission}
to save table. */
public static final String SAVE_TABLE = "SaveTable";
/** {@link io.oasp.module.security.common.api.accesscontrol.AccessControlPermission}
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
more information on the subject.
Listing 8. TablemanagementRestServiceImpl.java

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.BadRequestException;
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;
}
/**

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

113

Devonfw Guide v2.4.0

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

114

Devonfw Guide v2.4.0

public List getAllTables() {
List allTables = this.tableManagement.findAllTables();
return allTables;
}
/**
*
* Delegates to {@link Tablemanagement#findFreeTables}.
*
*
*
* @return list of all existing free {@link TableEto}s
*/
@GET
@Path("/freetables/")
public List getFreeTables() {
return this.tableManagement.findFreeTables();
}
/**
*
* Delegates to {@link Tablemanagement#saveTable}.
*
*
*
* @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);
}
/**
*
* Delegates to {@link Tablemanagement#deleteTable}.
*
*
*
* @param id ID of the {@link TableEto} to be deleted

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
access to this functionality.

27.1.6. Step 6: Add pagination
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;

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

116

Devonfw Guide v2.4.0

import devonfw.tutorial.tablemanagement.common.api.datatype.TableState;
/**
*
* This is the {@link SearchCriteriaTo search criteria} {@link
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;

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

118

Devonfw Guide v2.4.0

TablemanagementRestServiceImpl class.

/**
* Delegates to {@link Tablemanagement#findTableEtos}.
*
* @param searchCriteriaTo the pagination and search criteria to be used for finding
tables.
* @return the {@link PaginatedListTo list} of matching {@link TableEto}s.
*/
@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}.
* @return the {@link List} of matching {@link TableEto}s.
*/
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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

119

Devonfw Guide v2.4.0

Listing 12. TableDao.java

/**
* Finds the {@link TableEntity orders} matching the given {@link
TableSearchCriteriaTo}.
*
* @param criteria is the {@link TableSearchCriteriaTo}.
* @return the {@link List} with the matching {@link TableEntity} objects.
*/
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:

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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:

// Add order by fields
addOrderBy(query, alias, table, criteria.getSort());
Now add the following method to TableDaoImpl:

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
sort parameters received.

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/.







This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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"}]

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
To get access to the BeanMapper, you can use this dependency in your pom.xml file:


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);

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.
For more information on dozer, refer here.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
can start with a simple tutorial in order to get the most clear idea about TDD.
The goal is create a simple calculator that has two methods: add(int,int) and sub(int,int).

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
public void addTest() {
assertThat(this.calculator.add(1, 2)).isEqualTo(3);
}
@Test
public void subTest() {
assertThat(this.calculator.sub(1, 2)).isEqualTo(-1);
}
}

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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:

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
In the pom.xml of your application add this dependency (that also adds transitive dependencies to
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:

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
business exception or
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
provides additional
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)

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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)
• T: Thread (Name of thread)
• L: Logger name (use class name)

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
your DAO interface extend from [Application]RevisionedDao instead of [Application]Dao.
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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
);

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

In your own projects, you can create additional templates. Please refer to the
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:

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

138

Devonfw Guide v2.4.0

CREATE TABLE STAFFMEMBER(
id BIGINT NOT NULL,
modificationCounter INTEGER NOT NULL,
firstname VARCHAR(255),
lastname VARCHAR(255),
login 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.
/**
*
* The {@link devonfw.tutorial.general.dataaccess.api.ApplicationPersistenceEntity
persistent entity} for
*
* {@link StaffMember}.
*/
@Entity
@Table(name = "StaffMember")
public class StaffMemberEntity extends ApplicationPersistenceEntity implements
StaffMember {

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
public void setName(String login) {
this.name = login;
}
@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) {

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
example, please select:
• CRUD DAO’s
• CRUD REST services
• CRUD logic layer (all in one)

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

143

Devonfw Guide v2.4.0

now

the

new

Permissions

added

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

Click in Advance Health Check

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
quickly end up loading and transferring way too much data.
• 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.

33.1. Business-Transfer-Objects
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).

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

146

Devonfw Guide v2.4.0

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

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).

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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:

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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 =
new String[] { "/login", "/security/**", "/services/rest/login",
"/services/rest/logout", "/jsclient/**"};
(...)
}

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.
• Add new dependency "spring-boot-starter-tomcat".

...

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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".

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

153

Devonfw Guide v2.4.0

Chapter 35. Cookbook
35.1. Devonfw Modules

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.
For more information, visit JasperReports

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:

36.1.1. Step 1: Adding the starter in your project
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

AuthenticationManagerAD

in

@Inject
private AuthenticationManagerAD authenticationManagerAD;

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

164

Devonfw Guide v2.4.0

@ComponentScan(basePackages = { "com.devonfw.module.winauthad" ,
"my.other.components.package" })

Step 3: Define the provider
Also,

in

the

BaseWebSecurityConfig.java

class,

add

the

LDAP

provider

to

the

AuthenticationManagerBuilder in the configureGlobal(AuthenticationManagerBuilder auth) method

auth.authenticationProvider(this.authenticationManagerAD.LdapAuthenticationProvider())
;

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
.authenticationProvider(this.authenticationManagerAD
.LdapAuthenticationProvider());
}

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

import
import
import
import

com.devonfw.module.winauthad.common.api.AuthenticationSource;
com.devonfw.module.winauthad.common.api.UserData;
com.devonfw.module.winauthad.common.impl.security.GroupMapperAD;
com.devonfw.module.winauthad.common.impl.security.PrincipalProfileImpl;

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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;
}
/**
* @return groupMapperAD
*/
public GroupMapperAD getGroupMapperAD() {
return this.groupMapperAD;
}
/**
* @param groupMapperAD new value of groupMapperAD.
*/
public void setGroupMapperAD(GroupMapperAD groupMapperAD) {
this.groupMapperAD = groupMapperAD;
}
@Override
public UserDetails mapUserFromContext(DirContextOperations ctx, String username,
Collection authorities) {
UserData user = new UserData(username, "", authorities);
try {
Attributes attributes = this.authenticationSource.searchUserByUsername(
username);

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

166

Devonfw Guide v2.4.0

String
String
FirstName
String
String

cn = attributes.get("cn").toString().substring(4);// Username
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);
ArrayList groups = this.groupMapperAD.groupsMapping(memberOf);
userProfile.setGroups(groups);
// determine granted authorities for spring-security...
Set authoritiesAD = new HashSet<>();
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);
// authorities.add(new SimpleGrantedAuthority(id));
}
}
for (AccessControl accessControl : accessControlSet) {
authoritiesAD.add(new AccessControlGrantedAuthority(accessControl));
}
user = new UserData(username, "", authoritiesAD);
user.setUserProfile(userProfile);
} catch (Exception e) {
e.printStackTrace();
UsernameNotFoundException exception = new UsernameNotFoundException
("Authentication failed.", e);
LOG.warn("Failed com.devonfw.module.winauthad.common.impl.security get user {}
in Active Directory."
+ username + exception);
throw exception;
}
return user;
}
@Override
public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
}
}

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.password=ENC(...)
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=
#AD
devon.winauth.ad.url=ldap://mydomain.com/OU=Users,DC=MYDOMAIN,DC=COM
devon.winauth.ad.domain=mydomain.com
devon.winauth.ad.username=user
devon.winauth.ad.encrypt=true
devon.winauth.ad.keyPass=keyPass
devon.winauth.ad.password=ENC(...)
devon.winauth.ad.userSearchFilter=(uid={0})
devon.winauth.ad.userSearchBase=
devon.winauth.ad.searchBy=sAMAccountName
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
get access to the module through a private field.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.
[...]
.addFilterAfter(this.sso.getSSOFilter(), BasicAuthenticationFilter.class
).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
without the login process.
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).

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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 {

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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);
String username = principal[1];
UserProfile profile = this.usermanagement.findUserProfileByLogin(username);
UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(profile, getAutoritiesByProfile
(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<>();
accessControlIds.add(profile.getRole().getName());
Set accessControlSet = new HashSet<>();
for (String id : accessControlIds) {
boolean success = this.accessControlProvider.collectAccessControls(id,
accessControlSet);
if (!success) {
// authorities.add(new SimpleGrantedAuthority(id));
}
}
for (AccessControl accessControl : accessControlSet) {
authorities.add(new AccessControlGrantedAuthority(accessControl));
}
return authorities;
}
}

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
http.addFilterAfter(this.sso.getSSOFilter(), BasicAuthenticationFilter.class
).exceptionHandling()
.authenticationEntryPoint(this.sso.getSSOFilterEntryPoint());
}

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

177

Devonfw Guide v2.4.0

is

returned

denoting

no

results.

In

case

of

an

invalid

Locale,

user

will

receive

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

183

Devonfw Guide v2.4.0

a.
b.
c.
d.

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

41.2.1. Adding the async starter to your project
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
application.properties of your Spring project adding the property devonfw.integration.onedirection.emitter or devonfw.integration.one-direction.subscriber.
For emitter applications set the one-direction.emitter property to true:

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.
Request-Reply channel
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

devonfw.integration.request-reply.emitter

or

devonfw.integration.request-

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

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

devonfw.integration.request-reply.subscriber=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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

195

Devonfw Guide v2.4.0

Request-Reply asynchronous channel
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:

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

devonfw.integration.request-reply-async.subscriber=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
application will receive and read it.
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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

196

Devonfw Guide v2.4.0

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

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

198

Devonfw Guide v2.4.0

For subscriber applications you must enable the channel through the corresponding property in the
application.properties file of your project.

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;
public void readSimpleMessage(){
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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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("MESSAGE IS: " + message.getPayload());
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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.
Default configuration for request-reply channel
The default configuration properties for this channel, provided by default with the Integration
module, are:

devonfw.integration.request-reply.emitter=false
devonfw.integration.request-reply.subscriber=false
devonfw.integration.request-reply.channelname=rr.Channel
devonfw.integration.request-reply.queuename=rr.queue
devonfw.integration.request-reply.receivetimeout=5000
• 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.
Sender-Receiver application configuration
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.

devonfw.integration.request-reply.emitter=true
Optionally,

you

can

edit

the

name

for

the

channel

and

for

the

queue

using

the

devonfw.integration.request-reply.channelname and devonfw.integration.request-reply.queuename
properties. As we just mentioned, the timeout for the response can be edited adding the
devonfw.integration.request-reply.receivetimeout property to our properties file and providing a
milliseconds value. By default the timeout is 5000 (5 seconds).
Sender-Receiver application example
After you have added the module dependency you need to inject it. Lets see how to send and
receive a simple message through that default request-reply channel.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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(){
String response = this.integration.sendAndReceive("Hello");
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.
Receiver-Sender application configuration
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.

devonfw.integration.request-reply.subscriber=true
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.
Receiver-Sender application example
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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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(){
this.integration.subscribeAndReply(this.requestHandler);
}
}
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("MESSAGE IS: " + m.getPayload());
System.out.println("***********************************");
return m.getPayload().toString().concat(" World");
}
}
As you can see we are simply printing the original message received, using the getPayload()
method, and then replying adding to it "World".
At this point we can run that second application and see what happens through the Active MQ web
client.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

devonfw.integration.request-reply-async.emitter=false
devonfw.integration.request-reply-async.subscriber=false
devonfw.integration.request-reply-async.channelname=async.Channel
devonfw.integration.request-reply-async.queuename=async.queue
devonfw.integration.request-reply-async.receivetimeout=5000
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.

devonfw.integration.request-reply-async.emitter=true
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
property devonfw.integration.request-reply-async.receivetimeout
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.

devonfw.integration.request-reply-async.subscriber=true
Sender-Receiver async example
After you have added the module dependency you need to inject it. Lets see how to send and
receive asynchronously a message through that default request-reply-async 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-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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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{
Future response = this.integration.sendAndReceiveAsync("Hello");
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.
Receiver-Sender async example
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
module default request-reply-async channel.

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);
}
}

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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("MESSAGE IS: " + m.getPayload());
System.out.println("***********************************");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return m.getPayload().toString().concat(" World");
}
}
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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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;
public void readSimpleMessage(){
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);

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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("MESSAGE IS: " + m.getPayload());
System.out.println("***********************************");
m.getPayload().toString().concat(" World");
}
}
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
and provide a reply.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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(){
this.integration.subscribeAndReplyTo("my-channel", "my-queue", this.
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 =
this.integration.createAsyncRequestReplyChannel("my-async-channel", "my-asyncqueue", this.responseHandler);

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

211

Devonfw Guide v2.4.0

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

the

properties

devonfw.integration.default.poolsize

and

devonfw.integration.default.receivetimeout to your application.properties file and providing a
value.
However, you can also define those values when creating the channel

IntegrationChannel demoAsyncChannel =
this.integration.createAsyncRequestReplyChannel("my-async-channel", "my-asyncqueue", this.responseHandler, 15, 10000);
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.integration.subscribeAndReplyAsyncTo("my-async-channel", "my-async-queue",
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.
Creating the headers
You can create the message headers using a Java Map object

Map headers = new HashMap();
headers.put("header1", "value1");
headers.put("header2", "value2");

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

212

Devonfw Guide v2.4.0

Sending the headers
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)
• default request-reply channel: integration.sendAndReceive("Hello", headers)
• default asynchronous request-reply channel: integration.sendAndReceiveAsync("Hello",
headers)
• new created channels: new_channel.send("Hello", headers)

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
Getting started Download and Setup 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:

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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 it`s crucial to allow the microservices to register in this component.
• url: which URL manages as area.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
adding

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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");
}
}

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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:

44.2.1. Step 1: Adding the starter in your project
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).

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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/

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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:

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
in there (don’t worry about that). Obviously, we’ll need to tell the Job about this SSH thing, adding
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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

357

Devonfw Guide v2.4.0

57.3.2. Upload files to a Nexus repository (direct upload)
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:

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

358

Devonfw Guide v2.4.0

curl -L -u myusername:mypassword --upload-file ext.zip https://url-to-your-nexusrepository
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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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:

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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:

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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",
"username": "***",
"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",
"password": "***",
"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:

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

366

Devonfw Guide v2.4.0

@Configuration
public class BeanJPAConfiguration {
public String username;
public String password;
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()
.username(this.username).
password(this.password).
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) {
this.username = jsonObj.getString("username");
this.password = jsonObj.getString("password");
if (jsonObj.has("jdbcurl")) {
this.url = jsonObj.getString("jdbcurl");
} else if (jsonObj.has("ssljdbcurl")) {
this.url = jsonObj.getString("ssljdbcurl");
}
}
}
}
}

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

/*.formLogin().successHandler(new
SimpleUrlAuthenticationSuccessHandler()).defaultSuccessUrl("/")
.failureUrl("/login.html?error").loginProcessingUrl("/j_spring_security_login").userna
meParameter("username")
.passwordParameter("password").and() // logout via POST is possible
.logout().logoutSuccessUrl("/login.html").and()*/
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
LoginController.java of core module.

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

access

to

application

context

root

by

adding

"/"

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

370

Devonfw Guide v2.4.0

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

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

371

Devonfw Guide v2.4.0

Chapter 61. Deployment on Wildfly
Following describes the steps to install and configure Wilfly 10.x
1. Download WildFly 10.x from http://wildfly.org/downloads/
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
asks for login credentials, provide "admin" as username and password.
Administration console is up and running at http://localhost:9990/console/App.html#home

To run .war file on Wildfly administration console, follow below steps:
• Click on start (highlighted in below screenshot)

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

372

Devonfw Guide v2.4.0

• Click on Add button (highlighted in below screenshot)

• Upload new deployement.

• Choose .war file for the deployment.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

373

Devonfw Guide v2.4.0

• Verify upload and finish

• 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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

374

Devonfw Guide v2.4.0

◦ Remove the @EnableGlobalMethodSecurity annotation
• Remove the SpringBootBatchApp file.
• In pom.xml
Add below dependecies:


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:

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.
• To download Web sphere Liberty Profile, Go to link and click on the link 'Download Liberty
(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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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



This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

add

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,

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.1. Download JAVA7
One can download java7 from here. Once java7 is downloaded, run .exe and follow instructions.

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
You can download tomcat externally and deploy war in it. For more information, please visit this
link.

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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
commands. Dockerfile must start with a FROM command.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

390

Devonfw Guide v2.4.0

# Usage: FROM [image name]
FROM ubuntu

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

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

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]
MAINTAINER authors_name(admin@email.com)

66.2.6. EXPOSE
Expose a port to outside.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

src/license/THIRD-PARTY.properties

true
src/license/override-THIRD-PARTY.properties


Apache-2.0|Apache 2.0
Apache-2.0|Apache License, Version 2.0
Apache-2.0|Apache Software License, Version 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.

68.4.1. Declare additional licenses
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.
Declare additional licenses in a "missing file" within each maven-subproject: /src/license/THIRDPARTY.properties.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

401

Devonfw Guide v2.4.0

# Generated by org.codehaus.mojo.license.AddThirdPartyMojo
#------------------------------------------------------------------------------# Already used licenses in project :
# - ASF 2.0
# - Apache 2
...
#------------------------------------------------------------------------------# Please fill the missing licenses for dependencies :
...
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.

68.4.2. Redefine wrongly detected licenses
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
maven-subproject: /src/license/override-THIRD-PARTY.properties

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 .

68.4.3. Merge licenses
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:

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

402

Devonfw Guide v2.4.0



Apache-2.0|Apache 2.0
Apache-2.0|Apache License, Version 2.0
Apache-2.0|Apache Software License, Version 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.

68.5. Retrieve licenses list
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
Running the aggregate-download-licenses goal creates two results.
1. a license.xml that contains all used OSS depenencies (even sub-dependencies) with respective
license information
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
the Mojo Maven License Plugin.
Within the parent project, run the following maven goal from command line.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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 {
...

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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,
host names, passwords, logins, timeouts, certificates, etc.) specifically for the different
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.

76.2.2. Business Configuration
The business configuration contains all business configuration values of the application, which can

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

77.2. Add your own Validations
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

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.Payload;
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 {
String message() default "Please provide a valid email address";
Class[] groups() default {};
Class[] payload() 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]}"

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.

This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).

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.Payload;
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}
spring.boot.admin.url=http://localhost:1111
management.security.enabled=false
spring.boot.admin.username=admin
spring.boot.admin.password=admin123
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}
spring.boot.admin.url=http://localhost:1111
management.security.enabled=false
spring.boot.admin.username=admin
spring.boot.admin.password=admin123
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);
break;
case 'email':
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
{{value}}< /b> {{value}}
// template to show when the async pipe is loading data
Also, to make easier to the user read what is his position, Covalent Teradata provides with a functionality to check columns and modify the value shown, we used that to make bold the name of the user which corresponds to the user who is registered at the moment. That is all regarding how to build your own OASP4Js application example, now is up to you add features, change styles and do everything you could imagine. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 567 Devonfw Guide v2.4.0 Chapter 98. An OASP4JS Application Structure 98.1. Overview What we have shown in the previous section is the aspect of a My Thai Star client app that consumes the services created with the Oasp4j server solution. From now on we are going to focus on the implementation of the components, services and directives to show how is it formed and how you can create your own Oasp4js client project with Devon framework. My Thai Star project is hosted on github and includes different technologies such as Java, .Net and Node for backend solutions and Angular and Xamarin as default clients. 98.1.1. The OASP4JS Project Using the Oasp4js approach for the client project we will have a structure of a main Angular project formed as follows: In the e2e folder will be all end-to-end tests. In the node modules folder, all installed dependencies will be stored. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 568 Devonfw Guide v2.4.0 The src folder contains all the application code. Finally, the rest of the files are config files for the different technologies involved in the project. 98.1.2. Angular folder structure Following Angular style guide rules, the structure of the application has been built this way: • app ◦ components ▪ sub-components ▪ shared ▪ services ▪ component files ◦ main app component • assets folder • environments folder • rest of angular files As can be seen in this image: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 569 Devonfw Guide v2.4.0 98.1.3. Components As we already saw in the previous chapter, Angular architecture is based on three types of elements: Components, Services, Modules and Directives. In this section we are going to focus on the components. We can distinguish them because they all are named with the extension .component.ts. Components are a single element of the application, but it can have, at the same time, more components in them, this is the case for the components that are main views: app(main component of the app), home, menu, book-table, cockpit-area or the components for the dialogs. These views have their own layout from Covalent Teradata to organize its contents as well as other components or tags to be displayed. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 570 Devonfw Guide v2.4.0 import {...} from '...' @Component({ selector: 'public-menu', templateUrl: './menu.component.html', styleUrls: ['./menu.component.scss'], }) export class MenuComponent implements OnInit { methods implementation... } Even though, there are also components that are an element of a template that has a complete meaning by themselves and can be reused multiple times and/or in multiple places, this is the case of components like, sidenav, header or menu-card, which is an element that accepts an input data with the menu information and displays it as a card, this component will be repeated for every single dish on the menu, so the better way to handle this is to isolate its logic and template in a component so the menu view just have to know about the existence of the component and the data it needs to work. To interact and navigate between the main views, Angular provides a Router that provides with functionalities to move between URL’s in the same app, in addition, it provides an HTML tag that will show the component that has been navigated to. This router tag is placed in the main app component, at the same level as the sidenav and the header, this means that these two components are on top of whatever the router shows, that is why we can always see the header no matter what component we are displaying through the router. Also, Angular Material provides a tab component, which can show content depending on which tab you clicked, but they are in the same component, an example of usage of this kind of components can be seen in the book-table view: This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 571 Devonfw Guide v2.4.0 This component view shows a card that can show an instant reservation or the creation of an event. 98.1.4. Services Ideally, all the logic should be taken out of the component, and let there only the calls to the services and minimal script interaction. Services is where all the logic should be, including calling the server. MyThaiStar components consume this services, as could be the price-calculator when a costumer makes an order: There are two exceptional cases in MyThaiStar of services that serve with a different proposal than serve to a specific component: Authentication and AuthGuard and HttpClient. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 572 Devonfw Guide v2.4.0 To secure the access to waiter cockpit, which is a forbidden area to anyone who is not a waiter, MyThaiStar counts with a service of authentication and a Router Guard. Guards are services that implements CanActivate function which returns a Boolean indicating if the navigation is valid or forbidden. If is forbidden, the router stands still where it is, and if it is valid, it navigates correctly. The authentication service serves as a storage and a validator of certain data regarding username, role, permissions and JWT token. HttpClient is an envelope of Http that implement the management of headers. The workflow is exact the same as the standard Http but as the project needed to incorporate a token to every call to a specific secured services, then, this token needed to be added and removed depending on call to the server, also, it has been extended to handle the error in case the token has expired or corrupted. When all of this correctly setup, we can do a log-in to the waiter cockpit, and if entered the correct credentials, the logged state will set to true, the login to the server will be correct returning the token and the header with this token will be setted giving as a result the correct navigation to the waiter cockpit: 98.1.5. Modules Through modules you can encapsulate whole functionalities or part of the application. All Angular This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 573 Devonfw Guide v2.4.0 apps have, at least, one module: app.module. But Angular encourages the use of more modules to organize all the components and services. In MyThaiStar every component and service is inside a module, making the app.module composed only by other smaller modules. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 574 Devonfw Guide v2.4.0 Chapter 99. OASP4JS Architecture Overview 99.1. Basic As Angular is the main framework for Oasp4JS, the architecture of the applications that is going to be used is the same as Angular: 99.2. Additional Functionalities This architecture will be enhanced with some functionalities from Covalent, Teradata and Angular Material: • Theming: functionality that Angular Material includes in its library and Covalent Teradata extends, it declares one primary color, one secondary color and one color for warning and alerts to be used in all the application. Also Covalent Teradata expects a color for the background and another for the foreground. This colors will be stored in one theme, where you can store as much as you want and be changed at the run-time by the user. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 575 Devonfw Guide v2.4.0 • Flex-box: Along with other CSS Utility Styles & Classes, Covalent Teradata comes with flex-box, useful for styling and organizing components inside of a view, which also has been extended by Covalent Teradata to achieve responsiveness. You can declare styles that change, hide or transform the component depending on the screen resolution of the device. This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International). 576 Devonfw Guide v2.4.0 Chapter 100. Angular Components In this chapter we are going to see how components work and how we can work with them. 100.1. What are Angular Components From Angular’s main page: "A component controls a patch of screen called a view." "You define a component’s application logic—what it does to support the view—inside a class. The class interacts with the view through an API of properties and methods. Components are the most basic building block of an UI in an Angular application. An Angular application is a tree of Angular components. They are internally composed by an HTML template and a class with all the methods needed to handle that template. HTML is the language of the Angular template. Almost all HTML syntax is valid template syntax. The