220 Commits

Author SHA1 Message Date
ffaf9a16c5 improved whois 2022-06-12 00:57:37 +02:00
698ddfd199 improved whois 2022-03-01 08:53:41 +01:00
ec82f06f88 improved whois 2022-02-25 13:53:11 +01:00
d427956422 improved whois 2022-02-25 13:13:27 +01:00
e82a4cadd4 updated dependencies 2022-01-23 13:57:51 +01:00
84b8b0545f dependency updates 2021-12-13 22:13:20 +01:00
cd2e3b0642 updated libraries 2021-02-21 16:30:23 +01:00
b129228b36 added addMessage(clientId, message, validationFailed) 2020-11-21 16:20:35 +01:00
57907d05e6 improved Audit 2020-09-30 20:01:20 +02:00
c983bde031 fixed NPE 2020-06-07 16:41:26 +02:00
0e21c9baf7 retrieve whois information and parse again without connection to server 2020-04-22 08:27:10 +02:00
e12127f6dd added validationFailed support 2020-02-26 23:08:16 +01:00
9090b2c66e added find by query method 2020-02-16 13:42:58 +01:00
799c7d52e3 made em configurable (required for unit testing) 2020-01-13 00:52:25 +01:00
6d9a87e15b added basic or clause support 2019-12-27 14:38:24 +01:00
4dddc1f3dc generalized StandardController 2019-12-25 23:04:55 +01:00
b4a28bb0b0 added subGraph support 2019-11-17 15:09:05 +01:00
d5dc39e8ff introduced StreamUtils 2019-11-17 15:08:07 +01:00
233164fb54 added UUID support 2019-10-28 14:51:59 +01:00
bea1d80f1e extended search by SearchFilter 2019-10-27 03:15:38 +01:00
571386046d started to add string filter support 2019-10-24 11:12:39 +01:00
103b8348d0 downgrade POI to 4.0.1 to avoid bug
https://bz.apache.org/bugzilla/show_bug.cgi?id=63845
2019-10-15 10:16:52 +02:00
06fe2231ec added getCommaListString method 2019-10-11 18:43:33 +02:00
113cb4a641 started to add searchFilter support 2019-10-04 13:50:22 +02:00
6d49dec89f prepared to support Enddatable in findAll 2019-10-04 07:32:00 +02:00
ee1a20836d added getStringValue method 2019-09-15 17:24:15 +02:00
626ab1b528 added missing gpg key configuration 2019-09-15 14:07:12 +02:00
aeab3a5ca9 updated API description 2019-09-15 13:58:16 +02:00
a2430f748f updated API description 2019-09-15 13:37:38 +02:00
af298c8060 removed pdf library 2019-09-15 13:37:30 +02:00
6348f81bed updated API description 2019-09-14 17:54:26 +02:00
13d16a1309 fixed project setup in pom.xml, fixed author tag 2019-09-13 09:15:19 +02:00
585b6d7d02 migrated shared-account into own project, updated license heaaders 2019-09-13 01:02:27 +02:00
a071cb5732 updated license header, merged shared-security 2019-09-12 23:43:14 +02:00
f804c11ceb update license header 2019-09-12 22:57:51 +02:00
218d7a7365 added getEnumString method 2019-09-12 19:03:08 +02:00
2128ad95bc removed deprecated API calls 2019-09-12 19:02:39 +02:00
53991b7f56 erged conflicts 2019-09-10 16:31:25 +01:00
59420af4a5 fixed author tag
fixed duplicate in taglib
2019-09-09 05:25:07 +02:00
fe8b0d272b removed broken sharepoint lib 2019-09-08 15:37:06 +02:00
89ddbbdbb2 upgrade to JUNIT5 2019-09-03 16:10:20 +02:00
c4d71f9614 added refresh method 2019-09-02 15:47:40 +02:00
97649c9306 enlarged config value size 2019-09-02 15:47:04 +02:00
0cbab859f8 made context root configurable 2019-08-28 07:53:05 +02:00
eb3bf3b71a introduced IdentifiableEntity 2019-08-19 13:04:24 +02:00
b138841faa fixed header 2019-08-19 13:04:06 +02:00
5a840dad7f updated libraries 2019-08-14 12:03:39 +02:00
0f98586434 fixed borken update (entity, updatedBy) method 2019-08-14 12:02:51 +02:00
08a0adcc6f added missing icons 2019-08-14 12:01:08 +02:00
8205ffaec3 fixed BLOCKED users are accepted 2019-08-14 12:00:45 +02:00
54f2e56a4c maded applications editable, made UUID visible 2019-08-14 10:34:23 +02:00
119fb04520 added support of multiple fallback suffixes 2019-08-02 08:45:34 +02:00
24dc927ab7 updated libraries
updated to JUNIT5
2019-07-12 15:07:13 +02:00
3ae4dba8fe added support for locked users 2019-06-25 13:06:53 +02:00
bf590223b8 introduced validity 2019-06-23 17:10:30 +02:00
1e5e15cda0 fixed entity duplication when trying to delete 2019-06-22 16:22:29 +02:00
40acbcd6ac introduced CommonAbstractController and StandardController 2019-06-22 01:00:56 +02:00
ab2a0e2301 added EntityUtil, ensured cloned entities are updated to represent a new entity 2019-06-21 09:45:16 +02:00
a8e0f9bd5f continued to add Auditable support 2019-06-20 16:09:09 +02:00
88d2509893 started to introduce embeded audit entity support 2019-06-20 13:11:07 +02:00
96892cf8f9 started to fix sharepoint API under JDK11 2019-06-17 14:21:11 +02:00
6829b65a2f Merge origin/master 2019-06-16 21:56:47 +02:00
a9e136d3ac enhanced enddateable support 2019-06-16 21:56:28 +02:00
aa478dbf9f added trace logging 2019-06-14 19:16:44 +02:00
2a75d2a1fe updated jackson to Wildfly 16 provided version 2019-06-12 16:21:10 +02:00
a8a609491f fixed log4j config 2019-06-12 16:20:41 +02:00
43f5401773 fixed broken imports 2019-06-10 23:04:57 +02:00
e9dede69cb excluded jackson which is provided by wildfly automatically 2019-06-10 14:45:28 +02:00
7a380dc214 added find method with support to init certain collections 2019-06-10 14:44:37 +02:00
212e4dad5d splitted database from account 2019-06-05 14:32:12 +02:00
d50f21f869 [maven-release-plugin] prepare for next development iteration 2019-06-05 12:37:24 +02:00
cf433524cd [maven-release-plugin] prepare release v1.1 2019-06-05 12:36:58 +02:00
a64051dc28 removed obsolete exception factory, will use admin-faces provided one instead 2019-06-04 09:50:25 +02:00
91f8c2b2f1 added generic filter support 2019-06-03 18:06:34 +02:00
7161c32a61 Merge origin/master 2019-05-28 17:41:40 +02:00
be34fa9e8d added LDAP search support 2019-05-28 17:41:10 +02:00
4ec319fc45 fixed reset password process 2019-05-17 16:35:34 +02:00
bb3d08360e disabled caching, changes made in account ui are not reflected in application 2019-05-17 16:35:16 +02:00
336e76f536 added caching and exception handling 2019-05-01 13:52:39 +02:00
70a45b1919 updated libraries 2019-04-29 09:37:14 +02:00
59969ccef4 added checks to avoid NPEs 2019-04-29 08:13:27 +02:00
4f3c3d4673 fixed tx join interceptor handling 2019-04-27 14:07:31 +01:00
70829f9204 improved project setup 2019-04-03 23:29:51 +02:00
91b967f008 improved account controller 2019-04-03 23:29:25 +02:00
eb332461aa updated to Primefaces 7.0, JDK11 2019-04-02 16:07:21 +02:00
4fd1710503 fixed typo 2019-04-02 14:52:50 +02:00
3d1f2a93cf fixed broken permission check 2019-04-02 14:48:09 +02:00
f8fe805ba6 added missing tx annoations 2019-03-28 12:30:22 +01:00
cc7e1b5e73 introduced EndDateable 2019-03-24 17:15:06 +01:00
4aafdb1222 added Serializable 2019-03-17 13:09:04 +01:00
cdc5a2ae8c added missing libraries 2019-03-16 12:44:23 +01:00
b49fb0e19d improved templating service 2019-03-16 12:44:13 +01:00
e75523e9dd upgraded to jackson2 2019-03-16 12:41:31 +01:00
0d0984bb53 added debug code and tests 2019-03-14 17:37:56 +01:00
827b110e7c added more date util functions 2019-03-14 15:11:47 +01:00
701f0dd3a0 removed obsolete dbm backup file 2019-03-06 08:22:36 +00:00
a7e845d514 improved API key handling 2019-03-06 01:55:33 +01:00
7b315f6fd0 made JTW expiration configurable 2019-02-18 22:17:52 +01:00
70bebd4ef8 hide comments by default 2019-02-17 17:45:56 +01:00
d76154e279 added more POI helper methods 2019-02-15 17:54:53 +01:00
b30b40769c improved error logging 2019-02-14 17:28:41 +01:00
be31b12d0a added default value for StringParameters 2019-02-14 17:28:33 +01:00
03281643ad added missing ControllerExceptions 2019-02-09 14:15:26 +01:00
6117cc6c10 ensured transaction is rolled back in case a ControllerException occurs 2019-02-06 16:55:37 +01:00
479145af6f fixed broken return value of getConfigValue 2019-02-06 16:55:01 +01:00
b32b756da1 fixed missing transaction handling 2019-02-04 16:51:19 +01:00
1c6bb1769c generalized nullSafeGet 2019-02-02 14:58:58 +01:00
6933900635 minor changes 2019-02-01 17:04:53 +01:00
763d6f57f2 simplified project setup 2019-02-01 16:55:33 +01:00
2540d2a454 convert controller exception to runtime exception to ensure container rolls back transactions 2019-01-30 16:05:07 +01:00
76795365c0 updated license header 2019-01-30 00:56:33 +01:00
5c7abf4c47 added convenience methods for multiple ui messages to be displayed 2019-01-30 00:56:18 +01:00
a3e7f6c527 upgrade to POI 4.0.1 2019-01-26 13:29:24 +01:00
73bb6a18ca upgrade to POI 4.0.1 2019-01-25 17:42:38 +01:00
15dc1f9e50 added PoiUtil class 2019-01-25 17:42:09 +01:00
825aa6cbf5 added missing license header 2019-01-25 17:41:53 +01:00
9f301136f0 added getUtcDate methods 2019-01-25 17:17:29 +01:00
4daa43ad04 made applyUpdateableChanges callable from parent class 2019-01-25 17:17:06 +01:00
5ab4d99dd3 moved some debug messages to trace 2019-01-24 16:58:11 +01:00
333508632c made JWTAuthentication filter work again. Ensured realms not supporting
the JTWtoken based are not considdered when logging in via API key
2019-01-24 13:18:05 +01:00
38592887c5 optimized project structure 2019-01-22 09:55:18 +01:00
49e0c9651e added Controller Exception on delete method 2019-01-22 09:55:05 +01:00
ac2cf1b784 improved caching 2019-01-18 18:05:55 +01:00
584bdfce4f fixed broken mainmenu 2019-01-18 16:47:51 +01:00
48a6ca9231 fixed broken persistence settings 2019-01-18 16:47:32 +01:00
37290a2ed7 simplified persistence settings 2019-01-18 16:27:26 +01:00
3ba593a15c enabled caching 2019-01-18 16:26:43 +01:00
c1410554f5 removed obsolete files 2019-01-18 16:23:36 +01:00
dc2ec093d2 added method to determine wether a call is render or response request 2019-01-18 14:29:14 +01:00
1aab667a8e fixed broken persistence implementation after refactor generalization 2019-01-18 14:28:35 +01:00
2659ba1ff5 removed obsolete version from pom 2019-01-18 14:27:43 +01:00
2965c6be4b added more generalizations 2019-01-17 18:24:15 +01:00
7790a6fe50 generalizhed database connection 2019-01-16 09:50:21 +01:00
12da8c2d8c ensured deployment is stopped, if application cannot be read 2019-01-14 19:26:27 +01:00
698b4477a2 fixed NPE when Account could not be injected 2019-01-11 17:33:14 +01:00
8291d3e3aa added summary only global messages 2019-01-11 17:32:44 +01:00
ecedc1872b restructured code
enhanced permission system
2019-01-10 18:08:36 +01:00
b552e0b8bc centralized used modules 2019-01-10 15:32:10 +01:00
f4145ca2fc improved logging 2019-01-09 20:08:44 +01:00
e114dcb9eb fixed broken isEmpty implementation 2019-01-04 10:16:36 +01:00
257def9140 generalized autoResizeColumns(sheet) function 2019-01-02 16:33:19 +01:00
d8e47d1eb0 improved logging 2018-12-20 13:48:40 +01:00
df86b707a6 ensured standard API response header is returned if an APIKeyError occurs 2018-12-18 15:43:57 +01:00
c3fafa4331 fixed broken POM which included javaee-api into application scope 2018-12-17 18:14:23 +01:00
0b123bec0f updated license headers 2018-12-14 10:48:10 +01:00
452ffa4f9d added obsolete netbeans files to .gitignore 2018-12-14 10:47:59 +01:00
aaa67135a8 switched to AccountRealm support including JWT support
updated setup according to new code structure
2018-12-14 09:59:13 +01:00
7acc23892b restructured code and introduced JWTAuthenticationFilter support to ensure user and roles are loaded when using JWT to sign in 2018-12-14 09:55:32 +01:00
6bad0e75a6 fixed missing check for disabled accounts 2018-12-12 16:37:09 +01:00
7ad25dc734 allowed removal of existing config values 2018-12-12 16:36:42 +01:00
5e42012907 Merge origin/master 2018-12-12 15:42:34 +01:00
c09a27583b added missing h:form for menu 2018-12-12 15:42:10 +01:00
1fed967100 fixed imports 2018-12-12 15:42:00 +01:00
87f389fc2d ensured transactions do not interfear with other transactions. This would require a XA data source 2018-12-12 14:25:37 +01:00
057dfd9c05 ensured exchange autodiscovery is disabled for null and empty string 2018-12-12 14:24:36 +01:00
f712e269c5 fixed potential NPE 2018-11-28 17:05:42 +01:00
350d045eb0 fixed broken query in ConfigService 2018-11-25 15:14:57 +01:00
9b8284a2cf fixed update of account role mapping 2018-11-25 15:14:39 +01:00
ac39be3848 added findByName for default application 2018-11-24 17:49:02 +01:00
0b044bac78 removed obsolete version information 2018-11-24 00:00:41 +01:00
c05ba11044 started to implement permission handling into views and pages 2018-11-22 14:54:48 +01:00
79c9ab623c started to introduce permission checks into controller 2018-11-22 14:53:46 +01:00
8c11d3424e updated to hibernate 5.3.6 to support Wildfly 14 2018-11-20 13:06:36 +01:00
c822b30ca0 added ldap support 2018-11-19 17:48:34 +01:00
ed0892b1dc added missing transactional annotation 2018-11-19 17:47:09 +01:00
06d625013b added parameter to clean database before reinstall 2018-11-19 17:46:55 +01:00
4f6f851e2b added batch job to backup productive database 2018-11-19 13:45:58 +01:00
798a178f42 updated prefil script 2018-11-19 13:45:43 +01:00
1e3d2986c5 fixed injection issue when using WF14 2018-11-19 13:45:22 +01:00
4d69e8e70a added missing accountPU annotations 2018-11-17 17:13:18 +01:00
46f2827338 migrated API key support from PCD 2018-11-16 16:21:43 +01:00
b2c2619dc4 added multi persistence unit support 2018-11-16 12:43:20 +01:00
349310ccf9 updated SQL scripts 2018-11-16 12:42:41 +01:00
1bb9b24bcf removed obsolete class 2018-11-16 12:19:30 +01:00
2dc317b84f renamed datasource 2018-11-15 12:39:46 +01:00
7b1d4f24ab reogranized source code 2018-11-15 11:48:27 +01:00
389e3a6a73 optimized startup sequence 2018-11-15 10:54:34 +01:00
6f5baaaa69 ensured only users which have a role assigned to the application can login 2018-11-14 17:41:39 +01:00
6533451d06 updated .gitignore 2018-11-14 17:17:56 +01:00
765589afdf completed update of account, splitted login from account 2018-11-14 17:17:04 +01:00
939f043b01 started to introduce application to config and to split account_login from account. 2018-11-12 22:11:05 +01:00
ed63692c0c updated gitignore, removed obsolete eclipse files 2018-11-12 17:51:52 +01:00
13da4a3e04 fixed broken filter on web.xml and shiro.ini 2018-11-12 17:19:34 +01:00
7fceccc109 fixed account handling
fixed view CDI integration
2018-11-12 16:13:00 +01:00
00925aa389 completed role permission setup 2018-11-12 10:02:23 +01:00
76114f6cf2 completed role permission setup 2018-11-12 10:01:55 +01:00
d1f72db6ac started to add application support for roles 2018-11-11 16:31:33 +01:00
ea3ebdddf5 added application support for permissions 2018-11-11 15:08:37 +01:00
c5a70b9d11 added filter to support project stage 2018-11-11 15:08:09 +01:00
634b51b051 converted permission edit to dialog based
and simplified implemenation
2018-11-10 16:49:05 +01:00
17c080faa2 added create / update methods 2018-11-10 16:48:32 +01:00
9ebb649458 added first draft of mapplication support 2018-11-10 14:03:34 +01:00
c30af64604 added frist draft of account ui 2018-11-10 14:03:10 +01:00
c254d27e84 added frist draft of account ui 2018-11-10 14:02:29 +01:00
b95ffdb417 introduced possibility to fallback to different principalSuffix to combine different users together 2018-11-06 00:40:28 +01:00
14e4c2cc6e improved logging 2018-11-06 00:37:19 +01:00
f07467fd3e improved logging 2018-10-29 12:35:35 +01:00
4eb6bb77e2 updated to POI 4.0.0 2018-10-26 14:02:50 +02:00
8c4f304d18 fixed pom.xml 2018-10-22 19:07:14 +02:00
d3e0e09718 improved logging 2018-10-22 19:07:03 +02:00
95d93ec222 added Date / LocalDate conversion helper 2018-10-22 19:06:45 +02:00
4d79cbc35b ensure autodiscover is only used if hostname is not set 2018-10-16 12:34:05 +02:00
530b6f500a add valuelist support 2018-09-19 19:01:06 +02:00
8b2da09418 introduced global messages 2018-09-18 13:02:41 +02:00
64c5d3b370 introduced option to load user preferences and fallback to system preference 2018-09-17 12:00:07 +02:00
c3961f9a28 added possibility to enable / disable redirect and configure the redirect URL 2018-09-12 18:33:54 +02:00
65cdf950c9 exposed user first and lastname 2018-08-28 16:35:03 +02:00
ecf8558e92 Delete 'configuration/nb-configuration.xml' 2018-08-24 11:22:58 +00:00
81cd34817b changed generic handling 2018-08-24 13:17:32 +02:00
a0cda5f498 added support for default values 2018-08-23 17:00:30 +02:00
4b2e4e9055 fixed pom settings 2018-08-22 10:53:52 +02:00
a0d77ca744 disabled tests to avoid network issues 2018-08-22 10:52:47 +02:00
9da7004c6b generalized poi workbook handler 2018-08-22 10:52:20 +02:00
0a47471b49 fixed pom settings 2018-08-22 10:50:44 +02:00
4559f20170 introduced account based configuration 2018-08-21 18:07:51 +02:00
077ab22438 moved mail related entities 2018-08-21 18:05:39 +02:00
da46ca4c16 restructured code 2018-08-21 11:16:51 +02:00
a68f19b868 added script to create a new release 2018-08-20 23:39:44 +02:00
6a05de2b30 removed obslete code 2018-08-20 23:39:18 +02:00
1feb529c9e [maven-release-plugin] prepare for next development iteration 2018-08-20 23:25:52 +02:00
335 changed files with 10559 additions and 18972 deletions

1154
.editorconfig Normal file

File diff suppressed because it is too large Load Diff

201
.gitignore vendored
View File

@ -1,10 +1,191 @@
/configuration/target/ # ---> NetBeans
/network/target/ nbproject/private/
/util/target/ build/
/sharepoint/api/target/ nbbuild/
/security/target/ dist/
/jeeutil/target/ nbdist/
/account/target/ nbactions.xml
/shiro-faces/target/ **/nb-configuration.xml
/pdf/target/ .nb-gradle/
/shared-poi-util/target/ **/faces-config.NavData
# ---> Eclipse
.project
.settings/
.classpath
# ---> Maven
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
# --> Idea
**/.idea
**/*.iml
# ---> Java
*.class
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
# ---> TeX
## Core latex/pdflatex auxiliary files:
*.aux
*.lof
*.log
*.lot
*.fls
*.out
*.toc
## Intermediate documents:
*.dvi
*-converted-to.*
# these rules might exclude image files for figures etc.
# *.ps
# *.eps
# *.pdf
## Bibliography auxiliary files (bibtex/biblatex/biber):
*.bbl
*.bcf
*.blg
*-blx.aux
*-blx.bib
*.brf
*.run.xml
## Build tool auxiliary files:
*.fdb_latexmk
*.synctex
*.synctex.gz
*.synctex.gz(busy)
*.pdfsync
## Auxiliary and intermediate files from other packages:
# algorithms
*.alg
*.loa
# achemso
acs-*.bib
# amsthm
*.thm
# beamer
*.nav
*.snm
*.vrb
#(e)ledmac/(e)ledpar
*.end
*.[1-9]
*.[1-9][0-9]
*.[1-9][0-9][0-9]
*.[1-9]R
*.[1-9][0-9]R
*.[1-9][0-9][0-9]R
*.eledsec[1-9]
*.eledsec[1-9]R
*.eledsec[1-9][0-9]
*.eledsec[1-9][0-9]R
*.eledsec[1-9][0-9][0-9]
*.eledsec[1-9][0-9][0-9]R
# glossaries
*.acn
*.acr
*.glg
*.glo
*.gls
# gnuplottex
*-gnuplottex-*
# hyperref
*.brf
# knitr
*-concordance.tex
*.tikz
*-tikzDictionary
# listings
*.lol
# makeidx
*.idx
*.ilg
*.ind
*.ist
# minitoc
*.maf
*.mtc
*.mtc[0-9]
*.mtc[1-9][0-9]
# minted
_minted*
*.pyg
# morewrites
*.mw
# mylatexformat
*.fmt
# nomencl
*.nlo
# sagetex
*.sagetex.sage
*.sagetex.py
*.sagetex.scmd
# sympy
*.sout
*.sympy
sympy-plots-for-*.tex/
# TikZ & PGF
*.dpth
*.md5
*.auxlock
# todonotes
*.tdo
# xindy
*.xdy
# WinEdt
*.bak
*.sav
# localized versions of JBOSS command line interface
*.local.cli
## project specific
/source/vvh-access-import/src/main/resources/hibernate.cfg.xml
/source/office-parent/office-entities/src/main/resources/META-INF/persistence.xml

5
_bin/createRelease.bat Normal file
View File

@ -0,0 +1,5 @@
@ECHO OFF
setlocal
set BASEPATH=%~dp0%
cd %BASEPATH%\..
mvn release:clean release:prepare -Dmaven.test.skip=true -Darguments="-Dmaven.test.skip=true -DskipTests"

View File

@ -1,96 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.muehlencord.shared</groupId>
<artifactId>shared-account</artifactId>
<version>1.0</version>
<packaging>ejb</packaging>
<parent>
<artifactId>shared</artifactId>
<groupId>de.muehlencord</groupId>
<version>1.0</version>
</parent>
<name>shared-account</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>de.muehlencord.shared</groupId>
<artifactId>shared-jeeutil</artifactId>
<type>jar</type>
<version>1.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- create EJB version 3.1 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ejb-plugin</artifactId>
<configuration>
<ejbVersion>3.1</ejbVersion>
<excludes>
<exclude>**/persistence.xml</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,8 +0,0 @@
DROP TABLE config;
CREATE TABLE config (
config_key varchar(100),
config_value varchar(200),
CONSTRAINT config_pk PRIMARY KEY (config_key)
);
INSERT INTO config (config_key, config_value) VALUES ('account.maxFailedLogins', '5');

View File

