├── .gitattributes
├── .gitignore
├── LICENSE
├── pom.xml
├── src
├── main
│ ├── java
│ │ └── victor
│ │ │ └── training
│ │ │ └── oo
│ │ │ ├── structural
│ │ │ ├── adapter
│ │ │ │ ├── AdapterSpringApp.java
│ │ │ │ ├── domain
│ │ │ │ │ ├── User.java
│ │ │ │ │ └── UserService.java
│ │ │ │ └── infra
│ │ │ │ │ ├── DummyData.java
│ │ │ │ │ ├── LdapUser.java
│ │ │ │ │ ├── LdapUserPhone.java
│ │ │ │ │ └── LdapUserWebserviceClient.java
│ │ │ ├── facade
│ │ │ │ ├── Facade.java
│ │ │ │ ├── FacadeSpringApp.java
│ │ │ │ ├── controller
│ │ │ │ │ └── CustomerController.java
│ │ │ │ ├── entity
│ │ │ │ │ ├── Customer.java
│ │ │ │ │ ├── Email.java
│ │ │ │ │ └── Site.java
│ │ │ │ ├── facade
│ │ │ │ │ ├── CustomerFacade.java
│ │ │ │ │ ├── StockFacade.java
│ │ │ │ │ └── dto
│ │ │ │ │ │ └── CustomerDto.java
│ │ │ │ ├── infra
│ │ │ │ │ └── EmailClient.java
│ │ │ │ └── repo
│ │ │ │ │ ├── CustomerRepository.java
│ │ │ │ │ ├── EmailRepository.java
│ │ │ │ │ └── SiteRepository.java
│ │ │ └── proxy
│ │ │ │ ├── ExpensiveOps.java
│ │ │ │ └── ProxySpringApp.java
│ │ │ └── stuff
│ │ │ ├── ConcurrencyUtil.java
│ │ │ ├── MyFrame.java
│ │ │ ├── ThreadUtils.java
│ │ │ └── surprise-cache.txt
│ └── resources
│ │ └── application.properties
└── test
│ └── java
│ └── victor
│ └── training
│ └── patterns
│ └── adapter
│ └── AdapterArchUnitTest.java
└── target
└── classes
└── application.properties
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled class file
2 | *.class
3 |
4 | # Log file
5 | *.log
6 |
7 | # BlueJ files
8 | *.ctxt
9 |
10 | # Mobile Tools for Java (J2ME)
11 | .mtj.tmp/
12 |
13 | # Package Files #
14 | *.jar
15 | *.war
16 | *.nar
17 | *.ear
18 | *.zip
19 | *.tar.gz
20 | *.rar
21 |
22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
23 | hs_err_pid*
24 | .idea
25 | *.iml
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | victor.training
6 | structural-patterns-spring
7 | 0.0.1-SNAPSHOT
8 |
9 |
10 | org.springframework.boot
11 | spring-boot-starter-parent
12 | 2.1.6.RELEASE
13 |
14 |
15 |
16 | 1.8
17 |
18 |
19 |
20 |
21 | org.springframework.boot
22 | spring-boot-starter-aop
23 |
24 |
25 | commons-lang
26 | commons-lang
27 | 2.6
28 |
29 |
30 | com.tngtech.archunit
31 | archunit-junit4
32 | 0.15.0
33 | test
34 |
35 |
36 | commons-io
37 | commons-io
38 | 1.4
39 |
40 |
41 |
42 | org.projectlombok
43 | lombok
44 |
45 |
46 | org.jooq
47 | jool
48 | 0.9.12
49 |
50 |
51 | junit
52 | junit
53 |
54 |
55 |
56 |
57 |
58 | org.springframework.boot
59 | spring-boot-maven-plugin
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/src/main/java/victor/training/oo/structural/adapter/AdapterSpringApp.java:
--------------------------------------------------------------------------------
1 | package victor.training.oo.structural.adapter;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.boot.CommandLineRunner;
5 | import org.springframework.boot.SpringApplication;
6 | import org.springframework.boot.autoconfigure.SpringBootApplication;
7 |
8 | import lombok.extern.slf4j.Slf4j;
9 | import victor.training.oo.structural.adapter.domain.UserService;
10 |
11 | @Slf4j
12 | @SpringBootApplication
13 | public class AdapterSpringApp implements CommandLineRunner {
14 | public static void main(String[] args) {
15 | SpringApplication.run(AdapterSpringApp.class, args);
16 | }
17 |
18 | @Autowired
19 | private UserService userService;
20 |
21 | public void run(String... args) throws Exception {
22 | userService.importUserFromLdap("jdoe");
23 | log.debug("Found users: " + userService.searchUserInLdap("doe"));
24 | }
25 | }
--------------------------------------------------------------------------------
/src/main/java/victor/training/oo/structural/adapter/domain/User.java:
--------------------------------------------------------------------------------
1 | package victor.training.oo.structural.adapter.domain;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Data;
5 |
6 | // This would normally be placed in a 'domain model' package
7 | @Data // i'm sorry
8 | @AllArgsConstructor
9 | public class User {
10 | private String username;
11 | private String fullName;
12 | private String workEmail;
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/src/main/java/victor/training/oo/structural/adapter/domain/UserService.java:
--------------------------------------------------------------------------------
1 | package victor.training.oo.structural.adapter.domain;
2 |
3 | import lombok.extern.slf4j.Slf4j;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.stereotype.Service;
6 | import victor.training.oo.structural.adapter.infra.LdapUser;
7 | import victor.training.oo.structural.adapter.infra.LdapUserWebserviceClient;
8 |
9 | import java.util.ArrayList;
10 | import java.util.List;
11 |
12 | @Slf4j
13 | @Service
14 | public class UserService {
15 | @Autowired
16 | private LdapUserWebserviceClient wsClient;
17 |
18 | public void importUserFromLdap(String username) {
19 | List list = wsClient.search(username.toUpperCase(), null, null);
20 | if (list.size() != 1) {
21 | throw new IllegalArgumentException("There is no single user matching username " + username);
22 | }
23 | LdapUser ldapUser = list.get(0);
24 | String fullName = ldapUser.getfName() + " " + ldapUser.getlName().toUpperCase();
25 | User user = new User(ldapUser.getuId(), fullName, ldapUser.getWorkEmail());
26 |
27 | if (user.getWorkEmail() != null) {
28 | log.debug("Send welcome email to " + user.getWorkEmail());
29 | }
30 | log.debug("Insert user in my database");
31 | }
32 |
33 | public List searchUserInLdap(String username) {
34 | List list = wsClient.search(username.toUpperCase(), null, null);
35 | List results = new ArrayList<>();
36 | for (LdapUser ldapUser : list) {
37 | String fullName = ldapUser.getfName() + " " + ldapUser.getlName().toUpperCase();
38 | User user = new User(ldapUser.getuId(), fullName, ldapUser.getWorkEmail());
39 | results.add(user);
40 | }
41 | return results;
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/victor/training/oo/structural/adapter/infra/DummyData.java:
--------------------------------------------------------------------------------
1 | package victor.training.oo.structural.adapter.infra;
2 |
3 | import java.util.Arrays;
4 | import java.util.Date;
5 |
6 | public class DummyData {
7 | public static final LdapUser ldapUser1 = new LdapUser();
8 | static {
9 | ldapUser1.setfName("John");
10 | ldapUser1.setlName("DOE");
11 | ldapUser1.setuId("jdoe");
12 | ldapUser1.setCreationDate(new Date());
13 | ldapUser1.setWorkEmail("0123456789");
14 | ldapUser1.setEmailAddresses(Arrays.asList(new LdapUserPhone("WORK", "jdoe@bigcorp.com")));
15 | }
16 | public static final LdapUser ldapUser2 = new LdapUser();
17 | static {
18 | ldapUser2.setfName("Jane");
19 | ldapUser2.setlName("DOE");
20 | ldapUser2.setuId("janedoe");
21 | ldapUser2.setCreationDate(new Date());
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/victor/training/oo/structural/adapter/infra/LdapUser.java:
--------------------------------------------------------------------------------
1 | package victor.training.oo.structural.adapter.infra;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Date;
5 | import java.util.List;
6 |
7 | public class LdapUser {
8 | private String uId;
9 | private String fName;
10 | private String lName;
11 | private Date creationDate;
12 | private String workEmail;
13 | private List emailAddresses = new ArrayList<>();
14 |
15 | public final String getuId() {
16 | return uId;
17 | }
18 | public final void setuId(String uId) {
19 | this.uId = uId;
20 | }
21 | public final String getfName() {
22 | return fName;
23 | }
24 | public final void setfName(String fName) {
25 | this.fName = fName;
26 | }
27 | public final String getlName() {
28 | return lName;
29 | }
30 | public final void setlName(String lName) {
31 | this.lName = lName;
32 | }
33 | public final Date getCreationDate() {
34 | return creationDate;
35 | }
36 | public final void setCreationDate(Date creationDate) {
37 | this.creationDate = creationDate;
38 | }
39 |
40 | public final String getWorkEmail() {
41 | return workEmail;
42 | }
43 | public final void setWorkEmail(String workEmail) {
44 | this.workEmail = workEmail;
45 | }
46 | public final List getEmailAddresses() {
47 | return emailAddresses;
48 | }
49 | public final void setEmailAddresses(List emailAddresses) {
50 | this.emailAddresses = emailAddresses;
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/victor/training/oo/structural/adapter/infra/LdapUserPhone.java:
--------------------------------------------------------------------------------
1 | package victor.training.oo.structural.adapter.infra;
2 |
3 | public class LdapUserPhone {
4 | private String type;
5 | private String val;
6 |
7 | public LdapUserPhone() {
8 | }
9 | public LdapUserPhone(String type, String val) {
10 | this.type = type;
11 | this.val = val;
12 | }
13 | public final String getType() {
14 | return type;
15 | }
16 | public final void setType(String type) {
17 | this.type = type;
18 | }
19 | public final String getVal() {
20 | return val;
21 | }
22 | public final void setVal(String val) {
23 | this.val = val;
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/victor/training/oo/structural/adapter/infra/LdapUserWebserviceClient.java:
--------------------------------------------------------------------------------
1 | package victor.training.oo.structural.adapter.infra;
2 |
3 | import java.util.Arrays;
4 | import java.util.List;
5 |
6 | import org.springframework.stereotype.Service;
7 |
8 | @Service
9 | public class LdapUserWebserviceClient {
10 |
11 | public List search(String uId, String fName, String lName) {
12 | // Imagine a search URL is formed here and a GET is then performed
13 | // Then, the response JSON list is converted to LdapUser objects
14 | return Arrays.asList(DummyData.ldapUser1/*, DummyData.ldapUser2*/);
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/victor/training/oo/structural/facade/Facade.java:
--------------------------------------------------------------------------------
1 | package victor.training.oo.structural.facade;
2 |
3 | import org.springframework.stereotype.Service;
4 |
5 | import java.lang.annotation.ElementType;
6 | import java.lang.annotation.Retention;
7 | import java.lang.annotation.RetentionPolicy;
8 | import java.lang.annotation.Target;
9 |
10 | @Retention(RetentionPolicy.RUNTIME)
11 | @Target(ElementType.TYPE)
12 | @Service
13 | public @interface Facade {
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/victor/training/oo/structural/facade/FacadeSpringApp.java:
--------------------------------------------------------------------------------
1 | package victor.training.oo.structural.facade;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.boot.CommandLineRunner;
5 | import org.springframework.boot.SpringApplication;
6 | import org.springframework.boot.autoconfigure.SpringBootApplication;
7 | import victor.training.oo.structural.facade.facade.CustomerFacade;
8 |
9 | @SpringBootApplication
10 | public class FacadeSpringApp implements CommandLineRunner{
11 | public static void main(String[] args) {
12 | SpringApplication.run(FacadeSpringApp.class);
13 | }
14 | @Autowired
15 | private CustomerFacade customerFacade;
16 |
17 | public void run(String... args) throws Exception {
18 | // pretend you are in a @RestController here
19 | customerFacade.registerCustomer(new victor.training.patterns.structural.facade.facade.dto.CustomerDto("John Doe", "john.doe@pentagon.us"));
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/victor/training/oo/structural/facade/controller/CustomerController.java:
--------------------------------------------------------------------------------
1 | package victor.training.oo.structural.facade.controller;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import org.springframework.stereotype.Controller;
5 | import victor.training.oo.structural.facade.facade.CustomerFacade;
6 |
7 | @Controller //faking it
8 | @RequiredArgsConstructor
9 | public class CustomerController {
10 | private final CustomerFacade customerFacade;
11 |
12 | // @GetMapping
13 | public victor.training.patterns.structural.facade.facade.dto.CustomerDto getById(long customerId) {
14 | return customerFacade.findById(customerId);
15 | }
16 |
17 | // @PostMapping
18 | public void register(victor.training.patterns.structural.facade.facade.dto.CustomerDto customerDto) {
19 | customerFacade.registerCustomer(customerDto);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/victor/training/oo/structural/facade/entity/Customer.java:
--------------------------------------------------------------------------------
1 | package victor.training.oo.structural.facade.entity;
2 |
3 | import lombok.Data;
4 |
5 | import java.util.Date;
6 |
7 | @Data
8 | public class Customer {
9 | private Long id;
10 | private String name;
11 | private String email;
12 | private Site site;
13 | private Date creationDate;
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/victor/training/oo/structural/facade/entity/Email.java:
--------------------------------------------------------------------------------
1 | package victor.training.oo.structural.facade.entity;
2 |
3 | import lombok.Data;
4 |
5 | @Data
6 | public class Email {
7 | private String to;
8 | private String from;
9 | private String subject;
10 | private String body;
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/victor/training/oo/structural/facade/entity/Site.java:
--------------------------------------------------------------------------------
1 | package victor.training.oo.structural.facade.entity;
2 |
3 | import lombok.Data;
4 |
5 | @Data // i'm sorry
6 | public class Site {
7 | private Long id;
8 | private String name;
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/victor/training/oo/structural/facade/facade/CustomerFacade.java:
--------------------------------------------------------------------------------
1 | package victor.training.oo.structural.facade.facade;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import victor.training.oo.structural.facade.Facade;
5 | import victor.training.oo.structural.facade.entity.Customer;
6 | import victor.training.oo.structural.facade.entity.Email;
7 | import victor.training.oo.structural.facade.infra.EmailClient;
8 | import victor.training.oo.structural.facade.repo.CustomerRepository;
9 | import victor.training.oo.structural.facade.repo.EmailRepository;
10 | import victor.training.oo.structural.facade.repo.SiteRepository;
11 | import victor.training.patterns.structural.facade.facade.dto.CustomerDto;
12 |
13 | import java.text.SimpleDateFormat;
14 |
15 | @Facade
16 | @RequiredArgsConstructor
17 | public class CustomerFacade {
18 | private final CustomerRepository customerRepo;
19 |
20 | private final EmailClient emailClient;
21 | private final EmailRepository emailRepo;
22 | private final SiteRepository siteRepo;
23 |
24 | public CustomerDto findById(long customerId) {
25 | Customer customer = customerRepo.findById(customerId);
26 | CustomerDto dto = new CustomerDto();
27 | dto.name = customer.getName();
28 | dto.email = customer.getEmail();
29 | dto.creationDateStr = new SimpleDateFormat("yyyy-MM-dd").format(customer.getCreationDate());
30 | dto.id = customer.getId();
31 | return dto;
32 | }
33 |
34 | public void registerCustomer(CustomerDto dto) {
35 | Customer customer = new Customer();
36 | customer.setEmail(dto.email);
37 | customer.setName(dto.name);
38 | customer.setSite(siteRepo.getReference(dto.countryId));
39 |
40 | if (customer.getName().trim().length() <= 5) {
41 | throw new IllegalArgumentException("Name too short");
42 | }
43 |
44 | if (customerRepo.customerExistsWithEmail(customer.getEmail())) {
45 | throw new IllegalArgumentException("Email already registered");
46 | }
47 | // Heavy logic
48 | // Heavy logic
49 | customerRepo.save(customer);
50 | // Heavy logic
51 |
52 | sendRegistrationEmail(customer.getEmail());
53 | }
54 |
55 | private void sendRegistrationEmail(String emailAddress) {
56 | System.out.println("Sending activation link via email to "+ emailAddress);
57 | Email email = new Email();
58 | email.setFrom("noreply");
59 | email.setTo(emailAddress);
60 | email.setSubject("Welcome!");
61 | email.setBody("You'll like it! Sincerely, Team");
62 |
63 | if (!emailRepo.emailWasSentBefore(email.hashCode())) {
64 | emailClient.sendEmail(email.getFrom(), email.getTo(), email.getSubject(), email.getBody());
65 | emailRepo.saveSentEmail(email);
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/java/victor/training/oo/structural/facade/facade/StockFacade.java:
--------------------------------------------------------------------------------
1 | package victor.training.oo.structural.facade.facade;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import victor.training.oo.structural.facade.Facade;
5 |
6 | @Facade
7 | @RequiredArgsConstructor
8 | public class StockFacade {
9 |
10 | public void checkStock() {
11 | // 3 lines of domain Logic
12 |
13 | // TODO same as in CustomerFacade but with other subject/body
14 |
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/victor/training/oo/structural/facade/facade/dto/CustomerDto.java:
--------------------------------------------------------------------------------
1 | package victor.training.patterns.structural.facade.facade.dto;
2 |
3 | public class CustomerDto {
4 | public Long id;
5 | public String name;
6 | public String email;
7 | public Long countryId;
8 | public String creationDateStr;
9 |
10 | public CustomerDto() {}
11 |
12 | public CustomerDto(String name, String email) {
13 | this.name = name;
14 | this.email = email;
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/victor/training/oo/structural/facade/infra/EmailClient.java:
--------------------------------------------------------------------------------
1 | package victor.training.oo.structural.facade.infra;
2 |
3 | import org.springframework.stereotype.Service;
4 |
5 | // We pretend this is an external API we have to use
6 | @Service
7 | public class EmailClient {
8 |
9 | public void sendEmail(String from, String to, String subject, String message) {
10 | // implementation goes here
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/victor/training/oo/structural/facade/repo/CustomerRepository.java:
--------------------------------------------------------------------------------
1 | package victor.training.oo.structural.facade.repo;
2 |
3 | import org.springframework.stereotype.Repository;
4 | import victor.training.oo.structural.facade.entity.Customer;
5 |
6 | @Repository
7 | public class CustomerRepository {
8 | public Customer getCustomerByEmail(String email) {
9 | return null; // TODO
10 | }
11 |
12 | public boolean customerExistsWithEmail(String email) {
13 | return false; // TODO
14 | }
15 |
16 | public void save(Customer customer) {
17 | // TODO
18 | }
19 |
20 | public Customer findById(long customerId) {
21 | return null; // TODO
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/victor/training/oo/structural/facade/repo/EmailRepository.java:
--------------------------------------------------------------------------------
1 | package victor.training.oo.structural.facade.repo;
2 |
3 | import org.springframework.stereotype.Repository;
4 | import victor.training.oo.structural.facade.entity.Email;
5 |
6 | @Repository
7 | public class EmailRepository {
8 |
9 | public boolean emailWasSentBefore(int emailHash) {
10 | return false; // TODO fake implementation
11 | }
12 |
13 | public void saveSentEmail(Email email) {
14 | // TODO
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/victor/training/oo/structural/facade/repo/SiteRepository.java:
--------------------------------------------------------------------------------
1 | package victor.training.oo.structural.facade.repo;
2 |
3 | import org.springframework.stereotype.Repository;
4 | import victor.training.oo.structural.facade.entity.Site;
5 |
6 | @Repository
7 | public class SiteRepository {
8 | public Site getReference(Long siteId) {
9 | return null; // TODO
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/victor/training/oo/structural/proxy/ExpensiveOps.java:
--------------------------------------------------------------------------------
1 | package victor.training.oo.structural.proxy;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.math.BigDecimal;
6 | import java.nio.file.Files;
7 | import java.nio.file.Path;
8 | import java.security.MessageDigest;
9 | import java.security.NoSuchAlgorithmException;
10 |
11 | import javax.xml.bind.DatatypeConverter;
12 |
13 | import org.apache.commons.io.FileUtils;
14 | import org.jooq.lambda.Unchecked;
15 | import org.springframework.cache.annotation.Cacheable;
16 | import org.springframework.stereotype.Service;
17 |
18 | import lombok.SneakyThrows;
19 | import lombok.extern.slf4j.Slf4j;
20 |
21 | @Slf4j
22 | public class ExpensiveOps {
23 |
24 | private static final BigDecimal TWO = new BigDecimal("2");
25 |
26 | public Boolean isPrime(int n) {
27 | log.debug("Computing isPrime({})", n);
28 | BigDecimal number = new BigDecimal(n);
29 | if (number.compareTo(TWO) <= 0) {
30 | return true;
31 | }
32 | if (number.remainder(TWO).equals(BigDecimal.ZERO)) {
33 | return false;
34 | }
35 | for (BigDecimal divisor = new BigDecimal("3");
36 | divisor.compareTo(number.divide(TWO)) < 0;
37 | divisor = divisor.add(TWO)) {
38 | if (number.remainder(divisor).equals(BigDecimal.ZERO)) {
39 | return false;
40 | }
41 | }
42 | return true;
43 | }
44 |
45 | @SneakyThrows
46 | public String hashAllFiles(File folder) {
47 | log.debug("Computing hashAllFiles({})", folder);
48 | MessageDigest md = MessageDigest.getInstance("MD5");
49 | for (int i = 0; i < 3; i++) { // pretend there is much more work to do here
50 | Files.walk(folder.toPath())
51 | .map(Path::toFile)
52 | .filter(File::isFile)
53 | .map(Unchecked.function(FileUtils::readFileToString))
54 | .forEach(s -> md.update(s.getBytes()));
55 | }
56 | byte[] digest = md.digest();
57 | return DatatypeConverter.printHexBinary(digest).toUpperCase();
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/victor/training/oo/structural/proxy/ProxySpringApp.java:
--------------------------------------------------------------------------------
1 | package victor.training.oo.structural.proxy;
2 |
3 | import static java.util.Arrays.asList;
4 |
5 | import java.io.File;
6 | import java.util.ArrayList;
7 | import java.util.List;
8 |
9 | import org.springframework.boot.CommandLineRunner;
10 | import org.springframework.boot.SpringApplication;
11 | import org.springframework.boot.autoconfigure.SpringBootApplication;
12 | import org.springframework.cache.annotation.EnableCaching;
13 | import org.springframework.context.annotation.EnableAspectJAutoProxy;
14 |
15 | import lombok.extern.slf4j.Slf4j;
16 |
17 | @Slf4j
18 | @EnableCaching
19 | @SpringBootApplication
20 | public class ProxySpringApp implements CommandLineRunner {
21 | public static void main(String[] args) {
22 | SpringApplication.run(ProxySpringApp.class, args);
23 | }
24 |
25 |
26 | // TODO [1] implement decorator
27 | // TODO [2] apply decorator via Spring
28 | // TODO [3] generic java.lang.reflect.Proxy
29 | // TODO [4] Spring aspect
30 | // TODO [5] Spring cache support
31 | // TODO [6] Back to singleton (are you still alive?)
32 | public void run(String... args) throws Exception {
33 | ExpensiveOps ops = new ExpensiveOps();
34 |
35 | log.debug("\n");
36 | log.debug("---- CPU Intensive ~ memoization?");
37 | log.debug("10000169 is prime ? ");
38 | log.debug("Got: " + ops.isPrime(10000169) + "\n");
39 | log.debug("10000169 is prime ? ");
40 | log.debug("Got: " + ops.isPrime(10000169) + "\n");
41 |
42 | log.debug("---- I/O Intensive ~ \"There are only two things hard in programming...\"");
43 | log.debug("Folder MD5: ");
44 | log.debug("Got: " + ops.hashAllFiles(new File(".")) + "\n");
45 | log.debug("Folder MD5: ");
46 | log.debug("Got: " + ops.hashAllFiles(new File(".")) + "\n");
47 | }
48 |
49 | }
--------------------------------------------------------------------------------
/src/main/java/victor/training/oo/stuff/ConcurrencyUtil.java:
--------------------------------------------------------------------------------
1 | package victor.training.oo.stuff;
2 |
3 | import java.text.SimpleDateFormat;
4 | import java.util.ArrayList;
5 | import java.util.Date;
6 | import java.util.List;
7 | import java.util.Random;
8 |
9 | public class ConcurrencyUtil {
10 | static Random random = new Random();
11 |
12 | public static void sleepSomeTime() {
13 | sleepSomeTime(10,100);
14 | }
15 |
16 | public static void sleepSomeTime(int min, int max) {
17 | sleep2(randomInt(min, max));
18 | }
19 |
20 | public static int randomInt(int min, int max) {
21 | return min + random.nextInt(max-min);
22 | }
23 |
24 | public static void sleep2(long millis) {
25 | try {
26 | Thread.sleep(millis);
27 | } catch (InterruptedException e) {
28 | e.printStackTrace();
29 | }
30 | }
31 |
32 | static List position = new ArrayList<>();
33 | public static void log(String message) {
34 | int PAD_SIZE = 20;
35 | String line = new SimpleDateFormat("hh:mm:ss.SSS").format(new Date()) + " ";
36 | String pad;
37 | String threadName = Thread.currentThread().getName();
38 | if (position.contains(threadName)) {
39 | pad = String.format("%" + (1+position.indexOf(threadName) * PAD_SIZE) + "s", "");
40 | } else {
41 | synchronized (ConcurrencyUtil.class) {
42 | pad = String.format("%" + (1+ position.size() * PAD_SIZE ) + "s", "");
43 | System.out.println(line + pad + threadName);
44 | System.out.println(line + pad + "=============");
45 | position.add(threadName);
46 | }
47 | }
48 | System.out.println(line + pad + message);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/victor/training/oo/stuff/MyFrame.java:
--------------------------------------------------------------------------------
1 | package victor.training.oo.stuff;
2 |
3 | import java.awt.GridLayout;
4 |
5 | import javax.swing.JButton;
6 | import javax.swing.JFrame;
7 | import javax.swing.JPanel;
8 | import javax.swing.JTextArea;
9 | import javax.swing.JTextField;
10 |
11 | public class MyFrame extends JFrame {
12 |
13 | public JButton button1 = new JButton("Press Me");
14 | public JPanel panel1 = new JPanel();
15 | public JPanel panel2 = new JPanel();
16 | public JTextArea textArea = new JTextArea(3, 8);
17 | public JTextField textField = new JTextField(8);
18 |
19 | public MyFrame() {
20 |
21 | getContentPane().setLayout(new GridLayout(1, 2, 20, 20));
22 | getContentPane().add(button1);
23 | getContentPane().add(panel1);
24 | panel1.add(panel2);
25 | panel2.add(textArea);
26 | panel1.add(textField);
27 |
28 | setTitle("OO example: Observer, Composite, TemplateMethod");
29 | setSize(300, 200);
30 | setLocationRelativeTo(null);
31 | setDefaultCloseOperation(EXIT_ON_CLOSE);
32 |
33 | setVisible(true);
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/victor/training/oo/stuff/ThreadUtils.java:
--------------------------------------------------------------------------------
1 | package victor.training.oo.stuff;
2 |
3 | public class ThreadUtils {
4 |
5 | public static void sleep(long millis) {
6 | try {
7 | Thread.sleep(millis);
8 | } catch (InterruptedException e) {
9 | throw new RuntimeException(e);
10 | }
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/victor/training/oo/stuff/surprise-cache.txt:
--------------------------------------------------------------------------------
1 | @Bean
2 | @Scope(value="thread", proxyMode = ScopedProxyMode.TARGET_CLASS)
3 | public CacheManager threadCaches() {
4 | log.debug("Creating a new thread cache");
5 | SimpleCacheManager cache = new SimpleCacheManager();
6 | cache.setCaches(singleton(new ConcurrentMapCache("countries")));
7 | return cache;
8 | }
9 |
10 | @Bean
11 | @Primary
12 | public CacheManager mainCacheManager() {
13 | return new CompositeCacheManager(threadCaches());
14 | }
--------------------------------------------------------------------------------
/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | logging.level.org.springframework=WARN
2 | logging.level.victor=DEBUG
3 | logging.pattern.console=%d{HH:mm:ss.SSS} %-16.16thread %-20.20logger{0} %message%n
4 | someProp=from file
5 |
6 | # for JMS-based command pattern
7 | spring.cloud.stream.bindings.output.destination=queue1
8 | spring.cloud.stream.bindings.input.destination=queue1
9 | # ensures 1 node in a group gets message (point-to-point, not a broadcast)
10 | spring.cloud.stream.bindings.input.group=barman
11 | spring.cloud.stream.bindings.input.consumer.concurrency=1
12 |
13 |
--------------------------------------------------------------------------------
/src/test/java/victor/training/patterns/adapter/AdapterArchUnitTest.java:
--------------------------------------------------------------------------------
1 | package victor.training.patterns.adapter;
2 |
3 | import com.tngtech.archunit.core.domain.JavaClasses;
4 | import com.tngtech.archunit.core.importer.ClassFileImporter;
5 | import com.tngtech.archunit.lang.syntax.ArchRuleDefinition;
6 | import com.tngtech.archunit.lang.syntax.elements.ClassesShouldConjunction;
7 | import org.junit.Test;
8 |
9 | public class AdapterArchUnitTest {
10 | @Test
11 | public void dependencyInversionTest() {
12 | JavaClasses classes = new ClassFileImporter().importPackages("victor.training");
13 |
14 | ClassesShouldConjunction domainDoesnDependOnInfra = ArchRuleDefinition.noClasses()
15 | .that().resideInAPackage("..adapter.domain")
16 | .should()
17 | .dependOnClassesThat().resideInAPackage("..adapter.infra");
18 |
19 | domainDoesnDependOnInfra.check(classes);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/target/classes/application.properties:
--------------------------------------------------------------------------------
1 | logging.level.org.springframework=WARN
2 | logging.level.victor=DEBUG
3 | logging.pattern.console=%d{HH:mm:ss.SSS} %-16.16thread %-20.20logger{0} %message%n
4 | someProp=from file
5 |
6 | # for JMS-based command pattern
7 | spring.cloud.stream.bindings.output.destination=queue1
8 | spring.cloud.stream.bindings.input.destination=queue1
9 | # ensures 1 node in a group gets message (point-to-point, not a broadcast)
10 | spring.cloud.stream.bindings.input.group=barman
11 | spring.cloud.stream.bindings.input.consumer.concurrency=1
12 |
13 |
--------------------------------------------------------------------------------