Compare commits

...

126 Commits

Author SHA1 Message Date
lucio 1fbd524fc4 added possibility to get Configuration folder outside tomcat 6 days ago
lucio 82c155e3a4 solved bug on version configuration 2 weeks ago
lucio 3aeca7c089 moved to jakarta 2 weeks ago
lucio e36605ec12 removed filters application to extensions 4 weeks ago
lucio 5c80342e8c Merge branch 'master' of
https://code-repo.d4science.org/gCubeSystem/common-smartgears.git
1 month ago
lucio a34e349c3b Merge branch 'master' of https://code-repo.d4science.org/gCubeSystem/common-smartgears.git 1 month ago
lucio 36a685733d maven parent updated to 1.2.0 1 month ago
lucio 64fb289c40 change InnerMethodName 1 month ago
ROBERTO CIRILLO b1b8e0df2e Update 'pom.xml'
restore maven-parent 1.2.0
1 month ago
ROBERTO CIRILLO aa58b66d79 Update 'pom.xml'
revert to maven-parent 1.1.0 just for test
1 month ago
lucio 6454f71672 commit for maven-parent test 1 month ago
lucio 409499956a pom updated 1 month ago
lucio bfce9d076d log updated 1 month ago
lucio b4faa4e2c5 solved problem with maven parent 1.2.0 that doesn't copy the resource
atuomatically and created request filters only per app instead of per
servlet
1 month ago
lucio 4d060fbb54 config validation on Credential was not working 1 month ago
lucio e193c80d11 removed logs 2 months ago
lucio 6dec0d7a6a updated 2 months ago
lucio 57d25b3ed3 updated for health api 2 months ago
lucio 5a771aeeb5 updated 2 months ago
lucio 101b0f1e0d changes on classpath 2 months ago
lucio a7f666556d modified persistence configuration 7 months ago
lucio c00d389357 updated configuration 11 months ago
lucio 873081893e unuseful resource removed 12 months ago
Lucio Lelii 725b19c168 Merge remote-tracking branch 'origin/smartgears4' 12 months ago
lucio be3d37d6c3 updated 1 year ago
lucio 766238c8e3 removed all JAXB dependencies 1 year ago
lucio e48f50e91b added automatic metrics for micrometer 1 year ago
lucio a76b823c49 update 1 year ago
lucio b9f17e5a0b added check 1 year ago
lucio b873f97187 Health extension implemented 1 year ago
lucio 98c41e7463 moved health part to a separate library 1 year ago
Lucio Lelii 3407ce025c health objects added 1 year ago
Lucio Lelii 29e02ac587 removed old import 1 year ago
Lucio Lelii d12e4a33b2 AuthorizationProvider moved from Configuration to context 1 year ago
Lucio Lelii de2c95f134 added compiler version 1 year ago
Lucio Lelii e7aabc3fb8 fired exception on expired token modified 1 year ago
Luca Frosini 1cf09963a2 Fixed pom to properly compile with JDK 11 1 year ago
Lucio Lelii 36e56d1e7d Merge branch 'feature/22955' of
https://code-repo.d4science.org/gCubeSystem/common-smartgears.git into
feature/22955

Conflicts:
	src/main/java/org/gcube/smartgears/handlers/application/request/RequestValidator.java