@ -1,91 +0,0 @@
/**
* Author: joern.muehlencord
* Created: 06.09.2015
*/
DROP TABLE account_role;
DROP TABLE account_history;
DROP TABLE account;
DROP TABLE role_permission;
DROP TABLE application_role;
DROP TABLE application_permission;
CREATE TABLE application_role (
id UUID NOT NULL,
role_name varchar(80) NOT NULL,
role_description varchar(200) NOT NULL,
CONSTRAINT pk_application_role_pk PRIMARY KEY (id),
CONSTRAINT uidx_application_id UNIQUE (id)
);
CREATE TABLE account (
id UUID NOT NULL,
username varchar(32) NOT NULL,
emailaddress varchar(200) NOT NULL,
firstname varchar(100) NOT NULL,
lastname varchar(100) NOT NULL,
account_password char(200) NOT NULL,
last_login timestamp with time zone,
last_failed_login timestamp with time zone,
failure_count int NOT NULL DEFAULT 0,
status varchar(10) NOT NULL DEFAULT 'NEW', -- NEW, INIT, OK, BLOCKED,
password_reset_ongoing boolean NOT NULL DEFAULT false,
password_reset_valid_to timestamp with time zone,
password_reset_hash char(200),
created_on timestamp with time zone NOT NULL DEFAULT (now() at time zone 'utc'),
created_by varchar(32) NOT NULL,
last_updated_on timestamp with time zone NOT NULL DEFAULT (now() at time zone 'utc'),
last_updated_by varchar(32) NOT NULL,
CONSTRAINT pk_account PRIMARY KEY (id),
CONSTRAINT uidx_username UNIQUE (username)
);
CREATE TABLE account_history (
id UUID NOT NULL,
account_id UUID NOT NULL,
message varchar(200),
failure_count int NOT NULL DEFAULT 0,
status varchar(20) NOT NULL, -- constants needed, after action - new, init, active, blocked, inactive, marked for deletion
last_updated_on timestamp with time zone NOT NULL DEFAULT (now() at time zone 'utc'),
last_updated_by varchar(32) NOT NULL,
CONSTRAINT pk_account_history PRIMARY KEY (id),
CONSTRAINT fk_account_history_username_fk FOREIGN KEY (account_id) REFERENCES account (id)
);
CREATE TABLE account_role (
account UUID NOT NULL,
account_role UUID NOT NULL,
CONSTRAINT pk_account_role PRIMARY KEY (account, account_role),
CONSTRAINT fk_account_role_account FOREIGN KEY (account) REFERENCES account(id),
CONSTRAINT fk_account_role_role_name FOREIGN KEY (account_role) REFERENCES application_role(id)
);
CREATE TABLE application_permission (
id UUID NOT NULL,
permission_name varchar(80) NOT NULL,
permission_description varchar(200) NOT NULL,
CONSTRAINT pk_application_permission PRIMARY KEY (id),
CONSTRAINT uidx_application_permission_name UNIQUE (permission_name)
);
CREATE TABLE role_permission (
application_role UUID NOT NULL,
role_permission UUID NOT NULL,
CONSTRAINT pk_role_permission_role_permission_name PRIMARY KEY (application_role, role_permission),
CONSTRAINT fk_role_permission_application_role FOREIGN KEY (application_role) REFERENCES application_role(id),
CONSTRAINT fk_role_permission_role_permission FOREIGN KEY (role_permission) REFERENCES application_permission(id)
);
INSERT INTO application_permission (id, permission_name, permission_description) values ('dfd0f8f1-4a51-4fdc-9a1c-a942bee9b649', 'test:view', 'Display test view');
INSERT INTO application_role (id, role_name, role_description) values ('5cd0aca0-5466-483d-8f3e-c369f8061131','Admin', 'Admin role');
INSERT INTO application_role (id, role_name, role_description) values ('da30060e-fd23-4016-a506-4e12e9322148', 'User', 'Standard user role');
-- INSERT INTO role_permission (role_name, permission_name) values ('Admin','test:view');
INSERT INTO account (id, username, emailaddress, firstname, lastname, account_password, created_by, last_updated_by) values('ab5c8337-6872-4aea-a9b9-78ea63706b8f','admin', 'joern@muehlencord.de', 'Joern', 'Muehlencord','$shiro1$SHA-256$500000$4bHPNH9k539UjdFLgm/HOA==$T/n8skgoGSOtNw/c9ScDlXCiGrx2cZF0Esrvf6WPq6g=', 'admin','admin'); --admin/secret
INSERT INTO account_role (account, account_role) values ('ab5c8337-6872-4aea-a9b9-78ea63706b8f', '5cd0aca0-5466-483d-8f3e-c369f8061131');
--select uuid_generate_v4();

View File

@ -1,21 +0,0 @@
DROP TABLE mail_template;
CREATE TABLE mail_template (
template_name varchar(40) NOT NULL,
template_value text NOT NULL,
CONSTRAINT mail_template_pk PRIMARY KEY (template_name)
);
INSERT INTO mail_template (template_name, template_value) VALUES('password_reset_html',
'<#ftl strip_whitespace = true>
<!DOCTYPE html>
<html>
<body>
Dear ${account.firstname},<br>
<br>
you requested to reset your password at ${parameter.url}. Please open the following URL to proceed.<br>
<a href="${parameter.resetUrl}">${parameter.resetUrl}</a><br>
<br>
</body>
</html>');

View File

@ -1,2 +0,0 @@
\i 01_accounts.sql
\i 02_templates.sql

View File

@ -1,54 +0,0 @@
package de.muehlencord.shared.account.business;
import de.muehlencord.shared.account.entity.ConfigEntity;
import java.io.Serializable;
import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.inject.Inject;
import javax.persistence.EntityManager;
/**
*
* @author joern.muehlencord
*/
@Singleton
@Startup
public class ConfigService implements Serializable {
private static final long serialVersionUID = -3195224653632853003L;
@Inject
EntityManager em;
private String storagePath = null;
private int maxFailedLogins = 5;
@PostConstruct
public void init() {
ConfigEntity configEntity = em.find(ConfigEntity.class, "storage.path");
if (configEntity != null) {
this.storagePath = configEntity.getConfigValue();
}
configEntity = em.find(ConfigEntity.class, "account.maxFailedLogins");
if (configEntity != null) {
this.maxFailedLogins = Integer.parseInt(configEntity.getConfigValue());
}
}
public String getConfigValue(String configKey) {
ConfigEntity configEntity = em.find(ConfigEntity.class, configKey);
return (configEntity == null ? null : configEntity.getConfigValue());
}
/* *** getter *** */
// FIXME remove, this is application specific
public String getStoragePath() {
return storagePath;
}
public int getMaxFailedLogins() {
return maxFailedLogins;
}
}

View File

@ -1,276 +0,0 @@
package de.muehlencord.shared.account.business.account;
import de.muehlencord.shared.account.business.ConfigService;
import de.muehlencord.shared.account.business.mail.MailException;
import de.muehlencord.shared.account.business.mail.MailService;
import de.muehlencord.shared.account.entity.AccountEntity;
import de.muehlencord.shared.account.entity.ApplicationRoleEntity;
import de.muehlencord.shared.account.util.SecurityUtil;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import javax.transaction.Transactional;
import org.apache.commons.lang3.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
/**
*
* @author joern.muehlencord
*/
@Stateless
public class AccountControl implements Serializable {
private static final Logger LOGGER = LoggerFactory.getLogger(AccountControl.class.getName());
private static final long serialVersionUID = 3424816272598108101L;
@EJB
private ConfigService configService;
@EJB
private MailService mailService;
@Inject
EntityManager em;
public List<AccountEntity> getAccounts() {
Query query = em.createQuery("SELECT a FROM AccountEntity a WHERE a.status <> :status", AccountEntity.class);
query.setParameter("status", AccountStatus.DISABLED.name());
return query.getResultList();
}
public AccountEntity getAccountEntity(String userName, boolean loadRoles) {
StringBuilder queryBuilder = new StringBuilder();
queryBuilder.append("SELECT a FROM AccountEntity a ");
if (loadRoles) {
queryBuilder.append("JOIN FETCH a.applicationRoleList ");
}
queryBuilder.append("WHERE a.username = :username");
Query query = em.createQuery(queryBuilder.toString());
query.setParameter("username", userName);
try {
return (AccountEntity) query.getSingleResult();
} catch (NoResultException ex) {
return null;
}
}
@Transactional
// TODO add role names from application because only application can know how its roles are named
public AccountEntity saveAccount(AccountEntity account, boolean isAdmin) {
Date now = new Date(); // Todo now in UTC
Subject currentUser = SecurityUtils.getSubject();
String currentLoggedInUser = currentUser.getPrincipal().toString();
account.setLastUpdatedBy(currentLoggedInUser);
account.setLastUpdatedOn(now);
boolean newAccount = (account.getCreatedOn() == null);
// new account
if (newAccount) {
account.setCreatedOn(now);
account.setCreatedBy(currentLoggedInUser);
// set default random password, user has to get password via lost passwort option afterwards
String randomPassword = RandomStringUtils.random(20, true, true);
String hashedPassword = SecurityUtil.createPassword(randomPassword);
account.setAccountPassword(hashedPassword);
em.persist(account);
} else {
em.merge(account);
// reload account from db and join roles
account = getAccountEntity(account.getUsername(), true);
}
// load Admin or User role from database
String roleName = (isAdmin ? "Admin" : "User");
Query roleQuery = em.createNamedQuery("ApplicationRoleEntity.findByRoleName");
roleQuery.setParameter("roleName", roleName);
ApplicationRoleEntity role = (ApplicationRoleEntity) roleQuery.getSingleResult();
if (role != null) {
// add new user add required role
// do not request based on newUser variable; this way existing users with missing role (for whatever reason)
// will be fixed automatically
if (account.getApplicationRoleList() == null || account.getApplicationRoleList().isEmpty()) {
account.setApplicationRoleList(new ArrayList<>());
account.getApplicationRoleList().add(role);
em.merge(account);
LOGGER.info("Added role " + roleName + " to user " + account.getUsername());
} else if (!account.getApplicationRoleList().get(0).equals(role)) {
// change role from User to Admin and vice versa
// user already exists, has existing role
// check if existing role is different from current role and change it
// be carefull: this only works as long as a user has exactly one role!
// he is either User or Admin
// TODO add "UserRole" to every user, make this default Role configurable
// TODO add AdminRole in addtion if needed
account.getApplicationRoleList().remove(0);
account.getApplicationRoleList().add(role);
em.merge(account);
LOGGER.info("Switched role of user " + account.getUsername() + " to " + roleName);
}
}
return account;
}
public void deleteAccount(AccountEntity account) throws AccountException {
Date now = new Date(); // Todo now in UTC
Subject currentUser = SecurityUtils.getSubject();
String currentUserName = currentUser.getPrincipal().toString();
if (account.getUsername().equals(currentUserName)) {
throw new AccountException("Cannot delete own account");
} else {
account.setStatus(AccountStatus.DISABLED.name());
account.setLastUpdatedBy(currentUserName);
account.setLastUpdatedOn(now);
em.merge(account);
}
}
public boolean initPasswordReset(String userName) {
try {
AccountEntity account = getAccountEntity(userName, false);
if (account == null) {
LOGGER.warn("Account with name " + userName + " not found");
return false;
}
if (account.getStatus().equals(AccountStatus.BLOCKED.name())) {
LOGGER.warn("Account " + userName + " is locked, cannot initialize password reset");
return false;
}
String randomString = RandomStringUtils.random(40, true, true);
Date validTo = new Date(); // TODO now in UTC
validTo = new Date(validTo.getTime() + 1000 * 600); // 10 minutes to react
account.setPasswordResetHash(randomString);
account.setPasswordResetOngoing(true);
account.setPasswordResetValidTo(validTo);
mailService.sendPasswortResetStartEmail(account, randomString);
em.merge(account);
return true;
} catch (MailException ex) {
LOGGER.error("Error while sending password reset mail. " + ex.toString());
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Error while sending password reset mail.", ex);
}
return false;
}
}
public boolean resetPassword(String userName, String newPassword, String resetPasswordToken) {
AccountEntity account = getAccountEntity(userName, false);
if (account == null) {
LOGGER.warn("Error while resetting password, no account with username " + userName + " found");
// TODO add extra logging for intrusion protection system like fail2ban
return false;
}
if (account.getPasswordResetOngoing() && (account.getPasswordResetHash() != null) && (account.getPasswordResetValidTo() != null)) {
Date now = new Date(); // TODO now in UTC
String storedHash = account.getPasswordResetHash().trim();
if (account.getPasswordResetValidTo().after(now)) {
if (storedHash.equals(resetPasswordToken)) {
// everything ok, reset password
executePasswordReset(account, newPassword);
LOGGER.info("Updated password for user " + userName);
return true;
} else {
// token is not valid, refuse to change password
LOGGER.warn("Trying to reset password for user " + userName + " but wrong token " + resetPasswordToken + " provided");
addLoginError(account);
return false;
}
} else {
// password reset token no longer valid
LOGGER.warn("Trying to reset password for user " + userName + " but token is no longer valid");
addLoginError(account);
return false;
}
} else {
// user is not is password reset mode
LOGGER.warn("Trying to reset password for user " + userName + " but password reset was not requested");
addLoginError(account);
return false;
}
}
private void executePasswordReset(AccountEntity account, String newPassword) {
Date now = new Date(); // TODO now in UTC
String hashedPassword = SecurityUtil.createPassword(newPassword);
account.setAccountPassword(hashedPassword);
account.setPasswordResetOngoing(false);
account.setPasswordResetHash(null);
account.setPasswordResetValidTo(null);
account.setLastUpdatedBy(account.getUsername());
account.setLastUpdatedOn(now);
em.merge(account);
}
public void updateLogin(AccountEntity account) {
Date now = new Date(); // TODO now in UTC
// a scucessful login ends a password reset procedure
if (account.getPasswordResetOngoing()) {
account.setPasswordResetOngoing(false);
account.setPasswordResetHash(null);
account.setPasswordResetValidTo(null);
account.setLastUpdatedOn(now);
account.setLastUpdatedBy(account.getUsername());
}
account.setLastLogin(now);
account.setFailureCount(0);
account.setStatus(AccountStatus.NORMAL.name());
em.merge(account);
}
public void addLoginError(AccountEntity account) {
Date now = new Date(); // TODO now in UTC
account.setLastFailedLogin(now);
account.setFailureCount(account.getFailureCount() + 1);
int maxFailedLogins = configService.getMaxFailedLogins();
if ((account.getFailureCount() >= maxFailedLogins) && (!account.getStatus().equals("LOCKED"))) { // TOD add status enum
// max failed logins reached, disabling user
LOGGER.info("Locking account " + account.getUsername() + " due to " + account.getFailureCount() + " failed logins");
account.setStatus(AccountStatus.BLOCKED.name());
}
// on a failed login request, disable password reset
account.setPasswordResetOngoing(false);
account.setPasswordResetHash(null);
account.setPasswordResetValidTo(null);
account.setLastUpdatedBy("system");
account.setLastUpdatedOn(now);
em.merge(account);
}
}

View File

@ -1,29 +0,0 @@
package de.muehlencord.shared.account.business.account;
/**
*
* @author Raimund
*/
public class AccountException extends Exception {
/**
* Creates a new instance of <code>AccountException</code> without detail
* message.
*/
public AccountException() {
}
/**
* Constructs an instance of <code>AccountException</code> with the
* specified detail message.
*
* @param msg the detail message.
*/
public AccountException(String msg) {
super(msg);
}
AccountException(String entity_updated__deleted_please_reload, boolean b) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
}

View File

@ -1,15 +0,0 @@
package de.muehlencord.shared.account.business.account;
/**
*
* @author joern.muehlencord
*/
public enum AccountStatus {
NEW, // account is created but never used
NORMAL, // normal account, at least on login, neither blocked or disabled
BLOCKED, // account is blocked after too many login failures or other security related events
DISABLED; // account is disabled and cannot be used anymore
}

View File

@ -1,70 +0,0 @@
package de.muehlencord.shared.account.business.account;
import de.muehlencord.shared.account.entity.ApplicationPermissionEntity;
import java.io.Serializable;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.List;
import java.util.ArrayList;
import javax.persistence.OptimisticLockException;
import javax.persistence.Query;
import javax.transaction.Transactional;
/**
*
* @author Joern Muehlencord <joern at muehlencord.de>
*/
@Stateless
public class ApplicationPermissionControl implements Serializable {
private static final long serialVersionUID = -3761100587901739481L;
@PersistenceContext
EntityManager em;
public List getApplicationPermissions() {
List<ApplicationPermissionEntity> permissionList = em.createNamedQuery("ApplicationPermissionEntity.findAll").getResultList();
if (permissionList == null) {
return new ArrayList<>();
} else {
return permissionList;
}
}
@Transactional
public void createOrUpdate(String name, String description) {
ApplicationPermissionEntity permission = findByName(name);
if (permission == null) {
permission = new ApplicationPermissionEntity(name, description);
em.persist(permission);
} else {
permission.setPermissionDescription(description);
em.merge(permission);
}
}
public void delete(ApplicationPermissionEntity permission) throws AccountException {
ApplicationPermissionEntity existingPermission = attach(permission);
em.remove(existingPermission);
}
public ApplicationPermissionEntity attach(ApplicationPermissionEntity permission) throws AccountException {
try {
return em.merge(permission);
} catch (OptimisticLockException ex) {
throw new AccountException("Entity updated / deleted, please reload", true);
}
}
private ApplicationPermissionEntity findByName(String name) {
Query query = em.createNamedQuery("ApplicationPermissionEntity.findByPermissionName");
query.setParameter("permissionName", name);
List<ApplicationPermissionEntity> permissions = query.getResultList();
if ((permissions == null) || (permissions.isEmpty())) {
return null;
} else {
return permissions.get(0);
}
}
}

View File

@ -1,135 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package de.muehlencord.shared.account.business.account;
import de.muehlencord.shared.account.entity.ApplicationPermissionEntity;
import de.muehlencord.shared.account.entity.ApplicationRoleEntity;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.transaction.Transactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author Joern Muehlencord <joern at muehlencord.de>
*/
@Stateless
public class ApplicationRoleControl implements Serializable {
private static final long serialVersionUID = 5962478269550134748L;
private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationRoleControl.class);
@EJB
ApplicationPermissionControl applicationPermissionControl;
@PersistenceContext
EntityManager em;
public List<ApplicationRoleEntity> getAllRoles() {
Query query = em.createNamedQuery("ApplicationRoleEntity.findAll");
List<ApplicationRoleEntity> roles = query.getResultList();
if (roles == null) {
return new ArrayList<>();
} else {
return roles;
}
}
@Transactional
public void createOrUpdate(String name, String description) {
ApplicationRoleEntity role = findByName(name);
if (role == null) {
role = new ApplicationRoleEntity(name, description);
em.persist(role);
} else {
role.setRoleDescription(description);
em.merge(role);
}
}
public void delete(ApplicationRoleEntity permission) throws AccountException {
ApplicationRoleEntity existingPermission = attach(permission);
em.remove(existingPermission);
}
public ApplicationRoleEntity attach(ApplicationRoleEntity permission) throws AccountException {
try {
return em.merge(permission);
} catch (OptimisticLockException ex) {
throw new AccountException("Entity updated / deleted, please reload", true);
}
}
private ApplicationRoleEntity findByName(String name) {
Query query = em.createNamedQuery("ApplicationRoleEntity.findByRoleName");
query.setParameter("roleName", name);
List<ApplicationRoleEntity> permissions = query.getResultList();
if ((permissions == null) || (permissions.isEmpty())) {
return null;
} else {
return permissions.get(0);
}
}
public List<ApplicationPermissionEntity> getRolePermissions(ApplicationRoleEntity role) throws AccountException {
ApplicationRoleEntity existingRole = em.find(ApplicationRoleEntity.class, role.getId());
List<ApplicationPermissionEntity> permissions = existingRole.getApplicationPermissionList();
permissions.size(); // force list to load
return permissions;
}
public List<ApplicationPermissionEntity> getNotAssignedApplicationPermissions(ApplicationRoleEntity role) {
try {
List<ApplicationPermissionEntity> rolePermissions = getRolePermissions(role);
List<ApplicationPermissionEntity> allPermssions = applicationPermissionControl.getApplicationPermissions();
List<ApplicationPermissionEntity> missingPermissions = new ArrayList<>();
allPermssions.stream().filter((perm) -> (!rolePermissions.contains(perm))).forEachOrdered((perm) -> {
missingPermissions.add(perm);
});
return missingPermissions;
} catch (AccountException ex) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(ex.toString(), ex);
} else {
LOGGER.debug(ex.toString());
}
return null;
}
}
@Transactional
public void addPermission(ApplicationRoleEntity role, ApplicationPermissionEntity permission) throws AccountException {
ApplicationRoleEntity existingRole = attach(role);
if (existingRole.getApplicationPermissionList() == null) {
existingRole.setApplicationPermissionList(new ArrayList<>());
}
existingRole.getApplicationPermissionList().add(permission);
em.merge(role);
}
@Transactional
public void removePermission(ApplicationRoleEntity role, ApplicationPermissionEntity permission) throws AccountException {
ApplicationRoleEntity existingRole = attach(role);
if ((existingRole.getApplicationPermissionList() != null) && (existingRole.getApplicationPermissionList().contains(permission))) {
existingRole.getApplicationPermissionList().remove(permission);
}
em.merge(role);
}
}

View File

@ -1,40 +0,0 @@
package de.muehlencord.shared.account.business.mail;
import de.muehlencord.shared.account.entity.AccountEntity;
import java.util.HashMap;
import java.util.Map;
/**
*
* @author jomu
*/
public class MailDatamodel {
private final AccountEntity account;
private final Map<String,Object> parameter;
public MailDatamodel() {
this.account = null;
this.parameter = new HashMap<>();
}
public MailDatamodel(AccountEntity account) {
this.parameter = new HashMap<>();
this.account = account;
}
public void addParameter(String name, Object value) {
this.parameter.put(name, value);
}
/* **** getter / setter **** */
public AccountEntity getAccount() {
return account;
}
public Map<String, Object> getParameter() {
return parameter;
}
}

View File

@ -1,36 +0,0 @@
package de.muehlencord.shared.account.business.mail;
/**
*
* @author Raimund
*/
public class MailException extends Exception {
/**
* Creates a new instance of <code>MailException</code> without detail
* message.
*/
public MailException() {
}
/**
* Constructs an instance of <code>MailException</code> with the specified
* detail message.
*
* @param msg the detail message.
*/
public MailException(String msg) {
super(msg);
}
/**
* Constructs an instance of <code>MailException</code> with the specified
* detail message.
*
* @param msg the detail message.
* @param th the root cause
*/
public MailException(String msg, Throwable th) {
super(msg, th);
}
}

View File

@ -1,192 +0,0 @@
package de.muehlencord.shared.account.business.mail;
import de.muehlencord.shared.account.configuration.AccountConfigurationKey;
import de.muehlencord.shared.account.entity.AccountEntity;
import java.util.Date;
import java.util.UUID;
import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import de.muehlencord.shared.account.configuration.AccountConfigurationValue;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author joern.muehlencord
*/
@Stateless
public class MailService implements Serializable {
private static final long serialVersionUID = -1937218474908356747L;
private static final Logger LOGGER = LoggerFactory.getLogger(MailService.class);
@EJB
private MailTemplateService mailTemplateService;
@Inject
@AccountConfigurationValue(key = AccountConfigurationKey.BaseUrl)
private String baseUrl;
@Inject
@AccountConfigurationValue(key = AccountConfigurationKey.PasswordResetUrl)
private String passwordResetUrl;
// TODO make this configurable by injection from the application it uses it, fall back to defaul mail setup if not available
@Resource(lookup = "java:jboss/mail/ssgMail")
private Session mailSession;
public String sendTestEmail(String recipient) throws MailException {
return sendMail(recipient, "Test email", "This is a test email");
}
public String sendTestHtmlEmail(String recipient) throws MailException {
Date now = new Date();
AccountEntity account = new AccountEntity(UUID.randomUUID(), "joern.muehlencord", "joern@muehlencord.de", "Jörn", "Mühlencord", "secret", 0, "NEW", false, now, "admin", now, "admin");
MailDatamodel dataModel = new MailDatamodel(account);
dataModel.addParameter("url", "http://url.de");
dataModel.addParameter("resetUrl", "http://reseturl.de");
return sendHTMLMail(recipient, "Test HTML Email", dataModel, "password_reset_html");
}
public String sendMail(String recipient, String subject, String body) throws MailException {
try {
MimeMessage message = new MimeMessage(mailSession);
message.setSubject(subject, "UTF-8");
message.setFrom();
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipient, false));
message.setText(body, "UTF-8");
return transportMail(message);
} catch (MessagingException ex) {
throw new MailException("Error while sending email.", ex);
}
}
public String sendHTMLMail(String recipient, String subject, MailDatamodel dataModel, String templateName) throws MailException {
return sendHTMLMail(recipient, subject, dataModel, templateName, null);
}
public String sendHTMLMail(String recipient, String subject, MailDatamodel dataModel, String htmlTemplateName, String plainTemplateName) throws MailException {
return sendHTMLMail(recipient, null, null, subject, dataModel, htmlTemplateName, plainTemplateName, "UTF-8", new ArrayList<>());
}
public String sendHTMLMail(String recipient, String ccRecipient, String bccRecipient, String subject, MailDatamodel dataModel, String htmlTemplateName, String plainTemplateName) throws MailException {
return sendHTMLMail(recipient, ccRecipient, bccRecipient, subject, dataModel, htmlTemplateName, plainTemplateName, "UTF-8", new ArrayList<>());
}
public String sendHTMLMail(String recipient, String ccRecipient, String bccRecipient, String subject,
MailDatamodel dataModel, String htmlTemplateName, String plainTemplateName, String encoding, List<File> attachments) throws MailException {
try {
MimeMessage message = new MimeMessage(mailSession);
message.setFrom(); // use default from
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipient, false));
if (ccRecipient != null) {
message.setRecipients(Message.RecipientType.CC, InternetAddress.parse(ccRecipient, false));
}
if (bccRecipient != null) {
message.setRecipients(Message.RecipientType.BCC, InternetAddress.parse(bccRecipient, false));
}
message.setSubject(subject, encoding);
Multipart contentMultiPart = new MimeMultipart("alternative");
String htmlBody = mailTemplateService.getStringFromTemplate(htmlTemplateName, dataModel);
MimeBodyPart htmlBodyPart = new MimeBodyPart();
htmlBodyPart.setContent(htmlBody, "text/html; charset=UTF-8");
contentMultiPart.addBodyPart(htmlBodyPart);
if (plainTemplateName != null) {
String plainBody = mailTemplateService.getStringFromTemplate(plainTemplateName, dataModel);
MimeBodyPart plainBodyPart = new MimeBodyPart();
plainBodyPart.setText(plainBody, "UTF-8");
contentMultiPart.addBodyPart(plainBodyPart);
}
if ((attachments == null) || (attachments.isEmpty())) {
message.setContent(contentMultiPart);
} else {
MimeBodyPart contentBodyPart = new MimeBodyPart();
contentBodyPart.setContent(contentMultiPart);
MimeMultipart messageMultipart = new MimeMultipart("related");
messageMultipart.addBodyPart(contentBodyPart);
for (File attachment : attachments) {
try {
MimeBodyPart attachmentBodyPart = new MimeBodyPart();
attachmentBodyPart.attachFile(attachment);
String contentType = Files.probeContentType(attachment.toPath());
if (contentType != null) {
attachmentBodyPart.setHeader("Content-Type", contentType);
}
messageMultipart.addBodyPart(attachmentBodyPart);
} catch (IOException ex) {
throw new MailException("Cannot attach " + attachment.toString() + " to email. Reason: " + ex.toString(), ex);
}
}
message.setContent (messageMultipart);
}
return transportMail(message);
} catch (MessagingException | MailTemplateException ex) {
throw new MailException("Error while sending email.", ex);
}
}
public String sendPasswortResetStartEmail(AccountEntity account, String token) throws MailException {
MailDatamodel model = new MailDatamodel(account);
/* old aproach via FacesContext - add this back as fallback if injection point if not configured
try {
// String absoluteWebPath = FacesContext.getCurrentInstance().getExternalContext().getApplicationContextPath();
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
String resetPage = "/login.xhtml?token=" + token;
URL baseUrl;
// TODO move out of this class, this is not mandatorily connected to a form
baseUrl = new URL(externalContext.getRequestScheme(),
externalContext.getRequestServerName(),
externalContext.getRequestServerPort(),
externalContext.getRequestContextPath());
model.addParameter("url", baseUrl.toString());
model.addParameter("resetUrl", baseUrl.toString() + resetPage);
} catch (MalformedURLException ex) {
throw new MailException("Error while sending email.", ex);
}
String baseUrl = configService.getConfigValue(configKey);
String resetUrlWithToken = baseUrl + "/login.xhtml?token=" + token;
*/
String resetUrlWithToken = passwordResetUrl + "?token=" + token;
model.addParameter("url", baseUrl);
model.addParameter("resetUrl", resetUrlWithToken);
return sendHTMLMail(account.getEmailaddress(), "Reset your password", model, "password_reset_html");
}
private String transportMail(Message message) throws MessagingException {
message.setSentDate(new Date());
Transport.send(message);
String messageId = message.getHeader("Message-ID")[0];
LOGGER.info("Mail sent to {}, messageid = {}", message.getAllRecipients()[0].toString(), messageId);
return messageId;
}
}

View File

@ -1,37 +0,0 @@
package de.muehlencord.shared.account.business.mail;
/**
*
* @author jomu
*/
public class MailTemplateException extends Exception {
/**
* Creates a new instance of <code>MailTemplateException</code> without
* detail message.
*/
public MailTemplateException() {
}
/**
* Constructs an instance of <code>MailTemplateException</code> with the
* specified detail message.
*
* @param msg the detail message.
*/
public MailTemplateException(String msg) {
super(msg);
}
/**
* Constructs an instance of <code>MailTemplateException</code> with the
* specified detail message.
*
* @param msg the detail message.
* @param th the root cause
*/
public MailTemplateException(String msg, Throwable th) {
super(msg, th);
}
}

View File

@ -1,67 +0,0 @@
package de.muehlencord.shared.account.business.mail;
import de.muehlencord.shared.account.entity.MailTemplateEntity;
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler;
import java.io.Serializable;
import java.io.StringWriter;
import java.io.Writer;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author jomu
*/
@Stateless
public class MailTemplateService implements Serializable {
private static final long serialVersionUID = -136113381443058697L;
private static final Logger LOGGER = LoggerFactory.getLogger(MailTemplateService.class.getName());
@Inject
EntityManager em;
public String getStringFromTemplate(String templateName, MailDatamodel dataModel) throws MailTemplateException {
try {
Query query = em.createNamedQuery("MailTemplateEntity.findByTemplateName");
query.setParameter("templateName", templateName);
MailTemplateEntity templateEntity = (MailTemplateEntity) query.getSingleResult();
if (templateEntity == null) {
LOGGER.error("Tempate with name " + templateName + " not found");
return null;
}
Configuration configuration = new Configuration(Configuration.VERSION_2_3_23);
configuration.setDefaultEncoding("UTF-8"); // FIXME make encoding configurable
configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
StringTemplateLoader stringLoader = new StringTemplateLoader();
stringLoader.putTemplate(templateEntity.getTemplateName(), templateEntity.getTemplateValue());
configuration.setTemplateLoader(stringLoader);
Template template = configuration.getTemplate(templateEntity.getTemplateName());
Writer out = new StringWriter();
template.process(dataModel, out);
String templateString = out.toString();
return templateString;
} catch (Exception ex) {
String hint = "Error while processing template with name " + templateName + ".";
LOGGER.error(hint + " " + ex.toString());
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(hint, ex);
}
throw new MailTemplateException(hint, ex);
}
}
}

View File

@ -1,19 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package de.muehlencord.shared.account.configuration;
/**
*
* @author Joern Muehlencord <joern at muehlencord.de>
*/
public enum AccountConfigurationKey {
// the base path of the application
BaseUrl,
// the full pass to the reset password page
PasswordResetUrl,
// injection handler
Producer;
}

View File

@ -1,30 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package de.muehlencord.shared.account.configuration;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.enterprise.util.Nonbinding;
import javax.inject.Qualifier;
/**
*
* @author Joern Muehlencord <joern at muehlencord.de>
*/
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface AccountConfigurationValue {
@Nonbinding
AccountConfigurationKey key();
}

View File

@ -1,16 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package de.muehlencord.shared.account.entity;
/**
*
* @author Joern Muehlencord <joern at muehlencord.de>
*/
public interface Account {
String getUsername();
}

View File

@ -1,348 +0,0 @@
package de.muehlencord.shared.account.entity;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Type;
/**
*
* @author joern.muehlencord
*/
@Entity
@Table(name = "account")
@XmlRootElement
@NamedQueries({
@NamedQuery(name = "AccountEntity.findAll", query = "SELECT a FROM AccountEntity a"),
@NamedQuery(name = "AccountEntity.findByUsername", query = "SELECT a FROM AccountEntity a WHERE a.username = :username"),
@NamedQuery(name = "AccountEntity.findByEmailaddress", query = "SELECT a FROM AccountEntity a WHERE a.emailaddress = :emailaddress"),
@NamedQuery(name = "AccountEntity.findByFirstname", query = "SELECT a FROM AccountEntity a WHERE a.firstname = :firstname"),
@NamedQuery(name = "AccountEntity.findByLastname", query = "SELECT a FROM AccountEntity a WHERE a.lastname = :lastname"),
@NamedQuery(name = "AccountEntity.findByAccountPassword", query = "SELECT a FROM AccountEntity a WHERE a.accountPassword = :accountPassword"),
@NamedQuery(name = "AccountEntity.findByLastLogin", query = "SELECT a FROM AccountEntity a WHERE a.lastLogin = :lastLogin"),
@NamedQuery(name = "AccountEntity.findByLastFailedLogin", query = "SELECT a FROM AccountEntity a WHERE a.lastFailedLogin = :lastFailedLogin"),
@NamedQuery(name = "AccountEntity.findByFailureCount", query = "SELECT a FROM AccountEntity a WHERE a.failureCount = :failureCount"),
@NamedQuery(name = "AccountEntity.findByStatus", query = "SELECT a FROM AccountEntity a WHERE a.status = :status"),
@NamedQuery(name = "AccountEntity.findByPasswordResetOngoing", query = "SELECT a FROM AccountEntity a WHERE a.passwordResetOngoing = :passwordResetOngoing"),
@NamedQuery(name = "AccountEntity.findByPasswordResetValidTo", query = "SELECT a FROM AccountEntity a WHERE a.passwordResetValidTo = :passwordResetValidTo"),
@NamedQuery(name = "AccountEntity.findByPasswordResetHash", query = "SELECT a FROM AccountEntity a WHERE a.passwordResetHash = :passwordResetHash"),
@NamedQuery(name = "AccountEntity.findByCreatedOn", query = "SELECT a FROM AccountEntity a WHERE a.createdOn = :createdOn"),
@NamedQuery(name = "AccountEntity.findByCreatedBy", query = "SELECT a FROM AccountEntity a WHERE a.createdBy = :createdBy"),
@NamedQuery(name = "AccountEntity.findByLastUpdatedOn", query = "SELECT a FROM AccountEntity a WHERE a.lastUpdatedOn = :lastUpdatedOn"),
@NamedQuery(name = "AccountEntity.findByLastUpdatedBy", query = "SELECT a FROM AccountEntity a WHERE a.lastUpdatedBy = :lastUpdatedBy")})
public class AccountEntity implements Serializable, Account {
private static final long serialVersionUID = 6216991757526150935L;
@Id
@Basic(optional = false)
@NotNull
@Column(name = "id")
@GeneratedValue(generator = "uuid2")
@GenericGenerator(name = "uuid2", strategy = "uuid2")
@Type(type = "pg-uuid")
private UUID id;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 32)
@Column(name = "username")
private String username;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 200)
@Column(name = "emailaddress")
private String emailaddress;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 100)
@Column(name = "firstname")
private String firstname;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 100)
@Column(name = "lastname")
private String lastname;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 200)
@Column(name = "account_password", columnDefinition = "bpchar(200)")
private String accountPassword;
@Column(name = "last_login")
@Temporal(TemporalType.TIMESTAMP)
private Date lastLogin;
@Column(name = "last_failed_login")
@Temporal(TemporalType.TIMESTAMP)
private Date lastFailedLogin;
@Basic(optional = false)
@NotNull
@Column(name = "failure_count")
private int failureCount;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 10)
@Column(name = "status")
private String status;
@Basic(optional = false)
@NotNull
@Column(name = "password_reset_ongoing")
private boolean passwordResetOngoing;
@Column(name = "password_reset_valid_to")
@Temporal(TemporalType.TIMESTAMP)
private Date passwordResetValidTo;
@Size(max = 200)
@Column(name = "password_reset_hash", columnDefinition = "bpchar(200)")
private String passwordResetHash;
@Basic(optional = false)
@NotNull
@Column(name = "created_on")
@Temporal(TemporalType.TIMESTAMP)
private Date createdOn;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 32)
@Column(name = "created_by")
private String createdBy;
@Basic(optional = false)
@NotNull
@Column(name = "last_updated_on")
@Temporal(TemporalType.TIMESTAMP)
private Date lastUpdatedOn;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 32)
@Column(name = "last_updated_by")
private String lastUpdatedBy;
@JoinTable(name = "account_role", joinColumns = {
@JoinColumn(name = "account", referencedColumnName = "id")}, inverseJoinColumns = {
@JoinColumn(name = "account_role", referencedColumnName = "id")})
@ManyToMany(fetch = FetchType.LAZY)
private List<ApplicationRoleEntity> applicationRoleList;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "accountId", fetch = FetchType.LAZY)
private List<AccountHistoryEntity> accountHistoryList;
public AccountEntity() {
}
public AccountEntity(UUID id) {
this.id = id;
}
public AccountEntity(UUID id, String username, String emailaddress, String firstname, String lastname, String accountPassword, int failureCount, String status, boolean passwordResetOngoing, Date createdOn, String createdBy, Date lastUpdatedOn, String lastUpdatedBy) {
this.id = id;
this.username = username;
this.emailaddress = emailaddress;
this.firstname = firstname;
this.lastname = lastname;
this.accountPassword = accountPassword;
this.failureCount = failureCount;
this.status = status;
this.passwordResetOngoing = passwordResetOngoing;
this.createdOn = createdOn;
this.createdBy = createdBy;
this.lastUpdatedOn = lastUpdatedOn;
this.lastUpdatedBy = lastUpdatedBy;
}
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
@Override
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmailaddress() {
return emailaddress;
}
public void setEmailaddress(String emailaddress) {
this.emailaddress = emailaddress;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getAccountPassword() {
return accountPassword;
}
public void setAccountPassword(String accountPassword) {
this.accountPassword = accountPassword;
}
public Date getLastLogin() {
return lastLogin;
}
public void setLastLogin(Date lastLogin) {
this.lastLogin = lastLogin;
}
public Date getLastFailedLogin() {
return lastFailedLogin;
}
public void setLastFailedLogin(Date lastFailedLogin) {
this.lastFailedLogin = lastFailedLogin;
}
public int getFailureCount() {
return failureCount;
}
public void setFailureCount(int failureCount) {
this.failureCount = failureCount;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public boolean getPasswordResetOngoing() {
return passwordResetOngoing;
}
public void setPasswordResetOngoing(boolean passwordResetOngoing) {
this.passwordResetOngoing = passwordResetOngoing;
}
public Date getPasswordResetValidTo() {
return passwordResetValidTo;
}
public void setPasswordResetValidTo(Date passwordResetValidTo) {
this.passwordResetValidTo = passwordResetValidTo;
}
public String getPasswordResetHash() {
return passwordResetHash;
}
public void setPasswordResetHash(String passwordResetHash) {
this.passwordResetHash = passwordResetHash;
}
public Date getCreatedOn() {
return createdOn;
}
public void setCreatedOn(Date createdOn) {
this.createdOn = createdOn;
}
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public Date getLastUpdatedOn() {
return lastUpdatedOn;
}
public void setLastUpdatedOn(Date lastUpdatedOn) {
this.lastUpdatedOn = lastUpdatedOn;
}
public String getLastUpdatedBy() {
return lastUpdatedBy;
}
public void setLastUpdatedBy(String lastUpdatedBy) {
this.lastUpdatedBy = lastUpdatedBy;
}
@XmlTransient
public List<ApplicationRoleEntity> getApplicationRoleList() {
return applicationRoleList;
}
public void setApplicationRoleList(List<ApplicationRoleEntity> applicationRoleList) {
this.applicationRoleList = applicationRoleList;
}
@XmlTransient
public List<AccountHistoryEntity> getAccountHistoryList() {
return accountHistoryList;
}
public void setAccountHistoryList(List<AccountHistoryEntity> accountHistoryList) {
this.accountHistoryList = accountHistoryList;
}
@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof AccountEntity)) {
return false;
}
AccountEntity other = (AccountEntity) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
@Override
public String toString() {
return "de.muehlencord.shared.account.entity.Account[ id=" + id + " ]";
}
}

View File

@ -1,172 +0,0 @@
package de.muehlencord.shared.account.entity;
import java.io.Serializable;
import java.util.Date;
import java.util.UUID;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Type;
/**
*
* @author joern.muehlencord
*/
@Entity
@Table(name = "account_history")
@XmlRootElement
@NamedQueries({
@NamedQuery(name = "AccountHistoryEntity.findAll", query = "SELECT a FROM AccountHistoryEntity a"),
@NamedQuery(name = "AccountHistoryEntity.findById", query = "SELECT a FROM AccountHistoryEntity a WHERE a.id = :id"),
@NamedQuery(name = "AccountHistoryEntity.findByMessage", query = "SELECT a FROM AccountHistoryEntity a WHERE a.message = :message"),
@NamedQuery(name = "AccountHistoryEntity.findByFailureCount", query = "SELECT a FROM AccountHistoryEntity a WHERE a.failureCount = :failureCount"),
@NamedQuery(name = "AccountHistoryEntity.findByStatus", query = "SELECT a FROM AccountHistoryEntity a WHERE a.status = :status"),
@NamedQuery(name = "AccountHistoryEntity.findByLastUpdatedOn", query = "SELECT a FROM AccountHistoryEntity a WHERE a.lastUpdatedOn = :lastUpdatedOn"),
@NamedQuery(name = "AccountHistoryEntity.findByLastUpdatedBy", query = "SELECT a FROM AccountHistoryEntity a WHERE a.lastUpdatedBy = :lastUpdatedBy")})
public class AccountHistoryEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Basic(optional = false)
@NotNull
@Column(name = "id")
@GeneratedValue(generator = "uuid2")
@GenericGenerator(name = "uuid2", strategy = "uuid2")
@Type(type = "pg-uuid")
private UUID id;
@Size(max = 200)
@Column(name = "message")
private String message;
@Basic(optional = false)
@NotNull
@Column(name = "failure_count")
private int failureCount;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 20)
@Column(name = "status")
private String status;
@Basic(optional = false)
@NotNull
@Column(name = "last_updated_on")
@Temporal(TemporalType.TIMESTAMP)
private Date lastUpdatedOn;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 32)
@Column(name = "last_updated_by")
private String lastUpdatedBy;
@JoinColumn(name = "account_id", referencedColumnName = "id")
@ManyToOne(optional = false)
private AccountEntity accountId;
public AccountHistoryEntity() {
}
public AccountHistoryEntity(UUID id) {
this.id = id;
}
public AccountHistoryEntity(UUID id, int failureCount, String status, Date lastUpdatedOn, String lastUpdatedBy) {
this.id = id;
this.failureCount = failureCount;
this.status = status;
this.lastUpdatedOn = lastUpdatedOn;
this.lastUpdatedBy = lastUpdatedBy;
}
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public int getFailureCount() {
return failureCount;
}
public void setFailureCount(int failureCount) {
this.failureCount = failureCount;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public Date getLastUpdatedOn() {
return lastUpdatedOn;
}
public void setLastUpdatedOn(Date lastUpdatedOn) {
this.lastUpdatedOn = lastUpdatedOn;
}
public String getLastUpdatedBy() {
return lastUpdatedBy;
}
public void setLastUpdatedBy(String lastUpdatedBy) {
this.lastUpdatedBy = lastUpdatedBy;
}
public AccountEntity getAccountId() {
return accountId;
}
public void setAccountId(AccountEntity accountId) {
this.accountId = accountId;
}
@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof AccountHistoryEntity)) {
return false;
}
AccountHistoryEntity other = (AccountHistoryEntity) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
@Override
public String toString() {
return "de.muehlencord.shared.account.entity.AccountHistory[ id=" + id + " ]";
}
}

View File

@ -1,136 +0,0 @@
package de.muehlencord.shared.account.entity;
import java.io.Serializable;
import java.util.List;
import java.util.UUID;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Type;
/**
*
* @author joern.muehlencord
*/
@Entity
@Table(name = "application_permission")
@XmlRootElement
@NamedQueries({
@NamedQuery(name = "ApplicationPermissionEntity.findAll", query = "SELECT a FROM ApplicationPermissionEntity a order by a.permissionName"),
@NamedQuery(name = "ApplicationPermissionEntity.findNotAssigned", query = "SELECT a FROM ApplicationPermissionEntity a LEFT OUTER JOIN a.applicationRoleList r WHERE r NOT IN :permissions"),
@NamedQuery(name = "ApplicationPermissionEntity.findByPermissionName", query = "SELECT a FROM ApplicationPermissionEntity a WHERE a.permissionName = :permissionName"),
@NamedQuery(name = "ApplicationPermissionEntity.findByPermissionDescription", query = "SELECT a FROM ApplicationPermissionEntity a WHERE a.permissionDescription = :permissionDescription")})
public class ApplicationPermissionEntity implements Serializable {
private static final long serialVersionUID = -8985982754544829534L;
@Id
@Basic(optional = false)
@NotNull
@Column(name = "id")
@GeneratedValue(generator = "uuid2")
@GenericGenerator(name = "uuid2", strategy = "uuid2")
@Type(type = "pg-uuid")
private UUID id;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 80)
@Column(name = "permission_name")
private String permissionName;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 200)
@Column(name = "permission_description")
private String permissionDescription;
@ManyToMany(mappedBy = "applicationPermissionList")
private List<ApplicationRoleEntity> applicationRoleList;
public ApplicationPermissionEntity() {
}
public ApplicationPermissionEntity(UUID id) {
this.id = id;
}
public ApplicationPermissionEntity(String permissionName, String permissionDescription) {
this.id = null;
this.permissionName = permissionName;
this.permissionDescription = permissionDescription;
}
public ApplicationPermissionEntity(UUID id, String permissionName, String permissionDescription) {
this.id = id;
this.permissionName = permissionName;
this.permissionDescription = permissionDescription;
}
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public String getPermissionName() {
return permissionName;
}
public void setPermissionName(String permissionName) {
this.permissionName = permissionName;
}
public String getPermissionDescription() {
return permissionDescription;
}
public void setPermissionDescription(String permissionDescription) {
this.permissionDescription = permissionDescription;
}
@XmlTransient
public List<ApplicationRoleEntity> getApplicationRoleList() {
return applicationRoleList;
}
public void setApplicationRoleList(List<ApplicationRoleEntity> applicationRoleList) {
this.applicationRoleList = applicationRoleList;
}
@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof ApplicationPermissionEntity)) {
return false;
}
ApplicationPermissionEntity other = (ApplicationPermissionEntity) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
@Override
public String toString() {
return "de.muehlencord.shared.account.entity.ApplicationPermission[ id=" + id + " ]";
}
}

View File