1 year ago
Luca Frosini d024c129d5 Removed -SNAPSHOT to release the component 1 year ago
Lucio Lelii b5f6959da4 check on empty token adde 1 year ago
ROBERTO CIRILLO f01ee71e32 Update 'pom.xml'
set gcube-bom 2.1.0-SNAPSHOT
1 year ago
Luca Frosini 952703c780 Added the Linux distribution version in the HostingNode resource 1 year ago
Lucio Lelii c92f119a0c adde validation on acess token 2 years ago
Lucio Lelii 551740212f put context logs to debug 2 years ago
Lucio Lelii b73a2963e5 Credentials moved to security library 2 years ago
Lucio Lelii 08838cb6d6 removed vos from frontpage 2 years ago
Lucio Lelii 2fe7d3d4bc InnerMethodName used for accounting 2 years ago
Lucio Lelii 4b75535a9d added logs 2 years ago
Lucio Lelii e5c0b8a23d latitude and longitude removed 2 years ago
Lucio Lelii b9e2298ca9 fixed bug on first publishing 2 years ago
Lucio Lelii 7e00459e9a logs added 2 years ago
Lucio Lelii 1b79f8e813 updated publisher interface 2 years ago
Lucio Lelii 58bb6a28e4 changed publisher interface 2 years ago
Lucio Lelii 710e8ef242 Proxy management improved 2 years ago
Lucio Lelii 08781a5f3a Profile and App events updated 2 years ago
Lucio Lelii 6f999bcedc added caller information 2 years ago
Lucio Lelii 80f5de688a allowed context retrieved on request 2 years ago
Lucio Lelii 8030b049d0 Publishing externalized to libraries in classpath 2 years ago
Lucio Lelii 961676484f removing gcore resource dependencies from smartgears 2 years ago
Lucio Lelii 4b619414a7 removed old providers 2 years ago
Lucio Lelii 64bef37271 Removed old VAleve 2 years ago
Lucio Lelii 230ae3bde9 auth provider modified 2 years ago
Lucio Lelii f54efc1e4e handler configuration removed
test removed
2 years ago
Lucio Lelii a285c20b38 removed context retriver handler and merged with Request validator 2 years ago
Lucio Lelii f6e49975d0 added import of authorization-util 2 years ago
Lucio Lelii 0bf0708efd commit for release 2 years ago
Lucio Lelii a71d8d1fac added roles to ExternalService client info 2 years ago
Lucio Lelii 9947bfbc7d update for 4.0.0 2 years ago
Lucio Lelii 72a5e54edd fixes issue [#23075] 2 years ago
Lucio Lelii 454533abcd update 2 years ago
Lucio Lelii 64d0c69ca2 update fro maintenance release 2 years ago
Lucio Lelii 3af9a558f6 https://support.d4science.org/issues/23003 2 years ago
Lucio Lelii 08b3cd590a new features for configuration file type changing 2 years ago
Lucio Lelii cf3c134953 moving smartgears to a .ini Configuration file type 2 years ago
Luca Frosini 515891e083 Merging patch of version 3.1.2 2 years ago
Lucio Lelii c23e98d9bb commit for release 2 years ago
Luca Frosini 635036bce1 Added Linux distribution version refs #22933 2 years ago
Lucio Lelii 430cf04416 removed all the patch added to solve auth-portlet bug 2 years ago
Lucio Lelii b84b4fad81 throws error on invalid policy 2 years ago
Lucio Lelii e9b6a7ad25 solved an issue on policy check 2 years ago
Lucio Lelii 0fe819c2ee changes 2 years ago
Lucio Lelii 4b1b6471ca policy validation added 2 years ago
Luca Frosini baab05f015 fixing code 2 years ago
Luca Frosini b46715dc05 Added missing reset 2 years ago
Luca Frosini 405a65282d Fixed code to instantiate SecretManager 2 years ago
Luca Frosini b703d6605b upgraded gcube-bom version 2 years ago
Luca Frosini 29ac4def10 Intregrated authorization-utils in place of ad-hoc code 2 years ago
Luca Frosini 9239e13120 Added authorization-utils refs #22871 2 years ago
Lucio Lelii 125dc5b332 - enabled policy check on smartgears
- container configuration for test added
2 years ago
Lucio Lelii 8455825bb1 Update 'pom.xml' 2 years ago
Lucio Lelii 9e5746fc37 Update 'CHANGELOG.md' 2 years ago
lucio.lelii 1e66ce01c0 pom updated 3 years ago
lucio.lelii c42094df38 List.of cannot be used in java8 3 years ago
Lucio Lelii 3441742d44 Merge pull request 'Make valid tokens that come without resource_access to be granted for minimal privileges on context identified by audience' (!1) from m.lettere/common-smartgears:master into minimal_privilege
Reviewed-on: #1
3 years ago
Marco Lettere db87ad7f3b Make valid tokens that come without resource_access to be granted for minimal privileges on context identified by audience 3 years ago
lucio.lelii 406016cd29 solved bug on user check 3 years ago
lucio.lelii 64c3f02996 - use of AccessTokenProvider
- use gcube-jackson instead of minimal-json for access token parsing
[#21097]
3 years ago
lucio.lelii e27784741b removed SNASPHOT postfix from version 3 years ago
lucio.lelii e8a32631f2 Merge branch 'master' of https://code-repo.d4science.org/gCubeSystem/common-smartgears.git 3 years ago
lucio.lelii bc1f02cdb5 check if response is already committed on error 3 years ago
Lucio Lelii 0ab8e327cf Update 'src/main/java/org/gcube/smartgears/handlers/application/request/RequestValidator.java'
removed call redirect when not authorized
3 years ago
Luca Frosini 943dc7065b Removed links which should not be there refs #20785 3 years ago
user1 47f09fe3a5 pom updated 3 years ago
user1 ed8e7d8811 changelog updated 3 years ago
Lucio Lelii fd766ce080 Merge branch 'master' of code-repo.d4science.org:gCubeSystem/common-smartgears 3 years ago
Lucio Lelii a4462eb325 commit for new IAM release 3 years ago
Luca Frosini fd6857ab60 Fixed scm section 3 years ago
ROBERTO CIRILLO 621fd9d20c Update 'pom.xml'
removed snapshot from bom
3 years ago
Luca Frosini d2f3af7f9e Fixed version on CHANGELOG 3 years ago
Luca Frosini f17d3e107d Removed -SNAPSHOT from version and lower bound dependencies range to
release the component refs #19616
3 years ago
Luca Frosini 9873a148f2 Ignored generated directory 4 years ago
Luca Frosini 7944226cca Removed servicearchive creation which is deprecated 4 years ago
Luca Frosini b503e017cc Updated descriptor 4 years ago
Luca Frosini 74dabde6f5 Fixed CHANGELOG 4 years ago
Luca Frosini 0f54034ef9 Removed dependency version which is already defined in gcube-bom 4 years ago
Luca Frosini 216f0f1389 Fixed CHANGELOG according to the new template 4 years ago
Luca Frosini c04b3df2ff Removed unused variable and the related imports 4 years ago
Luca Frosini f80301b867 Remvoed changelog which has been changed and moved in root folder 4 years ago
Luca Frosini 132f249e05 Removed unneeded import 4 years ago
Luca Frosini fb52eb958b Removed files from distro which has been changed and moved in root 4 years ago
Luca Frosini 568be1f0bb Ignore eclipse generated file 4 years ago
Luca Frosini 72dbecf46d Added required files e.g. CHANGELOG, LICENSE, README 4 years ago
Luca Frosini d462893125 Enhancing version to have zero-dependencies container 4 years ago
Lucio Lelii 441baf14b3 Update pom.xml 4 years ago
lucio c1e4956764 Merge branch 'master' of
gitea@code-repo.d4science.org:gCubeSystem/common-smartgears.git

Conflicts:
	pom.xml
4 years ago
lucio ef7c65e0fb multiple token generation in one call added 4 years ago

@ -1,43 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" path="target/generated-sources">
<attributes>
<attribute name="optional" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

4
.gitignore vendored

@ -0,0 +1,4 @@
/target/
/.classpath
/bin/
/bin/

@ -0,0 +1,3 @@
/org.eclipse.core.resources.prefs
/org.eclipse.jdt.core.prefs
/org.eclipse.m2e.core.prefs

@ -0,0 +1,155 @@
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
# Changelog for Common Smartgears
## [v4.0.0-SNAPSHOT]
- porting to keycloak
## [v3.2.0-SNAPSHOT]
- Added SecretManagerProvider thread local from authorization-utils [#22871]
- Added Linux distribution version [#22933]
## [v3.1.3] - 2022-03-21
- fixed bug on policies
## [v3.1.2] - 2022-01-19
- enabled policy check on smartgears
- container configuration for test added
## [v3.1.1] - 2021-09-29
- minimal privilege granted also on empty resource_access in JWT token
## [v3.1.0] - 2021-05-14
- use of AccessTokenProvider
- use gcube-jackson instead of minimal-json for access token parsing [#21097]
## [v3.0.2] - 2020-03-01
- check if response is already committed on error
## [v3.0.1] - 2020-11-18
- new Uma Token integration
## [v3.0.0] - 2020-10-20
- Switched container JSON management to gcube-jackson [#19283]
## [v.2.2.0] - 2020-01-23
- Multiple token are generated in the same call in place of one per call
## [v.2.1.9] - 2019-11-08
- Project adapted to be build with Jenkins
## [v.2.1.8] - 2019-05-27
- Support oauth2 protocol accepting token in the auhtorization header field
## [v.2.1.7] - 2019-02-26
- Added Proxy Address to Application Configuration
- Added protocol to Container Configuration (http by default)
- Changed the logs in accounting handler to log error or success and eventually error code
## [v.2.1.5] - 2017-09-19
- Added ThreadLocal InnerMethodName to set method name from application
## [v.2.1.4] - 2017-07-25
- Validation handler for application split in 2 different handlers: - ContextRetriever that set Token and Scope - RequestValidation that does all the required checks
## [v.2.1.3] - 2017-06-06
- Added gcube bom dependency
- Search for handlers in the root classpath
## [v.2.1.2] - 2017-05-02
- Modified the Authorization filter to accept also children scope when authorizeChildrenContext is enabled on ContianerConfiguration
- Shutdown of Accounting thread added
## [v.2.1.1] - 2017-03-16
- Minor issue on filter exclusion fixed
## [v.2.0.1] - 2016-12-15
- Proxy configuration added
- Solved a bug in events registration for ProfileManager
- Added a scheduler for period update of GCoreEnpoints
- Exclude modified to support exclude for sub-group of handlers
## [v.2.0.0] - 2016-11-07
- Integration with Authorization 2.0
## [v.1.2.7] - 2016-05-18
- Removed commons-io dependency [#2355]
## [v.1.2.6] - 2016-04-08
- Added missing class for service loader of org.gcube.smartgears.handlers.container.ContainerHandler [#2474]
- Added flush of accounting data [#1353]
## [v.1.2.5] - 2016-02-08
- Enhanced accounting version
## [v.1.2.4] - 2015-12-09
- Transparent accounting added on service calls
## [v.1.2.3] - 2015-07-27
- Authorization token control added
- Added support to HTTP Basic authorization
## [v.1.2.2] - 2015-04-27
- Fixed available space information on ghn profile
## [v.1.2.1] - 2014-02-13
- Scopes can be removed from container
- Node profile set to static
- Internal adjustments for move to Java 7
- Wildcard allowed in exclude directives
- Domain corrected derived in gHN profile
- Cleaner shutdown
- Further improvement in shutdown handling
## [v.1.0.0] - 2013-10-24
- First Release

@ -0,0 +1,312 @@
# European Union Public Licence V. 1.1
EUPL © the European Community 2007
This European Union Public Licence (the “EUPL”) applies to the Work or Software
(as defined below) which is provided under the terms of this Licence. Any use of
the Work, other than as authorised under this Licence is prohibited (to the
extent such use is covered by a right of the copyright holder of the Work).
The Original Work is provided under the terms of this Licence when the Licensor
(as defined below) has placed the following notice immediately following the
copyright notice for the Original Work:
Licensed under the EUPL V.1.1
or has expressed by any other mean his willingness to license under the EUPL.
## 1. Definitions
In this Licence, the following terms have the following meaning:
- The Licence: this Licence.
- The Original Work or the Software: the software distributed and/or
communicated by the Licensor under this Licence, available as Source Code and
also as Executable Code as the case may be.
- Derivative Works: the works or software that could be created by the Licensee,
based upon the Original Work or modifications thereof. This Licence does not
define the extent of modification or dependence on the Original Work required
in order to classify a work as a Derivative Work; this extent is determined by
copyright law applicable in the country mentioned in Article 15.
- The Work: the Original Work and/or its Derivative Works.
- The Source Code: the human-readable form of the Work which is the most
convenient for people to study and modify.
- The Executable Code: any code which has generally been compiled and which is
meant to be interpreted by a computer as a program.
- The Licensor: the natural or legal person that distributes and/or communicates
the Work under the Licence.
- Contributor(s): any natural or legal person who modifies the Work under the
Licence, or otherwise contributes to the creation of a Derivative Work.
- The Licensee or “You”: any natural or legal person who makes any usage of the
Software under the terms of the Licence.
- Distribution and/or Communication: any act of selling, giving, lending,
renting, distributing, communicating, transmitting, or otherwise making
available, on-line or off-line, copies of the Work or providing access to its
essential functionalities at the disposal of any other natural or legal
person.
## 2. Scope of the rights granted by the Licence
The Licensor hereby grants You a world-wide, royalty-free, non-exclusive,
sub-licensable licence to do the following, for the duration of copyright vested
in the Original Work:
- use the Work in any circumstance and for all usage, reproduce the Work, modify
- the Original Work, and make Derivative Works based upon the Work, communicate
- to the public, including the right to make available or display the Work or
- copies thereof to the public and perform publicly, as the case may be, the
- Work, distribute the Work or copies thereof, lend and rent the Work or copies
- thereof, sub-license rights in the Work or copies thereof.
Those rights can be exercised on any media, supports and formats, whether now
known or later invented, as far as the applicable law permits so.
In the countries where moral rights apply, the Licensor waives his right to
exercise his moral right to the extent allowed by law in order to make effective
the licence of the economic rights here above listed.
The Licensor grants to the Licensee royalty-free, non exclusive usage rights to
any patents held by the Licensor, to the extent necessary to make use of the
rights granted on the Work under this Licence.
## 3. Communication of the Source Code
The Licensor may provide the Work either in its Source Code form, or as
Executable Code. If the Work is provided as Executable Code, the Licensor
provides in addition a machine-readable copy of the Source Code of the Work
along with each copy of the Work that the Licensor distributes or indicates, in
a notice following the copyright notice attached to the Work, a repository where
the Source Code is easily and freely accessible for as long as the Licensor
continues to distribute and/or communicate the Work.
## 4. Limitations on copyright
Nothing in this Licence is intended to deprive the Licensee of the benefits from
any exception or limitation to the exclusive rights of the rights owners in the
Original Work or Software, of the exhaustion of those rights or of other
applicable limitations thereto.
## 5. Obligations of the Licensee
The grant of the rights mentioned above is subject to some restrictions and
obligations imposed on the Licensee. Those obligations are the following:
Attribution right: the Licensee shall keep intact all copyright, patent or
trademarks notices and all notices that refer to the Licence and to the
disclaimer of warranties. The Licensee must include a copy of such notices and a
copy of the Licence with every copy of the Work he/she distributes and/or
communicates. The Licensee must cause any Derivative Work to carry prominent
notices stating that the Work has been modified and the date of modification.
Copyleft clause: If the Licensee distributes and/or communicates copies of the
Original Works or Derivative Works based upon the Original Work, this
Distribution and/or Communication will be done under the terms of this Licence
or of a later version of this Licence unless the Original Work is expressly
distributed only under this version of the Licence. The Licensee (becoming
Licensor) cannot offer or impose any additional terms or conditions on the Work
or Derivative Work that alter or restrict the terms of the Licence.
Compatibility clause: If the Licensee Distributes and/or Communicates Derivative
Works or copies thereof based upon both the Original Work and another work
licensed under a Compatible Licence, this Distribution and/or Communication can
be done under the terms of this Compatible Licence. For the sake of this clause,
“Compatible Licence” refers to the licences listed in the appendix attached to
this Licence. Should the Licensees obligations under the Compatible Licence
conflict with his/her obligations under this Licence, the obligations of the
Compatible Licence shall prevail.
Provision of Source Code: When distributing and/or communicating copies of the
Work, the Licensee will provide a machine-readable copy of the Source Code or
indicate a repository where this Source will be easily and freely available for
as long as the Licensee continues to distribute and/or communicate the Work.
Legal Protection: This Licence does not grant permission to use the trade names,
trademarks, service marks, or names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the copyright notice.
## 6. Chain of Authorship
The original Licensor warrants that the copyright in the Original Work granted
hereunder is owned by him/her or licensed to him/her and that he/she has the
power and authority to grant the Licence.
Each Contributor warrants that the copyright in the modifications he/she brings
to the Work are owned by him/her or licensed to him/her and that he/she has the
power and authority to grant the Licence.
Each time You accept the Licence, the original Licensor and subsequent
Contributors grant You a licence to their contributions to the Work, under the
terms of this Licence.
## 7. Disclaimer of Warranty
The Work is a work in progress, which is continuously improved by numerous
contributors. It is not a finished work and may therefore contain defects or
“bugs” inherent to this type of software development.
For the above reason, the Work is provided under the Licence on an “as is” basis
and without warranties of any kind concerning the Work, including without
limitation merchantability, fitness for a particular purpose, absence of defects
or errors, accuracy, non-infringement of intellectual property rights other than
copyright as stated in Article 6 of this Licence.
This disclaimer of warranty is an essential part of the Licence and a condition
for the grant of any rights to the Work.
## 8. Disclaimer of Liability
Except in the cases of wilful misconduct or damages directly caused to natural
persons, the Licensor will in no event be liable for any direct or indirect,
material or moral, damages of any kind, arising out of the Licence or of the use
of the Work, including without limitation, damages for loss of goodwill, work
stoppage, computer failure or malfunction, loss of data or any commercial
damage, even if the Licensor has been advised of the possibility of such
damage. However, the Licensor will be liable under statutory product liability
laws as far such laws apply to the Work.
## 9. Additional agreements
While distributing the Original Work or Derivative Works, You may choose to
conclude an additional agreement to offer, and charge a fee for, acceptance of
support, warranty, indemnity, or other liability obligations and/or services
consistent with this Licence. However, in accepting such obligations, You may
act only on your own behalf and on your sole responsibility, not on behalf of
the original Licensor or any other Contributor, and only if You agree to
indemnify, defend, and hold each Contributor harmless for any liability incurred
by, or claims asserted against such Contributor by the fact You have accepted
any such warranty or additional liability.
## 10. Acceptance of the Licence
The provisions of this Licence can be accepted by clicking on an icon “I agree”
placed under the bottom of a window displaying the text of this Licence or by
affirming consent in any other similar way, in accordance with the rules of
applicable law. Clicking on that icon indicates your clear and irrevocable
acceptance of this Licence and all of its terms and conditions.
Similarly, you irrevocably accept this Licence and all of its terms and
conditions by exercising any rights granted to You by Article 2 of this Licence,
such as the use of the Work, the creation by You of a Derivative Work or the
Distribution and/or Communication by You of the Work or copies thereof.
## 11. Information to the public
In case of any Distribution and/or Communication of the Work by means of
electronic communication by You (for example, by offering to download the Work
from a remote location) the distribution channel or media (for example, a
website) must at least provide to the public the information requested by the
applicable law regarding the Licensor, the Licence and the way it may be
accessible, concluded, stored and reproduced by the Licensee.
## 12. Termination of the Licence
The Licence and the rights granted hereunder will terminate automatically upon
any breach by the Licensee of the terms of the Licence.
Such a termination will not terminate the licences of any person who has
received the Work from the Licensee under the Licence, provided such persons
remain in full compliance with the Licence.
## 13. Miscellaneous
Without prejudice of Article 9 above, the Licence represents the complete
agreement between the Parties as to the Work licensed hereunder.
If any provision of the Licence is invalid or unenforceable under applicable
law, this will not affect the validity or enforceability of the Licence as a
whole. Such provision will be construed and/or reformed so as necessary to make
it valid and enforceable.
The European Commission may publish other linguistic versions and/or new
versions of this Licence, so far this is required and reasonable, without
reducing the scope of the rights granted by the Licence. New versions of the
Licence will be published with a unique version number.
All linguistic versions of this Licence, approved by the European Commission,
have identical value. Parties can take advantage of the linguistic version of
their choice.
## 14. Jurisdiction
Any litigation resulting from the interpretation of this License, arising
between the European Commission, as a Licensor, and any Licensee, will be
subject to the jurisdiction of the Court of Justice of the European Communities,
as laid down in article 238 of the Treaty establishing the European Community.
Any litigation arising between Parties, other than the European Commission, and
resulting from the interpretation of this License, will be subject to the
exclusive jurisdiction of the competent court where the Licensor resides or
conducts its primary business.
## 15. Applicable Law
This Licence shall be governed by the law of the European Union country where
the Licensor resides or has his registered office.
This licence shall be governed by the Belgian law if:
- a litigation arises between the European Commission, as a Licensor, and any
- Licensee; the Licensor, other than the European Commission, has no residence
- or registered office inside a European Union country.
## Appendix
“Compatible Licences” according to article 5 EUPL are:
- GNU General Public License (GNU GPL) v. 2
- Open Software License (OSL) v. 2.1, v. 3.0
- Common Public License v. 1.0
- Eclipse Public License v. 1.0
- Cecill v. 2.0

@ -0,0 +1,79 @@
# Common Smartgears
A core gCube library which empower a servlet container (e.g. tomcat) with a set of functionality such as:
- node and application infrastructure registration
- authorization
- accounting
## Built With
* [OpenJDK](https://openjdk.java.net/) - The JDK used
* [Maven](https://maven.apache.org/) - Dependency Management
## Documentation
[SmartGears](https://wiki.gcube-system.org/gcube/SmartGears)
## Change log
See [Releases](https://code-repo.d4science.org/gCubeSystem/common-smartgears/releases).
## Authors
* **Luca Frosini** ([ORCID](https://orcid.org/0000-0003-3183-2291)) - [ISTI-CNR Infrascience Group](http://nemis.isti.cnr.it/groups/infrascience)
* **Lucio Lelii** - [ISTI-CNR Infrascience Group](http://nemis.isti.cnr.it/groups/infrascience)
* **Fabio Simeoni** - FAO of the UN, Italy
## How to Cite this Software
Tell people how to cite this software.
* Cite an associated paper?
* Use a specific BibTeX entry for the software?
@Manual{,
title = {Common Smartgears},
author = {{Frosini, Luca}, {Lelii, Lucio}, {Simeoni, Fabio}},
organization = {{ISTI - CNR}, {FAO}},
address = {{Pisa, Italy}, {Roma, Italy}},
year = 2019,
url = {http://www.gcube-system.org/}
}
## License
This project is licensed under the EUPL V.1.1 License - see the [LICENSE.md](LICENSE.md) file for details.
## About the gCube Framework
This software is part of the [gCubeFramework](https://www.gcube-system.org/ "gCubeFramework"): an
open-source software toolkit used for building and operating Hybrid Data
Infrastructures enabling the dynamic deployment of Virtual Research Environments
by favouring the realisation of reuse oriented policies.
The projects leading to this software have received funding from a series of European Union programmes including:
- the Sixth Framework Programme for Research and Technological Development
- DILIGENT (grant no. 004260).
- the Seventh Framework Programme for research, technological development and demonstration
- D4Science (grant no. 212488);
- D4Science-II (grant no.239019);
- ENVRI (grant no. 283465);
- iMarine(grant no. 283644);
- EUBrazilOpenBio (grant no. 288754).
- the H2020 research and innovation programme
- SoBigData (grant no. 654024);
- PARTHENOS (grant no. 654119);
- EGIEngage (grant no. 654142);
- ENVRIplus (grant no. 654182);
- BlueBRIDGE (grant no. 675680);
- PerformFish (grant no. 727610);
- AGINFRAplus (grant no. 731001);
- DESIRA (grant no. 818194);
- ARIADNEplus (grant no. 823914);
- RISIS2 (grant no. 824091);

@ -1,4 +0,0 @@
gCube System - License
------------------------------------------------------------
${gcube.license}

@ -1,70 +0,0 @@
The gCube System - ${name}
--------------------------------------------------
${description}
${gcube.description}
${gcube.funding}
Version
--------------------------------------------------
${version} (${buildDate})
Please see the file named "changelog.xml" in this directory for the release notes.
Authors
--------------------------------------------------
* Fabio Simeoni (fabio.simeoni@fao.org), FAO of the UN, Italy
* Luca Frosini (luca.frosini@isti.cnr.it), CNR, Italy
* Lucio Lelii (lucio.lelii@isti.cnr.it), CNT, Italy
Maintainers
-----------
* Luca Frosini (luca.frosini@isti.cnr.it), CNR, Italy
* Lucio Lelii (lucio.lelii@isti.cnr.it), CNT, Italy
Download information
--------------------------------------------------
Source code is available from SVN:
${scm.url}
Binaries can be downloaded from the gCube website:
${gcube.website}
Installation
--------------------------------------------------
Installation documentation is available on-line in the gCube Wiki:
${gcube.wikiRoot}/Smartgears
Documentation
--------------------------------------------------
Documentation is available on-line in the gCube Wiki:
${gcube.wikiRoot}/Smartgears
Support
--------------------------------------------------
Bugs and support requests can be reported in the gCube issue tracking tool:
${gcube.issueTracking}
Licensing
--------------------------------------------------
This software is licensed under the terms you may find in the file named "LICENSE" in this directory.

@ -1,68 +0,0 @@
<ReleaseNotes>
<Changeset component="common-smartgears-2.1.9" date="2019-03-21">
<Change>Support oauth2 protocol accepting token in the auhtorization header field</Change>
</Changeset>
<Changeset component="common-smartgears-2.1.7" date="2017-01-16">
<Change>Added Proxy Address to Application Configuration</Change>
<Change>Added protocol to Container Configuration (http by default)</Change>
<Change>Changed the logs in accounting handler to log error or success and eventually error code</Change>
</Changeset>
<Changeset component="common-smartgears-2.1.5" date="2017-07-18">
<Change>Added ThreadLocal InnerMethodName to set method name from application</Change>
</Changeset>
<Changeset component="common-smartgears-2.1.4" date="2017-05-30">
<Change>Validation handler for application split in 2 different handlers:
- ContextRetriever that set Token and Scope
- RequestValidation that does all the required checks
</Change>
</Changeset>
<Changeset component="common-smartgears-2.1.3" date="2017-05-12">
<Change>Added gcube bom dependency</Change>
<Change>Search for handlers in the root classpath</Change>
</Changeset>
<Changeset component="common-smartgears-2.1.2" date="2017-03-22">
<Change>Modified the Authorization filter to accept also children
scope when authorizeChildrenContext is enabled on ContianerConfiguration</Change>
<Change>Shutdown of Accounting thread added</Change>
</Changeset>
<Changeset component="common-smartgears-2.1.1" date="2017-01-25">
<Change>Minor issue on filter exclusion fixed</Change>
</Changeset>
<Changeset component="common-smartgears-2.1.0" date="2016-10-24">
<Change>proxy configuration added</Change>
<Change>solved a bug in events registration for ProfileManager</Change>
<Change>added a scheduler for period update of GCoreEnpoints</Change>
<Change>Exclude modified to support exclude for sub-group of handlers</Change>
</Changeset>
<Changeset component="common-smartgears-2.0.0" date="2016-03-10">
<Change>integration with Authorization 2.0</Change>
</Changeset>
<Changeset component="common-smartgears-1.2.6" date="2015-12-22">
<Change>Added flush of accounting data</Change>
</Changeset>
<Changeset component="common-smartgears-1.2.5" date="2015-12-22">
<Change>Changed accounting version</Change>
</Changeset>
<Changeset component="common-smartgears-1.2.4" date="2015-10-06">
<Change>Transparent accounting added on service calls</Change>
</Changeset>
<Changeset component="common-smartgears-1.2.3" date="2015-07-27">
<Change>Authorization token control added</Change>
<Change>Added support to HTTP Basic authorization</Change>
</Changeset>
<Changeset component="common-smartgears-1.2.2" date="2015-04-27">
<Change>Fixed available space information on ghn profile</Change>
</Changeset>
<Changeset component="common-smartgears-1.2.1" date="2014-02-13">
<Change>scopes can be removed from container</Change>
<Change>node profile set to static</Change>
<Change>internal adjustments for move to Java 7</Change>
<Change>wildcard allowed in exclude directives</Change>
<Change>domain corrected derived in gHN profile</Change>
<Change>cleaner shutdown</Change>
<Change>further improvement in shutdown handling</Change>
</Changeset>
<Changeset component="common-smartgears-1.0.0" date="2013-10-24">
<Change>First Release</Change>
</Changeset>
</ReleaseNotes>

@ -1,31 +0,0 @@
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
<id>servicearchive</id>
<formats>
<format>tar.gz</format>
</formats>
<baseDirectory>/</baseDirectory>
<fileSets>
<fileSet>
<directory>${distroDirectory}</directory>
<outputDirectory>${file.separator}</outputDirectory>
<useDefaultExcludes>true</useDefaultExcludes>
<includes>
<include>README</include>
<include>LICENSE</include>
<include>changelog.xml</include>
<include>profile.xml</include>
</includes>
<fileMode>755</fileMode>
<filtered>true</filtered>
</fileSet>
</fileSets>
<files>
<file>
<source>target${file.separator}${build.finalName}.${project.packaging}</source>
<outputDirectory>${file.separator}${artifactId}</outputDirectory>
</file>
</files>
</assembly>

@ -1,29 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
<Resource>
<ID />
<Type>Service</Type>
<Profile>
<Description>${description}</Description>
<Class>${serviceClass}</Class>
<Name>${artifactId}</Name>
<Version>1.0.0</Version>
<Packages>
<Software>
<Description>${description}</Description>
<Name>${artifactId}</Name>
<Version>${version}</Version>
<MavenCoordinates>
<groupId>${groupId}</groupId>
<artifactId>${artifactId}</artifactId>
<version>${version}</version>
</MavenCoordinates>
<Type>Library</Type>
<Files>
<File>${build.finalName}.${project.packaging}</File>
</Files>
</Software>
</Packages>
</Profile>
</Resource>

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<smartgears version="${project.version}" />

Binary file not shown.

@ -0,0 +1,2 @@
[smartgears]
version = ${project.version}

@ -5,205 +5,160 @@
<parent>
<artifactId>maven-parent</artifactId>
<groupId>org.gcube.tools</groupId>
<version>1.1.0</version>
<version>1.2.0</version>
<relativePath />
</parent>
<groupId>org.gcube.core</groupId>
<artifactId>common-smartgears</artifactId>
<version>2.1.10</version>
<version>4.0.0-SNAPSHOT</version>
<name>SmartGears</name>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.gcube.distribution</groupId>
<artifactId>gcube-bom</artifactId>
<version>LATEST</version>
<version>3.0.1-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<properties>
<distroDirectory>distro</distroDirectory>
<tomcat.version>7.0.42</tomcat.version>
<jersey.version>1.17.1</jersey.version>
<tomcat.version>10.1.19</tomcat.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<scm>
<connection>scm:git:https://code-repo.d4science.org/gCubeSystem/commmon-smartgears.git</connection>
<developerConnection>scm:git:https://code-repo.d4science.org/gCubeSystem/commmon-smartgears.git</developerConnection>
<url>https://code-repo.d4science.org/gCubeSystem/commmon-smartgears</url>
<connection>
scm:git:https://code-repo.d4science.org/gCubeSystem/common-smartgears.git</connection>
<developerConnection>
scm:git:httpstps://code-repo.d4science.org/gCubeSystem/common-smartgears.git</developerConnection>
<url>https://code-repo.d4science.org/gCubeSystem/common-smartgears</url>
</scm>
<dependencies>
<!--
https://mvnrepository.com/artifact/jakarta.servlet/jakarta.servlet-api -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</dependency>
<dependency>
<groupId>io.github.classgraph</groupId>
<artifactId>classgraph</artifactId>
<version>4.8.28</version>
</dependency>
<dependency>
<groupId>org.gcube.common</groupId>
<artifactId>health-api</artifactId>
<version>[1.0.0-SNAPSHOT, 2.0.0-SNAPSHOT)</version>
</dependency>
<!-- gCube Jackson -->
<dependency>
<groupId>org.gcube.common</groupId>
<artifactId>gcube-jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.common</groupId>
<artifactId>authorization-client</artifactId>
<artifactId>gcube-jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.common</groupId>
<artifactId>common-authorization</artifactId>
<artifactId>gcube-jackson-core</artifactId>
</dependency>
<!-- END gCube Jackson -->
<dependency>
<groupId>org.gcube.common</groupId>
<artifactId>common-security</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.data.publishing</groupId>
<artifactId>document-store-lib</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.accounting</groupId>
<artifactId>accounting-lib</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.resources</groupId>
<artifactId>registry-publisher</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.resources</groupId>
<artifactId>common-gcore-resources</artifactId>
<groupId>org.ini4j</groupId>
<artifactId>ini4j</artifactId>
<version>0.5.4</version>
</dependency>
<dependency>
<groupId>org.gcube.core</groupId>
<artifactId>common-validator</artifactId>
<version>[1.0.0-SNAPSHOT,2.0.0-SNAPSHOT)</version>
<version>[1.0.0,2.0.0-SNAPSHOT)</version>
</dependency>
<dependency>
<groupId>org.gcube.core</groupId>
<artifactId>common-scope</artifactId>
<artifactId>common-events</artifactId>
<version>[1.0.0,2.0.0-SNAPSHOT)</version>
</dependency>
<dependency>
<groupId>org.gcube.core</groupId>
<artifactId>common-events</artifactId>
<version>[1.0.0-SNAPSHOT,2.0.0-SNAPSHOT)</version>
<groupId>org.gcube.common.security</groupId>
<artifactId>gcube-secrets</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/io.micrometer/micrometer-core -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
<version>1.9.0</version>
</dependency>
<!-- ***************** test ******************* -->
<dependency>
<groupId>org.jboss.shrinkwrap.resolver</groupId>
<artifactId>shrinkwrap-resolver-depchain</artifactId>
<version>2.0.0-beta-2</version>
<type>pom</type>
<scope>test</scope>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<version>1.9.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-catalina -->
<!--
https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-catalina -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>${tomcat.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>${tomcat.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<version>${jersey.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>${tomcat.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-logging-log4j</artifactId>
<version>${tomcat.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>${tomcat.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.gcube.resources</groupId>
<artifactId>common-gcore-resources</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
<descriptor>${distroDirectory}/descriptor.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>servicearchive</id>
<phase>install</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- excludes probe package from jar -->
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.2</version>
<!-- version>2.3.2</version -->
<executions>
<execution>
<id>default-jar</id>
@ -219,17 +174,28 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<property>
<name>projectVersion</name>
<value>${project.version}</value>
</property>
</systemPropertyVariables>
</configuration>
</plugin>
<!-- include probe in attached war -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<!-- version>2.4</version -->
<configuration>
<primaryArtifact>false</primaryArtifact>
<classifier>probe</classifier>
<packagingIncludes>WEB-INF/classes/org/gcube/smartgears/probe/**/*</packagingIncludes>
<packagingIncludes>
WEB-INF/classes/org/gcube/smartgears/probe/**/*</packagingIncludes>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
<executions>
@ -242,48 +208,6 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.15</version>
<configuration>
<!-- tomcat annotation discovery won't work with the default manifest-only
jar -->
<useManifestOnlyJar>false</useManifestOnlyJar>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
<executions>
<!-- interpolates and copies configuration.properties -->
<execution>
<id>copy-configuration</id>
<goals>
<goal>copy-resources</goal>
</goals>
<phase>validate</phase>
<configuration>
<outputDirectory>src/main/resources/META-INF</outputDirectory>
<overwrite>true</overwrite>
<encoding>UTF-8</encoding>
<resources>
<resource>
<directory>${distroDirectory}</directory>
<includes>
<include>smartgears-config.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
</project>

@ -1,10 +1,12 @@
package org.gcube.smartgears;
import static java.util.Collections.emptyList;
import java.util.Set;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import jakarta.servlet.ServletContainerInitializer;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.context.container.ContainerContext;
@ -14,6 +16,19 @@ import org.gcube.smartgears.provider.ProviderFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.binder.jvm.ClassLoaderMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmInfoMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics;
import io.micrometer.core.instrument.binder.logging.LogbackMetrics;
import io.micrometer.core.instrument.binder.system.ProcessorMetrics;
import io.micrometer.core.instrument.binder.system.UptimeMetrics;
import io.micrometer.core.instrument.binder.tomcat.TomcatMetrics;
import io.micrometer.prometheus.PrometheusConfig;
import io.micrometer.prometheus.PrometheusMeterRegistry;
/**
* Bootstraps management of all deployed applications which require it.
*
@ -34,6 +49,8 @@ public class Bootstrap implements ServletContainerInitializer {
public Bootstrap() {
log.info("bootstrap started the container");
if (smartgearsHasStarted)
return;
@ -51,6 +68,8 @@ public class Bootstrap implements ServletContainerInitializer {
ApplicationManager appManager = new ApplicationManager();
log.info("check if is managed @ {}", application.getContextPath());
//act only on resources
if (isResource(application)) {
@ -72,20 +91,40 @@ public class Bootstrap implements ServletContainerInitializer {
+ " (see cause)", t);
}
}
} else log.info("is not managed @ {}", application.getContextPath());
}
// helpers
@SuppressWarnings("resource")
private void initialiseContainer() {
ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
try {
// TODO Ask why is needed?
Thread.currentThread().setContextClassLoader(ContainerManager.class.getClassLoader());
log.trace("smartgears is starting");
/* Get the ContainerContext. Look at DefaultProvider */
context = ProviderFactory.provider().containerContext();
PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
new ClassLoaderMetrics().bindTo(registry);
new JvmMemoryMetrics().bindTo(registry);
new JvmGcMetrics().bindTo(registry);
new ProcessorMetrics().bindTo(registry);
new JvmThreadMetrics().bindTo(registry);
new UptimeMetrics().bindTo(registry);
new ProcessorMetrics().bindTo(registry);
new TomcatMetrics(null, emptyList()).bindTo(registry);
new LogbackMetrics().bindTo(registry);
new JvmInfoMetrics().bindTo(registry);
Metrics.addRegistry(registry);
/* Validate the configuration retrieved by ContainerContext
* using gcube facilities annotation based
* ( i.e org.gcube.common.validator.annotations)
@ -100,6 +139,8 @@ public class Bootstrap implements ServletContainerInitializer {
//we let the container continue
} finally {//restore the classloader of the current application
Thread.currentThread().setContextClassLoader(contextCL);
}
}

@ -1,7 +1,7 @@
package org.gcube.smartgears;
import org.gcube.smartgears.extensions.resource.RemoteResource;
import org.gcube.smartgears.handlers.application.lifecycle.ProfileManager;
import org.gcube.smartgears.handlers.application.request.RequestAccounting;
import org.gcube.smartgears.handlers.application.request.RequestValidator;
import org.gcube.smartgears.handlers.container.lifecycle.AccountingManager;
@ -27,8 +27,8 @@ public class Constants {
/**
* The container configuration file path, relative to the container configuration directory.
*/
public static final String container_configuraton_file_path = "container.xml";
*/
public static final String container_configuraton_file_path = "container.ini";
/**
@ -36,25 +36,15 @@ public class Constants {
*/
public static final String container_profile_file_path = "ghn.xml";
/*
public static final String container_profile_file_path_copy = "ghn.xml.copy";
/**
* The container lifecycle configuration resource path.
*/
public static final String container_handlers_file_path = "/META-INF/container-handlers.xml";
public static final String container_handlers_file_name = "gcube-container-handlers.xml";
/**
* The library configuration resource path.
*/
public static final String library_configuration_file_path = "/META-INF/smartgears-config.xml";
*/
/**
* The name of the context property that contains the node profile.
*/
public static final String container_profile_property = "ghn-profile";
*/
/**
* The default value of for the container publication frequency.
@ -62,24 +52,11 @@ public class Constants {
public static final long default_container_publication_frequency_in_seconds = 60;
/**
* The application configuration resource path.
*/
public static final String configuration_file_path = "/WEB-INF/gcube-app.xml";
public static final String configuration_file_path = "/WEB-INF/application.yaml";
/**
* The application lifecycle configuration resource path.
*/
public static final String handlers_file_path = "/WEB-INF/gcube-handlers.xml";
/**
* The default application lifecycle configuration resource path.
*/
public static final String default_handlers_file_path = "/META-INF/default-handlers.xml";
public static final String application_handlers_file_name = "gcube-application-handlers.xml";
/**
* The wildcard exclude directive.
@ -92,16 +69,6 @@ public class Constants {
*/
public static final String root_mapping = "/gcube/resource";
/**
* The application extensions configuration resource path.
*/
public static final String extensions_file_path = "/WEB-INF/gcube-extensions.xml";
/**
* The default application extensions configuration resource path.
*/
public static final String default_extensions_file_path = "/META-INF/default-extensions.xml";
/**
* The application frontpage resource path.
*/
@ -117,6 +84,11 @@ public class Constants {
*/
public static final String request_validation = "request-validation";
/**
* The configuration name of {@link RequestMetrics}s.
*/
public static final String request_metrics = "request-metrics";
/**
* The configuration name of {@link RequestValidator}s.
*/
@ -140,8 +112,6 @@ public class Constants {
public static final String remote_management = "remote-management";
/**
* The path of the application profile file, relative to the service configuration directory.
*/

@ -0,0 +1,37 @@
package org.gcube.smartgears.configuration;
import org.gcube.common.security.credentials.Credentials;
import org.gcube.common.validator.annotations.IsValid;
import org.gcube.common.validator.annotations.NotNull;
import org.gcube.smartgears.security.AuthorizationProviderFactory;
public class AuthorizationProviderConfiguration {
@NotNull
AuthorizationProviderFactory<?> authProviderFactory;
@NotNull @IsValid
Credentials credentials;
public AuthorizationProviderConfiguration(AuthorizationProviderFactory<?> authProviderFactory,
Credentials credentials) {
super();
this.authProviderFactory = authProviderFactory;
this.credentials = credentials;
}
public AuthorizationProviderFactory<?> getAuthProviderFactory() {
return authProviderFactory;
}
public Credentials getCredentials() {
return credentials;
}
@Override
public String toString() {
return "AuthorizationProviderConfiguration [authProviderFactory=" + authProviderFactory.getClass() + "]";
}
}

@ -0,0 +1,11 @@
package org.gcube.smartgears.configuration;
import org.gcube.com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.gcube.com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import org.gcube.com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
@JsonTypeInfo(include=As.PROPERTY, use=Id.CLASS, property= "className")
public interface ComponentConfiguration {
String getLocation();
}

@ -0,0 +1,6 @@
package org.gcube.smartgears.configuration;
public interface Configurable {
void configure(ComponentConfiguration configuration);
}

@ -0,0 +1,14 @@
package org.gcube.smartgears.configuration;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Retention(RUNTIME)
@Target(ElementType.TYPE)
public @interface ConfiguredWith {
public Class<? extends ComponentConfiguration> value();
}

@ -8,5 +8,6 @@ package org.gcube.smartgears.configuration;
*/
public enum Mode {
online,
offline
offline,
root
}

@ -0,0 +1,37 @@
package org.gcube.smartgears.configuration;
import org.gcube.com.fasterxml.jackson.annotation.JsonAutoDetect;
import org.gcube.com.fasterxml.jackson.annotation.JsonInclude;
import org.gcube.com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import org.gcube.com.fasterxml.jackson.annotation.JsonInclude.Include;
import org.gcube.common.validator.annotations.IsValid;
import org.gcube.common.validator.annotations.NotNull;
import org.gcube.smartgears.persistence.PersistenceWriter;
@JsonInclude(value = Include.NON_NULL)
@JsonAutoDetect(fieldVisibility = Visibility.ANY)
public class PersistenceConfiguration {
@NotNull
private Class<? extends PersistenceWriter> implementationClass;
@IsValid
private ComponentConfiguration writerConfiguration;
protected PersistenceConfiguration() {}
public <T extends ComponentConfiguration> PersistenceConfiguration(Class<? extends PersistenceWriter> implementationClass, T writerConfiguration) {
super();
this.implementationClass = implementationClass;
this.writerConfiguration = writerConfiguration;
}
public Class<? extends PersistenceWriter> getImplementationClass() {
return this.implementationClass;
}
public ComponentConfiguration getWriterConfiguration() {
return writerConfiguration;
}
}

@ -0,0 +1,42 @@
package org.gcube.smartgears.configuration;
import org.gcube.common.validator.annotations.NotEmpty;
import org.gcube.common.validator.annotations.NotNull;
public class ProxyAddress {
@NotNull @NotEmpty
String protocol = "http";
@NotNull @NotEmpty
String hostname;
@NotNull
Integer port;
public String getProtocol() {
return protocol;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public String getHostname() {
return hostname;
}
public void setHostname(String hostname) {
this.hostname = hostname;
}
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
}

@ -0,0 +1,15 @@
package org.gcube.smartgears.configuration;
public class SmartgearsConfiguration {
private String version;
public SmartgearsConfiguration(String version) {
super();
this.version = version;
}
public String getVersion() {
return version;
}
}

@ -1,167 +1,182 @@
package org.gcube.smartgears.configuration.application;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import org.gcube.smartgears.configuration.Mode;
import org.gcube.smartgears.persistence.Persistence;
import org.gcube.com.fasterxml.jackson.annotation.JsonAutoDetect;
import org.gcube.com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import org.gcube.com.fasterxml.jackson.annotation.JsonIgnore;
import org.gcube.com.fasterxml.jackson.annotation.JsonInclude;
import org.gcube.com.fasterxml.jackson.annotation.JsonInclude.Include;
import org.gcube.com.fasterxml.jackson.annotation.JsonProperty;
import org.gcube.common.validator.ValidationError;
import org.gcube.common.validator.Validator;
import org.gcube.common.validator.ValidatorFactory;
import org.gcube.common.validator.annotations.NotEmpty;
import org.gcube.common.validator.annotations.NotNull;
import org.gcube.smartgears.configuration.PersistenceConfiguration;
/**
* The configuration of the application.
*
* @author Fabio Simeoni
* The configuration of a managed app.
* <p>
* Includes the list of its client services.
*
* @author Lucio Lelii
*
*/
public interface ApplicationConfiguration {
@JsonInclude(value = Include.NON_NULL)
@JsonAutoDetect(fieldVisibility = Visibility.ANY)
public class ApplicationConfiguration {
/**
* Returns the management mode of the application.
* @return the management mode
*/
Mode mode();
/**
* Returns the context path of the application
* @return the context path
*/
String context();
boolean proxied();
/**
* Sets the context path of the application
* @param context the context path
* @return this configuration
*/
ApplicationConfiguration context(String context);
/**
* Sets the management mode of this application.
* @param the management mode
* @return this configuration
*/
ApplicationConfiguration mode(Mode mode);
/**
* Returns the name of the application.
* @return the name
*/
String name();
/**
* Sets the name of the application.
* @param name the name
* @return this configuration
*/
ApplicationConfiguration name(String name);
/**
* Returns the class of the application
* @return the class
*/
String serviceClass();
/**
* Sets the class of the application.
* @param serviceClass the class
* @return this configuration
*/
ApplicationConfiguration serviceClass(String serviceClass);
/**
* Returns the version of the application.
* @return the version
*/
String version();
/**
* Sets the version of the application.
* @param version the version
* @return this configuration
*/
ApplicationConfiguration version(String version);
@NotNull
String name;
/**
* Returns the description of the application.
* @return the description
*/
String description();
@NotNull
String group;
/**
* Sets the description of the application.
* @param description the description
* @return this configuration
*/
ApplicationConfiguration description(String description);
ProxyAddress proxyAddress();
ApplicationConfiguration proxyAddress(ProxyAddress proxyaddress);
/**
* Returns the tokens in which the application operates when it first starts.
* @return the tokens
*/
Set<String> startTokens();
/**
* Sets the tokens in which the application operates when it first starts.
* @param scopes the scopes
* @return this configuration
*/
ApplicationConfiguration startTokens(Set<String> tokens);
/**
* Returns the persistence manager of the application.
* @return the manager
*/
Persistence persistence();
@NotNull
String version;
String description="";
@JsonIgnore
String context;
private boolean proxable = true;
Set<GCubeExclude> excludes= new HashSet<>();
Set<GCubeInclude> includes= new HashSet<>();
@NotEmpty @JsonProperty("persistence")
PersistenceConfiguration persistenceConfiguration;
public Set<GCubeExclude> excludes() {
return excludes;
}
public Set<GCubeInclude> includes() {
return includes;
}
public ApplicationConfiguration() {}
public String name() {
return name;
}
public String context() {
return context;
}
public ApplicationConfiguration excludes(GCubeExclude ... excludes) {
this.excludes=new HashSet<GCubeExclude>(Arrays.asList(excludes));
return this;
}
public ApplicationConfiguration includes(GCubeInclude... includes) {
this.includes=new HashSet<GCubeInclude>(Arrays.asList(includes));
return this;
}
public ApplicationConfiguration context(String context) {
this.context = context;
return this;
}
public ApplicationConfiguration name(String name) {
this.name=name;
return this;
}
public ApplicationConfiguration persistenceConfiguration(PersistenceConfiguration configuration) {
this.persistenceConfiguration = configuration;
return this;
}
public ApplicationConfiguration proxable(boolean proxable) {
this.proxable = proxable;
return this;
}
public String group() {
return group;
}
public ApplicationConfiguration group(String group) {
this.group=group;
return this;
}
public String version() {
return version;
}
public ApplicationConfiguration version(String version) {
this.version=version;
return this;
}
public String description() {
return description;
}
public ApplicationConfiguration description(String description) {
this.description=description;
return this;
}
public boolean proxable() {
return proxable;
}
public PersistenceConfiguration persistenceConfiguration() {
return persistenceConfiguration;
}
public void validate() {
List<String> msgs = new ArrayList<String>();
Validator validator = ValidatorFactory.validator();
for (ValidationError error : validator.validate(this))
msgs.add(error.toString());
if (!this.excludes().isEmpty() && !this.includes().isEmpty())
msgs.add("exclude tags and includes tags are mutually exclusive");
if (!msgs.isEmpty())
throw new IllegalStateException("invalid configuration: "+msgs);
}
@Override
public int hashCode() {
return Objects.hash(description, excludes, group, includes, name, proxable, version);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ApplicationConfiguration other = (ApplicationConfiguration) obj;
return Objects.equals(description, other.description)
&& Objects.equals(excludes, other.excludes) && Objects.equals(group, other.group)
&& Objects.equals(includes, other.includes) && Objects.equals(name, other.name)
&& proxable == other.proxable && Objects.equals(version, other.version);
}
/**
* Returns a set of request paths that should not be subjected to request management.
* @return the set of exclude paths.
*/
Set<Exclude> excludes();
/**
* Returns a set of request paths that should be subjected to request management.
* @return the set of exclude paths.
*/
Set<Include> includes();
/**
* Sets the persistence manager of the application.
* @param manager the manager
* @return this configuration
*/
ApplicationConfiguration persistence(Persistence manager);
/**
* Validates this configuration.
*
* @throws IllegalStateException if the configuration is not valid
*/
void validate();
/**
* Merges this configuration with another configuration
* @param config the other configuration
*/
void merge(ApplicationConfiguration config);
ApplicationConfiguration excludes(Exclude ... excludes);
ApplicationConfiguration includes(Include ... includes);
}

@ -1,18 +1,22 @@
package org.gcube.smartgears.configuration.application;
import static org.gcube.smartgears.utils.Utils.*;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.ServiceLoader;
import java.util.Set;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import org.gcube.smartgears.extensions.ApplicationExtension;
import org.gcube.smartgears.handlers.application.ApplicationHandler;
import java.util.LinkedList;
import java.util.List;
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
import org.gcube.smartgears.configuration.PersistenceConfiguration;
import org.gcube.smartgears.handlers.application.ApplicationLifecycleHandler;
import org.gcube.smartgears.handlers.application.RequestHandler;
import org.gcube.smartgears.handlers.application.lifecycle.ApplicationProfileManager;
import org.gcube.smartgears.handlers.application.request.RequestAccounting;
import org.gcube.smartgears.handlers.application.request.RequestMetrics;
import org.gcube.smartgears.handlers.application.request.RequestValidator;
import org.gcube.smartgears.persistence.LocalWriter;
import org.gcube.smartgears.persistence.LocalWriterConfiguration;
import org.gcube.smartgears.utils.Utils;
import org.yaml.snakeyaml.Yaml;
/**
* Binds {@link ApplicationConfiguration}s to and from XML serialisations.
@ -29,21 +33,28 @@ public class ApplicationConfigurationBinder {
* @return the configuration
* @throws RuntimeException if the serialisation is invalid
*/
public ApplicationConfiguration bind(InputStream stream) {
public ApplicationConfiguration load(InputStream stream) {
try {
Yaml yaml = new Yaml();
ObjectMapper mapper = new ObjectMapper();
String mapAsString = mapper.writeValueAsString(yaml.load(stream));
JAXBContext ctx = JAXBContext.newInstance(DefaultApplicationConfiguration.class);
ApplicationConfiguration conf = mapper.readValue(mapAsString, ApplicationConfiguration.class);
return (ApplicationConfiguration) ctx.createUnmarshaller().unmarshal(stream);
if (conf.persistenceConfiguration() == null) {
String location = String.format("%s/state/%s_%s", Utils.home(), conf.group(), conf.name());
File dir = new File(location);
if (!dir.exists())
dir.mkdirs();
} catch (JAXBException e) {
conf.persistenceConfiguration(
new PersistenceConfiguration(LocalWriter.class, new LocalWriterConfiguration(location)));
throw new RuntimeException("invalid service configuration", e);
}
}
finally {
closeSafely(stream);
return conf;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@ -54,96 +65,82 @@ public class ApplicationConfigurationBinder {
* @return the handlers
* @throws RuntimeException if the serialisation is invalid
*/
public ApplicationHandlers bindHandlers(InputStream stream) {
public ApplicationHandlers bindHandlers(ClassLoader classLoader) {
//collects handler classes
Set<Class<?>> classes = scanForHandlers();
List<RequestHandler> requestHandlers = new LinkedList<RequestHandler>();
try {
// ADDING BASE Handler (order is important)
requestHandlers.add(new RequestMetrics());
requestHandlers.add(new RequestValidator());
requestHandlers.add(new RequestAccounting());
JAXBContext ctx = JAXBContext.newInstance(classes.toArray(new Class<?>[0]));
// TODO scan RequestHAndler form classloader
return (ApplicationHandlers) ctx.createUnmarshaller().unmarshal(stream);
List<ApplicationLifecycleHandler> lifecycleHandlers = new LinkedList<ApplicationLifecycleHandler>();
} catch (JAXBException e) {
// ADDING BASE Handler (order is important)
lifecycleHandlers.add(new ApplicationProfileManager());
throw unchecked(e);
// TODO scan ApplicationLifecycleHandler form classloader
return new ApplicationHandlers(lifecycleHandlers, requestHandlers);
}
finally {
closeSafely(stream);
}
}
/**
* Returns the extensions of the application from their XML serialisation.
*
* @param stream the serialisation
* @return the extensions
* @throws RuntimeException if the serialisation is invalid
*
* public ApplicationExtensions
* bindExtensions(InputStream stream) {
*
* //collects handler classes Set<Class<?>> classes =
* scanForExtensions();
*
* try {
*
* JAXBContext ctx =
* JAXBContext.newInstance(classes.toArray(new
* Class<?>[0]));
*
* return (ApplicationExtensions)
* ctx.createUnmarshaller().unmarshal(stream);
*
* } catch (JAXBException e) {
*
* throw unchecked(e);
*
* } finally { closeSafely(stream); } }
*
*
*
* private Set<Class<?>> scanForExtensions() throws
* RuntimeException {
*
* @SuppressWarnings("all")
* ServiceLoader<ApplicationExtension> handlerLoader =
* (ServiceLoader)
* ServiceLoader.load(ApplicationExtension.class);
*
* Set<Class<?>> scanned = new HashSet<Class<?>>();
*
* for (ApplicationExtension handler : handlerLoader) {
* Class<?> handlerClass = handler.getClass(); if
* (handlerClass.isInterface() ||
* handlerClass.getModifiers() == Modifier.ABSTRACT)
* continue; else scanned.add(handlerClass); }
*
* //add top-level configuration
* scanned.add(ApplicationExtensions.class);
*
* return scanned; }
*/
public ApplicationExtensions bindExtensions(InputStream stream) {
//collects handler classes
Set<Class<?>> classes = scanForExtensions();
try {
JAXBContext ctx = JAXBContext.newInstance(classes.toArray(new Class<?>[0]));
return (ApplicationExtensions) ctx.createUnmarshaller().unmarshal(stream);
} catch (JAXBException e) {
throw unchecked(e);
}
finally {
closeSafely(stream);
}
}
private Set<Class<?>> scanForHandlers() throws RuntimeException {
@SuppressWarnings("all")
ServiceLoader<ApplicationHandler> handlerLoader = (ServiceLoader) ServiceLoader.load(ApplicationHandler.class);
Set<Class<?>> scanned = new HashSet<Class<?>>();
for (ApplicationHandler<?> handler : handlerLoader) {
Class<?> handlerClass = handler.getClass();
if (handlerClass.isInterface() || handlerClass.getModifiers() == Modifier.ABSTRACT)
continue;
else
scanned.add(handlerClass);
}
//add top-level configuration
scanned.add(ApplicationHandlers.class);
return scanned;
public void scanForApplicationHandlers(ClassLoader currentClassLoader) {
// TODO Auto-generated method stub
}
private Set<Class<?>> scanForExtensions() throws RuntimeException {
@SuppressWarnings("all")
ServiceLoader<ApplicationExtension> handlerLoader = (ServiceLoader) ServiceLoader.load(ApplicationExtension.class);
Set<Class<?>> scanned = new HashSet<Class<?>>();
for (ApplicationExtension handler : handlerLoader) {
Class<?> handlerClass = handler.getClass();
if (handlerClass.isInterface() || handlerClass.getModifiers() == Modifier.ABSTRACT)
continue;
else
scanned.add(handlerClass);
}
//add top-level configuration
scanned.add(ApplicationExtensions.class);
return scanned;
}
}

@ -1,75 +0,0 @@
package org.gcube.smartgears.configuration.application;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlRootElement;
import org.gcube.common.validator.ValidationError;
import org.gcube.common.validator.Validator;
import org.gcube.common.validator.ValidatorFactory;
import org.gcube.common.validator.annotations.IsValid;
import org.gcube.smartgears.extensions.ApplicationExtension;
import org.w3c.dom.Element;
/**
* The {@link ApplicationExtension}s that manage the application.
*
* @author Fabio Simeoni
*
*/
@XmlRootElement(name="extensions")
public class ApplicationExtensions {
@XmlAnyElement(lax=true) @IsValid
List<ApplicationExtension> extensions = new ArrayList<ApplicationExtension>();
public ApplicationExtensions() {}
/**
* Returns the extensions for the application.
* @return the extensions
*/
public List<ApplicationExtension> extensions() {
return extensions;
}
/**
* Sets the extensions for the application.
* @param extensions the extensions
* @return this configuration
*/
public ApplicationExtensions set(ApplicationExtension ... extensions) {
this.extensions = Arrays.asList(extensions);
return this;
}
@Override
public String toString() {
return extensions.toString();
}
public void validate() {
List<String> msgs = new ArrayList<String>();
Validator validator = ValidatorFactory.validator();
for (ValidationError error : validator.validate(this))
msgs.add(error.toString());
if (!msgs.isEmpty())
throw new IllegalStateException("invalid configuration: "+msgs);
}
//since we use @AnyElement, after deserialisation, we check there are no DOM elements
void afterUnmarshal(Unmarshaller u, Object parent) {
for (Object o : extensions)
if (o instanceof Element)
throw new RuntimeException("invalid extensions detected: "+Element.class.cast(o).getLocalName());
}
}

@ -1,21 +1,11 @@
package org.gcube.smartgears.configuration.application;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import org.gcube.common.validator.ValidationError;
import org.gcube.common.validator.Validator;
import org.gcube.common.validator.ValidatorFactory;
import org.gcube.common.validator.annotations.IsValid;
import org.gcube.smartgears.handlers.application.ApplicationHandler;
import org.gcube.smartgears.handlers.application.ApplicationLifecycleHandler;
import org.gcube.smartgears.handlers.application.RequestHandler;
import org.w3c.dom.Element;
/**
* The {@link ApplicationHandler}s that manage the application.
@ -23,24 +13,24 @@ import org.w3c.dom.Element;
* @author Fabio Simeoni
*
*/
@XmlRootElement(name="handlers")
public class ApplicationHandlers {
@XmlElement(name="lifecycle") @IsValid
private LifecycleHandlers lifecycleHandlers = new LifecycleHandlers();
private List<ApplicationLifecycleHandler> lifecycleHandlers = new LinkedList<ApplicationLifecycleHandler>();
@XmlElement(name="request") @IsValid
private RequestHandlers requestHandlers = new RequestHandlers();
private List<RequestHandler> requestHandlers = new LinkedList<RequestHandler>();
public ApplicationHandlers() {}
public ApplicationHandlers(List<ApplicationLifecycleHandler> lifecycleHandlers, List<RequestHandler> requestHandlers) {
this.lifecycleHandlers = lifecycleHandlers;
this.requestHandlers = requestHandlers;
}
/**
* Returns the {@link ApplicationLifecycleHandler}s for the service.
* @return the lifecycle handlers
*/
public List<ApplicationLifecycleHandler> lifecycleHandlers() {
return lifecycleHandlers.values;
return lifecycleHandlers;
}
/**
@ -48,8 +38,8 @@ public class ApplicationHandlers {
* @param handlers the lifecycle handlers
* @return this configuration
*/
public ApplicationHandlers set(ApplicationLifecycleHandler ... handlers) {
this.lifecycleHandlers = new LifecycleHandlers(Arrays.asList(handlers));
public ApplicationHandlers setLifecycleHandlers(List<ApplicationLifecycleHandler> handlers) {
this.lifecycleHandlers = handlers;
return this;
}
@ -58,7 +48,7 @@ public class ApplicationHandlers {
* @return the lifetime handlers
*/
public List<RequestHandler> requestHandlers() {
return requestHandlers.values;
return requestHandlers;
}
/**
@ -66,75 +56,11 @@ public class ApplicationHandlers {
* @param handlers the request handlers
* @return this configuration
*/
public ApplicationHandlers set(RequestHandler ... handlers) {
this.requestHandlers = new RequestHandlers(Arrays.asList(handlers));
public ApplicationHandlers setRequetHandlers(List<RequestHandler> handlers) {
this.requestHandlers = handlers;
return this;
}
public void validate() {
List<String> msgs = new ArrayList<String>();
Validator validator = ValidatorFactory.validator();
for (ValidationError error : validator.validate(this))
msgs.add(error.toString());
if (!msgs.isEmpty())
throw new IllegalStateException("invalid configuration: "+msgs);
}
//////////////// HELPER BINDING CLASSES
//used internally to introduce level of nesting in JAXB whilst preserving arbitrary extension
private static class LifecycleHandlers {
@SuppressWarnings("all")
LifecycleHandlers() { //needed for deserialisation
}
LifecycleHandlers(List<ApplicationLifecycleHandler> handlers) {
this.values=handlers;
}
@XmlAnyElement(lax=true)
List<ApplicationLifecycleHandler> values = new ArrayList<ApplicationLifecycleHandler>();
//since we use @AnyElement, after deserialisation, we check there are no DOM elements
@SuppressWarnings("unused")
void afterUnmarshal(Unmarshaller u, Object parent) {
for (Object o : values)
if (o instanceof Element)
throw new RuntimeException("invalid handler detected in configuration: "+Element.class.cast(o).getLocalName());
}
}
//used internally to introduce level of nesting in JAXB whilst preserving arbitrary extension
private static class RequestHandlers {
@SuppressWarnings("all")
RequestHandlers() { //needed for deserialisation
}
RequestHandlers(List<RequestHandler> handlers) {
this.values=handlers;
}
@XmlAnyElement(lax=true)
List<RequestHandler> values = new ArrayList<RequestHandler>();
//since we use @AnyElement, after deserialisation, we check there are no DOM elements
@SuppressWarnings("unused")
void afterUnmarshal(Unmarshaller u, Object parent) {
for (Object o : values)
if (o instanceof Element)
throw new RuntimeException("invalid handler detected in configuration: "+Element.class.cast(o).getLocalName());
}
}
public void mergeWith(ApplicationHandlers other){
List<ApplicationLifecycleHandler> lifecycles = other.lifecycleHandlers();

@ -1,167 +0,0 @@
package org.gcube.smartgears.configuration.application;
import static org.gcube.smartgears.configuration.Mode.offline;
import java.io.File;
import java.util.Set;
import org.gcube.smartgears.configuration.Mode;
import org.gcube.smartgears.configuration.container.ContainerConfiguration;
import org.gcube.smartgears.persistence.DefaultPersistence;
import org.gcube.smartgears.persistence.Persistence;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author Fabio Simeoni
* @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/
*/
public class BridgedApplicationConfiguration implements ApplicationConfiguration {
private static Logger log = LoggerFactory.getLogger(ApplicationConfiguration.class);
private final ContainerConfiguration container;
private final ApplicationConfiguration application;
public BridgedApplicationConfiguration(ContainerConfiguration container, ApplicationConfiguration config) {
this.container=container;
this.application=config;
if (application.persistence()==null) {
String location = container.persistence().location()+"/"+application.name();
File dir = new File(location);
if (!dir.exists())
dir.mkdirs();
application.persistence(new DefaultPersistence(location));
log.trace("setting persistence location for {} @ {}",application.name(), dir.getAbsolutePath());
}
}
public ApplicationConfiguration inner() {
return application;
}
public Mode mode() {
return container.mode()==offline?offline:application.mode();
}
@Override
public String context() {
return application.context();
}
@Override
public ApplicationConfiguration context(String context) {
return application.context(context);
}
public String name() {
return application.name();
}
public ApplicationConfiguration name(String name) {
return application.name(name);
}
public String serviceClass() {
return application.serviceClass();
}
public ApplicationConfiguration serviceClass(String group) {
return application.serviceClass(group);
}
public String version() {
return application.version();
}
public ApplicationConfiguration version(String version) {
return application.version(version);
}
public String description() {
return application.description();
}
public ProxyAddress proxyAddress() {
return application.proxyAddress();
}
public ApplicationConfiguration description(String description) {
return application.description(description);
}
public Persistence persistence() {
return application.persistence();
}
public ApplicationConfiguration persistence(Persistence manager) {
return application.persistence(manager);
}
public ApplicationConfiguration mode(Mode mode) {
return application.mode(mode);
}
public void validate() {
application.validate();
}
@Override
public Set<Exclude> excludes() {
return application.excludes();
}
@Override
public Set<Include> includes() {
return application.includes();
}
@Override
public void merge(ApplicationConfiguration config) {
application.merge(config);
}
@Override
public Set<String> startTokens() {
return application.startTokens();
}
@Override
public ApplicationConfiguration startTokens(Set<String> tokens) {
return application.startTokens(tokens);
}
@Override
public boolean proxied() {
return application.proxied();
}
@Override
public ApplicationConfiguration excludes(Exclude ... excludes) {
return application.excludes(excludes);
}
@Override
public ApplicationConfiguration includes(Include... includes) {
return application.includes(includes);
}
@Override
public ApplicationConfiguration proxyAddress(ProxyAddress proxyaddress) {
return application.proxyAddress(proxyaddress);
}
}

@ -1,237 +0,0 @@
package org.gcube.smartgears.configuration.application;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import org.gcube.common.validator.ValidationError;
import org.gcube.common.validator.Validator;
import org.gcube.common.validator.ValidatorFactory;
import org.gcube.common.validator.annotations.IsValid;
import org.gcube.common.validator.annotations.NotNull;
import org.gcube.smartgears.configuration.Mode;
import org.gcube.smartgears.persistence.DefaultPersistence;
import org.gcube.smartgears.persistence.Persistence;
/**
* The configuration of a managed app.
* <p>
* Includes the list of its client services.
*
* @author Fabio Simeoni
* @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/
*
*/
@XmlRootElement(name="application")
public class DefaultApplicationConfiguration implements ApplicationConfiguration {
@XmlAttribute
private Mode mode = Mode.online;
@XmlAttribute(name="context")
String context;
@XmlElement(name="name" , required=true)
@NotNull
String name;
@XmlElement(name="group", required=true)
@NotNull
String group;
@XmlElement(name="version", required=true)
@NotNull
String version;
@XmlTransient
Set<String> tokens = new HashSet<String>();
@XmlElement(name="description")
String description="";
@XmlElementRef
@IsValid
ProxyAddress proxyAddress;
@XmlElementRef
Set<Exclude> excludes= new LinkedHashSet<Exclude>();
@XmlElementRef
Set<Include> includes= new LinkedHashSet<Include>();
@XmlElementRef(type=DefaultPersistence.class)
@NotNull @IsValid
private Persistence persistenceManager;
@Override
public Set<Exclude> excludes() {
return excludes;
}
@Override
public Set<Include> includes() {
return includes;
}
public DefaultApplicationConfiguration() {}
@Override
public Mode mode() {
return mode;
}
@Override
public String name() {
return name;
}
@Override
public String context() {
return context;
}
@Override
public ApplicationConfiguration context(String context) {
this.context=context;
return this;
}
public ProxyAddress proxyAddress() {
return proxyAddress;
}
@Override
public ApplicationConfiguration excludes(Exclude ... excludes) {
this.excludes=new HashSet<Exclude>(Arrays.asList(excludes));
return this;
}
@Override
public ApplicationConfiguration includes(Include... includes) {
this.includes=new HashSet<Include>(Arrays.asList(includes));
return this;
}
@Override
public ApplicationConfiguration name(String name) {
this.name=name;
return this;
}
@Override
public String serviceClass() {
return group;
}
@Override
public ApplicationConfiguration serviceClass(String group) {
this.group=group;
return this;
}
@Override
public String version() {
return version;
}
@Override
public ApplicationConfiguration version(String version) {
this.version=version;
return this;
}
@Override
public Set<String> startTokens() {
return tokens;
}
@Override
public ApplicationConfiguration startTokens(Set<String> tokens) {
this.tokens.addAll(tokens);
return this;
}
@Override
public String description() {
return description;
}
@Override
public ApplicationConfiguration description(String description) {
this.description=description;
return this;
}
@Override
public boolean proxied() {
return proxyAddress!=null;
}
@Override
public Persistence persistence() {
return persistenceManager;
}
@Override
public ApplicationConfiguration persistence(Persistence manager) {
this.persistenceManager=manager;
return this;
}
@Override
public ApplicationConfiguration proxyAddress(ProxyAddress proxyaddress) {
this.proxyAddress = proxyaddress;
return this;
}
@Override
public ApplicationConfiguration mode(Mode mode) {
this.mode=mode;
return this;
}
@Override
public void validate() {
List<String> msgs = new ArrayList<String>();
Validator validator = ValidatorFactory.validator();
for (ValidationError error : validator.validate(this))
msgs.add(error.toString());
if (!this.excludes().isEmpty() && !this.includes().isEmpty())
msgs.add("exclude tags and includes tags are mutually exclusive");
if (!msgs.isEmpty())
throw new IllegalStateException("invalid configuration: "+msgs);
}
@Override
public void merge(ApplicationConfiguration config) {
mode(config.mode());
if (config.persistence()!=null)
persistence(config.persistence());
//scopes.addAll(config.startScopes());
}
}

@ -3,20 +3,14 @@ package org.gcube.smartgears.configuration.application;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlValue;
import org.gcube.common.validator.annotations.NotEmpty;
@XmlRootElement(name="exclude")
@XmlAccessorType(XmlAccessType.FIELD)
public class Exclude {
public class GCubeExclude {
@XmlAttribute(name="handlers")
@NotEmpty
private List<String> handlers = new ArrayList<String>();
@XmlValue
@NotEmpty
private String path;
public List<String> getHandlers() {
@ -27,14 +21,14 @@ public class Exclude {
return path;
}
protected Exclude() {}
protected GCubeExclude() {}
public Exclude(String path) {
public GCubeExclude(String path) {
super();
this.path = path;
}
public Exclude(List<String> handlers, String path) {
public GCubeExclude(List<String> handlers, String path) {
super();
this.handlers = handlers;
this.path = path;
@ -57,7 +51,7 @@ public class Exclude {
return false;
if (getClass() != obj.getClass())
return false;
Exclude other = (Exclude) obj;
GCubeExclude other = (GCubeExclude) obj;
if (handlers == null) {
if (other.handlers != null)
return false;

@ -3,20 +3,14 @@ package org.gcube.smartgears.configuration.application;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlValue;
import org.gcube.common.validator.annotations.NotEmpty;
@XmlRootElement(name="include")
@XmlAccessorType(XmlAccessType.FIELD)
public class Include {
public class GCubeInclude {
@XmlAttribute(name="handlers")
@NotEmpty
private List<String> handlers = new ArrayList<String>();
@XmlValue
@NotEmpty
private String path;
public List<String> getHandlers() {
@ -27,14 +21,14 @@ public class Include {
return path;
}
protected Include() {}
protected GCubeInclude() {}
public Include(String path) {
public GCubeInclude(String path) {
super();
this.path = path;
}
public Include(List<String> handlers, String path) {
public GCubeInclude(List<String> handlers, String path) {
super();
this.handlers = handlers;
this.path = path;
@ -57,7 +51,7 @@ public class Include {
return false;
if (getClass() != obj.getClass())
return false;
Include other = (Include) obj;
GCubeInclude other = (GCubeInclude) obj;
if (handlers == null) {
if (other.handlers != null)
return false;

@ -1,97 +0,0 @@
package org.gcube.smartgears.configuration.application;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import org.gcube.common.validator.annotations.NotNull;
@XmlRootElement(name="proxy")
public class ProxyAddress {
@XmlAttribute
String protocol = "http";
@XmlElement
@NotNull
String hostname;
@XmlElement
Integer port;
public String hostname() {
return hostname;
}
public ProxyAddress hostname(String hostname) {
this.hostname = hostname;
return this;
}
public Integer port() {
return port;
}
public ProxyAddress port(int port) {
this.port = port;
return this;
}
public String protocol() {
return protocol;
}
public ProxyAddress protocol(String protocol) {
this.protocol = protocol;
return this;
}
@Override
public String toString() {
return "ProxyAddress [protocol=" + protocol + ", hostname=" + hostname + ", port=" + port + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((hostname == null) ? 0 : hostname.hashCode());
result = prime * result + ((port == null) ? 0 : port.hashCode());
result = prime * result + ((protocol == null) ? 0 : protocol.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ProxyAddress other = (ProxyAddress) obj;
if (hostname == null) {
if (other.hostname != null)
return false;
} else if (!hostname.equals(other.hostname))
return false;
if (port == null) {
if (other.port != null)
return false;
} else if (!port.equals(other.port))
return false;
if (protocol == null) {
if (other.protocol != null)
return false;
} else if (!protocol.equals(other.protocol))
return false;
return true;
}
}

@ -0,0 +1,99 @@
package org.gcube.smartgears.configuration.container;
import static org.gcube.smartgears.Constants.default_container_publication_frequency_in_seconds;
import org.gcube.common.validator.annotations.NotEmpty;
import org.gcube.common.validator.annotations.NotNull;
import org.gcube.smartgears.configuration.Mode;
public class BaseConfiguration {
Mode mode = Mode.online;
@NotNull @NotEmpty
String hostname;
@NotNull
Integer port;
@NotNull @NotEmpty
String protocol="http";
boolean authorizeChildrenContext = false;
@NotNull @NotEmpty
String infrastructure;
long publicationFrequencyInSeconds = default_container_publication_frequency_in_seconds;
public Mode getMode() {
return mode;
}
public String getHostname() {
return hostname;
}
public Integer getPort() {
return port;
}
public String getProtocol() {
return protocol;
}
public boolean isAuthorizeChildrenContext() {
return authorizeChildrenContext;
}
public String getInfrastructure() {
return infrastructure;
}
public long getPublicationFrequencyInSeconds() {
return publicationFrequencyInSeconds;
}
public void setPublicationFrequencyInSeconds(long publicationFrequencyInSeconds) {
this.publicationFrequencyInSeconds = publicationFrequencyInSeconds;
}
public void setMode(Mode mode) {
this.mode = mode;
}
public void setHostname(String hostname) {
this.hostname = hostname;
}
public void setPort(Integer port) {
this.port = port;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public void setAuthorizeChildrenContext(boolean authorizeChildrenContext) {
this.authorizeChildrenContext = authorizeChildrenContext;
}
public void setInfrastructure(String infrastructure) {
this.infrastructure = infrastructure;
}
@Override
public String toString() {
return "BaseConfiguration [mode=" + mode + ", hostname=" + hostname + ", port=" + port + ", protocol="
+ protocol + ", authorizeChildrenContext=" + authorizeChildrenContext + ", infrastructure="
+ infrastructure + ", publicationFrequency=" + publicationFrequencyInSeconds
+ "]";
}
}

@ -1,20 +1,10 @@
package org.gcube.smartgears.configuration.container;
import static org.gcube.smartgears.Constants.default_container_publication_frequency_in_seconds;
import static org.gcube.smartgears.utils.Utils.notNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import org.gcube.common.validator.ValidationError;
import org.gcube.common.validator.Validator;
@ -22,90 +12,87 @@ import org.gcube.common.validator.ValidatorFactory;
import org.gcube.common.validator.annotations.IsValid;
import org.gcube.common.validator.annotations.NotEmpty;
import org.gcube.common.validator.annotations.NotNull;
import org.gcube.smartgears.configuration.AuthorizationProviderConfiguration;
import org.gcube.smartgears.configuration.Mode;
import org.gcube.smartgears.configuration.PersistenceConfiguration;
import org.gcube.smartgears.configuration.ProxyAddress;
import org.gcube.smartgears.configuration.application.ApplicationConfiguration;
import org.gcube.smartgears.configuration.application.DefaultApplicationConfiguration;
import org.gcube.smartgears.persistence.DefaultPersistence;
import org.gcube.smartgears.persistence.Persistence;
/**
* The configuration of the container.
*
* @author Fabio Simeoni
* @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/
* @author Luca Frosini (ISTI - CNR)
*/
@XmlRootElement(name="container")
public class ContainerConfiguration {
@XmlAttribute
private Mode mode = Mode.online;
@XmlElement
@NotNull @IsValid
String hostname;
@XmlElement
@NotNull
Integer port;
@XmlElement(name ="authentication-endpoint")
String authenticationEnpoint = null;
@XmlElement(name ="protocol")
@NotNull @IsValid
private BaseConfiguration baseConfiguration;
@IsValid
private Map<String,String> properties = new HashMap<String, String>();
@NotNull @IsValid
String protocol="http";
@XmlElement
boolean authorizeChildrenContext = false;
private Site site;
@IsValid
private ProxyAddress proxy;
@NotEmpty @NotNull
private String accountingFallbackLocation;
private List<ApplicationConfiguration> apps = new ArrayList<ApplicationConfiguration>();
@XmlElement
@NotNull@IsValid
String infrastructure;
@XmlElement
@NotNull @IsValid
Site site;
@XmlElement(name="token")
@NotNull @NotEmpty
List<String> tokens = new ArrayList<String>();
private PersistenceConfiguration persistenceConfiguration;
@XmlTransient
Set<String> allowedContext = new HashSet<String>();
@XmlElementRef(type=DefaultApplicationConfiguration.class)
List<ApplicationConfiguration> apps = new ArrayList<ApplicationConfiguration>();
@XmlElement(name="property")
@IsValid
List<Property> properties = new ArrayList<Property>();
@NotNull @IsValid
private AuthorizationProviderConfiguration authorizationConfiguration;
@XmlElement(name="publication-frequency")
long publicationFrequency = default_container_publication_frequency_in_seconds;
@XmlElementRef(type=DefaultPersistence.class)
@IsValid
private Persistence persistenceManager;
protected void setBaseConfiguration(BaseConfiguration baseConfiguration) {
this.baseConfiguration = baseConfiguration;
}
protected void setProperties(Map<String, String> properties) {
this.properties = properties;
}
protected void setSite(Site site) {
this.site = site;
}
protected void setProxy(ProxyAddress proxy) {
this.proxy = proxy;
}
protected void setAccountingFallbackLocation(String accountingFallbackLocation) {
this.accountingFallbackLocation = accountingFallbackLocation;
}
protected void setPersistenceConfiguration(PersistenceConfiguration persistenceConfiguration) {
this.persistenceConfiguration = persistenceConfiguration;
}
protected void setAuthorizationProviderConfiguration(
AuthorizationProviderConfiguration authorizationConfiguration) {
this.authorizationConfiguration = authorizationConfiguration;
}
public void setApps(List<ApplicationConfiguration> apps) {
this.apps = apps;
}
/**
* Returns the management mode for the container.
* @return the management mode
*/
public Mode mode() {
return mode;
}
/**
* Sets the management mode for the container.
* @param mode the management mode
* @return this configuration
*/
public ContainerConfiguration mode(Mode mode) {
this.mode=mode;
return this;
return baseConfiguration.getMode();
}
/**
* Returns the application configurations included in this configuration.
* @return the application configurations
@ -113,21 +100,21 @@ public class ContainerConfiguration {
public List<ApplicationConfiguration> apps() {
return apps;
}
/**
* Returns the configuration of an application with a given context path.
* @param context the context path
* @return the application configuration
*/
public ApplicationConfiguration app(String context) {
for (ApplicationConfiguration app : apps)
if (context.equals(app.context()))
return app;
return null;
}
/**
* Adds the configuration of an application to this configuration.
* @param app the application configuration
@ -146,7 +133,7 @@ public class ContainerConfiguration {
apps.add(app);
return this;
}
/**
* Returns the geographical site of the container.
* @return the site
@ -155,143 +142,77 @@ public class ContainerConfiguration {
return site;
}
/**
* Sets the geographical site of the container.
* @param site the site
* @return this configuration
*/
public ContainerConfiguration site(Site site) {
this.site=site;
return this;
}
/**
* Returns the infrastructure in which the container is running.
* @return the infrastructure
*/
public String infrastructure() {
return infrastructure;
return baseConfiguration.getInfrastructure();
}
/**
* Sets the infrastructure in which the container is running.
* @param infrastructure the infrastructure
* @return this configuration
*/
public ContainerConfiguration infrastructure(String infrastructure) {
this.infrastructure=infrastructure;
return this;
}
/**
* Returns the host name of the container.
* @return the host name;
*/
public String hostname() {
return hostname;
return baseConfiguration.getHostname();
}
/**
* Sets the host name of the container.
* @param name the host name
* @return this configuration
*/
public ContainerConfiguration hostname(String name) {
this.hostname=name;
return this;
}
/**
* Returns the port at which the container is listening for requests.
* @return the port
*/
public int port() {
return port;
return baseConfiguration.getPort();
}
/**
* Returns the port at which the container is listening for requests.
* @return the port
*/
public String protocol() {
return protocol;
}
public String authenticationEnpoint() {
return authenticationEnpoint;
}
public ContainerConfiguration authenticationEnpoint(String endpoint) {
this.authenticationEnpoint = endpoint;
return this;
return baseConfiguration.getProtocol();
}
/**
* Sets the port at which the container is listening for requests.
* @param port the port
* @return this configuration
*/
public ContainerConfiguration port(int port) {
this.port=port;
return this;
}
public ContainerConfiguration protocol(String protocol) {
this.protocol=protocol;
return this;
}
public boolean authorizeChildrenContext() {
return authorizeChildrenContext;
}
public ContainerConfiguration authorizeChildrenContext(boolean authorizeChildrenContext) {
this.authorizeChildrenContext = authorizeChildrenContext;
return this;
public boolean authorizeChildrenContext() {
return baseConfiguration.isAuthorizeChildrenContext();
}
/**
* Returns the VOs in which the container initially operates.
* @return the VOs
* Returns the proxy of the container.
* @return the proxy
*/
public List<String> startTokens() {
return tokens;
public ProxyAddress proxy() {
return proxy;
}
/**
* Sets the VOs in which the container initially operates.
* @param vos the VOs
* @return this configuration
* Returns the persistence manager of the container.
* @return the manager
*/
public ContainerConfiguration startTokens(List<String> tokens) {
notNull("start Tokens",tokens);
this.tokens = tokens;
return this;
public PersistenceConfiguration persistenceConfiguration() {
return this.persistenceConfiguration;
}
/**
* Returns the persistence manager of the container.
* @return the manager
*/
public Persistence persistence() {
return persistenceManager;
public String accountingFallbackLocation() {
return accountingFallbackLocation;
}
/**
* Sets the persistence manager of the container.
* @param manager the manager
* @return this configuration
* Returns the authorization configuration.
* @return AuthorizationProviderConfiguration the configuration
*/
public ContainerConfiguration persistence(Persistence manager) {
this.persistenceManager=manager;
return this;
public AuthorizationProviderConfiguration authorizationConfiguration() {
return authorizationConfiguration;
}
/**
@ -299,48 +220,15 @@ public class ContainerConfiguration {
* @return the properties
*/
public Map<String,String> properties() {
Map<String,String> map = new HashMap<String, String>();
for (Property prop : properties)
map.put(prop.name, prop.value);
return map;
return Collections.unmodifiableMap(properties);
}
/**
* Adds a configuration property to the container.
* @param the name of the property
* @param the value of the property
* @return this configuration
*/
public ContainerConfiguration property(String name, String value) {
properties.add(new Property(name, value));
return this;
}
/**
* Returns the publication frequency for the container's profile.
* @return the frquency;
*/
public long publicationFrequency() {
return publicationFrequency;
}
/**
* Sets the publication frequency for the container's profile.
* @param frequency the frequency
* @return this configuration
*/
public ContainerConfiguration publicationFrequency(long frequency) {
this.publicationFrequency=frequency;
return this;
}
public Set<String> allowedContexts() {
return allowedContext;
}
public void allowedContexts(Set<String> allowedContexts) {
this.allowedContext = allowedContexts;
return baseConfiguration.getPublicationFrequencyInSeconds();
}
/**
@ -353,168 +241,23 @@ public class ContainerConfiguration {
List<String> msgs = new ArrayList<String>();
Validator validator = ValidatorFactory.validator();
for (ValidationError error : validator.validate(this))
msgs.add(error.toString());
if (!msgs.isEmpty())
throw new IllegalStateException("invalid configuration: "+msgs);
}
static class Property {
@XmlAttribute @NotNull
String name;
@XmlAttribute @NotNull
String value;
Property() {}
Property(String key, String value) {
this.name=key;
this.value=value;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((value == null) ? 0 : value.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Property other = (Property) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (value == null) {
if (other.value != null)
return false;
} else if (!value.equals(other.value))
return false;
return true;
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((allowedContext == null) ? 0 : allowedContext.hashCode());
result = prime * result + ((apps == null) ? 0 : apps.hashCode());
result = prime * result + ((authenticationEnpoint == null) ? 0 : authenticationEnpoint.hashCode());
result = prime * result + (authorizeChildrenContext ? 1231 : 1237);
result = prime * result + ((hostname == null) ? 0 : hostname.hashCode());
result = prime * result + ((infrastructure == null) ? 0 : infrastructure.hashCode());
result = prime * result + ((mode == null) ? 0 : mode.hashCode());
result = prime * result + ((persistenceManager == null) ? 0 : persistenceManager.hashCode());
result = prime * result + ((port == null) ? 0 : port.hashCode());
result = prime * result + ((properties == null) ? 0 : properties.hashCode());
result = prime * result + ((protocol == null) ? 0 : protocol.hashCode());
result = prime * result + (int) (publicationFrequency ^ (publicationFrequency >>> 32));
result = prime * result + ((site == null) ? 0 : site.hashCode());
result = prime * result + ((tokens == null) ? 0 : tokens.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ContainerConfiguration other = (ContainerConfiguration) obj;
if (allowedContext == null) {
if (other.allowedContext != null)
return false;
} else if (!allowedContext.equals(other.allowedContext))
return false;
if (apps == null) {
if (other.apps != null)
return false;
} else if (!apps.equals(other.apps))
return false;
if (authenticationEnpoint == null) {
if (other.authenticationEnpoint != null)
return false;
} else if (!authenticationEnpoint.equals(other.authenticationEnpoint))
return false;
if (authorizeChildrenContext != other.authorizeChildrenContext)
return false;
if (hostname == null) {
if (other.hostname != null)
return false;
} else if (!hostname.equals(other.hostname))
return false;
if (infrastructure == null) {
if (other.infrastructure != null)
return false;
} else if (!infrastructure.equals(other.infrastructure))
return false;
if (mode != other.mode)
return false;
if (persistenceManager == null) {
if (other.persistenceManager != null)
return false;
} else if (!persistenceManager.equals(other.persistenceManager))
return false;
if (port == null) {
if (other.port != null)
return false;
} else if (!port.equals(other.port))
return false;
if (properties == null) {
if (other.properties != null)
return false;
} else if (!properties.equals(other.properties))
return false;
if (protocol == null) {
if (other.protocol != null)
return false;
} else if (!protocol.equals(other.protocol))
return false;
if (publicationFrequency != other.publicationFrequency)
return false;
if (site == null) {
if (other.site != null)
return false;
} else if (!site.equals(other.site))
return false;
if (tokens == null) {
if (other.tokens != null)
return false;
} else if (!tokens.equals(other.tokens))
return false;
return true;
}
@Override
public String toString() {
return "ContainerConfiguration [mode=" + mode + ", hostname=" + hostname + ", port=" + port + ", authenticationEnpoint=" + authenticationEnpoint + ", protocol=" + protocol
+ ", authorizeChildrenContext=" + authorizeChildrenContext + ", infrastructure=" + infrastructure
+ ", site=" + site + ", tokens=" + tokens + ", allowedContext=" + allowedContext + ", apps=" + apps
+ ", properties=" + properties + ", publicationFrequency=" + publicationFrequency
+ ", persistenceManager=" + persistenceManager + "]";
return "ContainerConfiguration [baseConfiguration=" + baseConfiguration + ", properties=" + properties
+ ", site=" + site + ", proxy=" + proxy + ", accountingFallbackLocation=" + accountingFallbackLocation
+ ", persistence=" + persistenceConfiguration.getImplementationClass().getSimpleName()
+ ", authorizationProvider=" + authorizationConfiguration + "]";
}
}

@ -1,18 +1,31 @@
package org.gcube.smartgears.configuration.container;
import static org.gcube.smartgears.utils.Utils.*;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.ServiceLoader;
import java.util.Set;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import org.gcube.common.security.credentials.Credentials;
import org.gcube.smartgears.configuration.AuthorizationProviderConfiguration;
import org.gcube.smartgears.configuration.ComponentConfiguration;
import org.gcube.smartgears.configuration.ConfiguredWith;
import org.gcube.smartgears.configuration.PersistenceConfiguration;
import org.gcube.smartgears.configuration.ProxyAddress;
import org.gcube.smartgears.configuration.SmartgearsConfiguration;
import org.gcube.smartgears.handlers.container.ContainerHandler;
import org.gcube.smartgears.handlers.container.lifecycle.AccountingManager;
import org.gcube.smartgears.handlers.container.lifecycle.ContainerProfileManager;
import org.gcube.smartgears.persistence.LocalWriter;
import org.gcube.smartgears.persistence.LocalWriterConfiguration;
import org.gcube.smartgears.persistence.PersistenceWriter;
import org.gcube.smartgears.security.AuthorizationProviderFactory;
import org.gcube.smartgears.security.defaults.DefaultAuthorizationProviderFactory;
import org.gcube.smartgears.utils.Utils;
import org.ini4j.Ini;
import org.ini4j.Profile.Section;
/**
* Binds {@link ContainerConfiguration}s to and from XML serialisations.
@ -22,34 +35,138 @@ import org.gcube.smartgears.utils.Utils;
*/
public class ContainerConfigurationBinder {
/**
* Returns a {@link ContainerConfiguration} from its XML serialisation.
*
* @param stream the serialisation
* @return the configuration
* @throws RuntimeException if the serialisation is invalid
*/
public ContainerConfiguration bind(InputStream stream) {
public ContainerConfiguration load(InputStream stream) {
try {
Ini configurator = new Ini(stream);
ContainerConfiguration conf = new ContainerConfiguration();
Section nodeSection = configurator.get("node");
if (nodeSection != null) {
BaseConfiguration nodeConf = new BaseConfiguration();
nodeSection.to(nodeConf);
conf.setBaseConfiguration(nodeConf);
}
Section propertiesSection = configurator.get("properties");
if (propertiesSection != null)
conf.setProperties(propertiesSection.entrySet().stream()
.collect(Collectors.toMap(Entry::getKey, Entry::getValue)));
Section siteSection = configurator.get("site");
if (siteSection != null) {
Site siteConf = new Site();
siteSection.to(siteConf);
conf.setSite(siteConf);
}
initAuthorizationPart(configurator, conf);
initPersistencePart(configurator, conf);
initProxyPart(configurator, conf);
// TODO: find a solution for this shit
String location = Utils.home() + "/state";
File dir = new File(location);
if (!dir.exists())
dir.mkdirs();
conf.setAccountingFallbackLocation(location);
// END Shit
return conf;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
JAXBContext ctx = JAXBContext.newInstance(ContainerConfiguration.class);
ContainerConfiguration config = (ContainerConfiguration) ctx.createUnmarshaller().unmarshal(stream);
return config;
private void initProxyPart(Ini configurator, ContainerConfiguration conf) throws Exception {
Section proxySection = configurator.get("proxy");
if (proxySection != null) {
ProxyAddress proxyConf = new ProxyAddress();
proxySection.to(proxyConf);
conf.setProxy(proxyConf);
}
}
} catch (JAXBException e) {
@SuppressWarnings("unchecked")
private void initPersistencePart(Ini configurator, ContainerConfiguration conf) throws Exception {
Section persistenceSection = configurator.get("persistence");
if (persistenceSection != null) {
String type = persistenceSection.get("class");
if (type == null)
throw new Exception("ini file error: type not found in \"persistence\" section");
/*
* PersistenceWriter persistenceWriter; try { Object persistenceImpl =
* Class.forName(type).getDeclaredConstructor().newInstance(); persistenceWriter
* = PersistenceWriter.class.cast(persistenceImpl); }catch (Exception e) { throw
* new
* Exception("ini file error: invalid persistence type in \"persistence\" section"
* , e); }
*/
// persistenceSection.to(persistenceWriter);
Class<? extends PersistenceWriter> persistenceClass = null;
try {
persistenceClass = (Class<? extends PersistenceWriter>) Class.forName(type);
} catch (Exception e) {
throw new Exception("ini file error: invalid persistence type in \"persistence\" section", e);
}
if (!persistenceClass.isAnnotationPresent(ConfiguredWith.class))
throw new Exception(
"ini file error: invalid class type in \"persistence\" section,ConfiguredWith annotation not present");
Class<? extends ComponentConfiguration> writerConfClass = persistenceClass
.getAnnotation(ConfiguredWith.class).value();
ComponentConfiguration writerConfiguration = writerConfClass.getDeclaredConstructor().newInstance();
persistenceSection.to(writerConfiguration, ".");
conf.setPersistenceConfiguration(
new PersistenceConfiguration((Class<PersistenceWriter>) persistenceClass, writerConfiguration));
} else {
String location = Utils.home() + "/state";
File dir = new File(location);
if (!dir.exists())
dir.mkdirs();
conf.setPersistenceConfiguration(
new PersistenceConfiguration(LocalWriter.class, new LocalWriterConfiguration(location)));
}
throw new RuntimeException("invalid container configuration", e);
}
}
finally {
Utils.closeSafely(stream);
private void initAuthorizationPart(Ini configurator, ContainerConfiguration conf) throws Exception {
Section authorizationSection = configurator.get("authorization");
if (authorizationSection != null) {
String provider = authorizationSection.get("factory");
AuthorizationProviderFactory<?> authProviderFactory;
if (provider != null) {
try {
Object authProviderImpl = Class.forName(provider).getDeclaredConstructor().newInstance();
authProviderFactory = AuthorizationProviderFactory.class.cast(authProviderImpl);
} catch (Exception e) {
throw new Exception("ini file error: invalid provider type in \"authorization\" section", e);
}
} else
authProviderFactory = new DefaultAuthorizationProviderFactory();
authorizationSection.to(authProviderFactory, "factory.");
String type = authorizationSection.get("credentials.class");
if (type == null)
throw new Exception("ini file error: credentials type not found in \"authorization\" section");
Credentials credentials;
try {
Object credentialsImpl = Class.forName(type).getDeclaredConstructor().newInstance();
credentials = Credentials.class.cast(credentialsImpl);
} catch (Exception e) {
throw new Exception("ini file error: invalid credentials type in \"authorization\" section", e);
}
authorizationSection.to(credentials, "credentials.");
conf.setAuthorizationProviderConfiguration(
new AuthorizationProviderConfiguration(authProviderFactory, credentials));
}
}
/**
* Returns the handlers of the container from their XML serialisation.
*
@ -57,45 +174,35 @@ public class ContainerConfigurationBinder {
* @return the handlers
* @throws RuntimeException if the serialisation is invalid
*/
public ContainerHandlers bindHandlers(InputStream stream) {
public List<ContainerHandler> bindHandlers(ClassLoader classloader) {
//collects handler classes
Set<Class<?>> classes = scanForConfigurationElements();
LinkedList<ContainerHandler> handlers = new LinkedList<ContainerHandler>();
try {
// ADDING BASE Handlers (order is important)
handlers.add(new AccountingManager());
handlers.add(new ContainerProfileManager());
JAXBContext ctx = JAXBContext.newInstance(classes.toArray(new Class<?>[0]));
handlers.addAll(scanForContainerHadlers(classloader));
return (ContainerHandlers) ctx.createUnmarshaller().unmarshal(stream);
return handlers;
} catch (JAXBException e) {
}
throw unchecked(e);
private List<? extends ContainerHandler> scanForContainerHadlers(ClassLoader classloader) throws RuntimeException {
}
}
private Set<Class<?>> scanForConfigurationElements() throws RuntimeException {
@SuppressWarnings("all")
ServiceLoader<ContainerHandler> handlerLoader = (ServiceLoader) ServiceLoader.load(ContainerHandler.class);
Set<Class<?>> scanned = new HashSet<Class<?>>();
for (ContainerHandler handler : handlerLoader) {
Class<?> handlerClass = handler.getClass();
if (handlerClass.isInterface() || handlerClass.getModifiers() == Modifier.ABSTRACT)
continue;
else
scanned.add(handlerClass);
}
// TODO: scan for Container Handler
//add top-level configuration
scanned.add(ContainerHandlers.class);
return Collections.emptyList();
}
return scanned;
public SmartgearsConfiguration loadSmartgearsProperty() {
try {
Ini configurator = new Ini(this.getClass().getResourceAsStream("/META-INF/smartgears-config.ini"));
String version = configurator.get("smartgears").get("version");
return new SmartgearsConfiguration(version);
}catch (Exception e) {
throw new RuntimeException(e);
}
}
}

@ -1,50 +0,0 @@
package org.gcube.smartgears.configuration.container;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlRootElement;
import org.gcube.smartgears.handlers.container.ContainerHandler;
/**
* The {@link ContainerHandler}s that manage the application.
*
* @author Fabio Simeoni
*
*/
@XmlRootElement(name="handlers")
public class ContainerHandlers {
@XmlAnyElement(lax=true)
List<ContainerHandler> handlers = new ArrayList<ContainerHandler>();
public ContainerHandlers() {}
/**
* Returns the {@link ContainerHandler}s for the service.
* @return the lifecycle handlers
*/
public List<ContainerHandler> get() {
return handlers;
}
/**
* Sets the {@link ContainerHandler}s for the service.
* @param handlers the lifecycle handlers
* @return this configuration
*/
public ContainerHandlers set(ContainerHandler ... handlers) {
this.handlers = Arrays.asList(handlers);
return this;
}
public void mergeWith(ContainerHandlers other){
List<ContainerHandler> handlers = other.get();
for (ContainerHandler handler : handlers)
if (!this.get().contains(handler))
this.get().add(handler);
}
}

@ -1,8 +1,5 @@
package org.gcube.smartgears.configuration.container;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import org.gcube.common.validator.annotations.NotNull;
/**
@ -11,97 +8,28 @@ import org.gcube.common.validator.annotations.NotNull;
* @author Fabio Simeoni
*
*/
@XmlRootElement(name="site")
public class Site {
@XmlElement
@NotNull
String country;
@XmlElement
@NotNull
String location;
@XmlElement
@NotNull
String latitude;
@XmlElement
@NotNull
String longitude;
/**
* Returns the country.
* @return the country
*/
public String country() {
public String getCountry() {
return country;
}
/**
* Sets the country.
* @param the country
* @return this configuration
*/
public Site country(String country) {
this.country=country;
return this;
}
/**
* Returns the latitude.
* @return the latitude
*/
public String latitude() {
return latitude;
}
/**
* Sets the latitude.
* @param the latitude
* @return this configuration
*/
public Site latitude(String latitude) {
this.latitude=latitude;
return this;
public void setCountry(String country) {
this.country = country;
}
/**
* Returns the longitude.
* @return the longitude
*/
public String longitude() {
return longitude;
}
/**
* Sets the longitude.
* @param the longitude
* @return this configuration
*/
public Site longitude(String longitude) {
this.longitude=longitude;
return this;
}
/**
* Returns the location.
* @return the location
*/
public String location() {
public String getLocation() {
return location;
}
/**
* Sets the location.
* @param the location
* @return this location
*/
public Site location(String location) {
this.location=location;
return this;
public void setLocation(String location) {
this.location = location;
}
@Override
@ -109,9 +37,7 @@ public class Site {
final int prime = 31;
int result = 1;
result = prime * result + ((country == null) ? 0 : country.hashCode());
result = prime * result + ((latitude == null) ? 0 : latitude.hashCode());
result = prime * result + ((location == null) ? 0 : location.hashCode());
result = prime * result + ((longitude == null) ? 0 : longitude.hashCode());
return result;
}
@ -129,24 +55,12 @@ public class Site {
return false;
} else if (!country.equals(other.country))
return false;
if (latitude == null) {
if (other.latitude != null)
return false;
} else if (!latitude.equals(other.latitude))
return false;
if (location == null) {
if (other.location != null)
return false;
} else if (!location.equals(other.location))
return false;
if (longitude == null) {
if (other.longitude != null)
return false;
} else if (!longitude.equals(other.longitude))
return false;
return true;
}
}

@ -1,79 +0,0 @@
package org.gcube.smartgears.configuration.library;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import org.gcube.common.validator.ValidationError;
import org.gcube.common.validator.Validator;
import org.gcube.common.validator.ValidatorFactory;
import org.gcube.common.validator.annotations.NotEmpty;
@XmlRootElement(name="smartgears")
public class SmartGearsConfiguration {
@XmlAttribute @NotEmpty
private String version;
public SmartGearsConfiguration(){
}
public String version() {
return version;
}
public SmartGearsConfiguration version(String version) {
this.version=version;
return this;
}
/**
* Validates this configuration
*
* @throws IllegalStateException if the configuration is invalid
*/
public void validate() {
List<String> msgs = new ArrayList<String>();
Validator validator = ValidatorFactory.validator();
for (ValidationError error : validator.validate(this))
msgs.add(error.toString());
if (!msgs.isEmpty())
throw new IllegalStateException("invalid configuration: "+msgs);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((version == null) ? 0 : version.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SmartGearsConfiguration other = (SmartGearsConfiguration) obj;
if (version == null) {
if (other.version != null)
return false;
} else if (!version.equals(other.version))
return false;
return true;
}
}

@ -1,47 +0,0 @@
package org.gcube.smartgears.configuration.library;
import java.io.InputStream;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import org.gcube.smartgears.configuration.container.ContainerConfiguration;
import org.gcube.smartgears.utils.Utils;
/**
* Binds {@link ContainerConfiguration}s to and from XML serialisations.
*
* @author Fabio Simeoni
*
*/
public class SmartGearsConfigurationBinder {
/**
* Returns a {@link ContainerConfiguration} from its XML serialisation.
*
* @param stream the serialisation
* @return the configuration
* @throws RuntimeException if the serialisation is invalid
*/
public SmartGearsConfiguration bind(InputStream stream) {
try {
JAXBContext ctx = JAXBContext.newInstance(SmartGearsConfiguration.class);
SmartGearsConfiguration config = (SmartGearsConfiguration) ctx.createUnmarshaller().unmarshal(stream);
return config;
} catch (JAXBException e) {
throw new RuntimeException("invalid library configuration", e);
}
finally {
Utils.closeSafely(stream);
}
}
}

@ -1,13 +1,16 @@
package org.gcube.smartgears.context.application;
import javax.servlet.ServletContext;
import java.nio.file.Path;
import org.gcube.common.events.Hub;
import org.gcube.smartgears.configuration.application.ApplicationConfiguration;
import org.gcube.smartgears.context.Properties;
import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.lifecycle.application.ApplicationLifecycle;
import org.gcube.smartgears.persistence.Persistence;
import org.gcube.smartgears.persistence.PersistenceWriter;
import org.gcube.smartgears.security.AuthorizationProvider;
import jakarta.servlet.ServletContext;
/**
* The management context of an application.
@ -33,9 +36,6 @@ public interface ApplicationContext {
* @return the configuration
*/
ApplicationConfiguration configuration();
<T> T profile(Class<T> type);
/**
* Returns the lifecycle of the application.
@ -56,7 +56,7 @@ public interface ApplicationContext {
*
* @return the manager
*/
Persistence persistence();
PersistenceWriter persistence();
/**
* Returns the servlet context of the application.
@ -78,5 +78,18 @@ public interface ApplicationContext {
* @return the properties
*/
Properties properties();
/**
* Returns the authorization provider.
* @return the AuhtorizationProvider
**/
AuthorizationProvider authorizationProvider();
/**
* Returns the Path to the configuration Folder for the current app
*
* @return the Path to the folder , null if the Path is not present
*/
Path appSpecificConfigurationFolder();
}

@ -1,16 +1,27 @@
package org.gcube.smartgears.context.application;
import static org.gcube.smartgears.Constants.profile_property;
import static org.gcube.smartgears.Constants.profile_file_path;
import javax.servlet.ServletContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
import org.gcube.common.events.Hub;
import org.gcube.common.resources.gcore.GCoreEndpoint;
import org.gcube.smartgears.configuration.PersistenceConfiguration;
import org.gcube.smartgears.configuration.application.ApplicationConfiguration;
import org.gcube.smartgears.context.Properties;
import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.lifecycle.application.ApplicationLifecycle;
import org.gcube.smartgears.persistence.Persistence;
import org.gcube.smartgears.persistence.PersistenceWriter;
import org.gcube.smartgears.security.AuthorizationProvider;
import org.gcube.smartgears.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.servlet.ServletContext;
/**
* Default {@link ApplicationContext} implementation.
@ -20,13 +31,19 @@ import org.gcube.smartgears.persistence.Persistence;
*/
public class DefaultApplicationContext implements ApplicationContext {
private static Logger log = LoggerFactory.getLogger(DefaultApplicationContext.class);
private final ContainerContext container;
private final ServletContext sctx;
private final ApplicationConfiguration configuration;
private final ApplicationLifecycle lifecycle;
private final Properties properties;
private final Hub hub;
private final PersistenceWriter persistenceWriter;
private final String id;
private Path appSpecificConfigurationFolder;
private final static String APPS_CONFIG__DIR = "config/apps";
/**
* Crates an intance with mandatory parameters
@ -37,14 +54,38 @@ public class DefaultApplicationContext implements ApplicationContext {
* @param lifecycle the lifecycle
* @param properties the properties
*/
public DefaultApplicationContext(String id,ContainerContext container,ServletContext sctx,ApplicationConfiguration configuration, Hub hub, ApplicationLifecycle lifecycle, Properties properties) {
public DefaultApplicationContext(ContainerContext container,ServletContext sctx,ApplicationConfiguration configuration, Hub hub, ApplicationLifecycle lifecycle, Properties properties) {
PersistenceConfiguration persistenceWriterConf = configuration.persistenceConfiguration();
try {
persistenceWriter = persistenceWriterConf.getImplementationClass().getDeclaredConstructor().newInstance();
persistenceWriter.configure(persistenceWriterConf.getWriterConfiguration());
}catch (Exception e) {
throw new RuntimeException(e);
}
File file = persistenceWriter.file(profile_file_path);
String id = null;
if (file.exists()) {
log.info("loading persisted state for application {}", sctx.getContextPath());
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) {
id = (String) ois.readObject();
} catch (Exception e) {
log.error("error loading persisted state, creating new uuid", e);
}
}
if (id == null)
id = UUID.randomUUID().toString();
this.id = id;
this.container=container;
this.sctx = sctx;
this.configuration=configuration;
this.hub=hub;
this.lifecycle = lifecycle;
this.properties=properties;
this.appSpecificConfigurationFolder = getApplicationSpecificConfig();
this.properties=properties;
}
/**
@ -52,9 +93,21 @@ public class DefaultApplicationContext implements ApplicationContext {
* @param context the other instance
*/
public DefaultApplicationContext(ApplicationContext context) {
this(context.id(), context.container(),context.application(),context.configuration(),context.events(), context.lifecycle(), new Properties(context.properties()));
this(context.id(), context.persistence(), context.container(),context.application(),context.configuration(),context.events(), context.lifecycle(), new Properties(context.properties()));
}
private DefaultApplicationContext(String id, PersistenceWriter writer, ContainerContext container,ServletContext sctx,ApplicationConfiguration configuration, Hub hub, ApplicationLifecycle lifecycle, Properties properties) {
this.id = id;
this.container=container;
this.sctx = sctx;
this.configuration=configuration;
this.hub=hub;
this.lifecycle = lifecycle;
this.properties=properties;
this.persistenceWriter = writer;
}
@Override
public ServletContext application() {
return sctx;
@ -65,16 +118,6 @@ public class DefaultApplicationContext implements ApplicationContext {
return container;
}
@Override
@SuppressWarnings("all")
public <T> T profile(Class<T> type) {
if (type==GCoreEndpoint.class)
return (T) properties().lookup(profile_property).value(GCoreEndpoint.class);
throw new IllegalArgumentException("unsupported profile type: "+type);
}
@Override
public String name() { //little shortcut for ease of logging
return configuration.name();
@ -96,10 +139,10 @@ public class DefaultApplicationContext implements ApplicationContext {
}
@Override
public Persistence persistence() {
return configuration.persistence();
public PersistenceWriter persistence() {
return persistenceWriter;
}
@Override
public Properties properties() {
return properties;
@ -110,4 +153,42 @@ public class DefaultApplicationContext implements ApplicationContext {
return id;
}
/**
* Returns the authorization provider.
* @return the AuhtorizationProvider
**/
public AuthorizationProvider authorizationProvider() {
return container().authorizationProvider();
}
@Override
public Path appSpecificConfigurationFolder() {
return this.appSpecificConfigurationFolder;
}
private Path getApplicationSpecificConfig(){
String home = Utils.home();
File homeDir = new File(home);
if (!(homeDir.exists() && homeDir.isDirectory() && homeDir.canRead()))
throw new IllegalStateException("invalid node configuration: home " + home
+ " does not exist or is not a directory or cannot be accessed in read mode");
String appName = this.configuration.name();
Path appSpecificConfigurationPath = Paths.get(home, APPS_CONFIG__DIR, appName);
File appSpecificConfiguration = appSpecificConfigurationPath.toFile();
if (!(appSpecificConfiguration.exists() && appSpecificConfiguration.isDirectory() && appSpecificConfiguration.canRead())) {
log.warn("specific configuration folder for {} not found", appName);
return null;
}
log.info("reading specific app configuration folder @ {} ", appSpecificConfiguration.getAbsolutePath());
return appSpecificConfigurationPath;
}
}

@ -4,7 +4,8 @@ import org.gcube.common.events.Hub;
import org.gcube.smartgears.configuration.container.ContainerConfiguration;
import org.gcube.smartgears.context.Properties;
import org.gcube.smartgears.lifecycle.container.ContainerLifecycle;
import org.gcube.smartgears.persistence.Persistence;
import org.gcube.smartgears.persistence.PersistenceWriter;
import org.gcube.smartgears.security.AuthorizationProvider;
/**
* The management context of the container.
@ -21,11 +22,6 @@ public interface ContainerContext {
*/
ContainerConfiguration configuration();
/**
* Returns the resource profile of a given type of the container.
* @return the profile
*/
<T> T profile(Class<T> type);
/**
* Returns the lifecycle of the container
@ -43,7 +39,7 @@ public interface ContainerContext {
* Returns the persistence manager of the container.
* @return the manager
*/
Persistence persistence();
PersistenceWriter persistenceWriter();
/**
* Returns the properties of the container.
@ -52,5 +48,9 @@ public interface ContainerContext {
Properties properties();
String id();
AuthorizationProvider authorizationProvider();
}

@ -1,12 +1,21 @@
package org.gcube.smartgears.context.container;
import static org.gcube.smartgears.Constants.*;
import static org.gcube.smartgears.Constants.container_profile_file_path;
import java.io.File;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.UUID;
import org.gcube.common.events.Hub;
import org.gcube.common.resources.gcore.HostingNode;
import org.gcube.smartgears.configuration.PersistenceConfiguration;
import org.gcube.smartgears.configuration.container.ContainerConfiguration;
import org.gcube.smartgears.context.Properties;
import org.gcube.smartgears.lifecycle.container.ContainerLifecycle;
import org.gcube.smartgears.persistence.Persistence;
import org.gcube.smartgears.persistence.PersistenceWriter;
import org.gcube.smartgears.security.AuthorizationProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Default {@link ContainerContext} implementation.
@ -16,11 +25,15 @@ import org.gcube.smartgears.persistence.Persistence;
*/
public class DefaultContainerContext implements ContainerContext {
private static Logger log = LoggerFactory.getLogger(DefaultContainerContext.class);
private final ContainerConfiguration configuration;
private final ContainerLifecycle lifecycle;
private final Properties properties;
private final Hub hub;
private final AuthorizationProvider authorizationProvider;
private final String id;
private final PersistenceWriter persistenceWriter;
/**
* Creates an instance with mandatory parameters.
* @param configuration the configuration
@ -28,23 +41,49 @@ public class DefaultContainerContext implements ContainerContext {
* @param lifecycle the lifecycle
* @param properties the properties
*/
public DefaultContainerContext(String id,ContainerConfiguration configuration, Hub hub, ContainerLifecycle lifecycle,
public DefaultContainerContext(ContainerConfiguration configuration, Hub hub, ContainerLifecycle lifecycle, AuthorizationProvider authProvider,
Properties properties) {
this.id = id;
this.configuration=configuration;
this.hub=hub;
this.lifecycle = lifecycle;
this.properties=properties;
}
this.authorizationProvider = authProvider;
PersistenceConfiguration persistenceWriterConf = configuration.persistenceConfiguration();
try {
persistenceWriter = persistenceWriterConf.getImplementationClass().getDeclaredConstructor().newInstance();
persistenceWriter.configure(persistenceWriterConf.getWriterConfiguration());
}catch (Exception e) {
throw new RuntimeException(e);
}
File file = persistenceWriter.file(container_profile_file_path);
@SuppressWarnings("all")
public <T> T profile(Class<T> type) {
String id = null;
if (file.exists()) {
log.info("loading persisted state for container");
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) {
id = (String) ois.readObject();
} catch (Exception e) {
log.error("error loading persisted state, creating new uuid", e);
}
if (type==HostingNode.class)
return (T) properties().lookup(container_profile_property).value(HostingNode.class);
}
if (id == null) {
id = UUID.randomUUID().toString();
log.info("container id created is {}", id);
throw new IllegalArgumentException("unsupported profile type: "+type);
};
}
this.id = id;
}
/*
public HostingNode profile() {
return properties().lookup(container_profile_property).value(HostingNode.class);
};*/
@Override
public ContainerConfiguration configuration() {
@ -62,8 +101,8 @@ public class DefaultContainerContext implements ContainerContext {
}
@Override
public Persistence persistence() {
return configuration.persistence();
public PersistenceWriter persistenceWriter() {
return persistenceWriter;
}
@Override
@ -76,6 +115,8 @@ public class DefaultContainerContext implements ContainerContext {
return id;
}
public AuthorizationProvider authorizationProvider() {
return authorizationProvider;
}
}

@ -4,12 +4,12 @@ import java.io.IOException;
import java.util.Collections;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.gcube.smartgears.Constants;
import org.gcube.smartgears.configuration.application.Exclude;
import org.gcube.smartgears.configuration.application.GCubeExclude;
/**
* A resource-specifc API handled by an {@link HttpController}.
@ -53,10 +53,10 @@ public abstract class ApiResource extends HttpExtension {
}
@Override
public Set<Exclude> excludes() {
return Collections.singleton(new Exclude(Constants.root_mapping+mapping()));
public Set<GCubeExclude> excludes() {
return Collections.singleton(new GCubeExclude(Constants.root_mapping+mapping()));
}
/**
* Returns <code>true</code> if this resource supports a given method.
* @param method the method

@ -2,9 +2,9 @@ package org.gcube.smartgears.extensions;
import java.util.Set;
import javax.servlet.Servlet;
import jakarta.servlet.Servlet;
import org.gcube.smartgears.configuration.application.Exclude;
import org.gcube.smartgears.configuration.application.GCubeExclude;
import org.gcube.smartgears.context.application.ApplicationContext;
/**
@ -22,6 +22,8 @@ public interface ApplicationExtension extends Servlet {
*/
void init(ApplicationContext context) throws Exception;
void stop();
/**
* Returns the name of this extension.
* @return the name
@ -39,5 +41,5 @@ public interface ApplicationExtension extends Servlet {
* Returns the set of request paths that should be excluded from request management.
* @return the set of request paths that should be excluded from request management
*/
Set<Exclude> excludes();
Set<GCubeExclude> excludes();
}

@ -1,8 +1,13 @@
package org.gcube.smartgears.extensions;
import static org.gcube.smartgears.Constants.*;
import static org.gcube.smartgears.handlers.application.request.RequestError.*;
import static org.gcube.smartgears.utils.Utils.*;
import static org.gcube.smartgears.Constants.accept;
import static org.gcube.smartgears.Constants.allow;
import static org.gcube.smartgears.Constants.content_type;
import static org.gcube.smartgears.handlers.application.request.RequestError.incoming_contenttype_unsupported_error;
import static org.gcube.smartgears.handlers.application.request.RequestError.method_unsupported_error;
import static org.gcube.smartgears.handlers.application.request.RequestError.outgoing_contenttype_unsupported_error;
import static org.gcube.smartgears.handlers.application.request.RequestError.resource_notfound_error;
import static org.gcube.smartgears.utils.Utils.notNull;
import java.io.IOException;
import java.util.Collection;
@ -11,13 +16,13 @@ import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.gcube.smartgears.configuration.application.Exclude;
import org.gcube.smartgears.configuration.application.GCubeExclude;
import org.gcube.smartgears.context.application.ApplicationContext;
/**
@ -69,9 +74,17 @@ public class HttpController extends HttpExtension {
}
@Override
public Set<Exclude> excludes() {
public void stop(){
for (ApiResource resource : resources.values())
resource.stop();
}
@Override
public Set<GCubeExclude> excludes() {
Set<Exclude> resourceExcludes = new LinkedHashSet<Exclude>();
Set<GCubeExclude> resourceExcludes = new LinkedHashSet<GCubeExclude>();
for (ApiResource resource : resources.values())
resourceExcludes.addAll(resource.excludes());
@ -176,8 +189,6 @@ public class HttpController extends HttpExtension {
case OPTIONS:
resource.doOptions(request, response);
break;
case TRACE:
resource.doTrace(request, response);
}
}

@ -1,17 +1,15 @@
package org.gcube.smartgears.extensions;
import static org.gcube.common.events.impl.Utils.*;
import static org.gcube.common.events.impl.Utils.valid;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.http.HttpServlet;
import javax.xml.bind.annotation.XmlAttribute;
import jakarta.servlet.http.HttpServlet;
import org.gcube.common.validator.annotations.NotEmpty;
import org.gcube.smartgears.configuration.application.Exclude;
import org.gcube.smartgears.configuration.application.GCubeExclude;
import org.gcube.smartgears.context.application.ApplicationContext;
/**
* An {@link ApplicationExtension} that implements the {@link HttpServlet} interface
*
@ -27,13 +25,31 @@ public abstract class HttpExtension extends HttpServlet implements ApplicationEx
*
*/
public static enum Method {
GET, PUT, POST, HEAD, DELETE, OPTIONS, TRACE
GET("GET"),
PUT("PUT"),
POST("POST"),
HEAD("HEAD"),
DELETE("DELETE"),
OPTIONS("OPTIONS");
private String value;
private Method(String value) {
this.value = value;
}
public String getValue() {
return this.value;
}
}
@XmlAttribute @NotEmpty
@NotEmpty
private String name;
@XmlAttribute @NotEmpty
@NotEmpty
private String mapping;
private ApplicationContext context;
@ -50,7 +66,7 @@ public abstract class HttpExtension extends HttpServlet implements ApplicationEx
}
//extensions use init(context) instead
public final void init() throws javax.servlet.ServletException {
public final void init() throws jakarta.servlet.ServletException {
};
@Override
@ -59,8 +75,11 @@ public abstract class HttpExtension extends HttpServlet implements ApplicationEx
}
@Override
public Set<Exclude> excludes() {
return new HashSet<Exclude>(); //all managed by default
public void stop() {}
@Override
public Set<GCubeExclude> excludes() {
return new HashSet<GCubeExclude>(); //all managed by default
}
protected ApplicationContext context() {

@ -2,14 +2,14 @@ package org.gcube.smartgears.extensions;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.gcube.smartgears.handlers.application.request.RequestException;
import org.gcube.smartgears.utils.Utils;
@ -23,6 +23,7 @@ import org.gcube.smartgears.utils.Utils;
*/
public class RequestExceptionBarrier implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@ -31,10 +32,8 @@ public class RequestExceptionBarrier implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException {
try {
chain.doFilter(request, response);
chain.doFilter(request, response);
} catch (Throwable t) {

@ -1,17 +1,16 @@
package org.gcube.smartgears.extensions.resource;
import static org.gcube.smartgears.Constants.application_xml;
import static org.gcube.smartgears.Constants.application_json;
import static org.gcube.smartgears.extensions.HttpExtension.Method.GET;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.gcube.common.resources.gcore.Resources;
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
import org.gcube.smartgears.configuration.application.ApplicationConfiguration;
import org.gcube.smartgears.configuration.application.BridgedApplicationConfiguration;
import org.gcube.smartgears.extensions.ApiResource;
import org.gcube.smartgears.extensions.ApiSignature;
@ -27,7 +26,7 @@ public class ConfigurationResource extends ApiResource {
public static final String mapping = "/configuration";
private static final ApiSignature signature = handles(mapping).with(method(GET).produces(application_xml));
private static final ApiSignature signature = handles(mapping).with(method(GET).produces(application_json));
ConfigurationResource() {
super(signature);
@ -37,8 +36,8 @@ public class ConfigurationResource extends ApiResource {
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ApplicationConfiguration config = BridgedApplicationConfiguration.class.cast(context().configuration()).inner();
Resources.marshal(config,resp.getWriter());
ApplicationConfiguration config = context().configuration();
new ObjectMapper().writeValue(resp.getWriter(), config);
}
}

@ -4,29 +4,24 @@ import static org.gcube.smartgears.Constants.application_xhtml;
import static org.gcube.smartgears.Constants.frontpage_file_path;
import static org.gcube.smartgears.extensions.HttpExtension.Method.GET;
import static org.gcube.smartgears.handlers.application.request.RequestError.application_error;
import static org.gcube.smartgears.provider.ProviderFactory.provider;
import static org.gcube.smartgears.utils.Utils.closeSafely;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.gcube.common.resources.gcore.GCoreEndpoint;
import org.gcube.common.scope.impl.ScopeBean;
import org.gcube.smartgears.extensions.ApiResource;
import org.gcube.smartgears.extensions.ApiSignature;
import org.gcube.smartgears.provider.ProviderFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -93,42 +88,12 @@ public class FrontPageResource extends ApiResource {
values.put("version", context().configuration().version());
String infrastructure = context().container().configuration().infrastructure();
StringBuilder voValue = new StringBuilder();
Collection<String> scopes = context().profile(GCoreEndpoint.class).scopes().asCollection();
Set<String> vos = new HashSet<String>();
//pre-process
for (String scope : scopes) {
ScopeBean bean = new ScopeBean(scope);
switch (bean.type()) {
case INFRASTRUCTURE:
infrastructure = bean.name();
break;
case VO:
vos.add(bean.name());
break;
case VRE:
vos.add(bean.enclosingScope().name());
infrastructure=bean.enclosingScope().enclosingScope().name();
}
}
//build vo value
int i = 0;
int max = vos.size()-1;
for (String vo : vos) {
String voPrefix = i == 0 ? "" : (i==max?" and ":", ");
voValue.append(voPrefix+"<em>" + vo + "</em>");
i++;
}
values.put("infra", infrastructure);
values.put("vos", voValue.toString());
values.put("status", context().lifecycle().state().toString());
values.put("smartgears-version", provider().smartgearsConfiguration().version());
values.put("smartgears-version", ProviderFactory.provider().smartgearsConfiguration().getVersion());
return values;
}

@ -0,0 +1,105 @@
package org.gcube.smartgears.extensions.resource;
import static org.gcube.smartgears.Constants.application_json;
import static org.gcube.smartgears.extensions.HttpExtension.Method.GET;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Set;
import java.util.Timer;
import java.util.stream.Collectors;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
import org.gcube.common.health.api.HealthCheck;
import org.gcube.common.health.api.ReadinessChecker;
import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.extensions.ApiResource;
import org.gcube.smartgears.extensions.ApiSignature;
import org.gcube.smartgears.health.HealthManager;
import org.gcube.smartgears.health.HealthResponse;
import org.gcube.smartgears.health.HealthTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.github.classgraph.ClassGraph;
import io.github.classgraph.ClassInfo;
import io.github.classgraph.ClassInfoList;
import io.github.classgraph.ScanResult;
public class HealthResource extends ApiResource {
private static final long serialVersionUID = 1L;
private static Logger log = LoggerFactory.getLogger(HealthResource.class);
public static final String mapping = "/health";
private static final ApiSignature signature = handles(mapping).with(method(GET).produces(application_json));
private HealthManager manager;
private HealthTask task;
private Timer timer;
HealthResource() {
super(signature);
}
@Override
public void init(ApplicationContext context) throws Exception {
Set<Class<?>> annotatedReadiness;
try (ScanResult result = new ClassGraph().enableClassInfo().enableAnnotationInfo().scan()) {
ClassInfoList classInfos = result.getClassesWithAnnotation(ReadinessChecker.class.getName());
annotatedReadiness = classInfos.stream().map(ClassInfo::loadClass)
.filter(c -> HealthCheck.class.isAssignableFrom(c)).collect(Collectors.toSet());
}
manager = new HealthManager();
for (Class<?> readnessClass : annotatedReadiness)
try {
manager.register((HealthCheck) readnessClass.getDeclaredConstructor().newInstance());
log.info("added class {} to health manager", readnessClass.getCanonicalName());
} catch (Throwable e) {
log.error("healthChecker class {} cannot be instantiated", readnessClass.getCanonicalName(), e);
}
task = new HealthTask(manager);
timer = new Timer(true);
timer.scheduleAtFixedRate(task, 10000, 60000);
super.init(context);
}
public void stop() {
if (timer!=null)
timer.cancel();
}
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter out = resp.getWriter();
resp.setContentType("application/json");
resp.setCharacterEncoding("UTF-8");
HealthResponse response = readiness();
new ObjectMapper().writeValue(out, response);
out.flush();
}
public HealthResponse readiness() {
return task.getResponse();
}
}

@ -8,11 +8,9 @@ import static org.gcube.smartgears.handlers.application.request.RequestError.inv
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlValue;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.gcube.common.resources.gcore.Resources;
import org.gcube.smartgears.extensions.ApiResource;
@ -86,10 +84,8 @@ public class LifecycleResource extends ApiResource {
// helper classes
@XmlRootElement(name="state")
public static class State {
@XmlValue
public String value;
State() {

@ -0,0 +1,38 @@
package org.gcube.smartgears.extensions.resource;
import static org.gcube.smartgears.Constants.plain_text;
import static org.gcube.smartgears.extensions.HttpExtension.Method.GET;
import java.io.IOException;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.gcube.smartgears.extensions.ApiResource;
import org.gcube.smartgears.extensions.ApiSignature;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.prometheus.PrometheusMeterRegistry;
public class MetricsResource extends ApiResource {
private static final long serialVersionUID = 1L;
public static final String mapping = "/metrics";
private static final ApiSignature signature = handles(mapping).with(method(GET).produces(plain_text));
MetricsResource() {
super(signature);
}
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrometheusMeterRegistry registry = (PrometheusMeterRegistry) Metrics.globalRegistry.getRegistries().stream().findFirst().get();
registry.scrape(resp.getWriter());
}
}

@ -5,12 +5,10 @@ import static org.gcube.smartgears.extensions.HttpExtension.Method.GET;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.gcube.common.resources.gcore.GCoreEndpoint;
import org.gcube.common.resources.gcore.Resources;
import org.gcube.smartgears.extensions.ApiResource;
import org.gcube.smartgears.extensions.ApiSignature;
@ -36,7 +34,8 @@ public class ProfileResource extends ApiResource {
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Resources.marshal(context().profile(GCoreEndpoint.class),resp.getWriter());
//Resources.marshal(context().profile(),resp.getWriter());
//TODO: return something to show
}
}

@ -1,8 +1,6 @@
package org.gcube.smartgears.extensions.resource;
import static org.gcube.smartgears.Constants.*;
import javax.xml.bind.annotation.XmlRootElement;
import static org.gcube.smartgears.Constants.remote_management;
import org.gcube.smartgears.Constants;
import org.gcube.smartgears.extensions.ApiResource;
@ -14,7 +12,6 @@ import org.gcube.smartgears.extensions.HttpController;
* @author Fabio Simeoni
*
*/
@XmlRootElement(name = remote_management)
public class RemoteResource extends HttpController {
private static final String default_mapping = Constants.root_mapping+"/*";
@ -27,7 +24,7 @@ public class RemoteResource extends HttpController {
public RemoteResource() {
super(remote_management, default_mapping);
addResources(new FrontPageResource(), new ConfigurationResource(), new ProfileResource(),
new LifecycleResource());
new LifecycleResource(), new MetricsResource(), new HealthResource());
}
@Override

@ -21,6 +21,7 @@ public abstract class AbstractHandler {
return this.getClass().getCanonicalName().equals(handler.getClass().getCanonicalName());
}
//so far, largely a placeholder for future cross-handler behaviour
}

@ -0,0 +1,24 @@
package org.gcube.smartgears.handlers;
import java.util.Collection;
public class OfflineProfilePublisher implements ProfilePublisher {
@Override
public void addTo(Collection<String> tokens) {
}
@Override
public void addToAll() {
}
@Override
public void update() {
}
@Override
public void removeFrom(Collection<String> tokens) {
}
}

@ -0,0 +1,16 @@
package org.gcube.smartgears.handlers;
import java.util.Collection;
public interface ProfilePublisher {
void addTo(Collection<String> contexts);
void addToAll();
void update();
void removeFrom(Collection<String> contexts);
}

@ -1,7 +1,7 @@
package org.gcube.smartgears.handlers.application;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.gcube.smartgears.context.application.ApplicationContext;

@ -22,6 +22,10 @@ public abstract class RequestHandler extends AbstractHandler implements Applicat
abstract public String getName();
public boolean isUnfiltrable() {
return false;
}
/**
* Initialises the handler.
*
@ -64,5 +68,7 @@ public abstract class RequestHandler extends AbstractHandler implements Applicat
*/
public void stop() {
}
}

@ -1,7 +1,7 @@
package org.gcube.smartgears.handlers.application;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.gcube.smartgears.context.application.ApplicationContext;

@ -2,26 +2,22 @@ package org.gcube.smartgears.handlers.application.lifecycle;
import static org.gcube.common.events.Observes.Kind.resilient;
import static org.gcube.smartgears.Constants.profile_management;
import static org.gcube.smartgears.Constants.profile_property;
import static org.gcube.smartgears.handlers.ProfileEvents.addToContext;
import static org.gcube.smartgears.handlers.ProfileEvents.changed;
import static org.gcube.smartgears.handlers.ProfileEvents.published;
import static org.gcube.smartgears.handlers.ProfileEvents.removeFromContext;
import static org.gcube.smartgears.lifecycle.application.ApplicationLifecycle.activation;
import static org.gcube.smartgears.lifecycle.application.ApplicationLifecycle.failure;
import static org.gcube.smartgears.lifecycle.application.ApplicationLifecycle.stop;
import static org.gcube.smartgears.lifecycle.application.ApplicationState.failed;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import javax.xml.bind.annotation.XmlRootElement;
import org.gcube.common.events.Observes;
import org.gcube.common.events.Observes.Kind;
import org.gcube.common.resources.gcore.GCoreEndpoint;
import org.gcube.smartgears.Constants;
import org.gcube.smartgears.configuration.Mode;
import org.gcube.smartgears.context.Property;
import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.handlers.application.ApplicationLifecycleEvent;
@ -29,6 +25,8 @@ import org.gcube.smartgears.handlers.application.ApplicationLifecycleHandler;
import org.gcube.smartgears.lifecycle.application.ApplicationLifecycle;
import org.gcube.smartgears.lifecycle.application.ApplicationState;
import org.gcube.smartgears.lifecycle.container.ContainerLifecycle;
import org.gcube.smartgears.provider.ProviderFactory;
import org.gcube.smartgears.publishing.Publisher;
import org.gcube.smartgears.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -49,29 +47,24 @@ import org.slf4j.LoggerFactory;
* </ul>
*
* @author Fabio Simeoni
* @see ProfileBuilder
* @see ProfilePublisher
*/
@XmlRootElement(name = profile_management)
public class ProfileManager extends ApplicationLifecycleHandler {
public class ApplicationProfileManager extends ApplicationLifecycleHandler {
Logger log = LoggerFactory.getLogger(ProfileManager.class);
Logger log = LoggerFactory.getLogger(ApplicationProfileManager.class);
private ApplicationContext context;
private ProfileBuilder builder;
private ProfilePublisher publisher;
private ScheduledFuture<?> periodicUpdates;
private static final String PUBLISHED_PROP = "published";
private List<Publisher> publishers = ProviderFactory.provider().publishers();
@Override
public void onStart(ApplicationLifecycleEvent.Start e) {
context = e.context();
builder = new ProfileBuilder(context);
activated();
schedulePeriodicUpdates();
// note we don't fire profile events, but wait for the final startup
// outcome which
// will result in a state change. only then we publish and store the
@ -86,13 +79,13 @@ public class ProfileManager extends ApplicationLifecycleHandler {
private void activated(){
GCoreEndpoint profile = loadOrCreateProfile();
share(profile);
publisher = new ProfilePublisher(context);
publishers = context.container().configuration().mode()!=Mode.offline?
ProviderFactory.provider().publishers():
Collections.emptyList();
registerObservers();
schedulePeriodicUpdates();
}
// helpers
@ -103,141 +96,100 @@ public class ProfileManager extends ApplicationLifecycleHandler {
@Observes({ activation, stop, failure })
void onChanged(ApplicationLifecycle lc) {
GCoreEndpoint profile = context.profile(GCoreEndpoint.class);
profile.profile().deploymentData().status(lc.state().remoteForm());
log.debug("moving app {} to {}",context.name(), lc.state().remoteForm());
// since we do not know the observers, they will deal with
// failures and their consequences
// any that comes back will be logged in this event thread
context.events().fire(profile, changed);
context.events().fire(context, changed);
}
/*
@Observes(value = published)
void shareAfterPublish(GCoreEndpoint profile) {
share(profile); // publish may produce a new profile instance
}
}*/
@Observes(value = changed, kind = Kind.safe)
void publishAfterChange(GCoreEndpoint profile) {
void publishAfterChange(ApplicationContext context) {
boolean firstPublication = profile.scopes().isEmpty();
//if we've failed before first publication do not try to publish
//(we may well have failed there)
try {
if (firstPublication) {
if (context.lifecycle().state()!= failed)
publishFirstTime(profile);
}
else{
log.debug("update app {} profile",context.name());
publisher.update(); // if successful, triggers share.
if (!context.properties().contains(PUBLISHED_PROP)) {
log.info("publishing application for the first time");
context.properties().add(new Property(PUBLISHED_PROP, true));
if (context.lifecycle().state() != ApplicationState.failed) {
publishers.forEach(p -> {
try {
p.create(context,
context.container().authorizationProvider().getContexts());
}catch (Exception e) {
log.error("cannot publish {} for first time with publisher type {} (see details)",context.name(), p.getClass().getCanonicalName(), e);
}
});
}
}
catch (Exception e) {
log.error("cannot publish "+context.name()+" (see details)", e);
// since we've failed no published event is fired and profile
// will not be stored.
// we do it manually to ensure we leave some local trace of the
// changed profile.
//TODO: CHECK --- store(profile);
}
else
publishers.forEach(p -> {
try {
p.update(context);
}catch (Exception e) {
log.error("cannot publish {} with publisher type {} (see details)",context.name(), p.getClass().getCanonicalName(), e);
}
});
}
});
//registering ContextObserver in container HUB
context.container().events().subscribe(new Object() {
@Observes(value = addToContext)
void addTo(String token) {
try {
log.trace("publishing application with new token");
publisher.addTo(Collections.singleton(token));
publisher.update();
}catch (Exception e) {
log.error("cannot add token {} (see details)",token, e);
// since we've failed no published event is fired and profile
// will not be stored.
// we do it manually to ensure we leave some local trace of the
// changed profile.
//TODO: CHECK --- store(profile);
}
void addTo(String scope) {
log.info("add_to_context event arrived in app {}", context.name());
for (Publisher publisher: publishers)
try {
log.debug("publishing application in context {}", scope);
publisher.create(context,
Collections.singleton(scope));
}catch (Exception e) {
log.error("cannot add context {} with publisher type {} (see details)",scope, publisher.getClass().getCanonicalName(), e);
// since we've failed no published event is fired and profile
// will not be stored.
// we do it manually to ensure we leave some local trace of the
// changed profile.
//TODO: CHECK --- store(profile);
}
}
@Observes(value = removeFromContext)
void removeFrom(String token) {
try {
log.trace("unpublishing application with token");
publisher.removeFrom(Collections.singleton(token));
publisher.update();
}catch (Exception e) {
log.error("cannot remove token {} (see details)",token, e);
// since we've failed no published event is fired and profile
// will not be stored.
// we do it manually to ensure we leave some local trace of the
// changed profile.
//TODO: CHECK --- store(profile);
}
void removeFrom(String scope) {
log.info("remove_from_context event arrived in app {}", context.name());
for (Publisher publisher: publishers)
try {
log.debug("unpublishing application from scope {}", scope);
publisher.remove(context,
Collections.singleton(scope));
}catch (Exception e) {
log.error("cannot remove scope {} with publisher type {} (see details)",scope, publisher.getClass().getCanonicalName(), e);
// since we've failed no published event is fired and profile
// will not be stored.
// we do it manually to ensure we leave some local trace of the
// changed profile.
//TODO: CHECK --- store(profile);
}
}
});
}
private void share(GCoreEndpoint profile) {
log.trace("sharing profile for {}", context.name());
context.properties().add(new Property(profile_property, profile));
}
private void publishFirstTime(GCoreEndpoint profile) {
try {
publisher.addToAll();
} catch (Exception e) {
log.warn("publishing failed",e);
}
}
private GCoreEndpoint loadOrCreateProfile() {
return create();
}
private GCoreEndpoint create() {
log.info("creating profile for {}", context.name());
try {
GCoreEndpoint profile = new GCoreEndpoint();
profile.setId(context.id());
builder.fill(profile);
return profile;
} catch (RuntimeException e) {
// this is a critical startup failure: it will fail the application
throw new RuntimeException("cannot create profile for " + context.name(), e);
}
}
@ -264,16 +216,16 @@ public class ProfileManager extends ApplicationLifecycleHandler {
final Runnable updateTask = new Runnable() {
public void run() {
GCoreEndpoint profile = context.profile(GCoreEndpoint.class);
//if handling of event generates failures these will be reported
//for resilience we do not fail the application
log.trace("firing change event on application {} profile", context.name());
context.events().fire(profile,changed);
log.trace("firing change event on application {} ", context.name());
context.events().fire(context,changed);
}
};
periodicUpdates = Utils.scheduledServicePool.scheduleAtFixedRate(updateTask, Constants.application_republish_frequency_in_minutes, Constants.application_republish_frequency_in_minutes , TimeUnit.MINUTES);
periodicUpdates = Utils.scheduledServicePool.scheduleAtFixedRate(updateTask,
Constants.application_republish_frequency_in_minutes,
Constants.application_republish_frequency_in_minutes , TimeUnit.MINUTES);
}
@ -288,7 +240,7 @@ public class ProfileManager extends ApplicationLifecycleHandler {
periodicUpdates=null;
}
catch(Exception e) {
log.warn("could not stop periodic updates of application {} profile", context.name(),e);
log.warn("could not stop periodic updates of application {}", context.name(),e);
}
}
}
@ -296,8 +248,8 @@ public class ProfileManager extends ApplicationLifecycleHandler {
});
}
@Override
public String toString() {
return profile_management;

@ -1,74 +0,0 @@
package org.gcube.smartgears.handlers.application.lifecycle;
import java.net.URI;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import javax.servlet.ServletRegistration;
import org.gcube.common.resources.gcore.GCoreEndpoint;
import org.gcube.common.resources.gcore.HostingNode;
import org.gcube.smartgears.configuration.application.ApplicationConfiguration;
import org.gcube.smartgears.configuration.container.ContainerConfiguration;
import org.gcube.smartgears.context.application.ApplicationContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ProfileBuilder {
private static List<String> servletExcludes = Arrays.asList("default","jsp");
private static final Logger log = LoggerFactory.getLogger(ProfileBuilder.class);
private ApplicationContext context;
public ProfileBuilder(ApplicationContext context) {
this.context = context;
}
public void fill(GCoreEndpoint endpoint) {
ApplicationConfiguration configuration = context.configuration();
ContainerConfiguration container = context.container().configuration();
endpoint.profile()
.description(configuration.description())
.serviceName(configuration.name())
.serviceClass(configuration.serviceClass())
.version(configuration.version())
.serviceId(configuration.name() + configuration.serviceClass() + configuration.version())
.ghnId(context.container().profile(HostingNode.class).id());
endpoint.profile().newDeploymentData()
.activationTime(Calendar.getInstance())
.status((context.lifecycle().state().remoteForm()));
endpoint.profile().endpoints().clear();
String baseAddress;
if (configuration.proxied()){
String protocol = configuration.proxyAddress().protocol();
String port = configuration.proxyAddress().port()!=null?":"+configuration.proxyAddress().port():"";
baseAddress=String.format("%s://%s%s%s", protocol , configuration.proxyAddress().hostname(), port,context.application().getContextPath());
} else {
String protocol = container.protocol();
int port = container.port();
baseAddress=String.format("%s://%s:%d%s", protocol , container.hostname(), port,context.application().getContextPath());
}
for (ServletRegistration servlet : context.application().getServletRegistrations().values())
if (!servletExcludes.contains(servlet.getName()))
for (String mapping : servlet.getMappings()) {
String address = baseAddress+(mapping.endsWith("*")?mapping.substring(0,mapping.length()-2):mapping);
endpoint.profile().endpoints().add().nameAndAddress(servlet.getName(),URI.create(address));
}
}
}

@ -1,215 +0,0 @@
package org.gcube.smartgears.handlers.application.lifecycle;
import static org.gcube.smartgears.handlers.ProfileEvents.published;
import static org.gcube.smartgears.utils.Utils.notEmpty;
import static org.gcube.smartgears.utils.Utils.rethrowUnchecked;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.gcube.common.authorization.client.proxy.AuthorizationProxy;
import org.gcube.common.authorization.library.provider.SecurityTokenProvider;
import org.gcube.common.resources.gcore.GCoreEndpoint;
import org.gcube.informationsystem.publisher.ScopedPublisher;
import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.provider.ProviderFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Publishes the current resource profile of the application.
* <p>
*Distinguishes publication in new scopes ({@link #addTo(List)} from publication updates in existing scopes ({@link #update()}.
*
*
* @author Fabio Simeoni
*
*/
public class ProfilePublisher {
private static final Logger log = LoggerFactory.getLogger(ProfilePublisher.class);
//the underlying IS publisher
private final ScopedPublisher publisher;
private final ApplicationContext context;
private AuthorizationProxy authProxy ;
/**
* Creates an instance for a given application.
* @param context the context of the application
*/
public ProfilePublisher(ApplicationContext context) {
this.context = context;
this.publisher=ProviderFactory.provider().publisherFor(context);
this.authProxy = ProviderFactory.provider().authorizationProxy();
}
/**
* Adds for the first time the current resource profile of the application in one or more scopes.
* @param scopes the scopes
*/
public void addTo(Collection<String> tokens) {
notEmpty("tokens",tokens);
GCoreEndpoint profile = context.profile(GCoreEndpoint.class);
/* TODO: reintroduce it when scope will be removed
//TODO: remove when move to new IS
Collection<String> retainedContexts = new ArrayList<String>(context.container().configuration().allowedContexts());
retainedContexts.removeAll(profile.scopes().asCollection());
profile.scopes().asCollection().addAll(retainedContexts);
String previousToken = SecurityTokenProvider.instance.get();
try {
for (String token: tokens){
log.info("creating profile with token {}", token);
SecurityTokenProvider.instance.set(token);
profile = publisher.create(profile);
SecurityTokenProvider.instance.reset();
}
}catch (Exception e) {
rethrowUnchecked(e);
} finally{
SecurityTokenProvider.instance.set(previousToken);
}
*/
ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
String previousToken = SecurityTokenProvider.instance.get();
try{//This classloader set is needed for the jaxb context
if (previousToken==null)
SecurityTokenProvider.instance.set((String)tokens.toArray()[0]);
Thread.currentThread().setContextClassLoader(ProfilePublisher.class.getClassLoader());
profile = publisher.create(profile, resolveScopesFromTokens(tokens));
} catch (Exception e) {
rethrowUnchecked(e);
} finally{
SecurityTokenProvider.instance.set(previousToken);
Thread.currentThread().setContextClassLoader(contextCL);
}
sharePublished(profile);
log.debug("shared profile with scopes {}", profile.scopes().asCollection());
}
public void addToAll() {
this.addTo(context.configuration().startTokens());
}
public void update() {
GCoreEndpoint profile = context.profile(GCoreEndpoint.class);
/* TODO: reintroduce it when scope will be removed
String previousToken = SecurityTokenProvider.instance.get();
try {
for (String token: context.configuration().startTokens()){
SecurityTokenProvider.instance.set(token);
profile = publisher.update(profile);
SecurityTokenProvider.instance.reset();
}
}
catch (Exception e) {
rethrowUnchecked(e);
} finally{
SecurityTokenProvider.instance.set(previousToken);
}
*/
ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
String previousToken = SecurityTokenProvider.instance.get();
try{//This classloader set is needed for the jaxb context
if (previousToken==null)
SecurityTokenProvider.instance.set((String)context.configuration().startTokens().toArray()[0]);
Thread.currentThread().setContextClassLoader(ProfilePublisher.class.getClassLoader());
profile = publisher.update(profile);
} catch (Exception e) {
rethrowUnchecked(e);
} finally{
SecurityTokenProvider.instance.set(previousToken);
Thread.currentThread().setContextClassLoader(contextCL);
}
sharePublished(profile);
}
/**
* Removes the application from one or more scopes.
* @param scopes the scopes
*/
public void removeFrom(Collection<String> tokens) {
GCoreEndpoint profile = context.profile(GCoreEndpoint.class);
/* TODO: reintroduce it when scope will be removed
String previousToken = SecurityTokenProvider.instance.get();
try {
for (String token: tokens){
SecurityTokenProvider.instance.set(token);
profile = publisher.remove(profile);
SecurityTokenProvider.instance.reset();
}
}
catch (Exception e) {
rethrowUnchecked(e);
} finally{
SecurityTokenProvider.instance.set(previousToken);
}
*/
ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
String previousToken = SecurityTokenProvider.instance.get();
try{//This classloader set is needed for the jaxb context
if (previousToken==null)
SecurityTokenProvider.instance.set((String)tokens.toArray()[0]);
Thread.currentThread().setContextClassLoader(ProfilePublisher.class.getClassLoader());
profile = publisher.remove(profile, resolveScopesFromTokens(tokens));
} catch (Exception e) {
rethrowUnchecked(e);
} finally{
SecurityTokenProvider.instance.set(previousToken);
Thread.currentThread().setContextClassLoader(contextCL);
}
log.debug("after remove application profile contains scopes {}",profile.scopes().asCollection());
sharePublished(profile);
}
private void sharePublished(GCoreEndpoint profile) {
context.events().fire(profile,published);
}
private List<String> resolveScopesFromTokens(Collection<String> tokens){
List<String> scopes = new ArrayList<String>(tokens.size());
for (String token: tokens)
try{
scopes.add(this.authProxy.get(token).getContext());
}catch (Exception e) {
log.warn("error retrieving token {} , it should never happen",token);
}
return scopes;
}
}

@ -1,17 +1,12 @@
package org.gcube.smartgears.handlers.application.request;
import static org.gcube.smartgears.Constants.called_method_header;
import javax.xml.bind.annotation.XmlRootElement;
import org.gcube.accounting.datamodel.UsageRecord.OperationResult;
import org.gcube.accounting.datamodel.usagerecords.ServiceUsageRecord;
import org.gcube.accounting.persistence.AccountingPersistence;
import org.gcube.accounting.persistence.AccountingPersistenceFactory;
import org.gcube.common.authorization.library.provider.AuthorizationProvider;
import org.gcube.common.authorization.library.provider.SecurityTokenProvider;
import org.gcube.common.scope.api.ScopeProvider;
import org.gcube.common.security.providers.SecretManagerProvider;
import org.gcube.smartgears.Constants;
import org.gcube.smartgears.configuration.Mode;
import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.handlers.application.RequestEvent;
import org.gcube.smartgears.handlers.application.RequestHandler;
@ -20,98 +15,133 @@ import org.gcube.smartgears.utils.InnerMethodName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@XmlRootElement(name = Constants.request_accounting)
public class RequestAccounting extends RequestHandler {
private static Logger log = LoggerFactory.getLogger(RequestAccounting.class);
private static ThreadLocal<Long> startCallThreadLocal = new ThreadLocal<Long>();
private static ThreadLocal<Long> startCallThreadLocal = new ThreadLocal<Long>();
private static final String UNKNOWN = "Unknown";
@Override
public String getName() {
return Constants.request_accounting;
}
@Override
public void handleRequest(RequestEvent e) {
ApplicationContext context = e.context();
String calledMethod = e.request().getHeader(called_method_header);
if (calledMethod==null){
calledMethod = e.request().getRequestURI().substring(e.request().getContextPath().length());
ApplicationContext appContext = e.context();
String context = getContext(appContext);
if (InnerMethodName.get() == null) {
String calledMethod = e.request().getRequestURI().substring(e.request().getContextPath().length());
if (calledMethod.isEmpty())
calledMethod = "/";
calledMethod = e.request().getMethod() + " " + calledMethod;
InnerMethodName.set(calledMethod);
}
InnerMethodName.instance.set(calledMethod);
String caller = AuthorizationProvider.instance.get()!=null? AuthorizationProvider.instance.get().getClient().getId(): "UNKNOWN";
String caller = SecretManagerProvider.get() != null
? SecretManagerProvider.get().getOwner().getId()
: UNKNOWN;
startCallThreadLocal.set(System.currentTimeMillis());
log.info("REQUEST START ON {}:{}({}) CALLED FROM {}@{} IN SCOPE {} ",
context.configuration().name(),context.configuration().serviceClass(), InnerMethodName.instance.get(),
caller, e.request().getRemoteHost(), ScopeProvider.instance.get());
log.info("REQUEST START ON {}:{}({}) CALLED FROM {}@{} IN SCOPE {} ", appContext.configuration().name(),
appContext.configuration().group(), InnerMethodName.get(), caller,
e.request().getRemoteHost(), context);
}
@Override
public void handleResponse(ResponseEvent e) {
ApplicationContext context = e.context();
boolean resetScope = false;
if (ScopeProvider.instance.get()==null && SecurityTokenProvider.instance.get()==null){
String infrastructure = e.context().container().configuration().infrastructure();
ScopeProvider.instance.set("/"+infrastructure);
resetScope = true;
ApplicationContext appContext = e.context();
try {
String context = getContext(appContext);
String caller = SecretManagerProvider.get() != null
? SecretManagerProvider.get().getOwner().getId()
: UNKNOWN;
String callerQualifier = UNKNOWN;
// retieves caller Ip when there is a proxy
String callerIp = e.request().getHeader("x-forwarded-for");
if (callerIp == null)
callerIp = e.request().getRemoteHost();
boolean success = e.response().getStatus() < 400;
if (appContext.container().configuration().mode() != Mode.offline)
generateAccounting(caller, callerQualifier, callerIp == null ? UNKNOWN : callerIp, success, context,
appContext);
long durationInMillis = System.currentTimeMillis() - startCallThreadLocal.get();
/*
* Metrics.globalRegistry.timer("smartgears.requests",
* "response",Integer.toString(e.response().getStatus()) , "context", context,
* "result", success?"SUCCEDED":"FAILED", "caller-ip", callerIp,
* "caller-username", caller, "service-class",
* appContext.configuration().serviceClass(), "service-name",
* appContext.configuration().name(), "method",
* InnerMethodName.instance.get()).record(durationInMillis,
* TimeUnit.MILLISECONDS);
*/
log.info("REQUEST SERVED ON {}:{}({}) CALLED FROM {}@{} IN SCOPE {} {}(CODE {}) IN {} millis",
appContext.configuration().name(), appContext.configuration().group(),
InnerMethodName.get(), caller, callerIp, context, success ? "SUCCEDED" : "FAILED",
e.response().getStatus(), durationInMillis);
} catch (Exception e1) {
log.error("error on accounting", e);
throw e1;
} finally {
startCallThreadLocal.remove();
InnerMethodName.reset();
}
String caller = AuthorizationProvider.instance.get()!=null? AuthorizationProvider.instance.get().getClient().getId(): "UNKNOWN";
String callerQualifier = AuthorizationProvider.instance.get()!=null? AuthorizationProvider.instance.get().getTokenQualifier(): "UNKNOWN";
//retieves caller Ip when there is a proxy
String callerIp = e.request().getHeader("x-forwarded-for");
if(callerIp==null)
callerIp=e.request().getRemoteHost();
boolean success = e.response().getStatus()<400;
generateAccounting(caller,callerQualifier,callerIp==null?"UNKNOWN":callerIp , success, context);
log.info("REQUEST SERVED ON {}:{}({}) CALLED FROM {}@{} IN SCOPE {} {}(CODE {}) IN {} millis",
context.configuration().name(),context.configuration().serviceClass(), InnerMethodName.instance.get(),
caller, callerIp, ScopeProvider.instance.get(), success?"SUCCEDED":"FAILED", e.response().getStatus(), System.currentTimeMillis()-startCallThreadLocal.get());
startCallThreadLocal.remove();
InnerMethodName.instance.reset();
if (resetScope)
ScopeProvider.instance.reset();
}
void generateAccounting(String caller, String callerQualifier, String remoteHost, boolean success, ApplicationContext context){
AccountingPersistenceFactory.setFallbackLocation(context.container().persistence().location());
void generateAccounting(String caller, String callerQualifier, String remoteHost, boolean success,
String gcubeContext, ApplicationContext appContext) {
AccountingPersistenceFactory
.setFallbackLocation(appContext.container().configuration().accountingFallbackLocation());
AccountingPersistence persistence = AccountingPersistenceFactory.getPersistence();
ServiceUsageRecord serviceUsageRecord = new ServiceUsageRecord();
try{
try {
serviceUsageRecord.setConsumerId(caller);
serviceUsageRecord.setCallerQualifier(callerQualifier);
serviceUsageRecord.setScope(ScopeProvider.instance.get());
serviceUsageRecord.setServiceClass(context.configuration().serviceClass());
serviceUsageRecord.setServiceName(context.configuration().name());
serviceUsageRecord.setHost(context.container().configuration().hostname()+":"+context.container().configuration().port());
serviceUsageRecord.setCalledMethod(InnerMethodName.instance.get());
serviceUsageRecord.setScope(gcubeContext);
serviceUsageRecord.setServiceClass(appContext.configuration().group());
serviceUsageRecord.setServiceName(appContext.configuration().name());
serviceUsageRecord.setHost(appContext.container().configuration().hostname() + ":"
+ appContext.container().configuration().port());
serviceUsageRecord.setCalledMethod(InnerMethodName.get());
serviceUsageRecord.setCallerHost(remoteHost);
serviceUsageRecord.setOperationResult(success?OperationResult.SUCCESS:OperationResult.FAILED);
serviceUsageRecord.setDuration(System.currentTimeMillis()-startCallThreadLocal.get());
serviceUsageRecord.setOperationResult(success ? OperationResult.SUCCESS : OperationResult.FAILED);
serviceUsageRecord.setDuration(System.currentTimeMillis() - startCallThreadLocal.get());
persistence.account(serviceUsageRecord);
}catch(Exception ex){
log.warn("invalid record passed to accounting ",ex);
} catch (Exception ex) {
log.warn("invalid record passed to accounting ", ex);
}
}
private String getContext(ApplicationContext appContext) {
String infrastructure = appContext.container().configuration().infrastructure();
String context = "/" + infrastructure;
if (SecretManagerProvider.get() != null)
context = SecretManagerProvider.get().getContext();
return context;
}
@Override
public String toString() {
return getName();
}
}

@ -1,92 +0,0 @@
package org.gcube.smartgears.handlers.application.request;
import static org.gcube.common.authorization.client.Constants.authorizationService;
import static org.gcube.smartgears.Constants.scope_header;
import static org.gcube.smartgears.Constants.token_header;
import static org.gcube.smartgears.handlers.application.request.RequestError.internal_server_error;
import static org.gcube.smartgears.handlers.application.request.RequestError.invalid_request_error;
import javax.xml.bind.DatatypeConverter;
import javax.xml.bind.annotation.XmlRootElement;
import org.gcube.common.authorization.client.exceptions.ObjectNotFound;
import org.gcube.common.authorization.library.AuthorizationEntry;
import org.gcube.common.authorization.library.provider.AuthorizationProvider;
import org.gcube.common.authorization.library.provider.SecurityTokenProvider;
import org.gcube.common.authorization.library.utils.Caller;
import org.gcube.common.scope.api.ScopeProvider;
import org.gcube.smartgears.Constants;
import org.gcube.smartgears.handlers.application.RequestEvent;
import org.gcube.smartgears.handlers.application.RequestHandler;
import org.gcube.smartgears.handlers.application.ResponseEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@XmlRootElement(name = Constants.request_context_retriever)
public class RequestContextRetriever extends RequestHandler {
private static Logger log = LoggerFactory.getLogger(RequestContextRetriever.class);
private static final String BEARER_AUTH_PREFIX ="Bearer";
private static final String BASIC_AUTH_PREFIX ="Basic";
@Override
public String getName() {
return Constants.request_context_retriever;
}
@Override
public void handleRequest(RequestEvent call) {
String token = call.request().getParameter(token_header)==null? call.request().getHeader(token_header):call.request().getParameter(token_header);
String scope = call.request().getParameter(scope_header)==null? call.request().getHeader(scope_header):call.request().getParameter(scope_header);
if (token==null && call.request().getHeader(Constants.authorization_header)!=null){
String authorization = call.request().getHeader(Constants.authorization_header);
if (authorization.contains(BASIC_AUTH_PREFIX)) {
String base64Credentials = authorization.substring(BASIC_AUTH_PREFIX.length()).trim();
String credentials = new String(DatatypeConverter.parseBase64Binary(base64Credentials));
// credentials = username:password
final String[] values = credentials.split(":",2);
token = values[1];
} else if (authorization.contains(BEARER_AUTH_PREFIX))
token = authorization.substring(BEARER_AUTH_PREFIX.length()).trim();
}
//Gives priority to the token
if (token!=null)
this.retreiveAndSetInfo(token, call);
else if (scope!=null)
ScopeProvider.instance.set(scope);
}
@Override
public void handleResponse(ResponseEvent e) {
SecurityTokenProvider.instance.reset();
AuthorizationProvider.instance.reset();
ScopeProvider.instance.reset();
log.debug("resetting all the Thread local for this call.");
}
private void retreiveAndSetInfo(String token, RequestEvent call){
log.info("retrieving context using token {} ", token);
AuthorizationEntry authEntry = null;
try{
authEntry = authorizationService().get(token);
}catch(ObjectNotFound onf){
log.warn("rejecting call to {}, invalid token {}",call.context().name(),token);
invalid_request_error.fire(call.context().name()+" invalid token : "+token);
}catch(Exception e){
log.error("error contacting authorization service",e);
internal_server_error.fire("error contacting authorization service");
}
AuthorizationProvider.instance.set(new Caller(authEntry.getClientInfo(), authEntry.getQualifier()));
SecurityTokenProvider.instance.set(token);
ScopeProvider.instance.set(authEntry.getContext());
log.info("retrieved request authorization info {} in scope {} ", AuthorizationProvider.instance.get(), authEntry.getContext());
}
}

@ -1,6 +1,6 @@
package org.gcube.smartgears.handlers.application.request;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponse;
/**

@ -0,0 +1,48 @@
package org.gcube.smartgears.handlers.application.request;
import java.time.Duration;
import org.gcube.smartgears.Constants;
import org.gcube.smartgears.handlers.application.RequestEvent;
import org.gcube.smartgears.handlers.application.RequestHandler;
import org.gcube.smartgears.handlers.application.ResponseEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.micrometer.core.instrument.Metrics;
public class RequestMetrics extends RequestHandler {
private static Logger log = LoggerFactory.getLogger(RequestMetrics.class);
private static ThreadLocal<Long> startCallThreadLocal = new ThreadLocal<Long>();
private static final String HTTP_REQUEST_METRICS_NAME = "http.server.requests";
@Override
public String getName() {
return Constants.request_metrics;
}
@Override
public boolean isUnfiltrable() {
return true;
}
@Override
public void handleRequest(RequestEvent e) {
startCallThreadLocal.set(System.currentTimeMillis());
}
@Override
public void handleResponse(ResponseEvent e) {
try {
String statusCode = Integer.toString(e.response().getStatus());
Metrics.timer(HTTP_REQUEST_METRICS_NAME, "status", statusCode).record(Duration.ofMillis(System.currentTimeMillis() - startCallThreadLocal.get()));
startCallThreadLocal.remove();
}catch(Throwable t) {
log.warn("error setting Metrics",t);
}
}
}

@ -1,71 +1,75 @@
package org.gcube.smartgears.handlers.application.request;
import static org.gcube.common.authorization.client.Constants.authorizationService;
import static org.gcube.smartgears.Constants.token_header;
import static org.gcube.smartgears.handlers.application.request.RequestError.application_failed_error;
import static org.gcube.smartgears.handlers.application.request.RequestError.application_unavailable_error;
import static org.gcube.smartgears.handlers.application.request.RequestError.internal_server_error;
import static org.gcube.smartgears.handlers.application.request.RequestError.invalid_request_error;
import java.io.IOException;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import org.gcube.common.authorization.client.exceptions.ObjectNotFound;
import org.gcube.common.authorization.library.AuthorizationEntry;
import org.gcube.common.authorization.library.PolicyUtils;
import org.gcube.common.authorization.library.policies.Policy;
import org.gcube.common.authorization.library.provider.SecurityTokenProvider;
import org.gcube.common.authorization.library.provider.ServiceIdentifier;
import org.gcube.common.scope.api.ScopeProvider;
import org.gcube.common.scope.impl.ScopeBean;
import org.gcube.common.scope.impl.ScopeBean.Type;
import java.util.Set;
import org.gcube.common.keycloak.KeycloakClient;
import org.gcube.common.keycloak.KeycloakClientException;
import org.gcube.common.keycloak.KeycloakClientFactory;
import org.gcube.common.security.ContextBean;
import org.gcube.common.security.ContextBean.Type;
import org.gcube.common.security.providers.SecretManagerProvider;
import org.gcube.common.security.secrets.GCubeSecret;
import org.gcube.common.security.secrets.Secret;
import org.gcube.common.security.secrets.UmaTokenSecret;
import org.gcube.smartgears.Constants;
import org.gcube.smartgears.configuration.Mode;
import org.gcube.smartgears.configuration.container.ContainerConfiguration;
import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.handlers.application.RequestEvent;
import org.gcube.smartgears.handlers.application.RequestHandler;
import org.gcube.smartgears.utils.Utils;
import org.gcube.smartgears.handlers.application.ResponseEvent;
import org.gcube.smartgears.security.SimpleCredentials;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@XmlRootElement(name = Constants.request_validation)
public class RequestValidator extends RequestHandler {
@XmlAttribute(required=false, name="oauth")
@Deprecated
boolean oauthCompatibility = false;
private static Logger log = LoggerFactory.getLogger(RequestValidator.class);
private ApplicationContext context;
private static final String BEARER_AUTH_PREFIX ="Bearer";
private ApplicationContext appContext;
@Override
public String getName() {
return Constants.request_validation;
}
@Override
public void handleRequest(RequestEvent call) {
log.trace("executing request validator ON REQUEST");
context = call.context();
appContext = call.context();
SecretManagerProvider.set(getSecret(call));
validateAgainstLifecycle(call);
rejectUnauthorizedCalls(call);
validateScopeCall();
if (SecurityTokenProvider.instance.get()!=null)
validatePolicy(SecurityTokenProvider.instance.get(), call);
if (appContext.container().configuration().mode()!=Mode.offline) {
validateScopeCall();
validatePolicy(call);
}
}
@Override
public void handleResponse(ResponseEvent e) {
log.debug("resetting all the Thread local for this call.");
SecretManagerProvider.reset();
}
private void validateAgainstLifecycle(RequestEvent call) {
switch(context.lifecycle().state()) {
switch(appContext.lifecycle().state()) {
case stopped :
application_unavailable_error.fire(); break;
@ -77,49 +81,36 @@ public class RequestValidator extends RequestHandler {
//nothing to do, but avoids warnings
}
}
private void validateScopeCall() {
String scope = ScopeProvider.instance.get();
if (scope == null) {
log.warn("rejecting unscoped call to {}",context.name());
String context = SecretManagerProvider.get().getContext();
if (context == null) {
log.warn("rejecting unscoped call to {}",appContext.name());
invalid_request_error.fire("call is unscoped");
}
ScopeBean bean = new ScopeBean(scope);
ContainerConfiguration conf = context.container().configuration();
if (!conf.allowedContexts().contains(scope) &&
!(conf.authorizeChildrenContext() && bean.is(Type.VRE) && conf.allowedContexts().contains(bean.enclosingScope().toString()) ) ) {
log.warn("rejecting call to {} in invalid context {}, allowed context are {}",context.name(),scope,context.container().configuration().allowedContexts());
invalid_request_error.fire(context.name()+" cannot be called in scope "+scope);
ContextBean bean = new ContextBean(context);
ContainerConfiguration conf = appContext.container().configuration();
Set<String> allowedContexts =appContext.authorizationProvider().getContexts();
if (!allowedContexts.contains(context) &&
!(conf.authorizeChildrenContext() && bean.is(Type.VRE)
&& allowedContexts.contains(bean.enclosingScope().toString()) ) ) {
log.warn("rejecting call to {} in invalid context {}, allowed context are {}",appContext.name(),context,allowedContexts);
invalid_request_error.fire(appContext.name()+" cannot be called in scope "+context);
}
}
private void rejectUnauthorizedCalls(RequestEvent call){
String token = SecurityTokenProvider.instance.get();
String scope = ScopeProvider.instance.get();
if (token == null && scope==null){
log.warn("rejecting call to {}, authorization required",context.name(),token);
if (call.context().container().configuration().authenticationEnpoint()==null){
log.warn("rejecting call to {}, authorization required",context.name(),token);
RequestError.request_not_authorized_error.fire(context.name()+": authorization required");
}else {
log.info("authorization enpoint found on configuration, redirecting the call");
String recallLocation = String.format("http://%s:%d%s", call.context().container().configuration().hostname(), call.context().container().configuration().port(), call.uri());
//call.response().setHeader("Allowed-Contexts", call.context().container().configuration().allowedContexts().toString());
try {
call.response().sendRedirect(context.container().configuration().authenticationEnpoint()+"?Recall-Location="+recallLocation);
} catch (IOException e) {
log.error("errror redirecting call",e );
}
}
Secret secret = SecretManagerProvider.get();
if (secret == null){
log.warn("rejecting call to {}, authorization required",appContext.name());
RequestError.request_not_authorized_error.fire(appContext.name()+": authorization required");
}
}
@ -128,29 +119,45 @@ public class RequestValidator extends RequestHandler {
return getName();
}
private void validatePolicy(String token, RequestEvent call){
log.info("accessing policy validator with token {} ", token);
AuthorizationEntry authEntry = null;
try{
authEntry = authorizationService().get(token);
}catch(ObjectNotFound onf){
log.warn("rejecting call to {}, invalid token {}",context.name(),token);
invalid_request_error.fire(context.name()+" invalid token : "+token);
}catch(Exception e){
log.error("error contacting authorization service",e);
internal_server_error.fire("error contacting authorization service");
}
private void validatePolicy(RequestEvent call){
//TODO: must be re-thought
}
ServiceIdentifier serviceIdentifier = Utils.getServiceInfo(call.context()).getServiceIdentifier();
private Secret getSecret(RequestEvent call){
String token = call.request().getParameter(token_header)==null? call.request().getHeader(token_header):call.request().getParameter(token_header);
String authHeader = call.request().getHeader(Constants.authorization_header);
for (Policy policy: authEntry.getPolicies())
if (PolicyUtils.isPolicyValidForClient(policy.getServiceAccess(), serviceIdentifier)){
log.error("rejecting call to {} : {} is not allowed to contact the service ",context.name(),authEntry.getClientInfo().getId());
invalid_request_error.fire("rejecting call to "+context.name()+": "+authEntry.getClientInfo().getId()+" is not allowed to contact the service");
}
log.trace("authorization header is {}",authHeader);
log.trace("token header is {}", token);
log.info("d4s-user set to {} ", call.request().getHeader("d4s-user"));
String umaToken = null;
if (authHeader!=null && !authHeader.isEmpty())
if (authHeader.startsWith(BEARER_AUTH_PREFIX))
umaToken = authHeader.substring(BEARER_AUTH_PREFIX.length()).trim();
Secret secret = null;
if (umaToken!=null) {
secret = new UmaTokenSecret(umaToken);
SimpleCredentials credentials = (SimpleCredentials)appContext.authorizationProvider().getCredentials();
KeycloakClient client = KeycloakClientFactory.newInstance();
try {
if(!client.isAccessTokenVerified(secret.getContext(), credentials.getClientID(), credentials.getSecret(), umaToken))
RequestError.request_not_authorized_error.fire("access token verification error");
}catch (KeycloakClientException e) {
RequestError.internal_server_error.fire("error contacting keycloak client", e);
}
} else if (token!=null && !token.isEmpty())
try {
secret = new GCubeSecret(token);
}catch(Throwable t) {
RequestError.request_not_authorized_error.fire("gcube token verification error ("+t.getMessage()+")");
}
return secret;
}
}

@ -5,8 +5,6 @@ package org.gcube.smartgears.handlers.container.lifecycle;
import static org.gcube.smartgears.Constants.accounting_management;
import javax.xml.bind.annotation.XmlRootElement;
import org.gcube.accounting.persistence.AccountingPersistenceFactory;
import org.gcube.smartgears.handlers.container.ContainerHandler;
import org.gcube.smartgears.handlers.container.ContainerLifecycleEvent;
@ -14,9 +12,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/
* @author Luca Frosini (ISTI - CNR)
*/
@XmlRootElement(name = accounting_management)
public class AccountingManager extends ContainerHandler {
private static Logger logger = LoggerFactory.getLogger(AccountingManager.class);

@ -0,0 +1,218 @@
package org.gcube.smartgears.handlers.container.lifecycle;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.gcube.common.events.Observes.Kind.critical;
import static org.gcube.common.events.Observes.Kind.resilient;
import static org.gcube.smartgears.Constants.profile_management;
import static org.gcube.smartgears.handlers.ProfileEvents.changed;
import static org.gcube.smartgears.lifecycle.container.ContainerLifecycle.activation;
import static org.gcube.smartgears.lifecycle.container.ContainerLifecycle.failure;
import static org.gcube.smartgears.lifecycle.container.ContainerLifecycle.part_activation;
import static org.gcube.smartgears.lifecycle.container.ContainerLifecycle.shutdown;
import static org.gcube.smartgears.lifecycle.container.ContainerLifecycle.stop;
import static org.gcube.smartgears.lifecycle.container.ContainerState.active;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ScheduledFuture;
import org.gcube.common.events.Observes;
import org.gcube.smartgears.configuration.Mode;
import org.gcube.smartgears.context.Property;
import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.handlers.container.ContainerHandler;
import org.gcube.smartgears.handlers.container.ContainerLifecycleEvent;
import org.gcube.smartgears.lifecycle.container.ContainerLifecycle;
import org.gcube.smartgears.lifecycle.container.ContainerState;
import org.gcube.smartgears.managers.ContextEvents;
import org.gcube.smartgears.provider.ProviderFactory;
import org.gcube.smartgears.publishing.Publisher;
import org.gcube.smartgears.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* Manages the resource profile of the application.
* <p>
*
* The manager:
*
* <ul>
* <li>creates the profile when the application starts for the first time;
* <li>loads the profile when the application restarts;
* <li>publishes the profile when the application becomes active, and at any
* lifecycle change thereafter;
* <li>stores the profile locally after each publication;
* </ul>
*
* @author Fabio Simeoni
* @see ProfileBuilder
* @see ProfilePublisherImpl
*/
public class ContainerProfileManager extends ContainerHandler {
Logger log = LoggerFactory.getLogger(ContainerProfileManager.class);
private ContainerContext context;
private ScheduledFuture<?> periodicUpdates;
private static final String PUBLISHED_PROP = "published";
private List<Publisher> publishers;
@Override
public void onStart(ContainerLifecycleEvent.Start e) {
context = e.context();
activated();
// note we don't fire profile events, but wait for the final startup
// outcome which
// will result in a state change. only then we publish and store the
// profile
// this avoids the redundancy and performance penalty of storing and
// publishing multiple
// times in rapid succession (which would be correct). Revise if proves
// problematic in corner
// cases.
}
private void activated() {
publishers = context.configuration().mode() != Mode.offline ? ProviderFactory.provider().publishers()
: Collections.emptyList();
registerObservers();
schedulePeriodicUpdates();
}
private void registerObservers() {
context.events().subscribe(new Object() {
@Observes({ activation, part_activation, shutdown, stop, failure })
void onChanged(ContainerLifecycle lc) {
// since we do not know the observers, they will deal with failures and their
// consequences
// any that comes back will be logged in this event thread
context.events().fire(context, changed);
}
@Observes(value = changed, kind = critical)
void publishAfterChange(ContainerContext context) {
log.info("Publish after profile Change event called -- contains published prop? {}",context.properties().contains(PUBLISHED_PROP));
// if we've failed before first publication do not try to publish
// (we may well have failed there)
if (context.lifecycle().state() != ContainerState.failed) {
if (!context.properties().contains(PUBLISHED_PROP)) {
context.properties().add(new Property(PUBLISHED_PROP, true));
log.info("publishing container for the first time");
publishers.parallelStream().forEach(p -> {
try {
p.create(context, context.authorizationProvider().getContexts());
} catch (Throwable e) {
log.error(
"cannot publish container for first time with publisher type {} (see details)",
p.getClass().getCanonicalName(), e);
}
});
} else
publishers.parallelStream().forEach(p -> {
try {
p.update(context);
} catch (Throwable e) {
log.error("cannot publish container with publisher type {} (see details)",
p.getClass().getCanonicalName(), e);
}
});
}
}
@Observes(value = ContextEvents.ADD_CONTEXT_TO_CONTAINER)
void addTo(String scope) {
log.info("add_to_context event arrived in container");
for (Publisher publisher : publishers)
try {
log.trace("publishing container within new scope");
publisher.create(context, Collections.singleton(scope));
} catch (Exception e) {
log.error("cannot add container to {} with publisher type {} (see details)", scope,
publisher.getClass().getCanonicalName(), e);
// since we've failed no published event is fired and profile
// will not be stored.
// we do it manually to ensure we leave some local trace of the
// changed profile.
// TODO: CHECK --- store(profile);
}
}
@Observes(value = ContextEvents.REMOVE_CONTEXT_FROM_CONTAINER)
void removeFrom(String scope) {
log.info("remove_from_context event arrived in container");
for (Publisher publisher : publishers)
try {
log.trace("unpublishing container from context {}", scope);
publisher.remove(context, Collections.singleton(scope));
} catch (Exception e) {
log.error("cannot remove container from {} with publisher type {} (see details)", scope,
publisher.getClass().getCanonicalName(), e);
// since we've failed no published event is fired and profile
// will not be stored.
// we do it manually to ensure we leave some local trace of the
// changed profile.
// TODO: CHECK --- store(profile);
}
}
});
}
private void schedulePeriodicUpdates() {
// register to cancel updates
context.events().subscribe(new Object() {
// we register it in response to lifecycle events so that we can stop and resume
// along with application
@Observes(value = { activation, part_activation }, kind = resilient)
synchronized void restartPeriodicUpdates(ContainerLifecycle lc) {
// already running
if (periodicUpdates != null)
return;
if (lc.state() == active)
log.info("scheduling periodic updates of container profile");
else
log.info("resuming periodic updates of container profile");
final Runnable updateTask = new Runnable() {
public void run() {
context.events().fire(context, changed);
}
};
periodicUpdates = Utils.scheduledServicePool.scheduleAtFixedRate(updateTask, 3,
context.configuration().publicationFrequency(), SECONDS);
}
@Observes(value = { stop, failure, shutdown }, kind = resilient)
synchronized void cancelPeriodicUpdates(ContainerLifecycle ignore) {
if (periodicUpdates != null) {
log.trace("stopping periodic updates of container profile");
try {
periodicUpdates.cancel(true);
periodicUpdates = null;
} catch (Exception e) {
log.warn("could not stop periodic updates of container profile", e);
}
}
}
});
}
@Override
public String toString() {
return profile_management;
}
}

@ -0,0 +1,93 @@
package org.gcube.smartgears.handlers.container.lifecycle;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Luca Frosini (ISTI-CNR)
*/
public class LinuxDistributionInfo {
private static final Logger logger = LoggerFactory.getLogger(LinuxDistributionInfo.class);
public static final String LSB_RELEASE_COMMAND = "lsb_release -a";
public static final String OS_RELEASE_FILE_PATH = "/etc/os-release";
protected Map<String, String> info;
protected Map<String, String> getInfoViaLsbReleaseCommand() throws IOException {
logger.trace("Going to exec {}", LSB_RELEASE_COMMAND);
Process process = Runtime.getRuntime().exec(LSB_RELEASE_COMMAND);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
Map<String, String> map = parseBufferedReader(bufferedReader);
bufferedReader.close();
return map;
}
private Map<String, String> parseBufferedReader(BufferedReader bufferedReader) throws IOException {
Map<String, String> map = new HashMap<>();
String line = "";
while ((line = bufferedReader.readLine()) != null) {
String[] nameValue = parseLine(line);
map.put(nameValue[0], nameValue[1]);
}
return map;
}
private String[] parseLine(String line) {
String[] splitted = line.split("=");
if (splitted.length < 2) {
splitted = line.split(":");
}
String[] ret = new String[2];
ret[0] = splitted[0].trim();
ret[1] = splitted[1].trim().replace("\"", "");
return ret;
}
private Map<String, String> getInfoViaFile(File file) throws IOException {
logger.trace("Going to read file {}", file.getAbsolutePath());
BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
Map<String, String> map = parseBufferedReader(bufferedReader);
bufferedReader.close();
return map;
}
protected Map<String, String> getInfoViaOsReleaseFile() throws IOException {
File osReleaseFile = new File(OS_RELEASE_FILE_PATH);
return getInfoViaFile(osReleaseFile);
}
private Map<String, String> retriveInfo() {
try {
return getInfoViaLsbReleaseCommand();
} catch (IOException e) {
}
try {
return getInfoViaOsReleaseFile();
}catch (IOException e) {
}
return null;
}
public Map<String, String> getInfo() {
if (info == null) {
info = retriveInfo();
}
return info;
}
}

@ -1,366 +0,0 @@
package org.gcube.smartgears.handlers.container.lifecycle;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.gcube.common.resources.gcore.HostingNode;
import org.gcube.common.resources.gcore.HostingNode.Profile.NodeDescription.GHNType;
import org.gcube.common.resources.gcore.HostingNode.Profile.NodeDescription.Processor;
import org.gcube.common.resources.gcore.HostingNode.Profile.NodeDescription.Variable;
import org.gcube.common.resources.gcore.utils.Group;
import org.gcube.smartgears.configuration.container.ContainerConfiguration;
import org.gcube.smartgears.configuration.library.SmartGearsConfiguration;
import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.provider.ProviderFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Fabio Simeoni
* @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/
*
*/
public class ProfileBuilder {
private static Logger log = LoggerFactory.getLogger(ProfileBuilder.class);
private ContainerContext context;
public ProfileBuilder(ContainerContext context) {
this.context = context;
}
public HostingNode create() {
HostingNode node = new HostingNode();
ContainerConfiguration cfg = context.configuration();
node.newProfile().infrastructure(cfg.infrastructure());
addSiteTo(node);
String ip = "not resolved";
try {
ip = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
log.warn("unable to detect the IP address of the host");
}
node.profile().newDescription().activationTime(Calendar.getInstance()).name(cfg.hostname() + ":" + cfg.port());
node.profile().description().networkAdapters().add().mtu(0).name("local-adapter").ipAddress(ip).inboundIP("")
.outboundIP("");
node.profile().description().newOperatingSystem().name(System.getProperty("os.name"))
.version(System.getProperty("os.version")).release("");
node.profile().description().newArchitecture().platformType(System.getProperty("os.arch")).smpSize(0)
.smtSize(0);
ArrayList<HashMap<String, String>> info = cpuInfo();
Group<Processor> processors = node.profile().description().processors();
for (HashMap<String, String> map : info)
processors.add().bogomips(new BigDecimal(map.get("bogomips")))
.clockSpeedMhz(new BigDecimal(map.get("cpu_MHz"))).family(map.get("cpu_family"))
.modelName(map.get("model_name")).model(map.get("model")).vendor(map.get("vendor_id"))
.cacheL1(new Integer(map.get("cache_size"))).cacheL1D(0).cacheL1I(0).cacheL2(0);
addVariablesTo(node);
update(node,false);
node.profile().description().type(GHNType.Static);
// String type = (String) context.getProperty(GHNContext.GHN_TYPE, false);
// if (type.compareToIgnoreCase(Type.DYNAMIC.toString()) == 0) description.setType(Description.Type.Dynamic);
// else if (type.compareToIgnoreCase(Type.STATIC.toString()) == 0) description.setType(Description.Type.Static);
// else if (type.compareToIgnoreCase(Type.SELFCLEANING.toString()) == 0)
// description.setType(Description.Type.Selfcleaning);
//
// file system
node.profile().description().localFileSystems().add().name("").type("").readOnly(false)
.root(cfg.persistence().location());
return node;
}
@SuppressWarnings("all")
private ArrayList<HashMap<String, String>> cpuInfo() {
ArrayList<HashMap<String, String>> map = new ArrayList<HashMap<String, String>>();
File file = new File("/proc/cpuinfo");
if (!file.exists()) {
log.warn("cannot acquire CPU info (no /proc/cpuinfo)");
return map;
}
BufferedReader input = null;
try {
input = new BufferedReader(new FileReader(file));
String line = null;
HashMap<String, String> currentProcessor = null;
while ((line = input.readLine()) != null) {
if ((line.startsWith("processor"))) { // add the current processor to the map
if (currentProcessor != null)
map.add((HashMap) currentProcessor.clone());
currentProcessor = new HashMap<String, String>();
}
try {
if (line.contains("vendor_id"))
currentProcessor.put("vendor_id", line.split(":")[1].trim());
} catch (Exception ex) {
}
try {
if (line.contains("cpu family"))
currentProcessor.put("cpu_family", line.split(":")[1].trim());
} catch (Exception ex) {
}
try {
if ((line.contains("model\t")) || (line.contains("model\b")))
currentProcessor.put("model", line.split(":")[1].trim());
} catch (Exception ex) {
}
try {
if (line.contains("model name"))
currentProcessor.put("model_name", line.split(":")[1].trim());
} catch (Exception ex) {
}
try {
if (line.contains("cpu MHz"))
currentProcessor.put("cpu_MHz", line.split(":")[1].trim());
} catch (Exception ex) {
}
try {
if (line.contains("cache size"))
currentProcessor.put("cache_size", line.split(":")[1].trim().split(" ")[0]);
} catch (Exception ex) {
}
try {
if (line.contains("bogomips"))
currentProcessor.put("bogomips", line.split(":")[1].trim());
} catch (Exception ex) {
}
}
if (currentProcessor != null)
map.add(currentProcessor);
} catch (Exception e) {
log.warn("unable to acquire CPU info", e);
} finally {
if (input != null)
try {
input.close();
} catch (IOException e) {
log.warn("unable to close stream", e);
}
}
return map;
}
private long getFreeSpace() {
long free = 0;
try {
free = Files.getFileStore(Paths.get(context.configuration().persistence().location())).getUsableSpace()/1024;
} catch (IOException ioe) {
log.warn("unable to detect the free space on the disk", ioe);
}
return free;
}
public void update(HostingNode node,boolean onLoad) {
ContainerConfiguration cfg = context.configuration();
if (onLoad) {
log.info("updating ghn profile");
node.profile().description().activationTime(Calendar.getInstance()).name(cfg.hostname() + ":" + cfg.port());
addVariablesTo(node);
addSiteTo(node);
}
node.profile().description().status(context.lifecycle().state().remoteForm());
Map<String, Long> mem = memoryUsage();
node.profile().description().newMainMemory().ramAvailable(mem.get("MemoryAvailable"))
.ramSize(mem.get("MemoryTotalSize")).virtualAvailable(mem.get("VirtualAvailable"))
.virtualSize(mem.get("VirtualSize"));
node.profile().description().localAvailableSpace(getFreeSpace());
node.profile().description().uptime(uptime());
node.profile().description().lastUpdate(Calendar.getInstance());
Map<String, Double> loads = loadStatistics();
node.profile().description().newLoad().lastMin(loads.get("1min") == null ? 0 : loads.get("1min"))
.last5Mins(loads.get("5mins") == null ? 0 : loads.get("5mins"))
.last15Mins(loads.get("15mins") == null ? 0 : loads.get("15mins"));
}
private void addSiteTo(HostingNode node) {
ContainerConfiguration cfg = context.configuration();
node.profile().newSite().country(cfg.site().country()).location(cfg.site().location())
.latitude(cfg.site().latitude()).longitude(cfg.site().longitude()).domain(domainIn(cfg.hostname()));
}
private void addVariablesTo(HostingNode node) {
ContainerConfiguration cfg = context.configuration();
Group<Variable> variables = node.profile().description().environmentVariables();
// Cleaning variables to avoid duplicates
variables.removeAll(node.profile().description().environmentVariables());
Map<String, String> map = new HashMap<String, String>();
map.putAll(cfg.properties());
map.putAll(System.getenv());
for (Map.Entry<String, String> entry : map.entrySet()) {
String varname = entry.getKey();
if ((varname.compareToIgnoreCase("CLASSPATH") == 0) || (varname.compareToIgnoreCase("PATH") == 0)
|| (varname.contains("SSH")) || (varname.contains("MAIL"))
|| (varname.compareToIgnoreCase("LS_COLORS") == 0))
continue;
variables.add().keyAndValue(entry.getKey(), entry.getValue());
}
/* The following code is useless can be removed
Map<String, String> envvars = new HashMap<String, String>();
for (String varname : envvars.keySet()) {
// a bit of filtering
if ((varname.compareToIgnoreCase("CLASSPATH") == 0) || (varname.compareToIgnoreCase("PATH") == 0)
|| (varname.contains("SSH")) || (varname.contains("MAIL"))
|| (varname.compareToIgnoreCase("LS_COLORS") == 0))
continue;
variables.add().keyAndValue(varname, envvars.get(varname));
}
*/
variables.add().keyAndValue("Java", System.getProperty("java.version"));
SmartGearsConfiguration config = ProviderFactory.provider().smartgearsConfiguration();
variables.add().keyAndValue("SmartGears",config.version());
variables.add().keyAndValue("ghn-update-interval-in-secs", String.valueOf(cfg.publicationFrequency()));
}
public String uptime() {
String lines = "", linetemp = null;
try {
Process p = Runtime.getRuntime().exec("uptime");
p.waitFor();
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((linetemp = input.readLine()) != null)
lines += linetemp;
input.close();
p.destroy();
lines = lines.split(",")[0].split("up")[1].trim();
} catch (Exception e) {
log.warn("unable to detect the uptime of this machine", e);
lines = "unable to detect";
}
return lines;
}
public Map<String, Double> loadStatistics() {
Map<String, Double> result = new HashMap<String, Double>();
try {
File loadadv = new File("/proc/loadavg");
if (loadadv.exists()) {
Reader reader = new FileReader(loadadv);
int c;
StringBuilder content = new StringBuilder();
while ((c = reader.read()) != -1)
content.append((char) c);
reader.close();
Pattern p = Pattern.compile("^(.*?)\\s{1}(.*?)\\s{1}(.*?)\\s{1}(.*)$");
Matcher matcher = p.matcher(content.toString());
if ((matcher.matches()) && (matcher.groupCount() > 3)) {
result.put("1min", new Double(matcher.group(1)));
result.put("5mins", new Double(matcher.group(2)));
result.put("15mins", new Double(matcher.group(3).split("\\s")[0]));
}
}
} catch (Exception ioe) {
log.warn("unable to detect the load values of this machine", ioe);
}
return result;
}
@SuppressWarnings("all")
public Map<String, Long> memoryUsage() {
Map<String, Long> map = new HashMap<String, Long>();
java.lang.management.OperatingSystemMXBean mxbean = java.lang.management.ManagementFactory
.getOperatingSystemMXBean();
com.sun.management.OperatingSystemMXBean sunmxbean = (com.sun.management.OperatingSystemMXBean) mxbean;
long freeMemory = sunmxbean.getFreePhysicalMemorySize() / 1048576; // in MB
long availableMemory = sunmxbean.getTotalPhysicalMemorySize() / 1048576; // in MB
map.put("MemoryAvailable", freeMemory);
map.put("MemoryTotalSize", availableMemory);
long ramVirtualAvailable = Runtime.getRuntime().freeMemory() / 1048576; // in MB
long ramVirtualSize = Runtime.getRuntime().totalMemory() / 1048576; // in MB
map.put("VirtualAvailable", ramVirtualAvailable);
map.put("VirtualSize", ramVirtualSize);
return map;
}
private String domainIn(String hostname) {
Pattern pattern = Pattern.compile("([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})");
java.util.regex.Matcher regexMatcher = pattern.matcher(hostname);
if (regexMatcher.matches()) //it's an IP address, nothing to trim
return hostname;
String[] tokens = hostname.split("\\.");
if (tokens.length < 2)
return hostname;
else
return tokens[tokens.length-2]+ "." + tokens[tokens.length-1];
}
}

@ -1,289 +0,0 @@
package org.gcube.smartgears.handlers.container.lifecycle;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.gcube.common.events.Observes.Kind.critical;
import static org.gcube.common.events.Observes.Kind.resilient;
import static org.gcube.smartgears.Constants.container_profile_property;
import static org.gcube.smartgears.Constants.profile_management;
import static org.gcube.smartgears.handlers.ProfileEvents.addToContext;
import static org.gcube.smartgears.handlers.ProfileEvents.changed;
import static org.gcube.smartgears.handlers.ProfileEvents.published;
import static org.gcube.smartgears.handlers.ProfileEvents.removeFromContext;
import static org.gcube.smartgears.lifecycle.container.ContainerLifecycle.activation;
import static org.gcube.smartgears.lifecycle.container.ContainerLifecycle.failure;
import static org.gcube.smartgears.lifecycle.container.ContainerLifecycle.part_activation;
import static org.gcube.smartgears.lifecycle.container.ContainerLifecycle.shutdown;
import static org.gcube.smartgears.lifecycle.container.ContainerLifecycle.stop;
import static org.gcube.smartgears.lifecycle.container.ContainerState.active;
import java.util.Collections;
import java.util.concurrent.ScheduledFuture;
import javax.xml.bind.annotation.XmlRootElement;
import org.gcube.common.events.Observes;
import org.gcube.common.resources.gcore.HostingNode;
import org.gcube.smartgears.context.Property;
import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.handlers.container.ContainerHandler;
import org.gcube.smartgears.handlers.container.ContainerLifecycleEvent.Start;
import org.gcube.smartgears.lifecycle.container.ContainerLifecycle;
import org.gcube.smartgears.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* Manages the resource profile of the container.
* <p>
*
* The manager:
*
* <ul>
* <li>creates the profile when the container starts for the first time;
* <li>loads the profile when the container restarts;
* <li>publishes the profile when the container becomes active, and at any lifecycle change thereafter;
* <li>stores the profile locally after each publication;
* </ul>
*
* @author Fabio Simeoni
* @see ProfileBuilder
*/
@XmlRootElement(name = profile_management)
public class ProfileManager extends ContainerHandler {
private static Logger log = LoggerFactory.getLogger(ProfileManager.class);
private ContainerContext context;
private ProfileBuilder builder;
private ProfilePublisher publisher;
private ScheduledFuture<?> periodicUpdates;
@Override
public void onStart(Start e) {
context = e.context();
builder = new ProfileBuilder(context);
activated();
// note we don't fire profile events, but wait for the final startup response which
// will result in a state change. only then we publish and store the profile
// this avoids the redundancy and performance penalty of storing and publishing multiple
// times in rapid succession (which would be correct). Revise if proves problematic in corner
// cases.
}
private void activated(){
HostingNode profile = loadOrCreateProfile();
share(profile);
publisher = new ProfilePublisher(context);
registerObservers();
schedulePeriodicUpdates();
}
private void registerObservers() {
context.events().subscribe(new Object() {
@Observes({ activation, part_activation, shutdown, stop, failure })
void onChanged(ContainerLifecycle lc) {
HostingNode profile = context.profile(HostingNode.class);
profile.profile().description().status(lc.state().remoteForm());
// since we do not know the observers, they will deal with failures and their consequences
// any that comes back will be logged in this event thread
context.events().fire(profile, changed);
}
@Observes(value = published)
void shareAfterPublish(HostingNode profile) {
share(profile); // publish may produce a new profile instance
}
@Observes(value = changed, kind = critical)
void publishAfterChange(HostingNode profile) {
log.info("Publish after profile Change event called");
publish(profile); // if successful, triggers share and store.
}
@Observes(value = addToContext)
void addTo(String token) {
try {
log.trace("publishing container with new token");
publisher.addTo(Collections.singleton(token));
publisher.update();
}catch (Exception e) {
log.error("cannot add token {} (see details)",token, e);
// since we've failed no published event is fired and profile
// will not be stored.
// we do it manually to ensure we leave some local trace of the
// changed profile.
//TODO: CHECK --- store(profile);
}
}
@Observes(value = removeFromContext)
void removeFrom(String token) {
try {
log.trace("unpublishing container with new token");
publisher.removeFrom(Collections.singleton(token));
publisher.update();
}catch (Exception e) {
log.error("cannot remove token {} (see details)",token, e);
// since we've failed no published event is fired and profile
// will not be stored.
// we do it manually to ensure we leave some local trace of the
// changed profile.
//TODO: CHECK --- store(profile);
}
}
});
}
private HostingNode loadOrCreateProfile() {
return createProfile();
}
private void share(HostingNode profile) {
log.trace("sharing container profile");
context.properties().add(new Property(container_profile_property, profile));
}
private HostingNode createProfile() {
log.info("creating container profile");
try {
HostingNode node = builder.create();
node.setId(context.id());
return node;
} catch (Throwable e) {
// this is a critical startup failure: it will fail the application
throw new RuntimeException("cannot create container profile", e);
}
}
private void publish(HostingNode profile) {
//ContainerConfiguration configuration = context.configuration();
// first-publication vs. routine publication: when we delete scopes let's make sure there is
// at least one left of it will be re-triggered
boolean firstPublication = profile.scopes().isEmpty();
try {
if (firstPublication)
publisher.addToAll();
else
publisher.update();
} catch (Exception e) {
log.error("cannot publish container (see details)", e);
// since we've failed no published event is fired and profile will not be stored.
// we do it manually to ensure we leave some local trace of the changed profile.
//store(profile);
}
}
private void schedulePeriodicUpdates() {
// register to cancel updates
context.events().subscribe(
new Object() {
// we register it in response to lifecycle events so that we can stop and resume along with application
@Observes(value = { activation, part_activation }, kind = resilient)
synchronized void restartPeriodicUpdates(ContainerLifecycle lc) {
//already running
if (periodicUpdates!=null)
return;
if (lc.state()==active)
log.info("scheduling periodic updates of container profile");
else
log.info("resuming periodic updates of container profile");
final Runnable updateTask = new Runnable() {
public void run() {
HostingNode profile = context.profile(HostingNode.class);
try {
builder.update(profile, false);
}
catch(Exception e) {
//we may fail in the update of the profile
log.error("cannot complete periodic update of container profile",e);
}
//if handling of event generates failures these will be reported
//for resilience we do not fail the application
log.trace("firing change event on container profile");
context.events().fire(profile,changed);
}
};
periodicUpdates = Utils.scheduledServicePool.scheduleAtFixedRate(updateTask, 3, context.configuration()
.publicationFrequency(), SECONDS);
}
@Observes(value = { stop, failure, shutdown }, kind = resilient)
synchronized void cancelPeriodicUpdates(ContainerLifecycle ignore) {
if (periodicUpdates != null){
log.trace("stopping periodic updates of container profile");
try {
periodicUpdates.cancel(true);
periodicUpdates=null;
}
catch(Exception e) {
log.warn("could not stop periodic updates of container profile",e);
}
}
}
});
}
@Override
public String toString() {
return profile_management;
}
}

@ -1,233 +0,0 @@
package org.gcube.smartgears.handlers.container.lifecycle;
import static org.gcube.smartgears.utils.Utils.notEmpty;
import static org.gcube.smartgears.utils.Utils.rethrowUnchecked;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.gcube.common.authorization.client.proxy.AuthorizationProxy;
import org.gcube.common.authorization.library.provider.SecurityTokenProvider;
import org.gcube.common.resources.gcore.HostingNode;
import org.gcube.informationsystem.publisher.ScopedPublisher;
import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.handlers.ProfileEvents;
import org.gcube.smartgears.provider.ProviderFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Publishes the resource profile of the container.
* <p>
* Distinguishes publication in new scopes ({@link #addTo(List)} from publication updates in existing scopes ({@link #update(List)}.
*
* @author Fabio Simeoni
*
*/
public class ProfilePublisher {
private static final Logger log = LoggerFactory.getLogger(ProfilePublisher.class);
//the underlying IS publisher
private final ScopedPublisher publisher;
//private final AuthorizationProvider authorization;
private final ContainerContext context;
private AuthorizationProxy authProxy ;
/**
* Creates an instance for the container.
* @param context the context of the application
*/
public ProfilePublisher(ContainerContext context) {
this.context = context;
this.publisher=ProviderFactory.provider().publisherFor(context);
this.authProxy = ProviderFactory.provider().authorizationProxy();
}
/**
* Adds the current resource profile of the application in one or more
* scopes. The scopes are retrieved from tokens
* @param tokens the tokens
*/
public void addTo(Collection<String> tokens) {
notEmpty("tokens",tokens);
log.info("publishing container with tokens {}", tokens);
HostingNode profile = context.profile(HostingNode.class);
/* TODO: reintroduce it when scope will be removed
//TODO: remove when move to new IS
Collection<String> retainedContexts = new ArrayList<String>(context.configuration().allowedContexts());
retainedContexts.removeAll(profile.scopes().asCollection());
profile.scopes().asCollection().addAll(retainedContexts);
log.trace("profile scopes on create are {} ",profile.scopes().asCollection());
String previousToken = SecurityTokenProvider.instance.get();
try {
for (String token: tokens){
log.info("creating profile with token {}", token);
SecurityTokenProvider.instance.set(token);
profile = publisher.create(profile);
SecurityTokenProvider.instance.reset();
}
update();
} catch (Exception e) {
log.warn("error adding scopes",e);
rethrowUnchecked(e);
} finally{
SecurityTokenProvider.instance.set(previousToken);
}*/
ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
String previousToken = SecurityTokenProvider.instance.get();
try{//This classloader set is needed for the jaxb context
if (previousToken==null)
SecurityTokenProvider.instance.set((String)tokens.toArray()[0]);
Thread.currentThread().setContextClassLoader(ProfilePublisher.class.getClassLoader());
profile = publisher.create(profile, resolveScopesFromTokens(tokens));
} catch (Exception e) {
rethrowUnchecked(e);
} finally {
SecurityTokenProvider.instance.set(previousToken);
Thread.currentThread().setContextClassLoader(contextCL);
}
sharePublished(profile);
}
/**
* Adds the current resource profile of the application in one or more scopes.
*/
public void addToAll() {
addTo(context.configuration().startTokens());
}
/**
* Updates the current resource profile of the application in its current scopes.
*/
public void update() {
HostingNode profile = context.profile(HostingNode.class);
/* TODO: reintroduce it when scope will be removed
Collection<String> tokens = context.configuration().startTokens();
log.info("updating container with tokens {}", tokens);
String previousToken = SecurityTokenProvider.instance.get();
try {
for (String token: tokens){
SecurityTokenProvider.instance.set(token);
profile = publisher.update(profile);
SecurityTokenProvider.instance.reset();
}
sharePublished(profile);
}
catch (Exception e) {
log.warn("error updating container",e);
rethrowUnchecked(e);
} finally{
SecurityTokenProvider.instance.set(previousToken);
}*/
log.debug("[update] resource scopes are : {} ",profile.scopes().asCollection());
ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
String previousToken = SecurityTokenProvider.instance.get();
try{//This classloader set is needed for the jaxb context
if (previousToken==null)
SecurityTokenProvider.instance.set((String)context.configuration().startTokens().toArray()[0]);
Thread.currentThread().setContextClassLoader(ProfilePublisher.class.getClassLoader());
profile = publisher.update(profile);
} catch (Exception e) {
rethrowUnchecked(e);
} finally {
SecurityTokenProvider.instance.set(previousToken);
Thread.currentThread().setContextClassLoader(contextCL);
}
sharePublished(profile);
}
/**
* Removes the container from one or more scopes.
* @param tokens the tokens
*/
public void removeFrom(Collection<String> tokens) {
HostingNode profile = context.profile(HostingNode.class);
log.info("removing container with tokens {}", tokens);
/* TODO: reintroduce it when scope will be removed
String previousToken = SecurityTokenProvider.instance.get();
try {
for (String token: tokens){
SecurityTokenProvider.instance.set(token);
profile = publisher.remove(profile);
SecurityTokenProvider.instance.reset();
}
update();
}
catch (Exception e) {
log.warn("error removing scopes",e);
rethrowUnchecked(e);
} finally{
SecurityTokenProvider.instance.set(previousToken);
} */
ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
String previousToken = SecurityTokenProvider.instance.get();
try{//This classloader set is needed for the jaxb context
if (previousToken==null)
SecurityTokenProvider.instance.set((String)tokens.toArray()[0]);
Thread.currentThread().setContextClassLoader(ProfilePublisher.class.getClassLoader());
profile = publisher.remove(profile, resolveScopesFromTokens(tokens));
} catch (Exception e) {
rethrowUnchecked(e);
} finally {
SecurityTokenProvider.instance.set(previousToken);
Thread.currentThread().setContextClassLoader(contextCL);
}
log.debug("after remove container profile contains scopes {}",profile.scopes().asCollection());
sharePublished(profile);
}
private void sharePublished(HostingNode profile) {
context.events().fire(profile,ProfileEvents.published);
}
private List<String> resolveScopesFromTokens(Collection<String> tokens){
List<String> scopes = new ArrayList<String>(tokens.size());
for (String token: tokens)
try{
scopes.add(this.authProxy.get(token).getContext());
}catch (Exception e) {
log.warn("error retrieving token {} , it should never happen",token);
}
return scopes;
}
}

@ -0,0 +1,19 @@
package org.gcube.smartgears.health;
import java.util.LinkedList;
import java.util.List;
import org.gcube.common.health.api.HealthCheck;
public class HealthManager {
private List<HealthCheck> checks = new LinkedList<>();
public void register(HealthCheck check){
checks.add(check);
}
public List<HealthCheck> getChecks() {
return checks;
}
}

@ -0,0 +1,35 @@
package org.gcube.smartgears.health;
import java.util.List;
import org.gcube.com.fasterxml.jackson.annotation.JsonInclude;
import org.gcube.com.fasterxml.jackson.annotation.JsonInclude.Include;
import org.gcube.common.health.api.Status;
import org.gcube.common.health.api.response.HealthCheckResponse;
import org.gcube.common.validator.annotations.NotNull;
@JsonInclude(Include.NON_NULL)
public class HealthResponse {
@NotNull
private Status status;
private List<HealthCheckResponse> checks;
public HealthResponse(Status status, List<HealthCheckResponse> checks) {
super();
this.status = status;
this.checks = checks;
}
public Status getStatus() {
return status;
}
public List<HealthCheckResponse> getChecks() {
return checks;
}
}

@ -0,0 +1,49 @@
package org.gcube.smartgears.health;
import java.util.List;
import java.util.TimerTask;
import java.util.stream.Collectors;
import org.gcube.common.health.api.HealthCheck;
import org.gcube.common.health.api.Status;
import org.gcube.common.health.api.response.HealthCheckResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HealthTask extends TimerTask{
private static final Logger log = LoggerFactory.getLogger(HealthTask.class);
private HealthResponse response;
private HealthManager healthManager;
public HealthTask(HealthManager healthManager) {
this.healthManager = healthManager;
}
@Override
public void run() {
List<HealthCheck> checks = healthManager.getChecks();
List<HealthCheckResponse> responses = checks.stream().map(c -> this.wrap(c)).collect(Collectors.toList());
Status totalStatus = responses.stream().anyMatch(r -> r.getStatus().equals(Status.DOWN)) ? Status.DOWN : Status.UP;
this.response = new HealthResponse(totalStatus, responses);
log.trace("health task executed with total status {}",totalStatus);
}
public HealthResponse getResponse() {
return response;
}
private HealthCheckResponse wrap(HealthCheck check){
try {
return check.check();
}catch (Throwable t) {
return HealthCheckResponse.builder(check.getName()).down().error(t.getMessage()).build();
}
}
}

@ -0,0 +1,31 @@
package org.gcube.smartgears.health;
import java.util.Set;
import org.gcube.common.health.api.HealthCheck;
import org.gcube.common.health.api.ReadinessChecker;
import org.gcube.common.health.api.response.HealthCheckResponse;
import org.gcube.smartgears.provider.ProviderFactory;
@ReadinessChecker
public class KeyCloakHealthCheck implements HealthCheck{
private static final String CHECK_NAME = "authorization-check" ;
public String getName(){
return CHECK_NAME;
}
@Override
public HealthCheckResponse check() {
try {
Set<String> contexts = ProviderFactory.provider().containerContext().authorizationProvider().getContexts();
if (contexts.isEmpty())
return HealthCheckResponse.builder(CHECK_NAME).down().error("no contexts are defined for the client id provided").build();
return HealthCheckResponse.builder(CHECK_NAME).up().info(String.format("running contexts are %s", contexts)).build();
}catch (Exception e) {
return HealthCheckResponse.builder(CHECK_NAME).down().error(e.getMessage()).build();
}
}
}

@ -12,36 +12,27 @@ import static org.gcube.smartgears.provider.ProviderFactory.provider;
import java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletRegistration;
import jakarta.servlet.FilterRegistration;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
import jakarta.servlet.ServletRegistration;
import org.gcube.common.authorization.client.proxy.AuthorizationProxy;
import org.gcube.common.authorization.library.provider.SecurityTokenProvider;
import org.gcube.common.events.Observes;
import org.gcube.smartgears.Constants;
import org.gcube.smartgears.configuration.application.ApplicationExtensions;
import org.gcube.smartgears.configuration.application.ApplicationHandlers;
import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.extensions.ApplicationExtension;
import org.gcube.smartgears.extensions.RequestExceptionBarrier;
import org.gcube.smartgears.handlers.ProfileEvents;
import org.gcube.smartgears.handlers.application.ApplicationLifecycleEvent;
import org.gcube.smartgears.handlers.application.ApplicationLifecycleHandler;
import org.gcube.smartgears.handlers.application.ApplicationPipeline;
import org.gcube.smartgears.handlers.application.RequestHandler;
import org.gcube.smartgears.lifecycle.application.ApplicationLifecycle;
import org.gcube.smartgears.lifecycle.container.ContainerLifecycle;
import org.gcube.smartgears.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -59,6 +50,8 @@ public class ApplicationManager {
private ApplicationContext context;
private List<ApplicationExtension> extensions;
/**
* Starts application management.
*
@ -71,21 +64,12 @@ public class ApplicationManager {
try {
context = provider().contextFor(container, application);
for (Entry<String,? extends ServletRegistration> servlet : application.getServletRegistrations().entrySet())
log.trace("servlet {} : {} {} ", application.getServletContextName(),servlet.getKey(), servlet.getValue().getMappings());
context.configuration().validate();
/* if (context.configuration().secure() &&
container.configuration().securePort()==null)
throw new IllegalStateException(
String.format("Application %s cannot be managed because is declared as secure without a secure connector port declared in the container", context.application().getContextPath()));
*/
context.configuration().startTokens(generateTokensForApplication(container));
saveApplicationState();
// make context available to application in case it is gcube-aware
@ -95,13 +79,8 @@ public class ApplicationManager {
registerObservers();
ApplicationHandlers handlers = provider().handlersFor(context);
handlers.validate();
ApplicationExtensions extensions = provider().extensionsFor(context);
extensions.validate();
List<ApplicationLifecycleHandler> lifecycleHandlers = handlers.lifecycleHandlers();
List<RequestHandler> requestHandlers = handlers.requestHandlers();
@ -110,26 +89,28 @@ public class ApplicationManager {
log.trace("managing {} requests with {}", context.name(), requestHandlers);
log.trace("extending {} API with {}", context.name(), extensions);
extensions = provider().extensionsFor(context);
// order is important here: first add APIs
register(extensions);
registerExtension(extensions);
// then intercept them all
register(requestHandlers);
registerHandlersAsFilter(requestHandlers);
// start lifecycle management
start(lifecycleHandlers);
//adding the context name to the configuration
context.configuration().context(application.getContextPath());
// we're in business
context.lifecycle().moveTo(active);
return context;
} catch (RuntimeException e) {
if (context != null) {
log.error("error starting application {}",context.name(), e);
@ -142,31 +123,8 @@ public class ApplicationManager {
}
private Set<String> generateTokensForApplication(ContainerContext container){
log.info("generating token for app {}",context.configuration().name());
Set<String> tokens = new HashSet<String>();
AuthorizationProxy authProxy = provider().authorizationProxy();
for (String containerToken :container.configuration().startTokens())
tokens.add(generateApplicationToken(containerToken, authProxy));
return tokens;
}
private String generateApplicationToken(String containerToken, AuthorizationProxy authProxy){
SecurityTokenProvider.instance.set(containerToken);
try {
log.info("generating token for app {} with container token {} ",context.configuration().name(), containerToken);
return authProxy.generateServiceToken(Utils.getServiceInfo(context));
} catch (Exception e) {
throw new RuntimeException("error contacting authorization service",e);
} finally{
SecurityTokenProvider.instance.reset();
}
}
private void saveApplicationState() {
File file = context.configuration().persistence().file(profile_file_path);
File file = context.persistence().file(profile_file_path);
try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file))){
oos.writeObject(context.id());
}catch (Exception e) {
@ -192,7 +150,10 @@ public class ApplicationManager {
context.lifecycle().tryMoveTo(stopped);
context.events().fire(context, ApplicationLifecycle.stop);
if (extensions != null)
unregister(extensions);
stopLifecycleHandlers();
log.info("stopping application events for {}", context.name());
@ -206,49 +167,36 @@ public class ApplicationManager {
}
private void register(List<RequestHandler> rqHandlers) {
private void registerHandlersAsFilter(List<RequestHandler> rqHandlers) {
ServletContext app = context.application();
// attach filters based on request pipeline to each servlet
Collection<? extends ServletRegistration> servlets = app.getServletRegistrations().values();
for (ServletRegistration servlet : servlets) {
String name = servlet.getName();
if (name.equals("default") || name.equals("jsp")) // skip page-resolving servlets
continue;
for (String mapping : servlet.getMappings()) {
RequestManager requestFilter = new RequestManager(context, name, rqHandlers);
String appName = app.getContextPath().replace("/", "");
RequestManager requestFilter = new RequestManager(context, appName, rqHandlers);
FilterRegistration.Dynamic filter = app.addFilter(name + "-filter-"+mapping.replaceAll("/", ""), requestFilter);
FilterRegistration.Dynamic filter = app.addFilter(appName + "-filter", requestFilter);
log.trace("filter {} for requestfilter {} in contextPath {} is null ?? {} ",name ,requestFilter, mapping, (filter==null));
filter.addMappingForUrlPatterns(null, false, "/*");
filter.addMappingForUrlPatterns(null, false, mapping);
}
}
}
private void register(ApplicationExtensions extensions) {
private void registerExtension(List<ApplicationExtension> extensions) {
ServletContext application = context.application();
for (ApplicationExtension extension : extensions.extensions())
for (ApplicationExtension extension : extensions)
try {
extension.init(context);
if (context.configuration().includes().isEmpty()) {
//register excludes for extension in case of includes they are excluded by default
context.configuration().excludes().addAll(extension.excludes());
}
String mapping = extension.mapping();
application.addServlet(context.configuration().name() + "-" + extension.name(), extension)
@ -269,6 +217,14 @@ public class ApplicationManager {
}
}
private void unregister(List<ApplicationExtension> extensions) {
for (ApplicationExtension extension : extensions)
extension.stop();
}
private void start(List<ApplicationLifecycleHandler> handlers) {
@ -307,26 +263,6 @@ public class ApplicationManager {
log.warn("cannot stop {} after container has stopped", context.name());
}
@Observes(value = ContextEvents.ADD_TOKEN_TO_APPLICATION, kind = critical)
void onAddToken(String containerToken) {
log.trace("event add received with token {} ",containerToken);
String appToken = generateApplicationToken(containerToken, provider().authorizationProxy());
context.configuration().startTokens().add(appToken);
log.trace("app token created : {} ", appToken);
context.events().fire(appToken, ProfileEvents.addToContext);
context.events().fire(appToken, Constants.token_registered);
}
@Observes(value = ContextEvents.REMOVE_TOKEN_FROM_APPLICATION, kind = critical)
void onRemoveToken(String containerToken) {
log.trace("event remove received with token {} ",containerToken);
String appToken = generateApplicationToken(containerToken, provider().authorizationProxy());
context.configuration().startTokens().remove(appToken);
log.trace("app token removed : {} ", appToken);
context.events().fire(appToken, ProfileEvents.removeFromContext);
context.events().fire(appToken, Constants.token_removed);
}
};
context.container().events().subscribe(observer);
@ -337,7 +273,8 @@ public class ApplicationManager {
@Override
public void contextInitialized(ServletContextEvent sce) {
log.info("initilizing context {} ",context.name());
context.events().fire(context.application().getContextPath(), ApplicationLifecycle.activation);
context.events().fire(context, ApplicationLifecycle.activation);
log.info("webApp {} initialized ",context.name());
}

@ -10,19 +10,12 @@ import static org.gcube.smartgears.provider.ProviderFactory.provider;
import java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.gcube.common.authorization.client.exceptions.ObjectNotFound;
import org.gcube.common.authorization.client.proxy.AuthorizationProxy;
import org.gcube.common.authorization.library.AuthorizationEntry;
import org.gcube.common.authorization.library.provider.ClientInfo;
import org.gcube.common.authorization.library.provider.ContainerInfo;
import org.gcube.common.events.Observes;
import org.gcube.common.events.Observes.Kind;
import org.gcube.smartgears.configuration.container.ContainerHandlers;
import org.gcube.smartgears.configuration.Mode;
import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.handlers.ProfileEvents;
@ -47,8 +40,6 @@ public class ContainerManager {
public static ContainerManager instance = new ContainerManager();
private AuthorizationProxy authProvider = provider().authorizationProxy();
private ContainerContext context;
private ContainerPipeline pipeline;
@ -64,33 +55,27 @@ public class ContainerManager {
try {
// TODO Ask if is not enough that is already done in
// Bootstrap.initialiseContainer() function;
context.configuration().validate();
validateContainer(context);
if (context.configuration().mode()!=Mode.offline)
validateContainer(context);
saveContainerState();
ContainerHandlers handlers = provider().containerHandlers();
List<ContainerHandler> handlers = provider().containerHandlers();
log.trace("managing container lifecycle with {}", handlers.get());
log.trace("managing container lifecycle with {}", handlers);
startHandlers(handlers.get());
startHandlers(handlers);
context.lifecycle().moveTo(active);
log.trace("loading keys for starting token ...");
//loadKeyForToken(context.configuration().startTokens());
log.trace("keys loaded for starting token ...");
return context;
}
catch(RuntimeException e) {
} catch (RuntimeException e) {
log.error("cannot manage container (see cause)",e);
log.error("cannot manage container (see cause)", e);
if (context!=null)
if (context != null)
context.lifecycle().moveTo(failed);
throw e;
@ -98,13 +83,11 @@ public class ContainerManager {
}
private void saveContainerState() {
File file = context.configuration().persistence().file(container_profile_file_path);
try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file))){
File file = context.persistenceWriter().file(container_profile_file_path);
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file))) {
oos.writeObject(context.id());
oos.writeObject(context.configuration().startTokens());
}catch (Exception e) {
} catch (Exception e) {
log.error("error serializing cointainer state");
throw new RuntimeException(e);
}
@ -112,98 +95,33 @@ public class ContainerManager {
}
private void validateContainer(ContainerContext context) {
List<String> tokensToRemove = new ArrayList<String>();
Set<String> foundContexts= new HashSet<String>();
for (String token : context.configuration().startTokens()){
String tokenContext = resolveTokenForAdd(foundContexts, token);
if (tokenContext!=null){
log.info("the container will be started in context {}",tokenContext);
foundContexts.add(tokenContext);
} else
tokensToRemove.add(token);
}
if (foundContexts.isEmpty()){
log.error("no valid starting token are specified, moving the container to failed");
throw new RuntimeException("no valid starting token are specified");
}
context.configuration().startTokens().removeAll(tokensToRemove);
context.configuration().allowedContexts(foundContexts);
}
// List<String> tokensToRemove = new ArrayList<String>();
context.configuration().validate();
Set<String> foundContexts;
private String resolveTokenForAdd(Set<String> alreadyAddedContext, String token){
try {
AuthorizationEntry entry = authProvider.get(token);
ClientInfo info = entry.getClientInfo();
log.info("resolved authorization entry for container {}",entry);
if (alreadyAddedContext.contains(entry.getContext())){
log.warn("the token {} cannot be used, another token with the same context {} found ", entry.getContext());
} else if(!entry.getContext().startsWith("/"+context.configuration().infrastructure())){
log.warn("the token {} cannot be used, is not in the infrastructure {} of the container ", token,context.configuration().infrastructure());
}else if (!(info instanceof ContainerInfo)){
log.warn("the token {} cannot be used, is not for a container token ", token);
} else if (!((ContainerInfo)info).getHost().equals(context.configuration().hostname())
|| context.configuration().port()!=((ContainerInfo)info).getPort()){
log.warn("the token {} cannot be used, the client id {} resolved with the token is not the same of the one specified in this container ", token, info.getId());
} else
return entry.getContext();
}catch(ObjectNotFound onf){
log.error("token {} not valid", token);
foundContexts = context.authorizationProvider().getContexts();
} catch (Exception e) {
log.error("error contacting authorization for token {}",token,e);
log.error("error authorizing container", e);
throw new RuntimeException("error authorizing container, moving the container to failed", e);
}
return null;
}
public void manage(ApplicationContext app) {
if (foundContexts.isEmpty()) {
log.error("no valid contexts found, moving the container to failed");
throw new RuntimeException("no valid contexts found, moving the container to failed");
}
app.events().subscribe(this);
}
public void manage(ApplicationContext app) {
app.events().subscribe(this);
}
@Observes(value={ApplicationLifecycle.failure,ApplicationLifecycle.stop},kind=Kind.critical)
@Observes(value = { ApplicationLifecycle.failure, ApplicationLifecycle.stop }, kind = Kind.critical)
void monitorApplication(ApplicationLifecycle lifecycle) {
context.lifecycle().tryMoveTo(ContainerState.partActive);
}
@Observes(value=ContextEvents.ADD_TOKEN_TO_CONTAINER,kind=Kind.critical)
void addToken(String token) {
log.trace("adding token {} to container", token);
String newContext;
if ((newContext = resolveTokenForAdd(context.configuration().allowedContexts(), token))!=null) {
context.configuration().startTokens().add(token);
context.configuration().allowedContexts().add(newContext);
saveContainerState();
//loadKeyForToken(Arrays.asList(token));
context.events().fire(token, ContextEvents.ADD_TOKEN_TO_APPLICATION);
context.events().fire(token, ProfileEvents.addToContext);
log.trace("token added and event fired");
} else log.warn("trying to add an invalid token");
}
@Observes(value=ContextEvents.REMOVE_TOKEN_FROM_CONTAINER,kind=Kind.critical)
void removeToken(String token) {
log.trace("removing token {} from container", token);
AuthorizationEntry entry;
try {
entry = authProvider.get(token);
} catch (Exception e) {
log.error("error resolving token to remove");
return;
}
if (context.configuration().startTokens().contains(token)) {
context.configuration().startTokens().remove(token);
context.configuration().allowedContexts().remove(entry.getContext());
saveContainerState();
context.events().fire(token, ContextEvents.REMOVE_TOKEN_FROM_APPLICATION);
context.events().fire(token, ProfileEvents.removeFromContext);
log.trace("token removed and event fired");
} else log.warn("cannot remove token, it is not present in the container");
}
/**
* Stops container management on remote request.
*
@ -213,18 +131,20 @@ public class ContainerManager {
stop(false);
}
/**
* Stops container management on remote request or container shutdown.
*
*/
public void stop(boolean shutdown) {
//two cases: stop-on-shutdown and stop-on-request, some listeners will be selective about this,
// two cases: stop-on-shutdown and stop-on-request, some listeners will be
// selective about this,
//shutdown is triggered by probe app, which is notified among other apps
//if other app have been already notified, the container may already be part-active.
//apps still to notify will listen only on stop, hence won't react to this but will go down when their turn arrives.
// shutdown is triggered by probe app, which is notified among other apps
// if other app have been already notified, the container may already be
// part-active.
// apps still to notify will listen only on stop, hence won't react to this but
// will go down when their turn arrives.
if (context == null)
return;
@ -233,26 +153,23 @@ public class ContainerManager {
try {
context.lifecycle().tryMoveTo(shutdown?down:stopped);
context.lifecycle().tryMoveTo(shutdown ? down : stopped);
stopHandlers();
//no further reactions
// no further reactions
log.info("stopping container events");
context.events().stop();
Utils.scheduledServicePool.shutdownNow();
}
catch (RuntimeException e) {
} catch (RuntimeException e) {
log.warn("cannot stop container management (see cause)", e);
}
}
//helpers
// helpers
private void startHandlers(List<ContainerHandler> handlers) {
@ -269,7 +186,6 @@ public class ContainerManager {
}
}
private void stopHandlers() {
if (pipeline == null)
@ -282,46 +198,32 @@ public class ContainerManager {
returnPipeline.forward(new ContainerLifecycleEvent.Stop(context));
}
/*
private void loadKeyForToken(List<String> tokens) {
String initialToken = SecurityTokenProvider.instance.get();
//TODO: change this
String filePath = "/tmp/keys";
File PathDirs = new File(filePath+"/");
PathDirs.mkdirs();
try{
for (String token : tokens) {
try{
SecurityTokenProvider.instance.set(token);
File key = authProvider.getSymmKey(filePath);
log.trace("loading key {} file name ",key.getAbsolutePath());
log.trace("loaded key {} file name ",key.getAbsolutePath());
}catch(Exception e){
log.warn("error loading key for token {}", token, e);
}
}
loadFileIntoClasspath(PathDirs);
}finally{
SecurityTokenProvider.instance.set(initialToken);
}
}
private void loadFileIntoClasspath(File file){
try {
URL url = file.toURI().toURL();
ClassLoader currentClassloader = Thread.currentThread().getContextClassLoader().getParent()==null?
Thread.currentThread().getContextClassLoader() : Thread.currentThread().getContextClassLoader().getParent();
URLClassLoader classLoader = (URLClassLoader)currentClassloader;
Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
method.setAccessible(true);
method.invoke(classLoader, url);
} catch (Exception ex) {
log.error("error loading file into classpath",ex);
}
}
*/
/*
* private void loadKeyForToken(List<String> tokens) { String initialToken =
* SecurityTokenProvider.instance.get();
*
* //TODO: change this String filePath = "/tmp/keys"; File PathDirs = new
* File(filePath+"/"); PathDirs.mkdirs(); try{ for (String token : tokens) {
* try{ SecurityTokenProvider.instance.set(token); File key =
* authProvider.getSymmKey(filePath);
* log.trace("loading key {} file name ",key.getAbsolutePath());
* log.trace("loaded key {} file name ",key.getAbsolutePath()); }catch(Exception
* e){ log.warn("error loading key for token {}", token, e); } }
* loadFileIntoClasspath(PathDirs); }finally{
* SecurityTokenProvider.instance.set(initialToken); } }
*
*
* private void loadFileIntoClasspath(File file){ try { URL url =
* file.toURI().toURL();
*
* ClassLoader currentClassloader =
* Thread.currentThread().getContextClassLoader().getParent()==null?
* Thread.currentThread().getContextClassLoader() :
* Thread.currentThread().getContextClassLoader().getParent();
*
* URLClassLoader classLoader = (URLClassLoader)currentClassloader; Method
* method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
* method.setAccessible(true); method.invoke(classLoader, url); } catch
* (Exception ex) { log.error("error loading file into classpath",ex); } }
*/
}

@ -2,12 +2,9 @@ package org.gcube.smartgears.managers;
public class ContextEvents {
public static final String ADD_TOKEN_TO_CONTAINER ="AddTokenToContainer";
public static final String ADD_CONTEXT_TO_CONTAINER ="AddContextToContainer";
public static final String ADD_TOKEN_TO_APPLICATION ="AddTokenToApplication";
public static final String REMOVE_CONTEXT_FROM_CONTAINER ="RemoveContextFromContainer";
public static final String REMOVE_TOKEN_FROM_CONTAINER ="RemoveTokenFromContainer";
public static final String REMOVE_TOKEN_FROM_APPLICATION ="RemoveTokenFromApplication";
}

@ -8,19 +8,21 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.gcube.common.authorization.library.exception.AuthorizationException;
import org.gcube.smartgears.configuration.application.Exclude;
import org.gcube.smartgears.configuration.application.Include;
import org.gcube.smartgears.Constants;
import org.gcube.smartgears.configuration.application.GCubeExclude;
import org.gcube.smartgears.configuration.application.GCubeInclude;
import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.context.application.DefaultApplicationContext;
import org.gcube.smartgears.handlers.application.ApplicationPipeline;
@ -65,7 +67,7 @@ public class RequestManager implements Filter {
HttpServletRequest httprequest = (HttpServletRequest) request;
HttpServletResponse httpresponse = (HttpServletResponse) response;
List<RequestHandler> filterHandlers = getPipelineHandlers(httprequest, handlers);
if (filterHandlers.isEmpty()){
@ -98,7 +100,7 @@ public class RequestManager implements Filter {
// dispatch to other filters for this servlet
chain.doFilter(request, response);
}catch(ServletException t){
log.error("error in doFilter",t.getRootCause());
log.error("error in doFilter",t);
handleError(httprequest,httpresponse,t.getRootCause());
}
@ -123,40 +125,49 @@ public class RequestManager implements Filter {
String query = request.getQueryString();
log.debug("servletPath is {} and pathInfo is {}",request.getServletPath(), request.getPathInfo());
log.trace("servletPath is {} and pathInfo is {}",request.getServletPath(), request.getPathInfo());
if ("wsdl".equals(query) || "wsdl=1".equals(query))
//excludes also mandatory filter for extensions
if ("wsdl".equals(query) || "wsdl=1".equals(query) ||
request.getServletPath().equals(Constants.root_mapping))
return Collections.emptyList();
String path = request.getServletPath()==null?"":request.getServletPath();
path += request.getPathInfo() ==null?"":request.getPathInfo();
log.debug("check wich handler should be excluded {}", path);
log.trace("check which handler should be excluded {}", path);
if (!context.configuration().excludes().isEmpty()) {
for (Exclude exclude : context.configuration().excludes()){
log.debug("excludes are not empty");
for (GCubeExclude exclude : context.configuration().excludes()){
String excludePath= exclude.getPath();
log.trace("exclude is {}",exclude);
if (
(WILDCARD).equals(excludePath) ||
log.debug("exclude is {}",exclude);
if ((WILDCARD).equals(excludePath) ||
(excludePath.endsWith(WILDCARD) && path!=null && path.startsWith(excludePath.substring(0,excludePath.length()-2))) ||
excludePath.equals(path) || (path.endsWith("/") && excludePath.equals(path.substring(0, path.length()-1)))
){
//ALL handler are filtered
if (exclude.getHandlers().isEmpty()) return Collections.emptyList();
//ALL handler are filtered except for unfilterable
if (exclude.getHandlers().isEmpty()) {
List<RequestHandler> unfilterables = handlersToFilter.stream()
.filter(RequestHandler::isUnfiltrable).collect(Collectors.toList());
log.trace("exclude handler is empty so unfilterable handlers are {}",unfilterables);
return handlersToFilter.stream()
.filter(RequestHandler::isUnfiltrable).collect(Collectors.toList());
}
List<RequestHandler> filteredHandlers = new ArrayList<>();
for (RequestHandler rh : handlersToFilter){
if (!exclude.getHandlers().contains(rh.getName()))
for (RequestHandler rh : handlersToFilter)
if (rh.isUnfiltrable() || !exclude.getHandlers().contains(rh.getName()))
filteredHandlers.add(rh);
}
return filteredHandlers;
}
}
} else if (!context.configuration().includes().isEmpty()) {
for (Include include : context.configuration().includes()){
for (GCubeInclude include : context.configuration().includes()){
String includePath= include.getPath();
log.trace("include is {}",include);
if (
@ -168,16 +179,16 @@ public class RequestManager implements Filter {
if (include.getHandlers().isEmpty()) return handlersToFilter;
List<RequestHandler> filteredHandlers = new ArrayList<>();
for (RequestHandler rh : handlersToFilter){
if (include.getHandlers().contains(rh.getName()))
for (RequestHandler rh : handlersToFilter)
if (rh.isUnfiltrable() || include.getHandlers().contains(rh.getName()))
filteredHandlers.add(rh);
}
return filteredHandlers;
}
}
return new ArrayList<>();
}
log.trace("returning original handlers");
return handlersToFilter;
}
@ -213,37 +224,6 @@ public class RequestManager implements Filter {
}
// helpers
/*
private boolean shouldExcludeRequest(HttpServletRequest request) {
String query = request.getQueryString();
log.debug("servletPath is {} and pathInfo is {}",request.getServletPath(), request.getPathInfo());
if ("wsdl".equals(query) || "wsdl=1".equals(query))
return true;
String path = request.getServletPath()==null?"":request.getServletPath();
path += request.getPathInfo() ==null?"":request.getPathInfo();
log.debug("check if should exclude call with path {}", path);
for (Exclude exclude : context.configuration().excludes()){
if (!exclude.getHandlers().isEmpty()) continue;
String excludePath= exclude.getPath();
log.trace("exclude is {}",exclude);
if (
(EXCLUDE_ALL).equals(exclude) ||
(excludePath.endsWith(EXCLUDE_ALL) && path!=null && path.startsWith(excludePath.substring(0,excludePath.length()-2))) ||
excludePath.equals(path) || (path.endsWith("/") && excludePath.equals(path.substring(0, path.length()-1)))
)
return true;
}
return false;
}*/
private void handleError(HttpServletRequest request, HttpServletResponse response,Throwable t) throws IOException {
@ -260,7 +240,8 @@ public class RequestManager implements Filter {
RequestException.class.cast(t).error():
application_error;
response.resetBuffer();
if (!response.isCommitted())
response.resetBuffer();
if (error == request_not_authorized_error){
response.setHeader("WWW-Authenticate", "Basic realm=\"Smartgears\"");
log.info("setting WWW-Authenticate to response header");

@ -1,96 +0,0 @@
package org.gcube.smartgears.persistence;
import static org.gcube.smartgears.utils.Utils.*;
import java.io.File;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import org.gcube.common.validator.annotations.NotNull;
@XmlRootElement(name="persistence")
public class DefaultPersistence implements Persistence {
@XmlAttribute(name="location") @NotNull
private String location;
public DefaultPersistence() {}
public DefaultPersistence(String location) {
notNull("persistence location",location);
this.location=location;
validate();
}
@Override
public String location() {
return location;
}
@Override
public File writefile(String path) {
notNull("relative path", path);
return fileAt(new File(location, path).getAbsolutePath()).toWrite();
}
@Override
public File file(String path) {
notNull("relative path", path);
return fileAt(new File(location, path).getAbsolutePath()).toRead();
}
//called after JAXB unmarshalling to purge unavailable handlers
void afterUnmarshal(Unmarshaller u, Object parent) {
validate();
}
public void validate() {
File locationDir = new File(location);
if (!(locationDir.exists() && locationDir.isDirectory() && locationDir.canRead() && locationDir.canWrite()))
throw new IllegalStateException("invalid node configuration: home "+location+" does not exist or is not a directory or cannot be accessed in read/write mode");
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((location == null) ? 0 : location.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DefaultPersistence other = (DefaultPersistence) obj;
if (location == null) {
if (other.location != null)
return false;
} else if (!location.equals(other.location))
return false;
return true;
}
@Override
public String toString() {
return "local persistence in "+location;
}
}

@ -0,0 +1,68 @@
package org.gcube.smartgears.persistence;
import static org.gcube.smartgears.utils.Utils.fileAt;
import static org.gcube.smartgears.utils.Utils.notNull;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
import org.gcube.smartgears.configuration.ComponentConfiguration;
import org.gcube.smartgears.configuration.ConfiguredWith;
@ConfiguredWith(LocalWriterConfiguration.class)
public class LocalWriter implements PersistenceWriter {
private String location;
@Override
public void configure(ComponentConfiguration configuration) {
this.location = ((LocalWriterConfiguration) configuration).getLocation();
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
@Override
public File writefile(String path) {
notNull("relative path", path);
return fileAt(new File(location, path).getAbsolutePath()).toWrite();
}
@Override
public File file(String path) {
notNull("relative path", path);
return fileAt(new File(location, path).getAbsolutePath()).toRead();
}
public void validate() {
File locationDir = new File(location);
if (!(locationDir.exists() && locationDir.isDirectory() && locationDir.canRead() && locationDir.canWrite()))
throw new IllegalStateException("invalid node configuration: home "+location+" does not exist or is not a directory or cannot be accessed in read/write mode");
}
@Override
public long getFreeSpace() {
try {
return Files.getFileStore(Paths.get(location)).getUsableSpace();
}catch (Exception e) {
return -1;
}
}
}

@ -0,0 +1,24 @@
package org.gcube.smartgears.persistence;
import org.gcube.common.validator.annotations.NotEmpty;
import org.gcube.common.validator.annotations.NotNull;
import org.gcube.smartgears.configuration.ComponentConfiguration;
public class LocalWriterConfiguration implements ComponentConfiguration{
@NotEmpty @NotNull
private String location;
protected LocalWriterConfiguration() {}
public LocalWriterConfiguration(String location) {
super();
this.location = location;
}
public String getLocation() {
return location;
}
}

@ -1,13 +0,0 @@
package org.gcube.smartgears.persistence;
import java.io.File;
public interface Persistence {
String location();
File file(String path);
File writefile(String path);
}

@ -0,0 +1,17 @@
package org.gcube.smartgears.persistence;
import java.io.File;
import org.gcube.smartgears.configuration.Configurable;
public interface PersistenceWriter extends Configurable{
File file(String path);
File writefile(String path);
long getFreeSpace();
String getLocation();
}

@ -1,7 +1,7 @@
package org.gcube.smartgears.probe;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import jakarta.servlet.ServletContextListener;
import jakarta.servlet.annotation.WebListener;
import org.gcube.smartgears.managers.ContainerManager;
import org.slf4j.Logger;
@ -12,12 +12,12 @@ public class ContainerListener implements ServletContextListener {
public static Logger log = LoggerFactory.getLogger(ContainerListener.class);
public void contextDestroyed(javax.servlet.ServletContextEvent sce) {
public void contextDestroyed(jakarta.servlet.ServletContextEvent sce) {
log.trace("shutting down container from probe");
ContainerManager.instance.stop(true);
};
public void contextInitialized(javax.servlet.ServletContextEvent sce) {
public void contextInitialized(jakarta.servlet.ServletContextEvent sce) {
log.trace("starting up probe...");
};
}

@ -1,66 +1,52 @@
package org.gcube.smartgears.provider;
import static org.gcube.common.authorization.client.Constants.authorizationService;
import static org.gcube.smartgears.Constants.configuration_file_path;
import static org.gcube.smartgears.Constants.container_configuraton_file_path;
import static org.gcube.smartgears.Constants.container_handlers_file_name;
import static org.gcube.smartgears.Constants.application_handlers_file_name;
import static org.gcube.smartgears.Constants.container_handlers_file_path;
import static org.gcube.smartgears.Constants.container_profile_file_path;
import static org.gcube.smartgears.Constants.default_extensions_file_path;
import static org.gcube.smartgears.Constants.default_handlers_file_path;
import static org.gcube.smartgears.Constants.extensions_file_path;
import static org.gcube.smartgears.Constants.ghn_home_env;
import static org.gcube.smartgears.Constants.ghn_home_property;
import static org.gcube.smartgears.Constants.handlers_file_path;
import static org.gcube.smartgears.Constants.library_configuration_file_path;
import static org.gcube.smartgears.Constants.profile_file_path;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.Set;
import java.util.stream.Collectors;
import jakarta.servlet.ServletContext;
import javax.servlet.ServletContext;
import org.gcube.common.authorization.client.proxy.AuthorizationProxy;
import org.gcube.common.events.Hub;
import org.gcube.common.events.impl.DefaultHub;
import org.gcube.common.scan.ClasspathScanner;
import org.gcube.common.scan.ClasspathScannerFactory;
import org.gcube.common.scan.matchers.NameMatcher;
import org.gcube.common.scan.resources.ClasspathResource;
import org.gcube.informationsystem.publisher.RegistryPublisherFactory;
import org.gcube.informationsystem.publisher.ScopedPublisher;
import org.gcube.smartgears.configuration.Mode;
import org.gcube.common.security.credentials.Credentials;
import org.gcube.smartgears.configuration.SmartgearsConfiguration;
import org.gcube.smartgears.configuration.application.ApplicationConfiguration;
import org.gcube.smartgears.configuration.application.ApplicationConfigurationBinder;
import org.gcube.smartgears.configuration.application.ApplicationExtensions;
import org.gcube.smartgears.configuration.application.ApplicationHandlers;
import org.gcube.smartgears.configuration.application.BridgedApplicationConfiguration;
import org.gcube.smartgears.configuration.container.ContainerConfiguration;
import org.gcube.smartgears.configuration.container.ContainerConfigurationBinder;
import org.gcube.smartgears.configuration.container.ContainerHandlers;
import org.gcube.smartgears.configuration.library.SmartGearsConfiguration;
import org.gcube.smartgears.configuration.library.SmartGearsConfigurationBinder;
import org.gcube.smartgears.context.Properties;
import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.context.application.DefaultApplicationContext;
import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.context.container.DefaultContainerContext;
import org.gcube.smartgears.extensions.ApplicationExtension;
import org.gcube.smartgears.extensions.resource.RemoteResource;
import org.gcube.smartgears.handlers.container.ContainerHandler;
import org.gcube.smartgears.lifecycle.application.ApplicationLifecycle;
import org.gcube.smartgears.lifecycle.container.ContainerLifecycle;
import org.gcube.smartgears.persistence.DefaultPersistence;
import org.gcube.smartgears.publishing.Publisher;
import org.gcube.smartgears.publishing.SmartgearsProfilePublisher;
import org.gcube.smartgears.security.AuthorizationProvider;
import org.gcube.smartgears.security.AuthorizationProviderFactory;
import org.gcube.smartgears.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.github.classgraph.ClassGraph;
import io.github.classgraph.ClassInfo;
import io.github.classgraph.ClassInfoList;
import io.github.classgraph.ScanResult;
/**
* Default implementation of the {@link Provider} interface.
*
@ -72,107 +58,56 @@ public class DefaultProvider implements Provider {
private static Logger log = LoggerFactory.getLogger(Provider.class);
private ContainerContext containerContext;
//TODO: do the same with applicationContext (with a map)
// TODO: do the same with applicationContext (with a map)
private File configFile = null;
protected DefaultProvider(File configFile) {
this.configFile = configFile;
}
List<Publisher> publishers;
protected DefaultProvider(){};
protected DefaultProvider() {
};
@SuppressWarnings("unchecked")
@Override
public ContainerContext containerContext() {
if(containerContext==null){
if (containerContext == null) {
ContainerConfiguration configuration = containerConfiguration();
if (configuration.persistence()==null) {
String location = Utils.home()+"/state";
File dir = new File(location);
if (!dir.exists())
dir.mkdirs();
configuration.persistence(new DefaultPersistence(location));
log.trace("setting persistence location for container @ {}",dir.getAbsolutePath());
}
Hub hub = new DefaultHub();
ContainerLifecycle lifecycle = new ContainerLifecycle(hub);
AuthorizationProviderFactory<?> authfactory = configuration.authorizationConfiguration()
.getAuthProviderFactory();
Credentials credentials = configuration.authorizationConfiguration().getCredentials();
File file = configuration.persistence().file(container_profile_file_path);
String id = null;
List<String> tokens = null;
if (file.exists()){
log.info("loading persisted state for container");
try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))){
id = (String)ois.readObject();
tokens = (List<String>) ois.readObject();
}catch(Exception e){
log.error("error loading persisted state, creating new uuid",e);
}
}
if (id==null){
id = UUID.randomUUID().toString();
log.info("container id created is {}",id);
}
if (tokens!=null)
configuration.startTokens(tokens);
AuthorizationProvider authProvider = authfactory.connect(credentials);
containerContext = new DefaultContainerContext(id, configuration, hub, lifecycle, new Properties());
containerContext = new DefaultContainerContext(configuration, hub, lifecycle, authProvider,
new Properties());
}
return containerContext;
}
@Override
public ContainerHandlers containerHandlers() {
public List<ContainerHandler> containerHandlers() {
try {
InputStream config = getClass().getResourceAsStream(container_handlers_file_path);
if (config == null)
throw new IllegalStateException("invalid distribution: cannot find " + container_handlers_file_path);
ContainerConfigurationBinder binder = new ContainerConfigurationBinder();
ContainerHandlers defaultHandlers = binder.bindHandlers(config);
ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
if (currentClassLoader.getParent()!=null && !currentClassLoader.getParent().equals(ClassLoader.getSystemClassLoader())){
if (currentClassLoader.getParent() != null
&& !currentClassLoader.getParent().equals(ClassLoader.getSystemClassLoader())) {
log.trace("probably i'm in a webapp classloader");
currentClassLoader = currentClassLoader.getParent();
}
try{
if (currentClassLoader instanceof URLClassLoader){
URL[] urls = ((URLClassLoader) currentClassLoader).getURLs() ;
if (urls!=null && urls.length>0){
ClasspathScanner scanner = ClasspathScannerFactory.scanner(new HashSet<URL>(Arrays.asList(urls)));
Collection<ClasspathResource> resources = scanner.scan(new NameMatcher(container_handlers_file_name));
for (URL url: urls)
log.trace("URL: "+ url.toString());
if (resources==null || resources.isEmpty())
log.info("no custom container handlers found in the classpath");
for (ClasspathResource res : resources){
try{
ContainerHandlers customHandlers= binder.bindHandlers(res.stream());
defaultHandlers.mergeWith(customHandlers);
log.trace("container hadlers found in {}",res.name());
}catch(Exception e){
log.warn("error loading not default container handlers {}",res.name(),e);
}
}
}
} else log.info("this classloader is not instance of {} : ",URLClassLoader.class.getName(), currentClassLoader.getClass().getName());
}catch(Exception e){
log.warn("cannot load custom handlers for container from the root classloader",e);
}
List<ContainerHandler> defaultHandlers = binder.bindHandlers(currentClassLoader);
return defaultHandlers;
@ -186,63 +121,20 @@ public class DefaultProvider implements Provider {
@Override
public ApplicationContext contextFor(ContainerContext context, ServletContext application) {
ApplicationConfiguration configuration = null;
ApplicationConfiguration embedded = configurationFor(application);
ApplicationConfiguration external = context.configuration().app(application.getContextPath());
//shouldn't happen: management shouldn't have started at all
if (embedded==null && external==null)
throw new AssertionError("application @ "+application.getContextPath()+" is not distributed with "
+ configuration_file_path+" and there is no external configuration for it in "+container_configuraton_file_path);
//no embedded configuration
if (embedded == null) {
configuration = external ;
log.info("loaded configuration for application "+configuration.name()+" from "+container_configuraton_file_path);
}
else {
configuration = embedded;
if (external == null)
log.info("loaded configuration for application "+configuration.name()+" from "+configuration_file_path);
else {
configuration.merge(external);
log.info("loaded configuration for application "+configuration.name()+" from "+configuration_file_path+" and "+container_configuraton_file_path);
}
}
// TODO we can check scopes here instead of in BridgedApplicationConfiguration constructor
ApplicationConfiguration bridgedConfiguration = new BridgedApplicationConfiguration(context.configuration(),
configuration);
// shouldn't happen: management shouldn't have started at all
if (embedded == null)
throw new AssertionError("application @ " + application.getContextPath() + " is not distributed with "
+ configuration_file_path + " and there is no external configuration for it in "
+ container_configuraton_file_path);
Hub hub = new DefaultHub();
ApplicationLifecycle lifecycle = new ApplicationLifecycle(hub, configuration.name());
File file = bridgedConfiguration.persistence().file(profile_file_path);
String id= null;
if (file.exists()){
log.info("loading persisted state for application {}", application.getContextPath());
try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))){
id = (String)ois.readObject();
}catch(Exception e){
log.error("error loading persisted state, creating new uuid",e);
}
}
if (id==null)
id = UUID.randomUUID().toString();
ApplicationLifecycle lifecycle = new ApplicationLifecycle(hub, embedded.name());
return new DefaultApplicationContext(id, context, application, bridgedConfiguration, hub, lifecycle,
return new DefaultApplicationContext(context, application, embedded, hub, lifecycle,
new Properties());
}
@ -251,140 +143,46 @@ public class DefaultProvider implements Provider {
try {
// it's in a library, using
InputStream defaultHandlersStream = getClass().getResourceAsStream(default_handlers_file_path);
if (defaultHandlersStream == null)
throw new IllegalStateException("invalid distribution: cannot find " + default_handlers_file_path);
ApplicationConfigurationBinder binder = new ApplicationConfigurationBinder();
ApplicationHandlers defaultHandlers = binder.bindHandlers(defaultHandlersStream);
//searching for smartegars related application handlers in the common classloader
// searching for smartegars related application handlers in the common
// classloader
ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
if (currentClassLoader.getParent()!=null && !currentClassLoader.getParent().equals(ClassLoader.getSystemClassLoader())){
if (currentClassLoader.getParent() != null
&& !currentClassLoader.getParent().equals(ClassLoader.getSystemClassLoader())) {
log.trace("probably i'm in a webapp classloader");
currentClassLoader = currentClassLoader.getParent();
}
try{
if (currentClassLoader instanceof URLClassLoader){
URL[] urls = ((URLClassLoader) currentClassLoader).getURLs() ;
if (urls!=null && urls.length>0){
ClasspathScanner scanner = ClasspathScannerFactory.scanner(new HashSet<URL>(Arrays.asList(urls)));
Collection<ClasspathResource> resources = scanner.scan(new NameMatcher(application_handlers_file_name));
if (resources==null || resources.isEmpty())
log.info("no custom smartgears related application handlers found in the classpath");
for (ClasspathResource res : resources){
try{
ApplicationHandlers customHandlers= binder.bindHandlers(res.stream());
defaultHandlers.mergeWith(customHandlers);
log.trace("application hadlers found in {}",res.name());
}catch(Exception e){
log.warn("error loading smartgears related application handlers {}",res.name(),e);
}
}
}
}
}catch(Exception e){
log.warn("cannot load smartgears related handlers for application from the root classloader",e);
}
InputStream appSpecificHandlersStream = context.application().getResourceAsStream(handlers_file_path);
if (appSpecificHandlersStream !=null ){
defaultHandlers.mergeWith(binder.bindHandlers(appSpecificHandlersStream));
log.trace("{} uses default lifecycle with app spceific handler as it includes {}", context.name(), handlers_file_path);
} else
log.trace("{} uses the default lifecycle as it does not include {}", context.name(), handlers_file_path);
return defaultHandlers;
} catch (RuntimeException e) {
throw new RuntimeException("cannot install handlers for application @ " + context.name()+" (see cause) ", e);
}
}
@Override
public ApplicationExtensions extensionsFor(ApplicationContext context) {
try {
InputStream config = context.application().getResourceAsStream(extensions_file_path);
if (config == null) {
log.trace("{} uses default extensions as it does not include {}", context.name(), extensions_file_path);
// it's in a library, using
config = getClass().getResourceAsStream(default_extensions_file_path);
if (config == null)
throw new IllegalStateException("invalid distribution: cannot find " + default_extensions_file_path);
} else
log.info("{} uses custom extensions @ {}", context.name(), extensions_file_path);
ApplicationConfigurationBinder binder = new ApplicationConfigurationBinder();
return binder.bindExtensions(config);
ApplicationHandlers defaultHandlers = binder.bindHandlers(currentClassLoader);
return defaultHandlers;
} catch (RuntimeException e) {
throw new RuntimeException("cannot install extensions for application @ " + context.name()+" (see cause) ", e);
throw new RuntimeException("cannot install handlers for application @ " + context.name() + " (see cause) ",
e);
}
}
@Override
public SmartGearsConfiguration smartgearsConfiguration() {
try {
InputStream config = getClass().getResourceAsStream(library_configuration_file_path);
if (config == null)
throw new IllegalStateException("invalid distribution: cannot find " + library_configuration_file_path);
SmartGearsConfigurationBinder binder = new SmartGearsConfigurationBinder();
SmartGearsConfiguration configuration = binder.bind(config);
configuration.validate();
return configuration;
} catch (RuntimeException e) {
throw new RuntimeException("cannot read library configuration (see cause) ", e);
}
public List<ApplicationExtension> extensionsFor(ApplicationContext context){
return List.of(new RemoteResource());
}
// helpers
private ApplicationConfiguration configurationFor(ServletContext application) {
try {
InputStream config = application.getResourceAsStream(configuration_file_path);
if (config == null)
if (config == null)
return null;
ApplicationConfigurationBinder binder = new ApplicationConfigurationBinder();
return binder.bind(config);
return binder.load(config);
} catch (RuntimeException e) {
@ -395,76 +193,101 @@ public class DefaultProvider implements Provider {
private ContainerConfiguration containerConfiguration() {
String home = Utils.home();
if (configFile == null) {
if (home == null)
throw new IllegalStateException("invalid node configuration: the environment variable " + ghn_home_env
+ " or the system property " + ghn_home_property + " must be defined");
String home = Utils.home();
File homeDir = new File(home);
if (home == null)
throw new IllegalStateException("invalid node configuration: the environment variable " + ghn_home_env
+ " or the system property " + ghn_home_property + " must be defined");
if (!(homeDir.exists() && homeDir.isDirectory() && homeDir.canRead() && homeDir.canWrite()))
throw new IllegalStateException("invalid node configuration: home "+home+" does not exist or is not a directory or cannot be accessed in read/write mode");
File homeDir = new File(home);
File config = new File(homeDir,container_configuraton_file_path);
if (!(homeDir.exists() && homeDir.isDirectory() && homeDir.canRead() && homeDir.canWrite()))
throw new IllegalStateException("invalid node configuration: home " + home
+ " does not exist or is not a directory or cannot be accessed in read/write mode");
if (!(config.exists() && config.canRead()))
throw new IllegalStateException("invalid node configuration: file "+config.getAbsolutePath()+" does not exist or cannot be accessed");
configFile = new File(homeDir, container_configuraton_file_path);
log.trace("reading container configuration @ {} ", config.getAbsolutePath());
ContainerConfigurationBinder binder = new ContainerConfigurationBinder();
FileInputStream stream = null;
try {
stream = new FileInputStream(config);
}
catch(Exception e) {
throw new RuntimeException("unexpected exception reading container configuration file see cause)",e);
log.trace("reading container configuration @ {} ", configFile.getAbsolutePath());
}
ContainerConfiguration configuration = binder.bind(stream);
if (!(configFile.exists() && configFile.canRead()))
throw new IllegalStateException("invalid node configuration: file " + configFile.getAbsolutePath()
+ " does not exist or cannot be accessed");
try {
stream.close();
}
catch(Exception e) {
log.warn("could not close stream when reading container configuration @ "+config.getAbsolutePath()+" (see cause)",e);
ContainerConfiguration configuration;
try (InputStream stream = new FileInputStream(configFile)) {
configuration = new ContainerConfigurationBinder().load(stream);
} catch (Exception e) {
throw new IllegalStateException(
"invalid node configuration: file " + configFile.getAbsolutePath() + " is invalid", e);
}
return configuration;
}
/*
@Override
public RegistryPublisher publisherFor(ContainerContext context) {
return context.configuration().mode()==Mode.online?
RegistryPublisherFactory.create(): new OfflinePublisher();
}
@Override
public RegistryPublisher publisherFor(ApplicationContext context) {
return context.configuration().mode()==Mode.online?
RegistryPublisherFactory.create(): new OfflinePublisher();
}*/
public synchronized List<Publisher> publishers() {
if (this.publishers == null) {
@Override
public ScopedPublisher publisherFor(ContainerContext context) {
return context.configuration().mode()==Mode.online? RegistryPublisherFactory.scopedPublisher()
: new OfflinePublisher();
}
Set<Class<?>> annotatedPublishers;
@Override
public ScopedPublisher publisherFor(ApplicationContext context) {
return context.configuration().mode()==Mode.online? RegistryPublisherFactory.scopedPublisher()
: new OfflinePublisher();
try (ScanResult result = new ClassGraph().enableClassInfo().enableAnnotationInfo()
.addClassLoader(Thread.currentThread().getContextClassLoader()).scan()) {
ClassInfoList classInfos = result.getClassesWithAnnotation(SmartgearsProfilePublisher.class.getName());
annotatedPublishers = classInfos.stream().map(ClassInfo::loadClass)
.filter(c -> Publisher.class.isAssignableFrom(c)).collect(Collectors.toSet());
}
/*
* Collection<URL> urls =
* ClasspathHelper.forClassLoader(Thread.currentThread().getContextClassLoader()
* ); urls.removeIf(url -> url.toString().endsWith(".so") ||
* url.toString().endsWith(".zip") );
*
*
* ConfigurationBuilder reflectionConf = new
* ConfigurationBuilder().addUrls(urls).setScanners(new
* TypeAnnotationsScanner(), new SubTypesScanner());
*
* Reflections reflection = new Reflections(reflectionConf);
*
* = reflection.getTypesAnnotatedWith(SmartgearsProfilePublisher.class);
*/
List<Publisher> foundPublishers = new ArrayList<Publisher>();
for (Class<?> annotatedPublisher : annotatedPublishers) {
try {
foundPublishers.add((Publisher) annotatedPublisher.getDeclaredConstructor().newInstance());
log.info("added class {} to publishers", annotatedPublisher);
} catch (Throwable e) {
log.error("publisher class {} cannot be instantiated", annotatedPublisher.getCanonicalName(), e);
}
}
this.publishers = foundPublishers;
if (foundPublishers.isEmpty())
log.warn("no publishers found in classloader");
}
return this.publishers;
}
@Override
public AuthorizationProxy authorizationProxy() {
return authorizationService();
public SmartgearsConfiguration smartgearsConfiguration() {
ContainerConfigurationBinder binder = new ContainerConfigurationBinder();
return binder.loadSmartgearsProperty();
}
/*
* @Override public AuthorizationProvider authorizationProvider() { return
* containerContext.authorizationProvider(); }
*/
}

@ -1,56 +0,0 @@
package org.gcube.smartgears.provider;
import java.lang.reflect.Method;
import java.util.List;
import org.gcube.common.resources.gcore.Resource;
import org.gcube.informationsystem.publisher.ScopedPublisher;
import org.gcube.informationsystem.publisher.exception.RegistryNotFoundException;
import org.gcube.smartgears.configuration.Mode;
/**
* An implementation of {@link ScopedPublisher} that simulates remote publication.
* <p>
* Used for applications and or containers that operate in {@link Mode#offline}.
*
* @author Fabio Simeoni
*
*/
public class OfflinePublisher implements ScopedPublisher {
@Override
public <T extends Resource> T update(T resource){
// do nothing
return resource;
}
@Override
public <T extends Resource> T create(T resource, List<String> scopes)
throws RegistryNotFoundException {
// fragile! bypass restrictions reflectively and set new scope
for (String scope : scopes)
try {
Method m = resource.getClass().getSuperclass().getDeclaredMethod("addScope", String.class);
m.setAccessible(true);
m.invoke(resource, scope);
} catch (Exception e) {
throw new RuntimeException("could not simulate publication in scope " + scope, e);
}
return resource;
}
@Override
public <T extends Resource> T remove(T resource, List<String> scopes)
throws RegistryNotFoundException {
for (String scope : scopes)
try {
Method m = resource.getClass().getSuperclass().getDeclaredMethod("removeScope", String.class);
m.setAccessible(true);
m.invoke(resource, scope);
} catch (Exception e) {
throw new RuntimeException("could not simulate publication remove from scope " + scope, e);
}
return resource;
}
}

@ -1,15 +1,16 @@
package org.gcube.smartgears.provider;
import javax.servlet.ServletContext;
import java.util.List;
import org.gcube.common.authorization.client.proxy.AuthorizationProxy;
import org.gcube.informationsystem.publisher.ScopedPublisher;
import org.gcube.smartgears.configuration.application.ApplicationExtensions;
import jakarta.servlet.ServletContext;
import org.gcube.smartgears.configuration.SmartgearsConfiguration;
import org.gcube.smartgears.configuration.application.ApplicationHandlers;
import org.gcube.smartgears.configuration.container.ContainerHandlers;
import org.gcube.smartgears.configuration.library.SmartGearsConfiguration;
import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.extensions.ApplicationExtension;
import org.gcube.smartgears.handlers.container.ContainerHandler;
import org.gcube.smartgears.publishing.Publisher;
/**
* Provides dependencies for container and application management.
@ -24,8 +25,9 @@ public interface Provider {
/**
* Returns the runtime properties.
* @return the properties.
*/
SmartGearsConfiguration smartgearsConfiguration();
*/
/**
* Assembles and returns the context of the container.
@ -37,15 +39,14 @@ public interface Provider {
* Returns the handlers associated with the container.
* @return the handlers
*/
ContainerHandlers containerHandlers();
List<ContainerHandler> containerHandlers();
/**
* Returns an implementation of the IS publisher for the container
* @param application the context of the container
* @return the publisher implementation
*/
ScopedPublisher publisherFor(ContainerContext application);
List<Publisher> publishers();
//application-level dependencies
@ -65,25 +66,8 @@ public interface Provider {
*/
ApplicationHandlers handlersFor(ApplicationContext application);
/**
* Returns the API extensions associated with a given application.
* @param application the context of the application
* @return the extensions
*/
ApplicationExtensions extensionsFor(ApplicationContext application);
/**
* Returns an implementation of the IS publisher for a given application
* @param application the context of the application
* @return the publisher implementation
*/
ScopedPublisher publisherFor(ApplicationContext application);
List<ApplicationExtension> extensionsFor(ApplicationContext application);
/**
* Returns an implementation of the IS publisher for a given application
* @param application the context of the application
* @return the publisher implementation
*/
AuthorizationProxy authorizationProxy();
SmartgearsConfiguration smartgearsConfiguration();
}

@ -0,0 +1,63 @@
package org.gcube.smartgears.publishing;
import java.util.Set;
import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.context.container.ContainerContext;
public interface Publisher {
/**
* creates the container resource in the context
*
* @param container
* @param contexts the new contexts where the resource must be created
* @return
*/
boolean create(ContainerContext container, Set<String> contexts);
/**
* creates the application resource in the contexts
*
* @param application
* @param contexts the new contexts where the resource must be created
* @return
*/
boolean create(ApplicationContext application, Set<String> contexts);
/**
* updates the application resource
*
* @param application
* @return
*/
boolean update(ApplicationContext application);
/**
* updates the container resource
*
* @param container
* @return
*/
boolean update(ContainerContext container);
/**
* removes the application resource from the contexts
*
* @param application
* @param contexts the contexts from where the resource must be removed
* @return
*/
boolean remove(ApplicationContext application, Set<String> contexts);
/**
* removes the container resource from the contexts
* @param application
* @param contexts the contexts from where the resource must be removed
* @return
*/
boolean remove(ContainerContext application, Set<String> contexts);
}

@ -0,0 +1,5 @@
package org.gcube.smartgears.publishing;
public @interface SmartgearsProfilePublisher {
}

@ -0,0 +1,15 @@
package org.gcube.smartgears.security;
import java.util.Set;
import org.gcube.common.security.credentials.Credentials;
import org.gcube.common.security.secrets.Secret;
public interface AuthorizationProvider {
Set<String> getContexts();
Secret getSecretForContext(String context);
Credentials getCredentials();
}

@ -0,0 +1,9 @@
package org.gcube.smartgears.security;
import org.gcube.common.security.credentials.Credentials;
public interface AuthorizationProviderFactory<T extends AuthorizationProvider> {
T connect(Credentials credentials);
}

@ -0,0 +1,68 @@
package org.gcube.smartgears.security;
import org.gcube.common.security.credentials.Credentials;
import org.gcube.common.validator.annotations.NotEmpty;
import org.gcube.common.validator.annotations.NotNull;
public class SimpleCredentials implements Credentials{
@NotNull @NotEmpty
String clientID;
@NotNull @NotEmpty
String secret;
public String getClientID() {
return clientID;
}
public void setClientID(String clientID) {
this.clientID = clientID;
}
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((clientID == null) ? 0 : clientID.hashCode());
result = prime * result + ((secret == null) ? 0 : secret.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SimpleCredentials other = (SimpleCredentials) obj;
if (clientID == null) {
if (other.clientID != null)
return false;
} else if (!clientID.equals(other.clientID))
return false;
if (secret == null) {
if (other.secret != null)
return false;
} else if (!secret.equals(other.secret))
return false;
return true;
}
@Override
public String toString() {
return "SimpleCredentials [clientID=" + clientID + ", secret=" + secret + "]";
}
}

@ -0,0 +1,76 @@
package org.gcube.smartgears.security.defaults;
import java.net.URL;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.gcube.common.keycloak.KeycloakClient;
import org.gcube.common.keycloak.KeycloakClientFactory;
import org.gcube.common.keycloak.model.AccessToken.Access;
import org.gcube.common.keycloak.model.ModelUtils;
import org.gcube.common.keycloak.model.TokenResponse;
import org.gcube.common.security.ContextBean;
import org.gcube.common.security.secrets.Secret;
import org.gcube.common.security.secrets.UmaTokenSecret;
import org.gcube.smartgears.security.AuthorizationProvider;
import org.gcube.smartgears.security.SimpleCredentials;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DefaultAuthorizationProvider implements AuthorizationProvider {
private static Logger LOG = LoggerFactory.getLogger(DefaultAuthorizationProvider.class);
private KeycloakClient client = KeycloakClientFactory.newInstance();
private SimpleCredentials credentials;
private String endpoint;
public DefaultAuthorizationProvider(SimpleCredentials credentials, String endpoint) {
this.credentials = credentials;
this.endpoint = endpoint;
}
@Override
public Set<String> getContexts() {
Set<String> contexts = new HashSet<String>();
try {
TokenResponse response = client.queryOIDCToken(new URL(this.endpoint), credentials.getClientID(), credentials.getSecret());
Map<String, Access> resourceAccess = ModelUtils.getAccessTokenFrom(response).getResourceAccess();
for (String context : resourceAccess.keySet()) {
try {
ContextBean scope = new ContextBean(context.replaceAll("%2F", "/"));
contexts.add(scope.toString());
LOG.debug("found context {}",context);
}catch (IllegalArgumentException e) {
LOG.debug("invalid context found in token: {}", context);
}
}
} catch (Exception e) {
LOG.error("error getting OIDToken from keycloak",e);
return Collections.emptySet();
}
return contexts;
}
@Override
public Secret getSecretForContext(String context) {
try {
TokenResponse response = client.queryUMAToken(new URL(this.endpoint), credentials.getClientID(), credentials.getSecret(), context, null);
return new UmaTokenSecret(response.getAccessToken());
} catch (Exception e) {
LOG.error("error getting OIDToken from keycloak",e);
throw new RuntimeException("error getting access token for context "+context, e);
}
}
public SimpleCredentials getCredentials() {
return credentials;
}
}

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

Loading…
Cancel
Save