@ -1,152 +0,0 @@
package de.muehlencord.shared.account.entity;
import java.io.Serializable;
import java.util.List;
import java.util.UUID;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Type;
/**
*
* @author joern.muehlencord
*/
@Entity
@Table(name = "application_role")
@XmlRootElement
@NamedQueries({
@NamedQuery(name = "ApplicationRoleEntity.findAll", query = "SELECT a FROM ApplicationRoleEntity a ORDER BY a.roleName")
, @NamedQuery(name = "ApplicationRoleEntity.findByRoleName", query = "SELECT a FROM ApplicationRoleEntity a WHERE a.roleName = :roleName")
, @NamedQuery(name = "ApplicationRoleEntity.findByRoleDescription", query = "SELECT a FROM ApplicationRoleEntity a WHERE a.roleDescription = :roleDescription")})
public class ApplicationRoleEntity implements Serializable {
private static final long serialVersionUID = -8324054525780893823L;
@Id
@Basic(optional = false)
@NotNull
@Column(name = "id")
@GeneratedValue(generator = "uuid2")
@GenericGenerator(name = "uuid2", strategy = "uuid2")
@Type(type = "pg-uuid")
private UUID id;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 80)
@Column(name = "role_name")
private String roleName;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 200)
@Column(name = "role_description")
private String roleDescription;
@ManyToMany(mappedBy = "applicationRoleList")
private List<AccountEntity> accountList;
@JoinTable(name = "role_permission", joinColumns = {
@JoinColumn(name = "application_role", referencedColumnName = "id")}, inverseJoinColumns = {
@JoinColumn(name = "role_permission", referencedColumnName = "id")})
@ManyToMany
private List<ApplicationPermissionEntity> applicationPermissionList;
public ApplicationRoleEntity() {
}
public ApplicationRoleEntity(UUID id) {
this.id = id;
}
public ApplicationRoleEntity(String roleName, String roleDescription) {
this.id = null;
this.roleName = roleName;
this.roleDescription = roleDescription;
}
public ApplicationRoleEntity(UUID id, String roleName, String roleDescription) {
this.id = id;
this.roleName = roleName;
this.roleDescription = roleDescription;
}
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getRoleDescription() {
return roleDescription;
}
public void setRoleDescription(String roleDescription) {
this.roleDescription = roleDescription;
}
@XmlTransient
public List<AccountEntity> getAccountList() {
return accountList;
}
public void setAccountList(List<AccountEntity> accountList) {
this.accountList = accountList;
}
@XmlTransient
public List<ApplicationPermissionEntity> getApplicationPermissionList() {
return applicationPermissionList;
}
public void setApplicationPermissionList(List<ApplicationPermissionEntity> applicationPermissionList) {
this.applicationPermissionList = applicationPermissionList;
}
@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof ApplicationRoleEntity)) {
return false;
}
ApplicationRoleEntity other = (ApplicationRoleEntity) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
@Override
public String toString() {
return "de.muehlencord.shared.account.entity.ApplicationRole[ id=" + id + " ]";
}
}

View File

@ -1,111 +0,0 @@
package de.muehlencord.shared.account.entity;
import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Cacheable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.QueryHint;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
/**
*
* @author joern.muehlencord
*/
@Entity
@Table(name = "config")
@XmlRootElement
@Cacheable(true)
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL, region = "Configuration")
@NamedQueries({
@NamedQuery(name = "ConfigEntity.findAll", query = "SELECT c FROM ConfigEntity c ORDER BY c.configKey",
hints = {
@QueryHint(name = "org.hibernate.cacheable", value = "true")
, @QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")})
, @NamedQuery(name = "ConfigEntity.findByConfigKey", query = "SELECT c FROM ConfigEntity c WHERE c.configKey = :configKey",
hints = {
@QueryHint(name = "org.hibernate.cacheable", value = "true")
, @QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")})
, @NamedQuery(name = "ConfigEntity.findByConfigValue", query = "SELECT c FROM ConfigEntity c WHERE c.configValue = :configValue",
hints = {
@QueryHint(name = "org.hibernate.cacheable", value = "true")
, @QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")})
})
public class ConfigEntity implements Serializable {
private static final long serialVersionUID = -2013982316933782223L;
@Id
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 100)
@Column(name = "config_key")
private String configKey;
@Size(max = 200)
@Column(name = "config_value")
private String configValue;
public ConfigEntity() {
}
public ConfigEntity(String configKey) {
this.configKey = configKey;
}
public ConfigEntity(String configKey, String configValue) {
this.configKey = configKey;
this.configValue = configValue;
}
public String getConfigKey() {
return configKey;
}
public void setConfigKey(String configKey) {
this.configKey = configKey;
}
public String getConfigValue() {
return configValue;
}
public void setConfigValue(String configValue) {
this.configValue = configValue;
}
@Override
public int hashCode() {
int hash = 0;
hash += (configKey != null ? configKey.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof ConfigEntity)) {
return false;
}
ConfigEntity other = (ConfigEntity) object;
if ((this.configKey == null && other.configKey != null) || (this.configKey != null && !this.configKey.equals(other.configKey))) {
return false;
}
return true;
}
@Override
public String toString() {
return "de.muehlencord.shared.account.entity.Config[ configKey=" + configKey + " ]";
}
}

View File

@ -1,95 +0,0 @@
package de.muehlencord.shared.account.entity;
import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;
/**
*
* @author joern.muehlencord
*/
@Entity
@Table(name = "mail_template")
@XmlRootElement
@NamedQueries({
@NamedQuery(name = "MailTemplateEntity.findAll", query = "SELECT m FROM MailTemplateEntity m"),
@NamedQuery(name = "MailTemplateEntity.findByTemplateName", query = "SELECT m FROM MailTemplateEntity m WHERE m.templateName = :templateName"),
@NamedQuery(name = "MailTemplateEntity.findByTemplateValue", query = "SELECT m FROM MailTemplateEntity m WHERE m.templateValue = :templateValue")})
public class MailTemplateEntity implements Serializable {
private static final long serialVersionUID = 4527399247302581555L;
@Id
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 40)
@Column(name = "template_name")
private String templateName;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 2147483647)
@Column(name = "template_value")
private String templateValue;
public MailTemplateEntity() {
}
public MailTemplateEntity(String templateName) {
this.templateName = templateName;
}
public MailTemplateEntity(String templateName, String templateValue) {
this.templateName = templateName;
this.templateValue = templateValue;
}
public String getTemplateName() {
return templateName;
}
public void setTemplateName(String templateName) {
this.templateName = templateName;
}
public String getTemplateValue() {
return templateValue;
}
public void setTemplateValue(String templateValue) {
this.templateValue = templateValue;
}
@Override
public int hashCode() {
int hash = 0;
hash += (templateName != null ? templateName.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof MailTemplateEntity)) {
return false;
}
MailTemplateEntity other = (MailTemplateEntity) object;
if ((this.templateName == null && other.templateName != null) || (this.templateName != null && !this.templateName.equals(other.templateName))) {
return false;
}
return true;
}
@Override
public String toString() {
return "de.muehlencord.shared.account.entity.MailTemplate[ templateName=" + templateName + " ]";
}
}

View File

@ -1,150 +0,0 @@
package de.muehlencord.shared.account.ui;
import de.muehlencord.shared.account.business.account.AccountControl;
import de.muehlencord.shared.account.entity.AccountEntity;
import de.muehlencord.shared.jeeutil.FacesUtil;
import java.io.IOException;
import java.io.Serializable;
import javax.ejb.EJB;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.view.ViewScoped;
import javax.inject.Named;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author joern.muehlencord
*/
@Named(value = "loginView")
@ViewScoped
public class LoginView implements Serializable {
private static final long serialVersionUID = -1164860380769648432L;
@EJB
private AccountControl accountService;
private String username = null;
private String password = null;
private boolean rememberMe = false;
private String resetPasswordToken = null;
private static final Logger LOGGER = LoggerFactory.getLogger(LoginView.class.getName());
public void authenticate() {
// Example using most common scenario of username/password pair:
UsernamePasswordToken token = new UsernamePasswordToken(getUsername(), getPassword());
// "Remember Me" built-in:
token.setRememberMe(rememberMe);
Subject currentUser = SecurityUtils.getSubject();
LOGGER.info("Submitting login with username of " + username);
try {
currentUser.login(token);
// user logged in, update account entity
AccountEntity account = accountService.getAccountEntity(username, true);
accountService.updateLogin(account);
// redirect to home
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
String fallbackUrl = "/web/index.xhtml"; // TODO make configurable
// ec.redirect(url);
WebUtils.redirectToSavedRequest((ServletRequest) ec.getRequest(), (ServletResponse) ec.getResponse(), fallbackUrl);
} catch (IOException | AuthenticationException ex) {
// Could catch a subclass of AuthenticationException if you like
String hint = "Error while authenticating user " + username;
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(hint, ex);
} else {
LOGGER.error(hint + " Reason: " + ex.toString());
}
FacesUtil.addErrorMessage("Login failed");
AccountEntity account = accountService.getAccountEntity(username, false);
if (account != null) {
accountService.addLoginError(account);
}
} finally {
token.clear();
}
}
public void logout() {
Subject currentUser = SecurityUtils.getSubject();
try {
currentUser.logout();
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
String url = ec.getRequestContextPath() + "/login.xhtml";
ec.redirect(url);
} catch (Exception e) {
LOGGER.warn(e.toString());
}
}
public String executePasswordReset() {
boolean passwordResetted = accountService.resetPassword(username, password, resetPasswordToken);
if (passwordResetted) {
// TODO add email notification on updated user account
FacesUtil.addInfoMessage("Password resetted");
return login();
} else {
// TODO add email notificaton on failed password reset
FacesUtil.addErrorMessage("Password reset failed");
return login();
}
}
/* **** naviation rules **** */
public String login() {
return "/login.xhtml"; // TODO make configurable
}
/* *** getter / setter */
public String getUsername() {
return username;
}
public void setUsername(String un) {
this.username = un;
}
public String getPassword() {
return password;
}
public void setPassword(String pw) {
this.password = pw;
}
public boolean isRememberMe() {
return rememberMe;
}
public void setRememberMe(boolean rememberMe) {
this.rememberMe = rememberMe;
}
public String getResetPasswordToken() {
return resetPasswordToken;
}
public void setResetPasswordToken(String resetPasswordToken) {
this.resetPasswordToken = resetPasswordToken;
}
}

View File

@ -1,48 +0,0 @@
package de.muehlencord.shared.account.ui;
import de.muehlencord.shared.account.business.account.AccountControl;
import de.muehlencord.shared.jeeutil.FacesUtil;
import java.io.Serializable;
import javax.ejb.EJB;
import javax.faces.view.ViewScoped;
import javax.inject.Named;
/**
*
* @author joern@muehlencord.de
*/
@Named (value = "lostPasswordView")
@ViewScoped
public class LostPasswordView implements Serializable {
private static final long serialVersionUID = -1793445795465830069L;
@EJB
private AccountControl accountService;
private String userName;
private boolean passwordResetStarted = false;
public String initPasswordReset() {
if (accountService.initPasswordReset(userName)) {
passwordResetStarted = true;
FacesUtil.addInfoMessage("Password reset started, please check your email account");
} else {
FacesUtil.addErrorMessage("Error while resetting password. Please contact your administrator");
}
return "/login.xhtml"; // TODO make configurable, get from LoginView?
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public boolean getPasswordResetStarted() {
return passwordResetStarted;
}
}

View File

@ -1,33 +0,0 @@
package de.muehlencord.shared.account.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.shiro.authc.credential.DefaultPasswordService;
import org.apache.shiro.crypto.hash.DefaultHashService;
import org.apache.shiro.crypto.hash.Sha512Hash;
/**
*
* @author jomu
*/
public class SecurityUtil {
private final static Logger LOGGER = LoggerFactory.getLogger(SecurityUtil.class.getName());
public static String createPassword(String clearTextPassword) {
// TODO read values from shiro.ini
DefaultHashService hashService = new DefaultHashService();
hashService.setHashIterations(500000); //
hashService.setHashAlgorithmName(Sha512Hash.ALGORITHM_NAME);
hashService.setGeneratePublicSalt(true);
DefaultPasswordService passwordService = new DefaultPasswordService();
passwordService.setHashService(hashService);
// try to encrypt password
String encryptedPassword = passwordService.encryptPassword(clearTextPassword);
LOGGER.trace(encryptedPassword);
return encryptedPassword;
}
}

View File

@ -1,101 +0,0 @@
package de.muehlencord.shared.account.util;
import java.util.HashSet;
import java.util.Set;
import javax.naming.NamingException;
import javax.naming.ldap.LdapContext;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.activedirectory.ActiveDirectoryRealm;
import org.apache.shiro.realm.ldap.LdapContextFactory;
import org.apache.shiro.realm.ldap.LdapUtils;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author joern.muehlencord
*/
public class UserNameActiveDirectoryRealm extends ActiveDirectoryRealm {
private static final Logger LOGGER = LoggerFactory.getLogger(UserNameActiveDirectoryRealm.class);
private boolean permissionsLookupEnabled = true;
@Override
protected AuthenticationInfo queryForAuthenticationInfo(AuthenticationToken token, LdapContextFactory ldapContextFactory) throws NamingException {
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
LdapContext ctx = null;
String userName = upToken.getUsername();
try {
if (principalSuffix != null) {
if (!userName.contains(principalSuffix)) {
userName += principalSuffix;
}
}
// Binds using the username and password provided by the user.
LOGGER.debug("start creating context");
ctx = ldapContextFactory.getLdapContext(userName, upToken.getCredentials());
LOGGER.debug("context created");
} finally {
LdapUtils.closeContext(ctx);
}
LOGGER.debug("building authentication info");
AuthenticationInfo authInfo = buildAuthenticationInfo(userName, upToken.getPassword());
LOGGER.debug("authentifaction info created");
return authInfo;
}
/**
* Builds an {@link org.apache.shiro.authz.AuthorizationInfo} object by
* querying the active directory LDAP context for the groups that a user is
* a member of. The groups are then translated to role names by using the
* configured {@link #groupRolesMap}.
* <p/>
* This implementation expects the <tt>principal</tt> argument to be a
* String username.
* <p/>
* Subclasses can override this method to determine authorization data
* (roles, permissions, etc) in a more complex way. Note that this default
* implementation does not support permissions, only roles.
*
* @param principals the principal of the Subject whose account is being
* retrieved.
* @param ldapContextFactory the factory used to create LDAP connections.
* @return the AuthorizationInfo for the given Subject principal.
* @throws NamingException if an error occurs when searching the LDAP
* server.
*/
@Override
protected AuthorizationInfo queryForAuthorizationInfo(PrincipalCollection principals, LdapContextFactory ldapContextFactory) throws NamingException {
Set<String> roleNames;
if (this.permissionsLookupEnabled) {
String username = (String) getAvailablePrincipal(principals);
// Perform context search
LdapContext ldapContext = ldapContextFactory.getSystemLdapContext();
try {
roleNames = getRoleNamesForUser(username, ldapContext);
} finally {
LdapUtils.closeContext(ldapContext);
}
} else {
roleNames = new HashSet<>();
}
return buildAuthorizationInfo(roleNames);
}
public boolean isPermissionsLookupEnabled() {
return permissionsLookupEnabled;
}
public void setPermissionsLookupEnabled(boolean permissionsLookupEnabled) {
this.permissionsLookupEnabled = permissionsLookupEnabled;
}
}

View File

@ -1,2 +0,0 @@
Manifest-Version: 1.0

View File

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<jboss/>

View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="de.muehlencord.shared_shared-account_ejb_1.0-SNAPSHOTPU" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:postgresql://localhost:5432/radius"/>
<property name="javax.persistence.jdbc.user" value="jomu"/>
<property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver"/>
<property name="javax.persistence.jdbc.password" value="jomu"/>
</properties>
</persistence-unit>
</persistence>

View File

@ -1,44 +0,0 @@
package de.muehlencord.shared.account.business;
import de.muehlencord.shared.account.entity.ConfigEntity;
import javax.persistence.EntityManager;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import static org.mockito.Mockito.when;
import org.mockito.runners.MockitoJUnitRunner;
/**
*
* @author joern.muehlencord
*/
@RunWith(MockitoJUnitRunner.class)
public class ConfigServiceTest {
@InjectMocks
private ConfigService configService;
@Mock
private EntityManager entityManagerMock;
@Before
public void init() {
ConfigEntity configEntity = new ConfigEntity ("account.maxFailedLogins");
configEntity.setConfigValue("7");
when (entityManagerMock.find(ConfigEntity.class, "account.maxFailedLogins")).thenReturn (configEntity);
}
@Test
public void testGetMaxFailedLogins() {
configService.init();
assertEquals ("maxFailedLogins", 7, configService.getMaxFailedLogins());
}
}

View File

@ -1,53 +0,0 @@
package de.muehlencord.shared.account.util;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.subject.Subject;
import org.junit.Test;
import org.apache.shiro.mgt.SecurityManager;
import static org.junit.Assume.assumeNotNull;
/**
*
* @author Joern Muehlencord <joern at muehlencord.de>
*/
public class UserNameActiveDirectoryRealmTest {
@Test
public void testUsernameLogin() {
String userName = "user.name";
String password = "secret";
testLogin(userName, password);
}
@Test
public void testEmailaddressLogin() {
String userName = "user.name@domain.com";
String password = "secret";
testLogin(userName, password);
}
@Test(expected=AuthenticationException.class)
public void testWrongUserNamePassword() {
String userName = "test123";
String password = "secret";
testLogin(userName, password);
}
private void testLogin(String userName, String password) throws AuthenticationException {
assumeNotNull(UserNameActiveDirectoryRealmTest.class.getResource("/shiro.ini"));
IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
UsernamePasswordToken token = new UsernamePasswordToken(userName, password);
Subject currentUser = SecurityUtils.getSubject();
currentUser.login(token);
System.out.println("Logged in");
}
}

View File

@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"
debug="true">
<appender name="consoleAppender" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{ISO8601} %-5p [%c] %m%n" />
</layout>
</appender>
<category name="de.muehlencord">
<priority value="DEBUG"/>
</category>
<category name="org.apache.shiro">
<priority value="DEBUG"/>
</category>
<category name="com.sun">
<priority value="WARN"/>
</category>
<category name="javax.xml">
<priority value="WARN"/>
</category>
<category name="org.apache.commons">
<priority value="WARN"/>
</category>
<root>
<level value="INFO" />
<appender-ref ref="consoleAppender" />
</root>
</log4j:configuration>

View File

@ -1,30 +0,0 @@
[main]
contextFactory = org.apache.shiro.realm.ldap.JndiLdapContextFactory
contextFactory.url = ldaps://ldap.domain.com:636
contextFactory.systemUsername = user.name@domain.com
contextFactory.systemPassword = secret
contextFactory.environment[java.naming.security.protocol] = ssl
cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager
securityManager.cacheManager = $cacheManager
# HashService
hashService = org.apache.shiro.crypto.hash.DefaultHashService
hashService.hashIterations = 500000
hashService.hashAlgorithmName = SHA-512
hashService.generatePublicSalt = true
# Password service
passwordService = org.apache.shiro.authc.credential.DefaultPasswordService
passwordService.hashService = $hashService
# LDAP Realm setup
ldapRealm = de.muehlencord.shared.account.util.UserNameActiveDirectoryRealm
ldapRealm.principalSuffix = @domain.com
ldapRealm.ldapContextFactory = $contextFactory
ldapRealm.searchBase = dc=domain,dc=com
# LDAP (authentication) activation
authcStrategy = org.apache.shiro.authc.pam.AllSuccessfulStrategy
securityManager.realms = $ldapRealm
securityManager.authenticator.authenticationStrategy = $authcStrategy

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project-shared-configuration>
<!--
This file contains additional configuration written by modules in the NetBeans IDE.
The configuration is intended to be shared among all the users of project and
therefore it is assumed to be part of version control checkout.
Without this configuration present, some functionality in the IDE may be limited or fail altogether.
-->
<properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
<!--
Properties that influence various parts of the IDE, especially code formatting and the like.
You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
That way multiple projects can share the same settings (useful for formatting rules for example).
Any value defined here will override the pom.xml file value but is only applicable to the current project.
-->
<netbeans.checkstyle.format>true</netbeans.checkstyle.format>
</properties>
</project-shared-configuration>

View File

@ -1,15 +1,30 @@
<!--
Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>de.muehlencord.shared</groupId> <groupId>de.muehlencord.shared</groupId>
<artifactId>shared-configuration</artifactId> <artifactId>shared-configuration</artifactId>
<version>1.0</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<parent> <parent>
<artifactId>shared</artifactId> <artifactId>shared</artifactId>
<groupId>de.muehlencord</groupId> <groupId>de.muehlencord</groupId>
<version>1.0</version> <version>1.2-SNAPSHOT</version>
</parent> </parent>
<name>shared-configuration</name> <name>shared-configuration</name>
@ -20,14 +35,15 @@
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit</artifactId> <artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId> <artifactId>slf4j-api</artifactId>
<scope>provided</scope>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -1,10 +1,26 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.configuration; package de.muehlencord.shared.configuration;
import de.muehlencord.shared.configuration.converter.BooleanStringConverter; import de.muehlencord.shared.configuration.converter.BooleanStringConverter;
/** /**
* A Boolean parameter * A Boolean parameter
* @author joern@muehlencord.de *
* @author Joern Muehlencord (joern@muehlencord.de)
*/ */
public class BooleanParameter extends Parameter<Boolean> { public class BooleanParameter extends Parameter<Boolean> {
@ -14,17 +30,18 @@ public class BooleanParameter extends Parameter<Boolean> {
* @param name the name of the parameter * @param name the name of the parameter
*/ */
public BooleanParameter(String name) { public BooleanParameter(String name) {
super(name, new BooleanStringConverter(), true); this(name, new BooleanStringConverter(), true);
} }
/** /**
* creates a new mandatory parameter object * creates a new mandatory parameter object
* *
* @param name the name of the parameter * @param name the name of the parameter
* @param converter the converter object to convert the value of the parameter to string and vice versa * @param converter the converter object to convert the value of the
* parameter to string and vice versa
*/ */
public BooleanParameter(String name, StringConverter<Boolean> converter) { public BooleanParameter(String name, StringConverter<Boolean> converter) {
super(name, converter, true); this(name, converter, true);
} }
/** /**
@ -35,17 +52,43 @@ public class BooleanParameter extends Parameter<Boolean> {
*/ */
public BooleanParameter(String name, boolean mandatory) { public BooleanParameter(String name, boolean mandatory) {
super(name, new BooleanStringConverter(), mandatory); super(name, new BooleanStringConverter(), mandatory);
this.addValueList(Boolean.TRUE, Boolean.FALSE);
}
/**
* creates a new parameter object using default string converter
*
* @param name the name of the parameter
* @param mandatory detremines if this is a mandatory parameter or not
* @param defaultValue the default value for this parameter
*
*/
public BooleanParameter(String name, boolean mandatory, Boolean defaultValue) {
super(name, new BooleanStringConverter(), mandatory);
this.addValueList(Boolean.TRUE, Boolean.FALSE);
this.setDefaultValue(defaultValue);
} }
/** /**
* creates a new parameter object * creates a new parameter object
* *
* @param name the name of the parameter * @param name the name of the parameter
* @param converter the converter object to convert the value of the parameter to string and vice versa * @param converter the converter object to convert the value of the
* parameter to string and vice versa
* @param mandatory detremines if this is a mandatory parameter or not * @param mandatory detremines if this is a mandatory parameter or not
*/ */
public BooleanParameter(String name, StringConverter<Boolean> converter, boolean mandatory) { public BooleanParameter(String name, StringConverter<Boolean> converter, boolean mandatory) {
super(name, converter, mandatory); super(name, converter, mandatory);
this.addValueList(Boolean.TRUE, Boolean.FALSE);
}
@Override
public Boolean getDefaultValue() {
if (defaultValue == null) {
return false;
} else {
return defaultValue;
}
} }
} }

View File

@ -1,49 +1,67 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.configuration; package de.muehlencord.shared.configuration;
import java.util.Map; import java.util.Map;
/** /**
* Specifies a configurable objects.
* *
* @param <T> the type of the parameter * @author Joern Muehlencord (joern@muehlencord.de)
* @param <V> the value of the parameter - must be the same class as the type class of the parameter
* @author joern@muehlencord.de
*/ */
public interface Configuration<T, V extends T> { public interface Configuration {
/** /**
* adds a new parameter to the configuration * adds a new parameter to the configuration
* *
* @param <T> the type of the parameter to add
* @param p the parameter to add * @param p the parameter to add
* @throws ConfigurationException if the parameter cannot be added * @throws ConfigurationException if the parameter cannot be added
*/ */
public void addParameter(Parameter<T> p) throws ConfigurationException; public <T> void addParameter(Parameter<T> p) throws ConfigurationException;
/** /**
* sets the value of the given parameter * sets the value of the given parameter
* *
* @param p parameter to set * @param <T> the type of the parameter to add
* @param value value to set * @param p the parameter to add
* @param value the value the parameter should have
* @throws ConfigurationException if the parameter is not defined * @throws ConfigurationException if the parameter is not defined
*/ */
public void setParameterValue(Parameter<T> p, V value) throws ConfigurationException; public <T> void setParameterValue(Parameter<T> p, T value) throws ConfigurationException;
/** /**
* sets the value of parameter with given name * sets the value of parameter with given name
* *
* @param <T> the type of the parameter to add
* @param parameterName parameter to set * @param parameterName parameter to set
* @param value value to set * @param value value to set
* @throws ConfigurationException if the parameter is not defined * @throws ConfigurationException if the parameter is not defined
*/ */
public void setParameterValue(String parameterName, V value) throws ConfigurationException; public <T> void setParameterValue(String parameterName, T value) throws ConfigurationException;
/** /**
* sets the value of the given parameter * sets the value of the given parameter
* *
* @param <T> the type of the parameter to add
* @param p parameter to set * @param p parameter to set
* @param value value to set * @param value value to set
* @throws ConfigurationException if the parameter is not defined * @throws ConfigurationException if the parameter is not defined
*/ */
public void setParameterValueByString(Parameter<T> p, String value) throws ConfigurationException; public <T> void setParameterValueByString(Parameter<T> p, String value) throws ConfigurationException;
/** /**
* sets the value of the given parameter * sets the value of the given parameter
@ -57,22 +75,26 @@ public interface Configuration<T, V extends T> {
/** /**
* returns the value of the given parameter * returns the value of the given parameter
* *
* @param <T> the type of the parameter to add
* @param p the parameter to return the value for * @param p the parameter to return the value for
* @return the value of the given parameter; null if not set * @return the value of the given parameter; null if not set
* *
* @throws ConfigurationException if the parameter is not defined or if the value is not set * @throws ConfigurationException if the parameter is not defined or if the
* value is not set
*/ */
public V getParameterValue(Parameter<T> p) throws ConfigurationException; public <T> T getParameterValue(Parameter<T> p) throws ConfigurationException;
/** /**
* returns the value of the given parameter * returns the value of the given parameter
* *
* @param <T> the type of the parameter to add
* @param parameterName the name of the parameter to return the value for * @param parameterName the name of the parameter to return the value for
* @return the value of the given parameter; null if not set * @return the value of the given parameter; null if not set
* *
* @throws ConfigurationException if the parameter is not defined or if the value is not set * @throws ConfigurationException if the parameter is not defined or if the
* value is not set
*/ */
public V getParameterValue(String parameterName) throws ConfigurationException; public <T> T getParameterValue(String parameterName) throws ConfigurationException;
/** /**
* validates the configuration * validates the configuration
@ -84,7 +106,8 @@ public interface Configuration<T, V extends T> {
/** /**
* returns the map of parameters and values * returns the map of parameters and values
* *
* @param <T> the type of the parameter to add
* @return the map of parameters and values * @return the map of parameters and values
*/ */
public Map<Parameter<T>, V> getParameterMap(); public <T> Map<Parameter<T>, T> getParameterMap();
} }

View File

@ -1,8 +1,23 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.configuration; package de.muehlencord.shared.configuration;
/** /**
* *
* @author jomu * @author Joern Muehlencord (joern@muehlencord.de)
*/ */
public class ConfigurationException extends Exception { public class ConfigurationException extends Exception {

View File

@ -1,8 +1,23 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.configuration; package de.muehlencord.shared.configuration;
/** /**
* *
* @author jomu * @author Joern Muehlencord (joern@muehlencord.de)
*/ */
public class ConverterException extends Exception { public class ConverterException extends Exception {

View File

@ -1,3 +1,18 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.configuration; package de.muehlencord.shared.configuration;
import de.muehlencord.shared.configuration.converter.DateStringConverter; import de.muehlencord.shared.configuration.converter.DateStringConverter;
@ -5,7 +20,8 @@ import java.util.Date;
/** /**
* A Date parameter * A Date parameter
* @author joern@muehlencord.de *
* @author Joern Muehlencord (joern@muehlencord.de)
*/ */
public class DateParameter extends Parameter<Date> { public class DateParameter extends Parameter<Date> {
@ -32,7 +48,7 @@ public class DateParameter extends Parameter<Date> {
* creates a new parameter object using default string converter * creates a new parameter object using default string converter
* *
* @param name the name of the parameter * @param name the name of the parameter
* @param mandatory detremines if this is a mandatory parameter or not * @param mandatory determines if this is a mandatory parameter or not
*/ */
public DateParameter(String name, boolean mandatory) { public DateParameter(String name, boolean mandatory) {
super(name, new DateStringConverter(), mandatory); super(name, new DateStringConverter(), mandatory);
@ -43,7 +59,7 @@ public class DateParameter extends Parameter<Date> {
* *
* @param name the name of the parameter * @param name the name of the parameter
* @param converter the converter object to convert the value of the parameter to string and vice versa * @param converter the converter object to convert the value of the parameter to string and vice versa
* @param mandatory detremines if this is a mandatory parameter or not * @param mandatory determines if this is a mandatory parameter or not
*/ */
public DateParameter(String name, StringConverter<Date> converter, boolean mandatory) { public DateParameter(String name, StringConverter<Date> converter, boolean mandatory) {
super(name, converter, mandatory); super(name, converter, mandatory);

View File

@ -1,3 +1,18 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.configuration; package de.muehlencord.shared.configuration;
import java.util.HashMap; import java.util.HashMap;
@ -7,18 +22,16 @@ import java.util.Map;
/** /**
* *
* @param <T> the type of the parameter * @author Joern Muehlencord (joern@muehlencord.de)
* @param <V> the value of the parameter - must be the same class as the type class of the parameter
* @author joern@muehlencord.de
*/ */
public class DefaultConfiguration<T, V extends T> implements Configuration<T, V> { public class DefaultConfiguration implements Configuration {
/** /**
* the parameter map * the parameter map
*/ */
private final Map<Parameter<T>, V> parameterMap; private final Map<Parameter, Object> parameterMap;
/** mapping from name to parameter */ /** mapping from name to parameter */
private final Map<String, Parameter<T>> parameterNameMap; private final Map<String, Parameter> parameterNameMap;
/** /**
* creates a new instance of a configuration * creates a new instance of a configuration
@ -33,7 +46,7 @@ public class DefaultConfiguration<T, V extends T> implements Configuration<T, V>
* *
* @return the map of parameters and values * @return the map of parameters and values
*/ */
public Map<Parameter<T>, V> getParameterMap() { public Map<Parameter, Object> getParameterMap() {
return parameterMap; return parameterMap;
} }
@ -80,7 +93,7 @@ public class DefaultConfiguration<T, V extends T> implements Configuration<T, V>
* @throws ConfigurationException if the parameter is not defined * @throws ConfigurationException if the parameter is not defined
*/ */
@Override @Override
public void setParameterValue(Parameter<T> p, V value) throws ConfigurationException { public <T> void setParameterValue(Parameter<T> p, T value) throws ConfigurationException {
if (parameterMap.containsKey(p)) { if (parameterMap.containsKey(p)) {
parameterMap.put(p, value); parameterMap.put(p, value);
} else { } else {
@ -91,13 +104,14 @@ public class DefaultConfiguration<T, V extends T> implements Configuration<T, V>
/** /**
* overrides parameter map with given map * overrides parameter map with given map
* *
* @param <T> the type of the parameter to add
* @param map map to use * @param map map to use
* @throws ConfigurationException if the parameter is not defined * @throws ConfigurationException if the parameter is not defined
*/ */
public void setParameterValue (Map<Parameter<T>, V> map) throws ConfigurationException { public <T> void setParameterValue (Map<Parameter<T>, T> map) throws ConfigurationException {
for ( Map.Entry<Parameter<T>,V> entry : map.entrySet()) { for ( Map.Entry<Parameter<T>,T> entry : map.entrySet()) {
Parameter<T> key = entry.getKey(); Parameter<T> key = entry.getKey();
V value = entry.getValue(); T value = entry.getValue();
if (parameterMap.containsKey(key)) { if (parameterMap.containsKey(key)) {
parameterMap.put (key, value); parameterMap.put (key, value);
} else { } else {
@ -114,7 +128,7 @@ public class DefaultConfiguration<T, V extends T> implements Configuration<T, V>
* @throws ConfigurationException if the parameter is not defined * @throws ConfigurationException if the parameter is not defined
*/ */
@Override @Override
public void setParameterValue(String parameterName, V value) throws ConfigurationException { public <T> void setParameterValue(String parameterName, T value) throws ConfigurationException {
if (parameterNameMap.containsKey(parameterName)) { if (parameterNameMap.containsKey(parameterName)) {
Parameter<T> p = parameterNameMap.get(parameterName); Parameter<T> p = parameterNameMap.get(parameterName);
parameterMap.put(p, value); parameterMap.put(p, value);
@ -132,10 +146,10 @@ public class DefaultConfiguration<T, V extends T> implements Configuration<T, V>
* @throws ConfigurationException if the parameter is not defined * @throws ConfigurationException if the parameter is not defined
*/ */
@Override @Override
public void setParameterValueByString(Parameter<T> p, String stringValue) throws ConfigurationException { public <T> void setParameterValueByString(Parameter<T> p, String stringValue) throws ConfigurationException {
if (parameterMap.containsKey(p)) { if (parameterMap.containsKey(p)) {
try { try {
V value = p.getStringConverter().fromString(stringValue); T value = p.getStringConverter().fromString(stringValue);
parameterMap.put(p, value); parameterMap.put(p, value);
} catch (ConverterException ex) { } catch (ConverterException ex) {
throw new ConfigurationException("Error while setting parameter value for parameter " + p.getName() + ". Reason:" + ex.getMessage(), ex); throw new ConfigurationException("Error while setting parameter value for parameter " + p.getName() + ". Reason:" + ex.getMessage(), ex);
@ -155,11 +169,11 @@ public class DefaultConfiguration<T, V extends T> implements Configuration<T, V>
@Override @Override
public void setParameterValueByString(String parameterName, String stringValue) throws ConfigurationException { public void setParameterValueByString(String parameterName, String stringValue) throws ConfigurationException {
if (parameterNameMap.containsKey(parameterName)) { if (parameterNameMap.containsKey(parameterName)) {
Parameter<T> p = parameterNameMap.get(parameterName); Parameter p = parameterNameMap.get(parameterName);
if (parameterMap.containsKey(p)) { if (parameterMap.containsKey(p)) {
try { try {
V value = p.getStringConverter().fromString(stringValue); Object value = p.getStringConverter().fromString(stringValue);
parameterMap.put(p, value); parameterMap.put(p, value);
} catch (ConverterException ex) { } catch (ConverterException ex) {
throw new ConfigurationException("Error while setting parameter value for parameter " + p.getName() + ". Reason:" + ex.getMessage(), ex); throw new ConfigurationException("Error while setting parameter value for parameter " + p.getName() + ". Reason:" + ex.getMessage(), ex);
@ -181,9 +195,9 @@ public class DefaultConfiguration<T, V extends T> implements Configuration<T, V>
* @throws ConfigurationException if the value cannot be determined * @throws ConfigurationException if the value cannot be determined
*/ */
@Override @Override
public V getParameterValue(Parameter<T> p) throws ConfigurationException { public <T> T getParameterValue(Parameter<T> p) throws ConfigurationException {
if (parameterMap.containsKey(p)) { if (parameterMap.containsKey(p)) {
return parameterMap.get(p); return (T) parameterMap.get(p);
} else { } else {
throw new ConfigurationException("Parameter " + p.getName() + " not defined"); throw new ConfigurationException("Parameter " + p.getName() + " not defined");
} }
@ -198,7 +212,7 @@ public class DefaultConfiguration<T, V extends T> implements Configuration<T, V>
* @throws ConfigurationException if the parameter is not defined or if the value is not set * @throws ConfigurationException if the parameter is not defined or if the value is not set
*/ */
@Override @Override
public V getParameterValue(String parameterName) throws ConfigurationException { public <T> T getParameterValue(String parameterName) throws ConfigurationException {
if (parameterNameMap.containsKey(parameterName)) { if (parameterNameMap.containsKey(parameterName)) {
Parameter<T> p = parameterNameMap.get(parameterName); Parameter<T> p = parameterNameMap.get(parameterName);
return getParameterValue(p); return getParameterValue(p);
@ -215,14 +229,14 @@ public class DefaultConfiguration<T, V extends T> implements Configuration<T, V>
* *
* @throws ConfigurationException if a check fails * @throws ConfigurationException if a check fails
*/ */
private boolean validateParameter(Parameter<T> p) throws ConfigurationException { private <T> boolean validateParameter(Parameter<T> p) throws ConfigurationException {
// check if parameter is mandatory and available // check if parameter is mandatory and available
if ((p.isMandatory()) && (parameterMap.get(p) == null)) { if ((p.isMandatory()) && (parameterMap.get(p) == null)) {
return false; return false;
} }
// check if parameter has required parameters and if these are set // check if parameter has required parameters and if these are set
V parameterValue = getParameterValue(p); T parameterValue = getParameterValue(p);
if (parameterValue != null) { if (parameterValue != null) {
for (Parameter rp : p.getRequiredParameter()) { for (Parameter rp : p.getRequiredParameter()) {
if (getParameterValue(rp) == null) { if (getParameterValue(rp) == null) {

View File

@ -1,3 +1,18 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.configuration; package de.muehlencord.shared.configuration;
import de.muehlencord.shared.configuration.converter.IntegerStringConverter; import de.muehlencord.shared.configuration.converter.IntegerStringConverter;
@ -5,7 +20,7 @@ import de.muehlencord.shared.configuration.converter.IntegerStringConverter;
/** /**
* A Integer parameter * A Integer parameter
* *
* @author joern@muehlencord.de * @author Joern Muehlencord (joern@muehlencord.de)
*/ */
public class IntegerParameter extends Parameter<Integer> { public class IntegerParameter extends Parameter<Integer> {

View File

@ -1,39 +1,75 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.configuration; package de.muehlencord.shared.configuration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* *
* @param <T> type of parameter * @param <T> type of parameter
* @author jomu * @author Joern Muehlencord (joern@muehlencord.de)
*/ */
public abstract class Parameter<T> { public abstract class Parameter<T> {
private final static Logger LOGGER = LoggerFactory.getLogger(Parameter.class); private final static Logger LOGGER = LoggerFactory.getLogger(Parameter.class);
/** the name of the parameter */ /**
private String name; * the name of the parameter
/** the long description of this parameter */ */
private String description; protected String name;
/** boolean flag if this is a mandatory parameter or not */ /**
private boolean mandatory; * the long description of this parameter
/** the string converter to convert the object to string and back again */ */
private StringConverter<T> stringConverter; protected String description;
/** list of mandatory parameters, if this parameter is active */ /**
private List<Parameter<?>> requiredParameters; * boolean flag if this is a mandatory parameter or not
/** optional validator */ */
private Validator<T> validator; protected boolean mandatory;
/**
* the string converter to convert the object to string and back again
*/
protected StringConverter<T> stringConverter;
/**
* list of mandatory parameters, if this parameter is active
*/
protected List<Parameter<?>> requiredParameters; // TODO rename to depending parmeter
/**
* optional validator
*/
protected Validator<T> validator;
/**
* optional value list
*/
protected List<T> valueList;
/**
* the default value
*/
protected T defaultValue;
/** /**
* creates a new mandatory string parameter object * creates a new mandatory string parameter object
* *
* @param name the name of the parameter * @param name the name of the parameter
* @param converter the converter object to convert the value of the parameter to string and vice versa * @param converter the converter object to convert the value of the
* parameter to string and vice versa
*/ */
public Parameter(String name, StringConverter<T> converter) { public Parameter(String name, StringConverter<T> converter) {
this.name = name; this.name = name;
@ -42,13 +78,16 @@ public abstract class Parameter<T> {
this.stringConverter = converter; this.stringConverter = converter;
this.requiredParameters = new LinkedList<>(); this.requiredParameters = new LinkedList<>();
this.validator = null; this.validator = null;
this.valueList = null;
} }
/** /**
* creates a new string parameter object. If the parameter is mandatory or not can be specified. * creates a new string parameter object. If the parameter is mandatory or
* not can be specified.
* *
* @param name the name of the parameter * @param name the name of the parameter
* @param converter the converter object to convert the value of the parameter to string and vice versa * @param converter the converter object to convert the value of the
* parameter to string and vice versa
* @param mandatory determines if this is a mandatory parameter or not * @param mandatory determines if this is a mandatory parameter or not
*/ */
public Parameter(String name, StringConverter<T> converter, boolean mandatory) { public Parameter(String name, StringConverter<T> converter, boolean mandatory) {
@ -60,7 +99,8 @@ public abstract class Parameter<T> {
* creates a new string parameter object. * creates a new string parameter object.
* *
* @param name the name of the parameter * @param name the name of the parameter
* @param converter the converter object to convert the value of the parameter to string and vice versa * @param converter the converter object to convert the value of the
* parameter to string and vice versa
* @param description the long text description of this parameter * @param description the long text description of this parameter
* @param mandatory determines if this is a mandatory parameter or not * @param mandatory determines if this is a mandatory parameter or not
*/ */
@ -124,12 +164,45 @@ public abstract class Parameter<T> {
} }
/** /**
* returns true, if the given value is part of the possible value list or if the given value list is true * @param validator the validator to set
*/
public void setValidator(Validator<T> validator) {
this.validator = validator;
}
public void addValueList(T... values) {
if (valueList == null) {
valueList = new ArrayList<>();
}
valueList.addAll(Arrays.asList(values));
}
public List<T> getValueList() {
List<T> returnList = new ArrayList<>();
if (valueList != null) {
returnList.addAll(valueList);
}
return returnList;
}
public T getDefaultValue() {
return defaultValue;
}
public void setDefaultValue(T defaultValue) {
this.defaultValue = defaultValue;
}
/**
* returns true, if the given value is part of the possible value list or if
* the given value list is true
* *
* @param value the value to validate * @param value the value to validate
* @return true if no value list is defined or value is part of the value list; false otherwise * @return true if no value list is defined or value is part of the value
* list; false otherwise
*/ */
public boolean isValid(T value) { public boolean isValid(T value) {
if (valueList == null) {
if (validator == null) { if (validator == null) {
return true; return true;
} else { } else {
@ -137,10 +210,13 @@ public abstract class Parameter<T> {
validator.validate(value); validator.validate(value);
return true; return true;
} catch (ValidationException ex) { } catch (ValidationException ex) {
LOGGER.debug (ex.toString(), ex); LOGGER.debug(ex.toString(), ex);
return false; return false;
} }
} }
} else {
return valueList.contains(value);
}
} }
@Override @Override
@ -163,10 +239,4 @@ public abstract class Parameter<T> {
return hash; return hash;
} }
/**
* @param validator the validator to set
*/
public void setValidator(Validator<T> validator) {
this.validator = validator;
}
} }

View File

@ -1,3 +1,18 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.configuration; package de.muehlencord.shared.configuration;
import java.net.URI; import java.net.URI;
@ -5,7 +20,7 @@ import java.util.Date;
/** /**
* *
* @author jomu * @author Joern Muehlencord (joern@muehlencord.de)
*/ */
public abstract class ParameterFactory { public abstract class ParameterFactory {

View File

@ -1,9 +1,24 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.configuration; package de.muehlencord.shared.configuration;
/** /**
* *
* @param <T> the class type of the parameter value to convert * @param <T> the class type of the parameter value to convert
* @author jomu * @author Joern Muehlencord (joern@muehlencord.de)
*/ */
public interface StringConverter<T> { public interface StringConverter<T> {

View File

@ -1,10 +1,26 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.configuration; package de.muehlencord.shared.configuration;
import de.muehlencord.shared.configuration.converter.StringStringConverter; import de.muehlencord.shared.configuration.converter.StringStringConverter;
/** /**
* A String parameter * A String parameter
* @author joern@muehlencord.de *
* @author Joern Muehlencord (joern@muehlencord.de)
*/ */
public class StringParameter extends Parameter<String> { public class StringParameter extends Parameter<String> {
@ -17,11 +33,23 @@ public class StringParameter extends Parameter<String> {
super(name, new StringStringConverter(), true); super(name, new StringStringConverter(), true);
} }
/**
* creates a new mandatory parameter object using default string converter
*
* @param name the name of the parameter
* @param defaultValue the default value of the parameter to set.
*/
public StringParameter(String name, String defaultValue) {
super(name, new StringStringConverter(), true);
this.defaultValue = defaultValue;
}
/** /**
* creates a new mandatory parameter object * creates a new mandatory parameter object
* *
* @param name the name of the parameter * @param name the name of the parameter
* @param converter the converter object to convert the value of the parameter to string and vice versa * @param converter the converter object to convert the value of the
* parameter to string and vice versa
*/ */
public StringParameter(String name, StringConverter<String> converter) { public StringParameter(String name, StringConverter<String> converter) {
super(name, converter, true); super(name, converter, true);
@ -41,7 +69,8 @@ public class StringParameter extends Parameter<String> {
* creates a new parameter object * creates a new parameter object
* *
* @param name the name of the parameter * @param name the name of the parameter
* @param converter the converter object to convert the value of the parameter to string and vice versa * @param converter the converter object to convert the value of the
* parameter to string and vice versa
* @param mandatory detremines if this is a mandatory parameter or not * @param mandatory detremines if this is a mandatory parameter or not
*/ */
public StringParameter(String name, StringConverter<String> converter, boolean mandatory) { public StringParameter(String name, StringConverter<String> converter, boolean mandatory) {

View File

@ -1,3 +1,18 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.configuration; package de.muehlencord.shared.configuration;
import de.muehlencord.shared.configuration.converter.URIStringConverter; import de.muehlencord.shared.configuration.converter.URIStringConverter;
@ -5,7 +20,7 @@ import java.net.URI;
/** /**
* A URI parameter * A URI parameter
* @author joern@muehlencord.de * @author Joern Muehlencord (joern@muehlencord.de)
*/ */
public class URIParameter extends Parameter<URI> { public class URIParameter extends Parameter<URI> {

View File

@ -1,8 +1,23 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.configuration; package de.muehlencord.shared.configuration;
/** /**
* *
* @author joern@muehlencord.de * @author Joern Muehlencord (joern@muehlencord.de)
*/ */
public class ValidationException extends Exception { public class ValidationException extends Exception {

View File

@ -1,9 +1,24 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.configuration; package de.muehlencord.shared.configuration;
/** /**
* *
* @param <T> the type of the validator * @param <T> the type of the validator
* @author joern@muehlencord.de * @author Joern Muehlencord (joern@muehlencord.de)
*/ */
public interface Validator<T> { public interface Validator<T> {

View File

@ -1,3 +1,18 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.configuration.converter; package de.muehlencord.shared.configuration.converter;
import de.muehlencord.shared.configuration.ConverterException; import de.muehlencord.shared.configuration.ConverterException;
@ -6,7 +21,7 @@ import static java.lang.Boolean.parseBoolean;
/** /**
* *
* @author joern@muehlencord.de * @author Joern Muehlencord (joern@muehlencord.de)
*/ */
public class BooleanStringConverter implements StringConverter<Boolean> { public class BooleanStringConverter implements StringConverter<Boolean> {

View File

@ -1,3 +1,18 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.configuration.converter; package de.muehlencord.shared.configuration.converter;
import de.muehlencord.shared.configuration.ConverterException; import de.muehlencord.shared.configuration.ConverterException;
@ -8,7 +23,7 @@ import java.util.Locale;
/** /**
* *
* @author jomu * @author Joern Muehlencord (joern@muehlencord.de)
*/ */
public class DateStringConverter implements StringConverter<Date> { public class DateStringConverter implements StringConverter<Date> {

View File

@ -1,3 +1,18 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.configuration.converter; package de.muehlencord.shared.configuration.converter;
import de.muehlencord.shared.configuration.ConverterException; import de.muehlencord.shared.configuration.ConverterException;
@ -6,7 +21,7 @@ import static java.lang.Integer.parseInt;
/** /**
* *
* @author jomu * @author Joern Muehlencord (joern@muehlencord.de)
*/ */
public class IntegerStringConverter implements StringConverter<Integer> { public class IntegerStringConverter implements StringConverter<Integer> {

View File

@ -1,3 +1,18 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.configuration.converter; package de.muehlencord.shared.configuration.converter;
import de.muehlencord.shared.configuration.ConverterException; import de.muehlencord.shared.configuration.ConverterException;
@ -5,7 +20,7 @@ import de.muehlencord.shared.configuration.StringConverter;
/** /**
* *
* @author jomu * @author Joern Muehlencord (joern@muehlencord.de)
*/ */
public class StringStringConverter implements StringConverter<String> { public class StringStringConverter implements StringConverter<String> {

View File

@ -1,3 +1,18 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.configuration.converter; package de.muehlencord.shared.configuration.converter;
import de.muehlencord.shared.configuration.ConverterException; import de.muehlencord.shared.configuration.ConverterException;
@ -7,7 +22,7 @@ import static java.net.URI.create;
/** /**
* *
* @author jomu * @author Joern Muehlencord (joern@muehlencord.de)
*/ */
public class URIStringConverter implements StringConverter<URI> { public class URIStringConverter implements StringConverter<URI> {

View File

@ -1,3 +1,18 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.configuration.validator; package de.muehlencord.shared.configuration.validator;
import de.muehlencord.shared.configuration.ValidationException; import de.muehlencord.shared.configuration.ValidationException;
@ -5,7 +20,7 @@ import de.muehlencord.shared.configuration.Validator;
/** /**
* *
* @author joern@muehlencord.de * @author Joern Muehlencord (joern@muehlencord.de)
*/ */
public class IntgerRangeValidator implements Validator<Integer> { public class IntgerRangeValidator implements Validator<Integer> {

View File

@ -1,3 +1,18 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.configuration.validator; package de.muehlencord.shared.configuration.validator;
import de.muehlencord.shared.configuration.ValidationException; import de.muehlencord.shared.configuration.ValidationException;
@ -8,7 +23,7 @@ import java.util.List;
/** /**
* *
* @author joern@muehlencord.de * @author Joern Muehlencord (joern@muehlencord.de)
*/ */
public class StringValueListValidator implements Validator<String> { public class StringValueListValidator implements Validator<String> {

98
db/pom.xml Normal file
View File

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.muehlencord.shared</groupId>
<artifactId>shared-db</artifactId>
<packaging>ejb</packaging>
<parent>
<artifactId>shared</artifactId>
<groupId>de.muehlencord</groupId>
<version>1.2-SNAPSHOT</version>
</parent>
<name>shared-db</name>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<type>jar</type>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>shared-util</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<!-- Dev Tools -->
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
<version>1.18.12</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- create EJB version 3.1 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ejb-plugin</artifactId>
<configuration>
<ejbVersion>3.1</ejbVersion>
<excludes>
<exclude>**/persistence.xml</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,145 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.db;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.ejb.Lock;
import javax.ejb.LockType;
import javax.persistence.EntityGraph;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
/**
*
* @author Joern Muehlencord (joern@muehlencord.de)
* @param <T> the entity this controller will serve. All objects returned are of
* this type.
*/
public abstract class AbstractController<T extends Serializable> extends CommonAbstractController {
private final Class<T> entityClass;
protected AbstractController(Class<T> clazz) {
this.entityClass = clazz;
}
/**
* general find method
*
* @param id the primary key of the entity to search for.
* @return the found entity instance or null if the entity does not exist
*/
@Lock(LockType.READ)
public T find(Object id) {
return em.find(entityClass, id);
}
/**
* general find method with the option to initialize lists/sets of 1:n
* relations during the query.
*
* @param id the primary key of the entity to search for.
* @param subGraphItems the name of the subgraph items to initialize.
* Typically this is the name of the 1:n set or list.
* @return the found entity instance or null if the entity does not exist
*/
@Lock(LockType.READ)
public T find(Object id, String... subGraphItems) {
EntityGraph<T> graph = this.em.createEntityGraph(entityClass);
for (String subGraphItem : subGraphItems) {
graph.addSubgraph(subGraphItem);
}
Map<String, Object> hints = new HashMap<>();
hints.put("javax.persistence.loadgraph", graph);
return em.find(entityClass, id, hints);
}
@Lock(LockType.READ)
public List<T> find(String queryName, Map<String, Object> parameterMap) {
Query query = em.createNamedQuery(queryName);
parameterMap.entrySet().forEach(entry -> query.setParameter(entry.getKey(), entry.getValue()));
return query.getResultList();
}
/**
* returns a list of all entities.
*
* @return a list of all entities.
*/
@Lock(LockType.READ)
public List<T> findAll() throws ControllerException {
return findAll(new ArrayList<>());
}
/**
* returns a list of all entities.
*
* @param orderFields the list of field names to order the result by.
* @return a list of all entities.
*/
@Lock(LockType.READ)
public List<T> findAll(String... orderFields) throws ControllerException {
return findAll(Arrays.asList(orderFields));
}
/**
*
* @param orderFields the list of field names to order the result by.
* @return a list of all entities.
*/
@Lock(LockType.READ)
public List<T> findAll(List<String> orderFields) throws ControllerException {
return findAll(entityClass, orderFields);
}
/**
* searches for entities by specifying a filter map. The map contains a
* field name and field value.
*
* @param filters the filters to apply
* @param orderFields the fields to order the result by.
* @return a list of found entities.
*/
@Lock(LockType.READ)
public List<T> find(Map<String, Object> filters, List<String> orderFields) {
final CriteriaBuilder cb = em.getCriteriaBuilder();
final CriteriaQuery<T> criteria = cb.createQuery(entityClass);
final Root<T> r = criteria.from(entityClass);
Predicate filterCondition = getFilterCondition(cb, r, filters);
if (filterCondition != null) {
criteria.where(filterCondition);
}
List<Order> orderList = new ArrayList<>();
orderFields.stream().forEachOrdered(field -> orderList.add(cb.asc(r.get(field))));
final TypedQuery<T> query = em.createQuery(criteria.orderBy(orderList));
return query.getResultList();
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.db;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
/**
*
* @author Joern Muehlencord (joern@muehlencord.de)
*/
@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface ApplicationPU {
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.db;
import javax.annotation.Priority;
import javax.inject.Inject;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
import javax.persistence.EntityManager;
import javax.transaction.Transactional;
import static javax.transaction.Transactional.TxType.REQUIRED;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author Joern Muehlencord (joern@muehlencord.de)
*/
@Transactional(value = REQUIRED)
@Interceptor
@Priority(value = ApplicationTransactionJoinInterceptor.PRIORITY)
public class ApplicationTransactionJoinInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationTransactionJoinInterceptor.class);
// attach behind the interceptor of the container
public static final int PRIORITY = Interceptor.Priority.PLATFORM_BEFORE + 250;
@Inject
@ApplicationPU
private EntityManager em;
@AroundInvoke
public Object joinTransaction(InvocationContext context) throws Exception {
if (em == null) {
return context.proceed();
} else {
if (em.isJoinedToTransaction()) {
LOGGER.trace("transaction already joined");
} else {
LOGGER.trace("joining transaction");
em.joinTransaction();
}
}
return context.proceed();
}
}

View File

@ -0,0 +1,133 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.db;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
/**
* @author Joern Muehlencord (joern@muehlencord.de)
*/
@Embeddable
@Getter
@Setter
public class Audit implements Serializable {
private static final long serialVersionUID = -955765069412891842L;
@Basic(optional = false)
@NotNull
@Column(name = "valid_from")
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime validFrom;
@Column(name = "valid_to")
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime validTo;
@Basic(optional = false)
@NotNull
@Column(name = "created_on")
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime createdOn;
@Basic(optional = false)
@NotNull
@Column(name = "created_by")
private String createdBy;
@Basic(optional = false)
@NotNull
@Column(name = "last_updated_on")
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime lastUpdatedOn;
@Basic(optional = false)
@NotNull
@Column(name = "last_updated_by")
private String lastUpdatedBy;
public Audit() {
this.validFrom = LocalDateTime.now(ZoneOffset.UTC);
this.lastUpdatedOn = LocalDateTime.now(ZoneOffset.UTC);
this.createdOn = LocalDateTime.now(ZoneOffset.UTC);
}
public Audit withNewAudit(String userName) {
LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
this.setCreatedBy(userName);
this.setCreatedOn(now);
this.setLastUpdatedBy(userName);
this.setLastUpdatedOn(now);
this.setValidFrom(now);
return this;
}
public Audit withEndDate() {
return withEndDate (LocalDateTime.now(ZoneOffset.UTC));
}
private Audit withEndDate(LocalDateTime endDate) {
this.setValidTo(endDate);
return this;
}
public Audit withValidFrom(final LocalDateTime validFrom) {
this.validFrom = validFrom;
return this;
}
public Audit withValidTo(final LocalDateTime validTo) {
this.validTo = validTo;
return this;
}
public Audit withCreatedOn(final LocalDateTime createdOn) {
this.createdOn = createdOn;
return this;
}
public Audit withCreatedBy(final String createdBy) {
this.createdBy = createdBy;
return this;
}
public Audit withLastUpdatedOn(final LocalDateTime lastUpdatedOn) {
this.lastUpdatedOn = lastUpdatedOn;
return this;
}
public Audit withLastUpdatedBy(final String lastUpdatedBy) {
this.lastUpdatedBy = lastUpdatedBy;
return this;
}
}

View File

@ -0,0 +1,64 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.db;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Does not work, cannot Spring independent inject current username - implemented in AbstractController manually
*
* @author Joern Muehlencord (joern@muehlencord.de)
*/
public class AuditListener {
private static final Logger logger = LoggerFactory.getLogger(AuditListener.class);
// @PrePersist
public void prePersist(Auditable auditable) {
Audit audit = auditable.getAudit();
if (audit == null) {
audit = new Audit();
auditable.setAudit(audit);
}
LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
audit.setCreatedOn(now);
audit.setLastUpdatedOn(now);
// audit.setCreatedBy(LoggedUser.get()); // TODO where to get the user from
if (logger.isDebugEnabled()) {
logger.debug("prePersist executed");
}
}
// @PreUpdate
public void preUpdate(Auditable auditable) {
Audit audit = auditable.getAudit();
// TODO where to get the user from
LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
audit.setLastUpdatedOn(now);
if (logger.isDebugEnabled()) {
logger.debug("preUpdate executed");
}
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.db;
/**
*
* @author Joern Muehlencord (joern@muehlencord.de)
*/
public interface Auditable {
Audit getAudit();
void setAudit(Audit audit);
}

View File

@ -0,0 +1,730 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.db;
import de.muehlencord.shared.util.DateUtil;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import javax.ejb.Lock;
import javax.ejb.LockType;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.inject.Inject;
import javax.persistence.EntityGraph;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
/**
*
* @author Joern Muehlencord (joern@muehlencord.de)
*/
public abstract class CommonAbstractController {
/**
* the entity manager to use
*/
@Inject
@ApplicationPU
protected EntityManager em;
// required for unit testing
public void setEntityManager (EntityManager em) {
this.em = em;
}
@Lock(LockType.READ)
public <T extends Serializable> T find(Class<T> clazz, Object id) {
return em.find(clazz, id);
}
private <T> EntityGraph getEntityGraph(Class<T> clazz, String... subGraphItems) {
EntityGraph graph = this.em.createEntityGraph(clazz);
if (subGraphItems != null) {
for (String subGraphItem : subGraphItems) {
graph.addSubgraph(subGraphItem);
}
}
return graph;
}
@Lock(LockType.READ)
public <T extends Serializable> T find(Class<T> clazz, Object id, String... subGraphItems) {
EntityGraph graph = getEntityGraph(clazz, subGraphItems);
Map hints = new HashMap<>();
hints.put("javax.persistence.loadgraph", graph);
T entity = (T) em.find(clazz, id, hints);
return entity;
}
@Lock(LockType.READ)
public <T extends Serializable> List<T> find(Class<T> clazz, Map<String, Object> filters, List<String> orderFields, String... subGraphItems) {
return find(clazz, filters, orderFields, 0, 0, subGraphItems);
}
@Lock(LockType.READ)
public <T extends Serializable> List<T> find(Class<T> clazz, Map<String, Object> filters, List<String> orderFields, int limit, int offset, String... subGraphItems) {
final CriteriaBuilder cb = em.getCriteriaBuilder();
final CriteriaQuery<T> criteria = cb.createQuery(clazz);
final Root<T> r = criteria.from(clazz);
Predicate filterCondition = getFilterCondition(cb, r, filters);
if (filterCondition != null) {
criteria.where(filterCondition);
}
List<Order> orderList = new ArrayList<>();
orderFields.stream().forEachOrdered(field -> orderList.add(cb.asc(r.get(field))));
final TypedQuery<T> query = em.createQuery(criteria.orderBy(orderList))
.setFirstResult(offset);
if (limit > 0) {
query.setMaxResults(limit);
}
EntityGraph graph = getEntityGraph(clazz, subGraphItems);
query.setHint("javax.persistence.loadgraph", graph);
return query.getResultList();
}
@Lock(LockType.READ)
public <T extends Serializable> List<T> find(Class<T> clazz, List<SearchFilter> filters, List<String> orderFields, String... subGraphItems) throws ControllerException {
return find(clazz, filters, orderFields, 0, 0);
}
@Lock(LockType.READ)
public <T extends Serializable> List<T> find(Class<T> clazz, List<SearchFilter> filters, List<String> orderFields, int limit, int offset, String... subGraphItems) throws ControllerException {
final CriteriaBuilder cb = em.getCriteriaBuilder();
final CriteriaQuery<T> criteria = cb.createQuery(clazz);
final Root<T> r = criteria.from(clazz);
Predicate filterCondition = getFilterCondition(cb, r, filters);
if (filterCondition != null) {
criteria.where(filterCondition);
}
List<Order> orderList = new ArrayList<>();
if (orderFields != null) {
orderFields.stream().forEachOrdered(field -> orderList.add(cb.asc(r.get(field))));
}
final TypedQuery<T> query = em.createQuery(criteria.orderBy(orderList)).setFirstResult(offset);
if (limit > 0) {
query.setMaxResults(limit);
}
if (!ArrayUtils.isEmpty(subGraphItems)) {
EntityGraph graph = getEntityGraph(clazz, subGraphItems);
query.setHint("javax.persistence.loadgraph", graph);
}
return query.getResultList();
}
/**
*
* @param <T> the type of the entity to search for
* @param entityClass the entity class to return
* @return a list of all entities found
* @throws ControllerException if the search cannot be executed.
*/
@Lock(LockType.READ)
public <T extends Serializable> List<T> findAll(Class<T> entityClass) throws ControllerException {
return findAll(entityClass, new ArrayList<>());
}
/**
*
* @param <T> the type of the entity to search for
* @param entityClass the entity class to return
* @param orderFields the fields to order the result by.
* @return a list of all entities found
* @throws ControllerException if the search cannot be executed.
*/
@Lock(LockType.READ)
public <T extends Serializable> List<T> findAll(Class<T> entityClass, String... orderFields) throws ControllerException {
return findAll(entityClass, Arrays.asList(orderFields));
}
/**
*
* @param <T> the type of the entity to search for
* @param entityClass the entity class to return
* @param orderFields the fields to order the result by.
* @return a list of all entities found
* @throws ControllerException if the search cannot be executed.
*/
@Lock(LockType.READ)
public <T extends Serializable> List<T> findAll(Class<T> entityClass, List<String> orderFields) throws ControllerException {
final CriteriaBuilder cb = em.getCriteriaBuilder();
final CriteriaQuery<T> criteria = cb.createQuery(entityClass);
final Root<T> r = criteria.from(entityClass);
if (EndDateable.class.isAssignableFrom(entityClass.getClass())) {
Date now = DateUtil.getCurrentTimeInUTC();
List<SearchFilter> searchFilter = new ArrayList<>();
searchFilter.add(new DefaultSearchFilter("validFrom", Comparator.GREATER_OR_EQUAL_THAN, now));
searchFilter.add(new DefaultSearchFilter("validTo", Comparator.LESS_THAN, now));
Predicate filterCondition = getFilterCondition(cb, r, searchFilter);
criteria.where(filterCondition);
}
List<Order> orderList = new ArrayList<>();
orderFields.stream().forEachOrdered(field -> orderList.add(cb.asc(r.get(field))));
final TypedQuery<T> query = em.createQuery(criteria.orderBy(orderList));
return query.getResultList();
}
/* **** standard crud options *** */
/**
* updates the audit field of the entity class.
*
* @param audit the audit to apply
* @param onCreate specifies, whether the the update should be applied for a new entity or not. If yes, then also the createdBy /createdOn fields are updated. Otherwise these fields are skipped.
* @param changedBy the username to apply
* @return an updated audit object to use for the updated entity.
* @throws ControllerException if the audit object cannot be updated
*/
public Audit applyAuditChanges(Audit audit, boolean onCreate, String changedBy) throws ControllerException {
if (audit == null) {
audit = new Audit();
}
if (onCreate) {
audit.setCreatedBy(changedBy);
audit.setCreatedOn(LocalDateTime.now(ZoneOffset.UTC));
}
audit.setLastUpdatedBy(changedBy);
audit.setLastUpdatedOn(LocalDateTime.now(ZoneOffset.UTC));
return audit;
}
/**
* creates an new entity or if the entity already exists updates it
*
* @param <T> the type of the entity to search for
* @param entity the entity to create or update
* @param createdBy the username to apply write into the audit history
* @return the entity after it has been written to the database.
* @throws ControllerException if the the creation or update of the entity fails.
*/
@TransactionAttribute(TransactionAttributeType.REQUIRED)
@Transactional
@Lock(LockType.WRITE)
public <T extends IdentifiableEntity> T createOrUpdate(T entity, String createdBy) throws ControllerException {
if (entity.getId() == null) {
create(entity, createdBy);
return entity;
} else {
return update(entity, createdBy);
}
}
/**
* creates an new entity.
*
* @param <T> the type of the entity to handle
* @param entity the entity to create or update
* @param createdBy the username to apply write into the audit history
* @return the entity after it has been written to the database.
* @throws ControllerException if the the creation fails
*/
@TransactionAttribute(TransactionAttributeType.REQUIRED)
@Transactional
@Lock(LockType.WRITE)
public <T> T create(T entity, String createdBy) throws ControllerException {
if (Auditable.class.isAssignableFrom(entity.getClass())) {
Audit audit = ((Auditable) entity).getAudit();
((Auditable) entity).setAudit(applyAuditChanges(audit, true, createdBy));
}
if (EndDateable.class.isAssignableFrom(entity.getClass())) {
EndDateable endDateable = (EndDateable) entity;
if (endDateable.getValidity() == null) {
Validity validity = new Validity();
endDateable.setValidity(validity);
}
if (endDateable.getValidity().getValidFrom() == null) {
endDateable.getValidity().setValidFrom(DateUtil.getCurrentTimeInUTC());
}
}
em.persist(entity);
return entity;
}
/**
* updates an existing entity.
*
* @param <T> the type of the entity to handle
* @param entity the entity to update
* @param updatedBy the username to apply write into the audit history
* @return the entity after it has been written to the database.
* @throws ControllerException if the the updates fails
*/
private <T extends Serializable> T executeUpdate(T entity, String updatedBy) throws ControllerException {
T currentEntity = attach(entity);
if (Auditable.class.isAssignableFrom(currentEntity.getClass())) {
Audit audit = ((Auditable) currentEntity).getAudit();
((Auditable) currentEntity).setAudit(applyAuditChanges(audit, false, updatedBy));
}
if (EndDateable.class.isAssignableFrom(currentEntity.getClass())) {
// end date existing entity
EndDateable endDateable = (EndDateable) currentEntity;
Validity validity = endDateable.getValidity();
if (validity == null) {
validity = new Validity();
validity.setValidFrom(DateUtil.getCurrentTimeInUTC());
}
validity.setValidTo(DateUtil.getCurrentTimeInUTC());
endDateable.setValidity(validity);
// and create new entity instead
}
return currentEntity;
}
/**
* updates an existing entity.
*
* @param <T> the type of the entity to handle
* @param entity the entity to update
* @param updatedBy the username to apply write into the audit history
* @return the entity after it has been written to the database.
* @throws ControllerException if the the updates fails
*/
@TransactionAttribute(TransactionAttributeType.REQUIRED)
@Transactional
@Lock(LockType.WRITE)
public <T extends Serializable> T update(T entity, String updatedBy) throws ControllerException {
T currentEntity = executeUpdate(entity, updatedBy);
if (EndDateable.class.isAssignableFrom(entity.getClass())) {
T newEntity = EntityUtil.cloneToNewEntity(currentEntity);
em.merge(currentEntity);
return create(newEntity, updatedBy);
} else {
// if it is not enddatable, just update it (already done above)
// and save it
return em.merge(currentEntity);
}
}
/**
* Deletes an entity from the database. If the entity implements the {@link EndDateable} interface, the entity is not deleted but marked as deleted (end date set).
*
* @param <T> the type of the entity to handle
* @param entity the entity to delete
* @param deletedBy the username to apply write into the audit history
* @throws ControllerException if the deletion fails.
*/
@TransactionAttribute(TransactionAttributeType.REQUIRED)
@Transactional
@Lock(LockType.WRITE)
public <T extends Serializable> void delete(T entity, String deletedBy) throws ControllerException {
// if the entity is endDateable just set the validToDate to now intead of executing the deletion
if (EndDateable.class.isAssignableFrom(entity.getClass())) {
T currentEntity = executeUpdate(entity, deletedBy);
em.merge(currentEntity);
} else {
em.remove(attach(entity));
}
}
/**
* attaches the given entity
*
* @param <T> the type of the entity to handle
* @param entity the entity to attach
* @return the entity after it has been attached
*/
public <T extends Serializable> T attach(T entity) {
return em.merge(entity);
}
/**
* Refreshes an entity.
*
* @param <T> the type of the entity to handle
* @param entity the entity after it has been refreshed.
*/
public <T extends Serializable> void refresh(T entity) {
em.refresh(entity);
}
/* *** filter methods *** */
/**
* Creates a filter condition.
*
* @param <T> the type of the entity to handle
* @param cb the CriteriaBuilder to use
* @param root the root to use
* @param filters the filters to use
* @return the created filter condition
*/
protected <T extends Serializable> Predicate getFilterCondition(CriteriaBuilder cb, Root<T> root, Map<String, Object> filters) {
return getFilterCondition(cb, root, filters, null);
}
/**
* Creates a filter condition.
*
* @param <T> the type of the entity to handle
* @param cb the CriteriaBuilder to use
* @param root the root to use
* @param filters the filters to use
* @param excludeFilters the exclude filters to apply. Entities which match these filters, are not taken into the result list.
* @return the created filter condition
*/
protected <T extends Serializable> Predicate getFilterCondition(CriteriaBuilder cb, Root<T> root, Map<String, Object> filters, Map<String, Object> excludeFilters) {
Predicate includeFilterCondition = getFilterCondition(null, cb, root, filters, true);
Predicate excludeFilterCondition = getFilterCondition(null, cb, root, excludeFilters, false);
if ((includeFilterCondition == null) && (excludeFilterCondition == null)) {
return null;
} else if (includeFilterCondition == null) {
return excludeFilterCondition;
} else if (excludeFilterCondition == null) {
return includeFilterCondition;
} else {
return cb.and(includeFilterCondition, excludeFilterCondition);
}
}
/**
* extends the given filterCondition by the additional filters
*
* @param <T> the type of the entity to handle
* @param filterCondition the current filter condition
* @param cb the criteria builder to use
* @param root the root of the object to search for
* @param filters the filters to apply
* @param include if set to true, the filter is used as include filter (equals, in). If set to false, the filter is inverted and used as exclude filter (not equals, not in etc)
* @return the created filter condition
*/
protected <T extends Serializable> Predicate getFilterCondition(Predicate filterCondition, CriteriaBuilder cb, Root<T> root, Map<String, Object> filters, boolean include) {
String wildCard = "%";
if (filters != null) {
for (Map.Entry<String, Object> filter : filters.entrySet()) {
if (!"".equals(filter.getValue())) {
Path<String> path = getPathElement(root, filter);
// check for differnt types
// 1st String, either from Enum Status or from other free text string
if (filter.getValue() == null) {
Predicate predicate;
if (include) {
predicate = cb.isNull(path);
} else {
predicate = cb.isNotNull(path);
}
filterCondition = addFilterCondition(cb, filterCondition, predicate);
} else if (String.class.equals(filter.getValue().getClass())) {
switch (filter.getKey()) {
default:
String filterValue = filter.getValue().toString();
Predicate predicate;
if (filterValue.equals("{NULL}")) {
if (include) {
predicate = cb.isNull(path);
} else {
predicate = cb.isNotNull(path);
}
} else {
filterValue = filterValue.trim();
filterValue = filterValue.replace("?", "_");
filterValue = filterValue.replace("*", "%");
String[] values = filterValue.split("\\s+"); // split by whitespaces
Predicate[] partSearchPredicates = new Predicate[values.length];
for (int i = 0; i < values.length; i++) {
String value = wildCard + values[i] + wildCard;
if (include) {
partSearchPredicates[i] = cb.like(cb.upper(path), value.toUpperCase(Locale.US), '\\');
} else {
partSearchPredicates[i] = cb.notLike(cb.upper(path), value.toUpperCase(Locale.US), '\\');
}
}
predicate = cb.and(partSearchPredicates); // all parts must be available
}
filterCondition = addFilterCondition(cb, filterCondition, predicate);
}
} // 2nd for arrays, received from e.g. project selections
else if (filter.getValue().getClass().isArray()) {
Predicate condition = null;
Object[] values = (Object[]) filter.getValue();
if (values.length > 0) {
for (Object value : values) {
if (include) {
Predicate equalPredicate = cb.equal(path, value);
if (condition == null) {
condition = equalPredicate;
} else {
condition = cb.or(condition, equalPredicate);
}
} else {
Predicate equalPredicate = cb.notEqual(path, value);
if (condition == null) {
condition = equalPredicate;
} else {
condition = cb.and(condition, equalPredicate);
}
}
}
filterCondition = addFilterCondition(cb, filterCondition, condition);
}
} else { // at last object comparison
if (include) {
filterCondition = addFilterCondition(cb, filterCondition, cb.equal(path, filter.getValue()));
} else {
filterCondition = addFilterCondition(cb, filterCondition, cb.notEqual(path, filter.getValue()));
}
}
}
}
}
return filterCondition;
}
protected <T extends Serializable> Predicate getFilterCondition(CriteriaBuilder cb, Root<T> root, List<SearchFilter> searchFilter) throws ControllerException {
return getFilterCondition(null, cb, root, searchFilter);
}
protected <T extends Serializable> Predicate getFilterCondition(final Predicate filterCondition, CriteriaBuilder cb, Root<T> root, List<SearchFilter> searchFilter) throws ControllerException {
Predicate returnCondition = filterCondition;
for (SearchFilter currentFilter : searchFilter) {
if (currentFilter.getComparator() == null) {
throw new ControllerException(ControllerException.INTERNAL_ERROR, "Comparator must not be null");
}
if (currentFilter.getFieldName() == null) {
throw new ControllerException(ControllerException.INTERNAL_ERROR, "Fieldname must not be null");
}
Path<String> path = getPathElement(root, currentFilter);
if (currentFilter.getSearchValue() == null) {
Predicate predicate;
switch (currentFilter.getComparator()) {
case EQUAL:
predicate = cb.isNull(path);
break;
case NOT_EQUAL:
predicate = cb.isNotNull(path);
default:
throw new ControllerException(ControllerException.INTERNAL_ERROR, currentFilter.getComparator() + " not supported for searchValue null");
}
returnCondition = addFilterCondition(cb, returnCondition, predicate);
} else if (currentFilter.getSearchValue() instanceof String) {
String searchValue = (String) currentFilter.getSearchValue();
String wildCard = "%";
Predicate predicate;
switch (currentFilter.getComparator()) {
case EQUAL:
predicate = cb.equal(path, searchValue);
break;
case NOT_EQUAL:
predicate = cb.notEqual(path, searchValue);
break;
case CONTAINS:
String likeValue = wildCard + searchValue.toUpperCase(Locale.US) + wildCard;
predicate = cb.like(cb.upper(path), likeValue, '\\');
break;
default:
throw new ControllerException(ControllerException.INTERNAL_ERROR, currentFilter.getComparator() + "not support for searchValue " + searchValue);
}
returnCondition = addFilterCondition(cb, returnCondition, predicate);
} else if (currentFilter.getSearchValue() instanceof Date) {
// TODO - support Date, LocaleDate, LocalTime, LocalDateTime, ZoneDateTime
Date searchValue = (Date) currentFilter.getSearchValue();
Predicate predicate;
Path<Date> datePath = root.<Date>get(currentFilter.getFieldName());
switch (currentFilter.getComparator()) {
case EQUAL:
predicate = cb.equal(datePath, searchValue);
break;
case NOT_EQUAL:
predicate = cb.notEqual(datePath, searchValue);
break;
case LESS_THAN:
predicate = cb.lessThan(datePath, searchValue);
break;
case LESS_OR_EQUAL_THAN:
predicate = cb.lessThanOrEqualTo(datePath, searchValue);
break;
case GREATER_THAN:
predicate = cb.greaterThan(datePath, searchValue);
break;
case GREATER_OR_EQUAL_THAN:
predicate = cb.greaterThanOrEqualTo(datePath, searchValue);
break;
default:
throw new ControllerException(ControllerException.INTERNAL_ERROR, currentFilter.getComparator() + "not support for searchValue " + searchValue);
}
returnCondition = addFilterCondition(cb, returnCondition, predicate);
} else if (currentFilter.getSearchValue() instanceof Boolean) {
Boolean searchValue = (Boolean) currentFilter.getSearchValue();
Path<Boolean> booleanPath = root.<Boolean>get(currentFilter.getFieldName());
Predicate predicate;
switch (currentFilter.getComparator()) {
case EQUAL:
predicate = cb.equal(booleanPath, searchValue);
break;
case NOT_EQUAL:
predicate = cb.notEqual(booleanPath, searchValue);
break;
default:
throw new ControllerException(ControllerException.INTERNAL_ERROR, currentFilter.getComparator() + "not support for searchValue " + searchValue);
}
returnCondition = addFilterCondition(cb, returnCondition, predicate);
} else if (currentFilter.getSearchValue() instanceof UUID) {
UUID searchValue = (UUID) currentFilter.getSearchValue();
Path<UUID> booleanPath = root.<UUID>get(currentFilter.getFieldName());
Predicate predicate;
switch (currentFilter.getComparator()) {
case EQUAL:
predicate = cb.equal(booleanPath, searchValue);
break;
case NOT_EQUAL:
predicate = cb.notEqual(booleanPath, searchValue);
break;
default:
throw new ControllerException(ControllerException.INTERNAL_ERROR, currentFilter.getComparator() + "not support for searchValue " + searchValue);
}
returnCondition = addFilterCondition(cb, returnCondition, predicate);
} else if (currentFilter.getSearchValue() instanceof List) {
List<Object> searchValues = (List) currentFilter.getSearchValue();
Predicate predicate = null;
if (!searchValues.isEmpty()) {
for (Object obj : searchValues) {
SearchFilter sf = new DefaultSearchFilter(currentFilter.getFieldName(), currentFilter.getComparator(), obj);
List<SearchFilter> localFilterList = new ArrayList<>();
localFilterList.add(sf);
Predicate localPredicate = getFilterCondition(null, cb, root, localFilterList);
predicate = orFilterCondition(cb, predicate, localPredicate);
}
returnCondition = addFilterCondition(cb, returnCondition, predicate);
}
// Path
} else {
throw new ControllerException(ControllerException.INTERNAL_ERROR, "Filter for " + currentFilter.getSearchValue().getClass().getSimpleName() + " not yet implemented");
}
} // for all filters
return returnCondition;
}
/**
* Adds a filter condition to an existing condition
*
* @param cb the builder to use
* @param filterCondition the existing filter condition
* @param addCondition the condition to add to the existing condition.
* @return an updated filter condition.
*/
protected Predicate addFilterCondition(CriteriaBuilder cb, Predicate filterCondition, Predicate addCondition) {
if (addCondition == null) {
return filterCondition;
}
if (filterCondition == null) {
filterCondition = addCondition;
} else {
filterCondition = cb.and(filterCondition, addCondition);
}
return filterCondition;
}
/**
* Adds a filter condition to an existing condition
*
* @param cb the builder to use
* @param filterCondition the existing filter condition
* @param orCondition the condition to add to the existing condition.
* @return an updated filter condition.
*/
protected Predicate orFilterCondition(CriteriaBuilder cb, Predicate filterCondition, Predicate orCondition) {
if (orCondition == null) {
return filterCondition;
}
if (filterCondition == null) {
filterCondition = orCondition;
} else {
filterCondition = cb.or(filterCondition, orCondition);
}
return filterCondition;
}
private <T extends Serializable> Path<String> getPathElement(Root<T> root, Map.Entry<String, Object> filter) {
String[] pathElements = StringUtils.split(filter.getKey(), '.');
Path<String> path = null;
for (String element : pathElements) {
if (path == null) {
path = root.get(element);
} else {
path = path.get(element);
}
}
return path;
}
private <T extends Serializable> Path<String> getPathElement(Root<T> root, SearchFilter filter) {
String[] pathElements = StringUtils.split(filter.getFieldName(), '.');
Path<String> path = null;
for (String element : pathElements) {
if (path == null) {
path = root.get(element);
} else {
path = path.get(element);
}
}
return path;
}
/**
* returns null, if the list is empty or null itself.Returns the one element if there is exactly one element in the list. Otherwise an exception is thrown
*
* @param <T> the type of the entity to handle
* @param entityList the list to validate
* @return the one and only element of the provided list
* @throws ControllerException if the list contains more than one element.
*/
public <T> T ensureSingleElement(List<T> entityList) throws ControllerException {
if ((entityList == null) || (entityList.isEmpty())) {
return null;
}
if (entityList.size() > 1) {
throw new ControllerException(ControllerException.CAUSE_TOO_MANY_ROWS, "More than one element found in list - expected exactly one");
}
return entityList.get(0);
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.db;
/**
*
* @author Joern Muehlencord (joern@muehlencord.de)
*/
public enum Comparator {
EQUAL,
NOT_EQUAL,
CONTAINS,
LESS_THAN,
LESS_OR_EQUAL_THAN,
GREATER_THAN,
GREATER_OR_EQUAL_THAN;
}

View File

@ -0,0 +1,71 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.db;
import javax.ejb.ApplicationException;
/**
* Generic exception if an exception inside a Controller class occurs
*
* @author Joern Muehlencord (joern@muehlencord.de)
*/
@ApplicationException(rollback = true)
public class ControllerException extends Exception {
private static final long serialVersionUID = 5190280225284514859L;
public static final int INTERNAL_ERROR = 0;
public static final int CAUSE_ALREADY_EXISTS = 1;
public static final int CAUSE_NOT_FOUND = 2;
public static final int CAUSE_CANNOT_PERSIST = 3;
public static final int CAUSE_TOO_MANY_ROWS = 4;
public static final int CAUSE_CANNOT_DELETE = 5;
public static final int INVALID_FILTER = 6;
private final int causeCode;
/**
* Creates a new instance of <code>ControllerException</code> without detail message.
*
* @param cause the reason code
* @param message the detail message. The detail message is saved for later retrieval by the {@link #getMessage()} method.
*/
public ControllerException(int cause, String message) {
super(message);
this.causeCode = cause;
}
/**
*
* @param causeCode the reason code
* @param message the detail message. The detail message is saved for later retrieval by the {@link #getMessage()} method.
*
* @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method). (A {@code null} value is permitted, and
* indicates that the cause is nonexistent or unknown.)
*/
public ControllerException(int causeCode, String message, Throwable cause) {
super(message, cause);
this.causeCode = causeCode;
}
/**
* returns the cause code
*
* @return the cause code
*/
public int getCauseCode() {
return causeCode;
}
}

View File

@ -0,0 +1,68 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.db;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
*
* @author Joern Muehlencord (joern@muehlencord.de)
*/
public class DefaultSearchFilter implements SearchFilter {
private final String fieldName;
private final Comparator comparator;
private final Object searchValue;
private final Operator operator;
public DefaultSearchFilter(String fieldName, Comparator comparator, Object fieldValue) {
this.fieldName = fieldName;
this.comparator = comparator;
this.searchValue = fieldValue;
this.operator = null;
}
public DefaultSearchFilter(String fieldName, Comparator comparator, Operator operator, Object... fieldValue) {
this.fieldName = fieldName;
this.comparator = comparator;
List<Object> searchValues = new ArrayList();
searchValues.addAll(Arrays.asList(fieldValue));
this.searchValue = searchValues;
this.operator = operator;
}
@Override
public String getFieldName() {
return fieldName;
}
@Override
public Comparator getComparator() {
return comparator;
}
@Override
public Object getSearchValue() {
return searchValue;
}
public Operator getOperator() {
return operator;
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.db;
/**
* Enddateable entities are not deleted but an enddate is set to "now"
*
* @author Joern Muehlencord (joern@muehlencord.de)
*/
public interface EndDateable<T> {
void setValidity(Validity v);
Validity getValidity();
}

View File

@ -0,0 +1,119 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.db;
import de.muehlencord.shared.util.DateUtil;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import javax.persistence.Id;
import org.apache.commons.lang3.SerializationUtils;
import org.slf4j.LoggerFactory;
/**
*
* @author Joern Muehlencord (joern@muehlencord.de)
*/
public class EntityUtil {
private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(EntityUtil.class);
public static Field getIdField(Class<?> entity) {
if (entity == null) {
return null;
}
for (var f : entity.getDeclaredFields()) {
Id id = null;
Annotation[] as = f.getAnnotations();
for (Annotation a : as) {
if (a.annotationType() == Id.class) {
id = (Id) a;
}
}
if (id != null) {
return f;
}
}
// iterated over all fields, not found
return null;
}
public static <T> T setIdValue(T entity, Object fieldValue) throws ControllerException {
Field field = getIdField(entity.getClass());
if (field == null) {
return entity;
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("id column of {} is {}", entity.getClass().getSimpleName(), field.getName());
}
try {
PropertyDescriptor pd = new PropertyDescriptor(field.getName(), entity.getClass());
pd.getWriteMethod().invoke(entity, fieldValue);
return entity;
} catch (IllegalAccessException | InvocationTargetException | IntrospectionException ex) {
String hint = "Error setting value of field " + field.getName() + " to " + fieldValue;
if (fieldValue != null) {
hint += " of type " + fieldValue.getClass().getSimpleName();
}
throw new ControllerException(ControllerException.INTERNAL_ERROR, hint, ex);
}
}
/**
* clones the given entity and updates related fields so the entity appears as new.The following changes are
* executed
* <ul>
* <li>the Id field of the entity is set to null</li>
* <li>if the entity is auditable, the audit is set to null.</li>
* <li>if the entity is enddatable, validTo is set to null and validFrom is set to current sysdate (in UTC)
* </ul>
*
* @param <T> the entity to be cloned - must implement serializeable to be cloned
* @param entity the entity to be cloned - must implement serializeable to be cloned
* @return the cloned entity with updated fields as described above
* @throws de.muehlencord.shared.db.ControllerException if the id value cannot be set to null
*/
public static <T extends Serializable> T cloneToNewEntity(T entity) throws ControllerException {
T newEntity = SerializationUtils.clone(entity);
// ensure id column is set to null
newEntity = setIdValue(newEntity, null);
// remove audit if class is auditable - it is a new clone
if (Auditable.class.isAssignableFrom(newEntity.getClass())) {
((Auditable) newEntity).setAudit(null);
}
// set new valid dates if class is enddateable
if (EndDateable.class.isAssignableFrom(newEntity.getClass())) {
Validity validity = new Validity();
validity.setValidFrom(DateUtil.getCurrentTimeInUTC());
validity.setValidTo(null);
((EndDateable) newEntity).setValidity(validity);
}
return newEntity;
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.db;
import java.io.Serializable;
/**
*
* @author Joern Muehlencord (joern@muehlencord.de)
*/
public interface IdentifiableEntity extends Serializable{
Object getId();
String getIdString();
}

View File

@ -0,0 +1,25 @@
package de.muehlencord.shared.db;
import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
@MappedSuperclass
public abstract class NumericallyIdentifiedEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.db;
/**
*
* @author Joern Muehlencord (joern@muehlencord.de)
*/
public enum Operator {
AND, OR;
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.db;
/**
*
* @author Joern Muehlencord (joern@muehlencord.de)
*/
public interface SearchFilter {
Comparator getComparator();
String getFieldName();
Object getSearchValue();
}

View File

@ -0,0 +1,29 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.db;
import javax.ejb.Stateless;
/**
*
* @author Joern Muehlencord (joern@muehlencord.de)
*/
@Stateless
public class StandardController extends CommonAbstractController {
}

View File

@ -0,0 +1,59 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.db;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.NotNull;
/**
*
* @author Joern Muehlencord (joern@muehlencord.de)
*/
@Embeddable
public class Validity implements Serializable {
@Basic(optional = false)
@NotNull
@Column(name = "valid_from")
@Temporal(TemporalType.TIMESTAMP)
private Date validFrom;
@Column(name = "valid_to")
@Temporal(TemporalType.TIMESTAMP)
private Date validTo;
public Date getValidFrom() {
return validFrom;
}
public void setValidFrom(Date validFrom) {
this.validFrom = validFrom;
}
public Date getValidTo() {
return validTo;
}
public void setValidTo(Date validTo) {
this.validTo = validTo;
}
}

View File

@ -0,0 +1,109 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.db;
import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
/**
*
* @author Joern Muehlencord (joern@muehlencord.de)
*/
@Entity
@Table(name = "counter")
@NamedQueries({
@NamedQuery(name = "CounterEntity.findAll", query = "SELECT c FROM CounterEntity c")
, @NamedQuery(name = "CounterEntity.findByCounterKey", query = "SELECT c FROM CounterEntity c WHERE c.counterKey = :counterKey")
, @NamedQuery(name = "CounterEntity.findByCounterDefinition", query = "SELECT c FROM CounterEntity c WHERE c.counterDefinition = :counterDefinition")})
public class CounterEntity implements Serializable {
private static final long serialVersionUID = -5104103828013760003L;
@Id
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 100)
@Column(name = "counter_key")
private String counterKey;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 100)
@Column(name = "counter_definition")
private String counterDefinition;
public CounterEntity() {
}
public CounterEntity(String counterKey) {
this.counterKey = counterKey;
}
public CounterEntity(String counterKey, String counterDefinition) {
this.counterKey = counterKey;
this.counterDefinition = counterDefinition;
}
public String getCounterKey() {
return counterKey;
}
public void setCounterKey(String counterKey) {
this.counterKey = counterKey;
}
public String getCounterDefinition() {
return counterDefinition;
}
public void setCounterDefinition(String counterDefinition) {
this.counterDefinition = counterDefinition;
}
@Override
public int hashCode() {
int hash = 0;
hash += (counterKey != null ? counterKey.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof CounterEntity)) {
return false;
}
CounterEntity other = (CounterEntity) object;
if ((this.counterKey == null && other.counterKey != null) || (this.counterKey != null && !this.counterKey.equals(other.counterKey))) {
return false;
}
return true;
}
@Override
public String toString() {
return "de.muehlencord.office.entity.core.CounterEntity[ counterKey=" + counterKey + " ]";
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.db;
import java.lang.reflect.Field;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author Joern Muehlencord (joern@muehlencord.de)
*/
public class EntityUtilTest {
private static final Logger LOGGER = LoggerFactory.getLogger(EntityUtilTest.class);
@Test
public void testGetId() {
Field idField = EntityUtil.getIdField(CounterEntity.class);
assertNotNull(idField);
assertEquals("counterKey", idField.getName());
}
@Test
public void testSetIdNull() throws IllegalArgumentException, IllegalAccessException, ControllerException {
CounterEntity counter = new CounterEntity();
counter.setCounterDefinition("counterDefinitionValue");
counter.setCounterKey("counterKeyValue");
LOGGER.info("Counter after creation: {}", counter.toString());
Field idField = EntityUtil.getIdField(CounterEntity.class);
assertNotNull(idField);
assertEquals("counterKey", idField.getName());
// assertEquals("counterKeyValue", idField.get(counter));
counter = EntityUtil.setIdValue(counter, null);
// assertNull (idField.get (counter));
assertNull (counter.getCounterKey());
LOGGER.info("Counter after update: {}", counter.toString());
}
}

View File

@ -0,0 +1,211 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.db;
import java.io.Serializable;
import java.util.UUID;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
/**
*
* @author Joern Muehlencord (joern@muehlencord.de)
*/
@Entity
@Table(name = "address")
@NamedQueries({
@NamedQuery(name = "AddressEntity.findAll", query = "SELECT a FROM AddressEntity a"),
@NamedQuery(name = "AddressEntity.findByStreet", query = "SELECT a FROM AddressEntity a WHERE a.street = :street"),
@NamedQuery(name = "AddressEntity.findByPostalArea", query = "SELECT a FROM AddressEntity a WHERE a.postalArea = :postalArea"),
@NamedQuery(name = "AddressEntity.findByCity", query = "SELECT a FROM AddressEntity a WHERE a.city = :city"),
@NamedQuery(name = "AddressEntity.findByValidFrom", query = "SELECT a FROM AddressEntity a WHERE a.validFrom = :validFrom"),
@NamedQuery(name = "AddressEntity.findByValidTo", query = "SELECT a FROM AddressEntity a WHERE a.validTo = :validTo")})
public class TestEntity implements Serializable, Auditable, EndDateable<TestEntity> {
private static final long serialVersionUID = 1L;
@Id
@Basic(optional = false)
@NotNull
@Column(name = "id")
@GeneratedValue(generator = "uuid2")
private UUID id;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 200)
@Column(name = "street")
private String street;
@Size(max = 10)
@Column(name = "street_number")
private String streetNumber;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 10)
@Column(name = "postal_area")
private String postalArea;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 50)
@Column(name = "city")
private String city;
@Column(name = "government_district")
private String governmentDistrict;
@Min(value = -90)
@Max(value = 90)
@Column(name = "latitude")
private Double latitude;
@Min(value = -180)
@Max(value = 180)
@Column(name = "longitude")
private Double longitude;
@Embedded
private Validity validity;
@Embedded
private Audit audit;
public TestEntity() {
// empty constructor required for JPA
}
public UUID getId() {
return id;
}
public String getIdString() {
if (id == null) {
return "unknown";
} else {
return id.toString();
}
}
public void setId(UUID id) {
this.id = id;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getStreetNumber() {
return streetNumber;
}
public void setStreetNumber(String streetNumber) {
this.streetNumber = streetNumber;
}
public String getPostalArea() {
return postalArea;
}
public void setPostalArea(String postalArea) {
this.postalArea = postalArea;
}
public String getGovernmentDistrict() {
return governmentDistrict;
}
public void setGovernmentDistrict(String governmentDistrict) {
this.governmentDistrict = governmentDistrict;
}
public Double getLatitude() {
return latitude;
}
public void setLatitude(Double latitude) {
this.latitude = latitude;
}
public Double getLongitude() {
return longitude;
}
public void setLongitude(Double longitude) {
this.longitude = longitude;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
public Validity getValidity() {
return validity;
}
@Override
public void setValidity(Validity validity) {
this.validity = validity;
}
@Override
public Audit getAudit() {
return audit;
}
@Override
public void setAudit(Audit audit) {
this.audit = audit;
}
@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof TestEntity)) {
return false;
}
TestEntity other = (TestEntity) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
@Override
public String toString() {
return "de.muehlencord.office.entity.party.AddressEntity[ id=" + id + " ]";
}
}

View File

@ -1,24 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<artifactId>shared</artifactId> <artifactId>shared</artifactId>
<groupId>de.muehlencord</groupId> <groupId>de.muehlencord</groupId>
<version>1.0</version> <version>1.2-SNAPSHOT</version>
</parent> </parent>
<groupId>de.muehlencord.shared</groupId> <groupId>de.muehlencord.shared</groupId>
<artifactId>shared-jeeutil</artifactId> <artifactId>shared-jeeutil</artifactId>
<version>1.0</version> <packaging>ejb</packaging>
<packaging>jar</packaging>
<name>shared-jeeutil</name> <name>shared-jeeutil</name>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.inversoft</groupId> <groupId>io.fusionauth</groupId>
<artifactId>prime-jwt</artifactId> <artifactId>fusionauth-jwt</artifactId>
<version>1.3.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>javax</groupId> <groupId>javax</groupId>

View File

@ -0,0 +1,103 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.jeeutil;
import javax.faces.application.FacesMessage;
import javax.faces.application.FacesMessage.Severity;
/**
*
* @author Joern Muehlencord (joern@muehlencord.de)
*/
public class DefaultUIMessage implements UIMessage {
private static final long serialVersionUID = 3417186732917401077L;
public static DefaultUIMessage create(String message) {
return new DefaultUIMessage(UIMessageType.INFO, message, null);
}
public static DefaultUIMessage createWarning(String message) {
return new DefaultUIMessage(UIMessageType.WARNING, message, null);
}
public static DefaultUIMessage create(UIMessageType messageType, String message, String detail) {
return new DefaultUIMessage(messageType, message, detail);
}
private UIMessageType messageType;
private String message;
private String detail;
public DefaultUIMessage(UIMessageType messageType, String message) {
this.messageType = messageType;
this.message = message;
}
private DefaultUIMessage(UIMessageType messageType, String message, String detail) {
this.messageType = messageType;
this.message = message;
this.detail = detail;
}
@Override
public UIMessageType getMessageType() {
return messageType;
}
@Override
public String getMessage() {
return message;
}
@Override
public String getDetail() {
return (detail == null) ? "" : detail;
}
@Override
public FacesMessage getFacesMessage() {
return getFacesMessage(null);
}
@Override
public FacesMessage getFacesMessage(String prefix) {
Severity severity;
switch (getMessageType()) {
case INFO:
severity = FacesMessage.SEVERITY_INFO;
break;
case WARNING:
severity = FacesMessage.SEVERITY_WARN;
break;
case ERROR:
severity = FacesMessage.SEVERITY_WARN;
break;
default:
severity = FacesMessage.SEVERITY_ERROR;
break;
}
String summary;
if ((prefix == null) || (prefix.equals(""))) {
summary = getMessage();
} else {
summary = prefix + ": " + getMessage();
}
return new FacesMessage(severity, summary, getDetail());
}
}

View File

@ -1,11 +1,28 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.jeeutil; package de.muehlencord.shared.jeeutil;
import java.util.List;
import javax.faces.application.FacesMessage; import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext; import javax.faces.context.FacesContext;
/** /**
* Helper class for java faces application.
* *
* @author joern.muehlencord * @author Joern Muehlencord (joern@muehlencord.de)
*/ */
public abstract class FacesUtil { public abstract class FacesUtil {
@ -13,56 +30,313 @@ public abstract class FacesUtil {
// hide constructor of abstract class // hide constructor of abstract class
} }
/**
* returns true, if the current request is in the request phase. False otherwise.
*
* @return true, if the current request is in the request phase. False otherwise.
*/
public static boolean isRenderRequest() {
return !FacesContext.getCurrentInstance().getRenderResponse();
}
/**
* returns true, if the current request is in the response phase. True otherwise.
*
* @return true, if the current request is in the response phase. True otherwise.
*/
public static boolean isRenderResponse() {
return FacesContext.getCurrentInstance().getRenderResponse();
}
/**
* Adds the given message to the "messages" object
*
* @param message the message to display
* @deprecated use addMessage(clientId, message) or addGlobalMessage(message) instead
*/
@Deprecated
public static void addMessage(FacesMessage message) { public static void addMessage(FacesMessage message) {
FacesContext.getCurrentInstance().addMessage("messages", message); FacesContext.getCurrentInstance().addMessage("messages", message);
} }
/**
* Adds the given message to the object with the id specified by clientId.
*
* @param clientId the id of the object to bind the message to.
* @param message the message to display
*/
public static void addMessage(String clientId, FacesMessage message) { public static void addMessage(String clientId, FacesMessage message) {
FacesContext.getCurrentInstance().addMessage(clientId, message); FacesContext.getCurrentInstance().addMessage(clientId, message);
} }
/**
* Adds the given message to the object with the id specified by clientId.
*
* @param clientId the id of the object to bind the message to.
* @param message the message to display
*/
public static void addMessage(String clientId, FacesMessage message, boolean validationFailed) {
FacesContext.getCurrentInstance().addMessage(clientId, message);
if (validationFailed) {
FacesContext.getCurrentInstance().validationFailed();
}
}
/**
* Adds the given message as global message.
*
* @param message the message to display
*/
public static void addGlobalMessage(FacesMessage message) {
FacesContext.getCurrentInstance().addMessage(null, message);
}
/**
* Adds the given message as global message with severity "Information".
*
* @param summary the message to add
*/
public static void addGlobalInfoMessage(String summary) {
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, summary, null);
FacesContext.getCurrentInstance().addMessage(null, message);
}
/**
* Adds the given message as global message with severity "Information".
*
* @param summary the summary message to display
* @param detail the detailed message to display
*/
public static void addGlobalInfoMessage(String summary, String detail) {
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, summary, detail);
FacesContext.getCurrentInstance().addMessage(null, message);
}
/**
* Adds the given message as global message with severity "Warning".
*
* @param summary the summary message to display
*/
public static void addGlobalWarningMessage(String summary) {
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_WARN, summary, null);
FacesContext.getCurrentInstance().addMessage(null, message);
}
/**
* Adds the given message as global message with severity "Warning".
*
* @param summary the summary message to display
* @param detail the detailed message to display
*/
public static void addGlobalWarningMessage(String summary, String detail) {
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_WARN, summary, detail);
FacesContext.getCurrentInstance().addMessage(null, message);
}
/**
* Adds the given message as global message with severity "Error".
*
* @param summary the message to display
*/
public static void addGlobalErrorMessage(String summary) {
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, summary, null);
FacesContext.getCurrentInstance().addMessage(null, message);
}
/**
* Adds the given message as global message with severity "Error".
*
* @param summary the summary message to display
* @param detail the detailed message to display
*/
public static void addGlobalErrorMessage(String summary, String detail) {
addGlobalErrorMessage(summary, detail, false);
}
/**
* Adds the given message as global message with severity "Error".
*
* @param summary the summary message to display
* @param detail the detailed message to display
* @param valiationFailed if set to true, the method calls currentInstance().validationFailed() to invalidate the current request.
*/
public static void addGlobalErrorMessage(String summary, String detail, boolean valiationFailed) {
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, summary, detail);
FacesContext.getCurrentInstance().addMessage(null, message);
if (valiationFailed) {
FacesContext.getCurrentInstance().validationFailed();
}
}
/**
* Adds the given message as global message with severity "Fatal".
*
* @param summary the message to display
*/
public static void addGlobalFatalMessage(String summary) {
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_FATAL, summary, null);
FacesContext.getCurrentInstance().addMessage(null, message);
}
/**
* Adds the given message as global message with severity "Fatal".
*
* @param summary the summary message to display
* @param detail the detailed message to display
*/
public static void addGlobalFatalMessage(String summary, String detail) {
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_FATAL, summary, detail);
FacesContext.getCurrentInstance().addMessage(null, message);
}
/**
* Adds the given message to the object with id "messages" and severity "Information".
*
* @param summary the message to display
* @deprecated use addInfoMessage(clientId, summary) or addGlobalInfoMessage instead
*/
@Deprecated
public static void addInfoMessage(String summary) { public static void addInfoMessage(String summary) {
addInfoMessage(summary, ""); addInfoMessage(summary, "");
} }
/**
* Adds the given message to the object with id "messages" and severity "Information".
*
* @param summary the summary message to display
* @param detail the detailed message to display
* @deprecated use addInfoMessage (clientId, summary, detail) or addGlobalInfoMessage(summary, detail) instead
*/
@Deprecated
public static void addInfoMessage(String summary, String detail) { public static void addInfoMessage(String summary, String detail) {
addInfoMessage("messages", summary, detail); addInfoMessage("messages", summary, detail);
} }
/**
* Adds the given message with severity "Info" to the object with the id specified by clientId.
*
* @param clientId the id of the object to bind the message to.
* @param summary the summary message to display
* @param detail the detailed message to display
*/
public static void addInfoMessage(String clientId, String summary, String detail) { public static void addInfoMessage(String clientId, String summary, String detail) {
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, summary, detail); FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, summary, detail);
FacesContext.getCurrentInstance().addMessage(clientId, message); FacesContext.getCurrentInstance().addMessage(clientId, message);
} }
/**
* Adds the given message to the object with id "warnings" and severity "Error".
*
* @param summary the message to display
* @deprecated use addErrorMessage (clientId, summary, detail) or addGlobalErrorMessage(summary, detail) instead
*/
@Deprecated
public static void addErrorMessage(String summary) { public static void addErrorMessage(String summary) {
addErrorMessage("warnings", summary, ""); addErrorMessage("warnings", summary, "");
} }
/**
* Adds the given message to the object with id "warnings" and severity "Error".
*
*
* @param summary the summary message to display
* @param detail the detailed message to display
* @deprecated use addErrorMessage (clientId, summary, detail) or addGlobalErrorMessage(summary, detail) instead
*/
@Deprecated
public static void addErrorMessage(String summary, String detail) { public static void addErrorMessage(String summary, String detail) {
addErrorMessage("warnings", summary, detail); addErrorMessage("warnings", summary, detail);
} }
/**
* Adds the given message with severity "Error" to the object with the id specified by clientId.
*
* @param clientId the id of the object to bind the message to.
* @param summary the summary message to display
* @param detail the detailed message to display
*/
public static void addErrorMessage(String clientId, String summary, String detail) { public static void addErrorMessage(String clientId, String summary, String detail) {
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, summary, detail); FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, summary, detail);
FacesContext.getCurrentInstance().addMessage(clientId, message); FacesContext.getCurrentInstance().addMessage(clientId, message);
} }
/**
* Adds the given message to the object with id "warnings" and severity "Warning".
*
* @param summary the message to display
* @deprecated use addWarningMessage (clientId, summary, detail) or addGlobalWarningMessage(summary, detail) instead
*/
@Deprecated
public static void addWarningMessage(String summary) { public static void addWarningMessage(String summary) {
addWarningMessage("warnings", summary, ""); addWarningMessage("warnings", summary, "");
} }
/**
* Adds the given message to the object with id "warnings" and severity "Warning".
*
* @param summary the summary message to display
* @param detail the detailed message to display
* @deprecated use addWarningMessage (clientId, summary, detail) or addGlobalWarningMessage(summary, detail) instead
*/
@Deprecated
public static void addWarningMessage(String summary, String detail) { public static void addWarningMessage(String summary, String detail) {
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_WARN, summary, detail); FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_WARN, summary, detail);
FacesContext.getCurrentInstance().addMessage("warnings", message); FacesContext.getCurrentInstance().addMessage("warnings", message);
} }
/**
* Adds the given message with severity "Warning" to the object with the id specified by clientId.
*
* @param clientId the id of the object to bind the message to.
* @param summary the summary message to display
* @param detail the detailed message to display
*/
public static void addWarningMessage(String clientId, String summary, String detail) { public static void addWarningMessage(String clientId, String summary, String detail) {
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_WARN, summary, detail); FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_WARN, summary, detail);
FacesContext.getCurrentInstance().addMessage(clientId, message); FacesContext.getCurrentInstance().addMessage(clientId, message);
} }
/**
* Adds the given message to the object with id "warnings" and severity "Fatal".
*
* @param summary the summary message to display
* @param detail the detailed message to display
* @deprecated use addFatalMessage (clientId, summary, detail) or addGlobalFatalMessage(summary, detail) instead
*/
@Deprecated
public static void addFatalMessage(String summary, String detail) { public static void addFatalMessage(String summary, String detail) {
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_FATAL, summary, detail); FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_FATAL, summary, detail);
FacesContext.getCurrentInstance().addMessage("warnings", message); FacesContext.getCurrentInstance().addMessage("warnings", message);
} }
/**
* Adds the given message with severity "Fatal" to the object with the id specified by clientId.
*
* @param clientId the id of the object to bind the message to.
* @param summary the summary message to display
* @param detail the detailed message to display
*/
public static void addFatalMessage(String clientId, String summary, String detail) {
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_FATAL, summary, detail);
FacesContext.getCurrentInstance().addMessage(clientId, message);
}
/**
*
* @param messages the messages to add
*/
public static void addMessages(List<UIMessage> messages) {
messages.stream().map(msg -> msg.getFacesMessage()).forEach(msg -> {
if (msg.getSeverity() == FacesMessage.SEVERITY_ERROR) {
FacesUtil.addGlobalErrorMessage(msg.getSummary(), msg.getDetail());
} else if (msg.getSeverity() == FacesMessage.SEVERITY_WARN) {
FacesUtil.addGlobalWarningMessage(msg.getSummary(), msg.getDetail());
} else if (msg.getSeverity() == FacesMessage.SEVERITY_FATAL) {
FacesUtil.addGlobalFatalMessage(msg.getSummary(), msg.getDetail());
} else {
FacesUtil.addGlobalInfoMessage(msg.getSummary(), msg.getDetail());
}
});
}
} }

View File

@ -1,3 +1,18 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.jeeutil; package de.muehlencord.shared.jeeutil;
import java.io.Serializable; import java.io.Serializable;
@ -7,12 +22,14 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.HashMap; import java.util.HashMap;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.usertype.UserType; import org.hibernate.usertype.UserType;
/** /**
* http://octagen.at/2014/10/postgresql-custom-data-types-enum-in-hibernate/ * http://octagen.at/2014/10/postgresql-custom-data-types-enum-in-hibernate/
* *
* @author joern@muehlencord.de * @author Joern Muehlencord (joern@muehlencord.de)
*/ */
public abstract class GenericEnumType<T, E extends Enum<E>> implements UserType, Serializable { public abstract class GenericEnumType<T, E extends Enum<E>> implements UserType, Serializable {
@ -39,6 +56,16 @@ public abstract class GenericEnumType<T, E extends Enum<E>> implements UserType,
this.sqlType = sqlType; this.sqlType = sqlType;
} }
@Override
public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException {
return nullSafeGet(rs, names, owner);
}
@Override
public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException {
nullSafeSet(st, value, index);
}
@Override @Override
public Object assemble(Serializable cached, Object owner) { public Object assemble(Serializable cached, Object owner) {
return cached; return cached;

View File

@ -0,0 +1,37 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.jeeutil;
import java.io.Serializable;
import javax.faces.application.FacesMessage;
/**
*
* @author Joern Muehlencord (joern@muehlencord.de)
*/
public interface UIMessage extends Serializable {
String getDetail();
String getMessage();
UIMessageType getMessageType();
FacesMessage getFacesMessage();
FacesMessage getFacesMessage(String prefix);
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.jeeutil;
/**
*
* @author Joern Muehlencord (joern@muehlencord.de)
*/
public enum UIMessageType {
INFO,
WARNING,
ERROR;
}

View File

@ -1,23 +1,31 @@
/* /*
* To change this license header, choose License Headers in Project Properties. * Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
* To change this template file, choose Tools | Templates *
* and open the template in the editor. * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
package de.muehlencord.shared.jeeutil.jwt; package de.muehlencord.shared.jeeutil.jwt;
import io.fusionauth.jwt.Verifier;
import io.fusionauth.jwt.domain.JWT;
import io.fusionauth.jwt.hmac.HMACVerifier;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import org.primeframework.jwt.Verifier;
import org.primeframework.jwt.domain.JWT;
import org.primeframework.jwt.domain.JWTException;
import org.primeframework.jwt.hmac.HMACVerifier;
/** /**
* * @author Joern Muehlencord (joern@muehlencord.de)
* @author Joern Muehlencord <joern at muehlencord.de>
*/ */
public class JWTDecoder { public class JWTDecoder {
private boolean parsedSuccessfully = false; private boolean parsedSuccessfully;
private JWT jwt = null; private JWT jwt = null;
public JWTDecoder(String password, String issuer, String jwtString) throws JWTException { public JWTDecoder(String password, String issuer, String jwtString) throws JWTException {
@ -25,13 +33,8 @@ public class JWTDecoder {
throw new JWTException("password, issuer and jwt must not be null"); throw new JWTException("password, issuer and jwt must not be null");
} }
Verifier verifier = HMACVerifier.newVerifier(password); Verifier verifier = HMACVerifier.newVerifier(password);
// Verifier verifier = RSAVerifier.newVerifier(new String(Files.readAllBytes(Paths.get("public_key.pem"))));
try {
jwt = JWT.getDecoder().decode(jwtString, verifier); jwt = JWT.getDecoder().decode(jwtString, verifier);
parsedSuccessfully = jwt.issuer.equals(issuer); parsedSuccessfully = jwt != null && jwt.issuer.equals(issuer);
} catch (JWTException ex) {
jwt = null;
}
} }
public String getIssuer() { public String getIssuer() {

View File

@ -1,18 +1,28 @@
/* /*
* To change this license header, choose License Headers in Project Properties. * Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
* To change this template file, choose Tools | Templates *
* and open the template in the editor. * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
package de.muehlencord.shared.jeeutil.jwt; package de.muehlencord.shared.jeeutil.jwt;
import io.fusionauth.jwt.Signer;
import io.fusionauth.jwt.domain.JWT;
import io.fusionauth.jwt.hmac.HMACSigner;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import org.primeframework.jwt.Signer;
import org.primeframework.jwt.domain.JWT;
import org.primeframework.jwt.hmac.HMACSigner;
/** /**
* *
* @author Joern Muehlencord <joern at muehlencord.de> * @author Joern Muehlencord (joern@muehlencord.de)
*/ */
public abstract class JWTEncoder { public abstract class JWTEncoder {

View File

@ -1,14 +1,23 @@
/* /*
* To change this license header, choose License Headers in Project Properties. * Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
* To change this template file, choose Tools | Templates *
* and open the template in the editor. * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
package de.muehlencord.shared.jeeutil.jwt; package de.muehlencord.shared.jeeutil.jwt;
/** /**
* *
* @author Joern Muehlencord <joern at muehlencord.de> * @author Joern Muehlencord (joern@muehlencord.de)
*/ */
public class JWTException extends Exception { public class JWTException extends Exception {

View File

@ -1,7 +1,17 @@
/* /*
* To change this license header, choose License Headers in Project Properties. * Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
* To change this template file, choose Tools | Templates *
* and open the template in the editor. * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
package de.muehlencord.shared.jeeutil.jwt; package de.muehlencord.shared.jeeutil.jwt;
@ -12,7 +22,7 @@ import org.apache.shiro.web.filter.authc.AuthenticationFilter;
/** /**
* *
* @author Joern Muehlencord <joern at muehlencord.de> * @author Joern Muehlencord (joern@muehlencord.de)
*/ */
public class JWTGuard extends AuthenticationFilter { public class JWTGuard extends AuthenticationFilter {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018 jomu. * Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,7 +19,7 @@ import javax.ws.rs.core.Response;
/** /**
* *
* @author jomu * @author Joern Muehlencord (joern@muehlencord.de)
*/ */
public interface APIError { public interface APIError {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018 jomu. * Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -26,7 +26,7 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
/** /**
* *
* @author jomu * @author Joern Muehlencord (joern@muehlencord.de)
*/ */
@XmlRootElement @XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD) @XmlAccessorType(XmlAccessType.FIELD)
@ -60,14 +60,14 @@ public class APIErrorResponse {
this.rootCause = rootCauseMessage; this.rootCause = rootCauseMessage;
} }
public APIErrorResponse(Exception exception, Locale locale) { public APIErrorResponse(Exception exception) {
this.status = Response.Status.INTERNAL_SERVER_ERROR; this.status = Response.Status.INTERNAL_SERVER_ERROR;
this.errorCode = "0"; this.errorCode = "0";
this.message = exception.getLocalizedMessage(); this.message = exception.getLocalizedMessage();
this.rootCause = null; this.rootCause = null;
} }
public APIErrorResponse(Exception exception, Locale locale, Throwable th) { public APIErrorResponse(Exception exception, Throwable th) {
this.status = Response.Status.INTERNAL_SERVER_ERROR; this.status = Response.Status.INTERNAL_SERVER_ERROR;
this.errorCode = "0"; this.errorCode = "0";
this.message = exception.getLocalizedMessage(); this.message = exception.getLocalizedMessage();

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018 jomu. * Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -22,7 +22,7 @@ import javax.ws.rs.core.Response.ResponseBuilder;
/** /**
* *
* @author jomu * @author Joern Muehlencord (joern@muehlencord.de)
*/ */
public class APIException extends RuntimeException { public class APIException extends RuntimeException {
@ -31,7 +31,7 @@ public class APIException extends RuntimeException {
public static final String HTTP_HEADER_X_ROOT_CAUSE = "X-Root-Cause"; public static final String HTTP_HEADER_X_ROOT_CAUSE = "X-Root-Cause";
private static final long serialVersionUID = -4356132354448841938L; private static final long serialVersionUID = -4356132354448841938L;
private final Response httpResponse; private final transient Response httpResponse;
public APIException(APIError apiError, Locale locale) { public APIException(APIError apiError, Locale locale) {
httpResponse = createHttpResponse(new APIErrorResponse(apiError, locale)); httpResponse = createHttpResponse(new APIErrorResponse(apiError, locale));
@ -45,8 +45,12 @@ public class APIException extends RuntimeException {
httpResponse = createHttpResponse(new APIErrorResponse(apiError, locale, rootCause)); httpResponse = createHttpResponse(new APIErrorResponse(apiError, locale, rootCause));
} }
public APIException(Exception exception, Locale locale) { public APIException(APIError apiError, String locale, String rootCause) {
httpResponse = createHttpResponse(new APIErrorResponse(exception, locale)); httpResponse = createHttpResponse(new APIErrorResponse(apiError, new Locale(locale), rootCause));
}
public APIException(Exception exception) {
httpResponse = createHttpResponse(new APIErrorResponse(exception));
} }
public Response getHttpResponse() { public Response getHttpResponse() {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018 joern (at) muehlencord.de * Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,13 +21,16 @@ import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext; import javax.interceptor.InvocationContext;
import javax.validation.ConstraintViolationException; import javax.validation.ConstraintViolationException;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* * @author Joern Muehlencord (joern@muehlencord.de)
* @author jomu
*/ */
public class APIExceptionInterceptor { public class APIExceptionInterceptor {
private static final Logger logger = LoggerFactory.getLogger(APIExceptionInterceptor.class);
@Inject @Inject
Locale locale; Locale locale;
@ -39,6 +42,7 @@ public class APIExceptionInterceptor {
// if an exception is thrown during processing, this is passed in to the catch block below // if an exception is thrown during processing, this is passed in to the catch block below
proceedResponse = context.proceed(); proceedResponse = context.proceed();
} catch (Exception ex) { } catch (Exception ex) {
logger.debug(ex.getMessage(), ex);
Response errorResponse; Response errorResponse;
if (ex instanceof APIException) { if (ex instanceof APIException) {
errorResponse = ((APIException) ex).getHttpResponse(); errorResponse = ((APIException) ex).getHttpResponse();
@ -51,7 +55,7 @@ public class APIExceptionInterceptor {
// this exception is handled via the ConstraintViolationMapper // this exception is handled via the ConstraintViolationMapper
throw (ConstraintViolationException) ex.getCause(); throw (ConstraintViolationException) ex.getCause();
} else { } else {
errorResponse = new APIException(ex, locale).getHttpResponse(); errorResponse = new APIException(ex).getHttpResponse();
} }
return errorResponse; return errorResponse;
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018 joern (at) muehlencord.de * Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -22,7 +22,7 @@ import javax.ws.rs.ext.Provider;
/** /**
* *
* @author Joern Muehlencord (joern (at) muehlencord.de * @author Joern Muehlencord (joern@muehlencord.de)
*/ */
@Provider @Provider
public class BadRequestMapper implements ExceptionMapper<BadRequestException> { public class BadRequestMapper implements ExceptionMapper<BadRequestException> {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018 jomu. * Copyright 2019 Joern Muehlencord (joern@muehlencord.de).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -24,7 +24,7 @@ import javax.xml.bind.annotation.XmlRootElement;
/** /**
* *
* @author jomu * @author Joern Muehlencord (joern@muehlencord.de)
*/ */
@XmlRootElement @XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD) @XmlAccessorType(XmlAccessType.FIELD)

Some files were not shown because too many files have changed in this diff Show More