Compare commits

...

89 Commits

Author SHA1 Message Date
lucio 8c4ffc9b28 commit for release 2024-10-28 14:33:06 +01:00
lucio 855820b2fa support ticket #28304 2024-10-28 13:52:25 +01:00
lucio a9535591ab commit for release 2024-10-14 15:36:00 +02:00
lucio d2aaf739ee moved Authorization interfaces to common security 2024-10-02 11:45:44 +02:00
lucio c5c57f5b34 commit for release 2024-10-01 09:36:06 +02:00
lucio 4db089d043 updated gcube-bom 2024-09-30 13:42:14 +02:00
lucio f4ae6f1fd6 logs modified 2024-05-16 11:10:58 +02:00
lucio 7f446b680c solved a bug on secret validation 2024-05-16 11:08:10 +02:00
lucio 05dd9f2482 FUNDING file added 2024-05-08 11:22:19 +02:00
lucio 48347ea8f7 added configuration of accepted secret per app 2024-04-30 16:55:26 +02:00
lucio 51fbda1161 updated keycloak url 2024-04-09 16:42:09 +02:00
lucio bb55ce6f44 config dir moved in contexPath instead of ServiceName 2024-04-09 16:41:45 +02:00
lucio 1fbd524fc4 added possibility to get Configuration folder outside tomcat 2024-03-22 16:39:57 +01:00
lucio 82c155e3a4 solved bug on version configuration 2024-03-12 17:27:47 +01:00
lucio 3aeca7c089 moved to jakarta 2024-03-11 10:22:25 +01:00
lucio e36605ec12 removed filters application to extensions 2024-02-28 11:24:26 +01:00
lucio 5c80342e8c Merge branch 'master' of
https://code-repo.d4science.org/gCubeSystem/common-smartgears.git
2024-02-22 11:24:12 +01:00
lucio a34e349c3b Merge branch 'master' of https://code-repo.d4science.org/gCubeSystem/common-smartgears.git 2024-02-22 10:53:18 +01:00
lucio 36a685733d maven parent updated to 1.2.0 2024-02-22 10:51:41 +01:00
lucio 64fb289c40 change InnerMethodName 2024-02-21 20:22:49 +01:00
Roberto Cirillo b1b8e0df2e Update 'pom.xml'
restore maven-parent 1.2.0
2024-02-21 15:54:12 +01:00
Roberto Cirillo aa58b66d79 Update 'pom.xml'
revert to maven-parent 1.1.0 just for test
2024-02-21 15:52:29 +01:00
lucio 6454f71672 commit for maven-parent test 2024-02-21 15:13:44 +01:00
lucio 409499956a pom updated 2024-02-20 17:03:47 +01:00
lucio bfce9d076d log updated 2024-02-20 12:20:16 +01:00
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
2024-02-20 12:13:21 +01:00
lucio 4d060fbb54 config validation on Credential was not working 2024-02-17 01:24:03 +01:00
lucio e193c80d11 removed logs 2024-02-02 10:54:19 +01:00
lucio 6dec0d7a6a updated 2024-01-25 16:46:56 +01:00
lucio 57d25b3ed3 updated for health api 2024-01-24 16:29:51 +01:00
lucio 5a771aeeb5 updated 2024-01-19 22:51:34 +01:00
lucio 101b0f1e0d changes on classpath 2024-01-19 12:17:54 +01:00
lucio a7f666556d modified persistence configuration 2023-09-01 15:42:36 +02:00
lucio c00d389357 updated configuration 2023-05-05 15:45:00 +02:00
lucio 873081893e unuseful resource removed 2023-04-04 12:30:45 +02:00
Lucio Lelii 725b19c168 Merge remote-tracking branch 'origin/smartgears4' 2023-04-04 11:04:46 +02:00
lucio be3d37d6c3 updated 2023-03-31 14:16:23 +02:00
lucio 766238c8e3 removed all JAXB dependencies 2023-02-06 17:34:18 +01:00
lucio e48f50e91b added automatic metrics for micrometer 2023-02-01 17:18:13 +01:00
lucio a76b823c49 update 2023-02-01 14:56:57 +01:00
lucio b9f17e5a0b added check 2023-02-01 14:55:11 +01:00
lucio b873f97187 Health extension implemented 2023-02-01 14:40:31 +01:00
lucio 98c41e7463 moved health part to a separate library 2023-01-27 17:01:22 +01:00
Lucio Lelii 3407ce025c health objects added 2023-01-26 18:24:18 +01:00
Lucio Lelii 29e02ac587 removed old import 2023-01-23 15:41:21 +01:00
Lucio Lelii d12e4a33b2 AuthorizationProvider moved from Configuration to context 2023-01-19 16:16:14 +01:00
Lucio Lelii de2c95f134 added compiler version 2022-12-16 10:01:56 +01:00
Lucio Lelii e7aabc3fb8 fired exception on expired token modified 2022-12-15 16:33:44 +01:00
Luca Frosini 1cf09963a2 Fixed pom to properly compile with JDK 11 2022-11-16 11:40:24 +01:00
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
2022-11-11 17:27:30 +01:00
Luca Frosini d024c129d5 Removed -SNAPSHOT to release the component 2022-11-02 16:03:33 +01:00
Lucio Lelii b5f6959da4 check on empty token adde 2022-10-24 12:47:30 +02:00
Roberto Cirillo f01ee71e32 Update 'pom.xml'
set gcube-bom 2.1.0-SNAPSHOT
2022-10-19 16:41:24 +02:00
Luca Frosini 952703c780 Added the Linux distribution version in the HostingNode resource 2022-10-19 12:32:08 +02:00
Lucio Lelii c92f119a0c adde validation on acess token 2022-09-22 11:27:40 +02:00
Lucio Lelii 551740212f put context logs to debug 2022-08-03 12:29:53 +02:00
Lucio Lelii b73a2963e5 Credentials moved to security library 2022-07-26 15:46:43 +02:00
Lucio Lelii 08838cb6d6 removed vos from frontpage 2022-07-25 11:00:44 +02:00
Lucio Lelii 2fe7d3d4bc InnerMethodName used for accounting 2022-07-20 17:31:20 +02:00
Lucio Lelii 4b75535a9d added logs 2022-07-14 11:56:32 +02:00
Lucio Lelii e5c0b8a23d latitude and longitude removed 2022-07-06 09:55:42 +02:00
Lucio Lelii b9e2298ca9 fixed bug on first publishing 2022-07-04 16:31:15 +02:00
Lucio Lelii 7e00459e9a logs added 2022-06-30 12:33:55 +02:00
Lucio Lelii 1b79f8e813 updated publisher interface 2022-06-28 16:18:02 +02:00
Lucio Lelii 58bb6a28e4 changed publisher interface 2022-06-28 13:28:16 +02:00
Lucio Lelii 710e8ef242 Proxy management improved 2022-06-24 15:45:43 +02:00
Lucio Lelii 08781a5f3a Profile and App events updated 2022-06-22 18:50:54 +02:00
Lucio Lelii 6f999bcedc added caller information 2022-06-15 17:44:52 +02:00
Lucio Lelii 80f5de688a allowed context retrieved on request 2022-06-10 18:25:19 +02:00
Lucio Lelii 8030b049d0 Publishing externalized to libraries in classpath 2022-06-10 17:08:44 +02:00
Lucio Lelii 961676484f removing gcore resource dependencies from smartgears 2022-06-08 17:42:32 +02:00
Lucio Lelii 4b619414a7 removed old providers 2022-05-30 18:55:35 +02:00
Lucio Lelii 64bef37271 Removed old VAleve 2022-05-30 18:29:46 +02:00
Lucio Lelii 230ae3bde9 auth provider modified 2022-05-26 14:39:31 +02:00
Lucio Lelii f54efc1e4e handler configuration removed
test removed
2022-05-25 18:56:42 +02:00
Lucio Lelii a285c20b38 removed context retriver handler and merged with Request validator 2022-05-23 17:15:46 +02:00
Lucio Lelii f6e49975d0 added import of authorization-util 2022-05-04 19:11:33 +02:00
Lucio Lelii 9947bfbc7d update for 4.0.0 2022-03-31 11:58:49 +02:00
Lucio Lelii 454533abcd update 2022-03-29 15:05:28 +02:00
Lucio Lelii 08b3cd590a new features for configuration file type changing 2022-03-21 11:17:07 +01:00
Lucio Lelii cf3c134953 moving smartgears to a .ini Configuration file type 2022-03-17 17:17:15 +01:00
Luca Frosini 515891e083 Merging patch of version 3.1.2 2022-03-11 18:05:57 +01:00
Luca Frosini 635036bce1 Added Linux distribution version refs #22933 2022-03-11 11:37:32 +01:00
Luca Frosini baab05f015 fixing code 2022-02-28 17:07:57 +01:00
Luca Frosini b46715dc05 Added missing reset 2022-02-28 16:56:56 +01:00
Luca Frosini 405a65282d Fixed code to instantiate SecretManager 2022-02-28 13:26:40 +01:00
Luca Frosini b703d6605b upgraded gcube-bom version 2022-02-25 16:21:33 +01:00
Luca Frosini 29ac4def10 Intregrated authorization-utils in place of ad-hoc code 2022-02-25 15:07:09 +01:00
Luca Frosini 9239e13120 Added authorization-utils refs #22871 2022-02-24 17:49:38 +01:00
132 changed files with 3256 additions and 7269 deletions

1
.gitignore vendored
View File

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

View File

@ -2,13 +2,18 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
# Changelog for Common Smartgears # Changelog for Common Smartgears
## [v3.1.5] - 2022-04-20 ## [v4.0.0]
- Added roles to ExternalService Info on request handler verification - support ticket #28304
- porting to keycloak
- moved to jakarta and servlet6
- added token expiration
## [v3.1.4] - 2022-03-29
- fixes issue [#23075] ## [v3.2.0] - 2023-04-12
- Added SecretManagerProvider thread local from authorization-utils [#22871]
- Added Linux distribution version [#22933]
## [v3.1.3] - 2022-03-21 ## [v3.1.3] - 2022-03-21

26
FUNDING.md Normal file
View File

@ -0,0 +1,26 @@
# Acknowledgments
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](https://cordis.europa.eu/project/id/004260) (grant no. 004260).
- the Seventh Framework Programme for research, technological development and demonstration
- [D4Science](https://cordis.europa.eu/project/id/212488) (grant no. 212488);
- [D4Science-II](https://cordis.europa.eu/project/id/239019) (grant no.239019);
- [ENVRI](https://cordis.europa.eu/project/id/283465) (grant no. 283465);
- [iMarine](https://cordis.europa.eu/project/id/283644) (grant no. 283644);
- [EUBrazilOpenBio](https://cordis.europa.eu/project/id/288754) (grant no. 288754).
- the H2020 research and innovation programme
- [SoBigData](https://cordis.europa.eu/project/id/654024) (grant no. 654024);
- [PARTHENOS](https://cordis.europa.eu/project/id/654119) (grant no. 654119);
- [EGI-Engage](https://cordis.europa.eu/project/id/654142) (grant no. 654142);
- [ENVRI PLUS](https://cordis.europa.eu/project/id/654182) (grant no. 654182);
- [BlueBRIDGE](https://cordis.europa.eu/project/id/675680) (grant no. 675680);
- [PerformFISH](https://cordis.europa.eu/project/id/727610) (grant no. 727610);
- [AGINFRA PLUS](https://cordis.europa.eu/project/id/731001) (grant no. 731001);
- [DESIRA](https://cordis.europa.eu/project/id/818194) (grant no. 818194);
- [ARIADNEplus](https://cordis.europa.eu/project/id/823914) (grant no. 823914);
- [RISIS 2](https://cordis.europa.eu/project/id/824091) (grant no. 824091);
- [EOSC-Pillar](https://cordis.europa.eu/project/id/857650) (grant no. 857650);
- [Blue Cloud](https://cordis.europa.eu/project/id/862409) (grant no. 862409);
- [SoBigData-PlusPlus](https://cordis.europa.eu/project/id/871042) (grant no. 871042);

View File

@ -54,26 +54,6 @@ open-source software toolkit used for building and operating Hybrid Data
Infrastructures enabling the dynamic deployment of Virtual Research Environments Infrastructures enabling the dynamic deployment of Virtual Research Environments
by favouring the realisation of reuse oriented policies. 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 projects leading to this software have received funding from a series of European Union programmes see [FUNDING.md](FUNDING.md)
- 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);

View File

@ -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>

View File

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

Binary file not shown.

View File

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

214
pom.xml
View File

@ -5,42 +5,64 @@
<parent> <parent>
<artifactId>maven-parent</artifactId> <artifactId>maven-parent</artifactId>
<groupId>org.gcube.tools</groupId> <groupId>org.gcube.tools</groupId>
<version>1.1.0</version> <version>1.2.0</version>
<relativePath /> <relativePath />
</parent> </parent>
<groupId>org.gcube.core</groupId> <groupId>org.gcube.core</groupId>
<artifactId>common-smartgears</artifactId> <artifactId>common-smartgears</artifactId>
<version>3.1.5</version> <version>4.0.0</version>
<name>SmartGears</name> <name>SmartGears</name>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.gcube.distribution</groupId> <groupId>org.gcube.distribution</groupId>
<artifactId>gcube-bom</artifactId> <artifactId>gcube-bom</artifactId>
<version>2.0.1</version> <version>4.0.0</version>
<type>pom</type> <type>pom</type>
<scope>import</scope> <scope>import</scope>
</dependency> </dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>
<properties> <properties>
<distroDirectory>distro</distroDirectory> <tomcat.version>10.1.19</tomcat.version>
<tomcat.version>7.0.42</tomcat.version>
<jersey.version>1.17.1</jersey.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties> </properties>
<scm> <scm>
<connection>scm:git:https://code-repo.d4science.org/gCubeSystem/common-smartgears.git</connection> <connection>
<developerConnection>scm:git:https://code-repo.d4science.org/gCubeSystem/common-smartgears.git</developerConnection> 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> <url>https://code-repo.d4science.org/gCubeSystem/common-smartgears</url>
</scm> </scm>
<dependencies> <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 --> <!-- gCube Jackson -->
<dependency> <dependency>
<groupId>org.gcube.common</groupId> <groupId>org.gcube.common</groupId>
@ -55,146 +77,83 @@
<artifactId>gcube-jackson-core</artifactId> <artifactId>gcube-jackson-core</artifactId>
</dependency> </dependency>
<!-- END gCube Jackson --> <!-- END gCube Jackson -->
<dependency> <dependency>
<groupId>org.gcube.common</groupId> <groupId>org.gcube.common</groupId>
<artifactId>authorization-client</artifactId> <artifactId>common-security</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.gcube.common</groupId>
<artifactId>common-authorization</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.gcube.data.publishing</groupId> <groupId>org.gcube.data.publishing</groupId>
<artifactId>document-store-lib</artifactId> <artifactId>document-store-lib</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.gcube.accounting</groupId> <groupId>org.gcube.accounting</groupId>
<artifactId>accounting-lib</artifactId> <artifactId>accounting-lib</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId> <artifactId>slf4j-api</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.gcube.resources</groupId> <groupId>org.ini4j</groupId>
<artifactId>registry-publisher</artifactId> <artifactId>ini4j</artifactId>
<version>0.5.4</version>
</dependency> </dependency>
<dependency>
<groupId>org.gcube.resources</groupId>
<artifactId>common-gcore-resources</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.gcube.core</groupId> <groupId>org.gcube.core</groupId>
<artifactId>common-validator</artifactId> <artifactId>common-validator</artifactId>
<version>[1.0.0,2.0.0-SNAPSHOT)</version> <version>[1.0.0,2.0.0-SNAPSHOT)</version>
</dependency> </dependency>
<dependency>
<groupId>org.gcube.core</groupId>
<artifactId>common-scope</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.gcube.core</groupId> <groupId>org.gcube.core</groupId>
<artifactId>common-events</artifactId> <artifactId>common-events</artifactId>
<version>[1.0.0,2.0.0-SNAPSHOT)</version> <version>[1.0.0,2.0.0-SNAPSHOT)</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>javax.servlet</groupId> <groupId>org.gcube.common.security</groupId>
<artifactId>javax.servlet-api</artifactId> <artifactId>gcube-secrets</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/io.micrometer/micrometer-core -->
<!-- ***************** test ******************* -->
<dependency> <dependency>
<groupId>org.jboss.shrinkwrap.resolver</groupId> <groupId>io.micrometer</groupId>
<artifactId>shrinkwrap-resolver-depchain</artifactId> <artifactId>micrometer-core</artifactId>
<version>2.0.0-beta-2</version> <version>1.9.0</version>
<type>pom</type>
<scope>test</scope>
</dependency> </dependency>
<dependency>
<!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-catalina --> <groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<version>1.9.0</version>
</dependency>
<!--
https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-catalina -->
<dependency> <dependency>
<groupId>org.apache.tomcat</groupId> <groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId> <artifactId>tomcat-catalina</artifactId>
<version>${tomcat.version}</version> <version>${tomcat.version}</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </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> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<version>4.10</version> <version>4.10</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ch.qos.logback</groupId> <groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId> <artifactId>logback-classic</artifactId>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId> <artifactId>mockito-core</artifactId>
<version>1.9.0</version> <version>1.9.0</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.gcube.resources</groupId>
<artifactId>common-gcore-resources</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>
<!-- excludes probe package from jar --> <!-- excludes probe package from jar -->
<plugin> <plugin>
@ -215,7 +174,18 @@
</execution> </execution>
</executions> </executions>
</plugin> </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 --> <!-- include probe in attached war -->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
@ -224,8 +194,8 @@
<configuration> <configuration>
<primaryArtifact>false</primaryArtifact> <primaryArtifact>false</primaryArtifact>
<classifier>probe</classifier> <classifier>probe</classifier>
<packagingIncludes>
<packagingIncludes>WEB-INF/classes/org/gcube/smartgears/probe/**/*</packagingIncludes> WEB-INF/classes/org/gcube/smartgears/probe/**/*</packagingIncludes>
<failOnMissingWebXml>false</failOnMissingWebXml> <failOnMissingWebXml>false</failOnMissingWebXml>
</configuration> </configuration>
<executions> <executions>
@ -238,48 +208,6 @@
</execution> </execution>
</executions> </executions>
</plugin> </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> </plugins>
</build> </build>
</project> </project>

View File

@ -1,10 +1,12 @@
package org.gcube.smartgears; package org.gcube.smartgears;
import static java.util.Collections.emptyList;
import java.util.Set; import java.util.Set;
import javax.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext; import jakarta.servlet.ServletContext;
import javax.servlet.ServletException; import jakarta.servlet.ServletException;
import org.gcube.smartgears.context.application.ApplicationContext; import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.context.container.ContainerContext; import org.gcube.smartgears.context.container.ContainerContext;
@ -14,6 +16,19 @@ import org.gcube.smartgears.provider.ProviderFactory;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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. * Bootstraps management of all deployed applications which require it.
* *
@ -34,6 +49,8 @@ public class Bootstrap implements ServletContainerInitializer {
public Bootstrap() { public Bootstrap() {
log.info("bootstrap started the container");
if (smartgearsHasStarted) if (smartgearsHasStarted)
return; return;
@ -51,6 +68,8 @@ public class Bootstrap implements ServletContainerInitializer {
ApplicationManager appManager = new ApplicationManager(); ApplicationManager appManager = new ApplicationManager();
log.info("check if is managed @ {}", application.getContextPath());
//act only on resources //act only on resources
if (isResource(application)) { if (isResource(application)) {
@ -72,25 +91,45 @@ public class Bootstrap implements ServletContainerInitializer {
+ " (see cause)", t); + " (see cause)", t);
} }
} } else log.info("is not managed @ {}", application.getContextPath());
} }
// helpers // helpers
@SuppressWarnings("resource")
private void initialiseContainer() { private void initialiseContainer() {
ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
try { try {
// TODO Ask why is needed?
Thread.currentThread().setContextClassLoader(ContainerManager.class.getClassLoader());
log.trace("smartgears is starting"); log.trace("smartgears is starting");
/* Get the ContainerContext. Look at DefaultProvider */ /* Get the ContainerContext. Look at DefaultProvider */
context = ProviderFactory.provider().containerContext(); 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 /* Validate the configuration retrieved by ContainerContext
* using gcube facilities annotation based * using gcube facilities annotation based
* ( i.e org.gcube.common.validator.annotations) * ( i.e org.gcube.common.validator.annotations)
*/ */
//context.configuration().validate(); context.configuration().validate();
} catch (RuntimeException e) { } catch (RuntimeException e) {
@ -100,6 +139,8 @@ public class Bootstrap implements ServletContainerInitializer {
//we let the container continue //we let the container continue
} finally {//restore the classloader of the current application
Thread.currentThread().setContextClassLoader(contextCL);
} }
} }

View File

@ -1,7 +1,7 @@
package org.gcube.smartgears; package org.gcube.smartgears;
import org.gcube.smartgears.extensions.resource.RemoteResource; 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.application.request.RequestValidator;
import org.gcube.smartgears.handlers.container.lifecycle.AccountingManager; import org.gcube.smartgears.handlers.container.lifecycle.AccountingManager;
@ -28,7 +28,7 @@ public class Constants {
/** /**
* The container configuration file path, relative to the container configuration directory. * 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 = "ghn.xml";
/*
public static final String container_profile_file_path_copy = "ghn.xml.copy"; 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. * The name of the context property that contains the node profile.
*/
public static final String container_profile_property = "ghn-profile";
public static final String container_profile_property = "ghn-profile";
*/
/** /**
* The default value of for the container publication frequency. * 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; public static final long default_container_publication_frequency_in_seconds = 60;
/** /**
* The application configuration resource path. * 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. * The wildcard exclude directive.
@ -92,16 +69,6 @@ public class Constants {
*/ */
public static final String root_mapping = "/gcube/resource"; 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. * The application frontpage resource path.
*/ */
@ -117,6 +84,11 @@ public class Constants {
*/ */
public static final String request_validation = "request-validation"; 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. * The configuration name of {@link RequestValidator}s.
*/ */
@ -140,8 +112,6 @@ public class Constants {
public static final String remote_management = "remote-management"; public static final String remote_management = "remote-management";
/** /**
* The path of the application profile file, relative to the service configuration directory. * The path of the application profile file, relative to the service configuration directory.
*/ */

View File

@ -0,0 +1,37 @@
package org.gcube.smartgears.configuration;
import org.gcube.common.security.credentials.Credentials;
import org.gcube.common.security.factories.AuthorizationProviderFactory;
import org.gcube.common.validator.annotations.IsValid;
import org.gcube.common.validator.annotations.NotNull;
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() + "]";
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,167 +1,200 @@
package org.gcube.smartgears.configuration.application; 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 java.util.Set;
import org.gcube.smartgears.configuration.Mode; import org.gcube.com.fasterxml.jackson.annotation.JsonAutoDetect;
import org.gcube.smartgears.persistence.Persistence; 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. * The configuration of a managed app.
* <p>
* Includes the list of its client services.
* *
* @author Fabio Simeoni * @author Lucio Lelii
* *
*/ */
public interface ApplicationConfiguration { @JsonInclude(value = Include.NON_NULL)
@JsonAutoDetect(fieldVisibility = Visibility.ANY)
public class ApplicationConfiguration {
@NotNull
String name;
@NotNull
String group;
@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;
@JsonProperty("allowed-secrets")
List<String> allowedSecretClasses = null;
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 allowedSecrets(String ... classNames) {
this.allowedSecretClasses = Arrays.asList(classNames);
return this;
}
public List<String> allowedSecrets(){
return this.allowedSecretClasses;
}
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) {
* Returns the management mode of the application. this.group=group;
* @return the management mode return this;
*/ }
Mode mode();
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) {
* Returns the context path of the application this.description=description;
* @return the context path return this;
*/ }
String context();
boolean proxied(); public boolean proxable() {
return proxable;
}
/** public PersistenceConfiguration persistenceConfiguration() {
* Sets the context path of the application return persistenceConfiguration;
* @param context the context path }
* @return this configuration
*/
ApplicationConfiguration context(String context);
/** public void validate() {
* Sets the management mode of this application.
* @param the management mode
* @return this configuration
*/
ApplicationConfiguration mode(Mode mode);
List<String> msgs = new ArrayList<String>();
/** Validator validator = ValidatorFactory.validator();
* Returns the name of the application.
* @return the name
*/
String name();
for (ValidationError error : validator.validate(this))
msgs.add(error.toString());
/** if (!this.excludes().isEmpty() && !this.includes().isEmpty())
* Sets the name of the application. msgs.add("exclude tags and includes tags are mutually exclusive");
* @param name the name
* @return this configuration
*/
ApplicationConfiguration name(String name);
/** if (!msgs.isEmpty())
* Returns the class of the application throw new IllegalStateException("invalid configuration: "+msgs);
* @return the class }
*/
String serviceClass();
/** @Override
* Sets the class of the application. public int hashCode() {
* @param serviceClass the class return Objects.hash(description, excludes, group, includes, name, proxable, version, allowedSecretClasses);
* @return this configuration }
*/
ApplicationConfiguration serviceClass(String serviceClass);
/** @Override
* Returns the version of the application. public boolean equals(Object obj) {
* @return the version if (this == obj)
*/ return true;
String version(); if (obj == null)
return false;
/** if (getClass() != obj.getClass())
* Sets the version of the application. return false;
* @param version the version ApplicationConfiguration other = (ApplicationConfiguration) obj;
* @return this configuration return Objects.equals(description, other.description)
*/ && Objects.equals(excludes, other.excludes) && Objects.equals(group, other.group)
ApplicationConfiguration version(String version); && Objects.equals(includes, other.includes) && Objects.equals(name, other.name)
&& proxable == other.proxable && Objects.equals(version, other.version);
/** }
* Returns the description of the application.
* @return the description
*/
String description();
/**
* 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();
/**
* 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);
@Override
public String toString() {
return "ApplicationConfiguration [name=" + name + ", group=" + group + ", version=" + version + ", description="
+ description + ", context=" + context + ", proxable=" + proxable + ", excludes=" + excludes
+ ", includes=" + includes + ", persistenceConfiguration=" + persistenceConfiguration
+ ", allowedSecretClasses=" + this.allowedSecretClasses + "]";
}
} }

View File

@ -1,18 +1,22 @@
package org.gcube.smartgears.configuration.application; package org.gcube.smartgears.configuration.application;
import static org.gcube.smartgears.utils.Utils.*; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.Modifier; import java.util.LinkedList;
import java.util.HashSet; import java.util.List;
import java.util.ServiceLoader;
import java.util.Set;
import javax.xml.bind.JAXBContext; import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
import javax.xml.bind.JAXBException; import org.gcube.smartgears.configuration.PersistenceConfiguration;
import org.gcube.smartgears.handlers.application.ApplicationLifecycleHandler;
import org.gcube.smartgears.extensions.ApplicationExtension; import org.gcube.smartgears.handlers.application.RequestHandler;
import org.gcube.smartgears.handlers.application.ApplicationHandler; 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. * Binds {@link ApplicationConfiguration}s to and from XML serialisations.
@ -29,21 +33,30 @@ public class ApplicationConfigurationBinder {
* @return the configuration * @return the configuration
* @throws RuntimeException if the serialisation is invalid * @throws RuntimeException if the serialisation is invalid
*/ */
public ApplicationConfiguration bind(InputStream stream) { public ApplicationConfiguration load(InputStream stream) {
try { try {
Yaml yaml = new Yaml();
ObjectMapper mapper = new ObjectMapper();
String mapAsString = mapper.writeValueAsString(yaml.load(stream));
JAXBContext ctx = JAXBContext.newInstance(DefaultApplicationConfiguration.class); System.out.println(mapAsString);
return (ApplicationConfiguration) ctx.createUnmarshaller().unmarshal(stream); ApplicationConfiguration conf = mapper.readValue(mapAsString, ApplicationConfiguration.class);
} catch (JAXBException e) { 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();
throw new RuntimeException("invalid service configuration", e); conf.persistenceConfiguration(
new PersistenceConfiguration(LocalWriter.class, new LocalWriterConfiguration(location)));
} }
finally {
closeSafely(stream); return conf;
} catch (Exception e) {
throw new RuntimeException(e);
} }
} }
@ -54,25 +67,26 @@ public class ApplicationConfigurationBinder {
* @return the handlers * @return the handlers
* @throws RuntimeException if the serialisation is invalid * @throws RuntimeException if the serialisation is invalid
*/ */
public ApplicationHandlers bindHandlers(InputStream stream) { public ApplicationHandlers bindHandlers(ClassLoader classLoader) {
//collects handler classes List<RequestHandler> requestHandlers = new LinkedList<RequestHandler>();
Set<Class<?>> classes = scanForHandlers();
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);
}
} }
/** /**
@ -81,69 +95,54 @@ public class ApplicationConfigurationBinder {
* @param stream the serialisation * @param stream the serialisation
* @return the extensions * @return the extensions
* @throws RuntimeException if the serialisation is invalid * @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 public void scanForApplicationHandlers(ClassLoader currentClassLoader) {
Set<Class<?>> classes = scanForExtensions(); // TODO Auto-generated method stub
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;
}
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;
}
} }

View File

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

View File

@ -1,21 +1,11 @@
package org.gcube.smartgears.configuration.application; package org.gcube.smartgears.configuration.application;
import java.util.ArrayList; import java.util.LinkedList;
import java.util.Arrays;
import java.util.List; 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.ApplicationHandler;
import org.gcube.smartgears.handlers.application.ApplicationLifecycleHandler; import org.gcube.smartgears.handlers.application.ApplicationLifecycleHandler;
import org.gcube.smartgears.handlers.application.RequestHandler; import org.gcube.smartgears.handlers.application.RequestHandler;
import org.w3c.dom.Element;
/** /**
* The {@link ApplicationHandler}s that manage the application. * The {@link ApplicationHandler}s that manage the application.
@ -23,24 +13,24 @@ import org.w3c.dom.Element;
* @author Fabio Simeoni * @author Fabio Simeoni
* *
*/ */
@XmlRootElement(name="handlers")
public class ApplicationHandlers { public class ApplicationHandlers {
@XmlElement(name="lifecycle") @IsValid private List<ApplicationLifecycleHandler> lifecycleHandlers = new LinkedList<ApplicationLifecycleHandler>();
private LifecycleHandlers lifecycleHandlers = new LifecycleHandlers();
@XmlElement(name="request") @IsValid private List<RequestHandler> requestHandlers = new LinkedList<RequestHandler>();
private RequestHandlers requestHandlers = new RequestHandlers();
public ApplicationHandlers() {} public ApplicationHandlers(List<ApplicationLifecycleHandler> lifecycleHandlers, List<RequestHandler> requestHandlers) {
this.lifecycleHandlers = lifecycleHandlers;
this.requestHandlers = requestHandlers;
}
/** /**
* Returns the {@link ApplicationLifecycleHandler}s for the service. * Returns the {@link ApplicationLifecycleHandler}s for the service.
* @return the lifecycle handlers * @return the lifecycle handlers
*/ */
public List<ApplicationLifecycleHandler> lifecycleHandlers() { public List<ApplicationLifecycleHandler> lifecycleHandlers() {
return lifecycleHandlers.values; return lifecycleHandlers;
} }
/** /**
@ -48,8 +38,8 @@ public class ApplicationHandlers {
* @param handlers the lifecycle handlers * @param handlers the lifecycle handlers
* @return this configuration * @return this configuration
*/ */
public ApplicationHandlers set(ApplicationLifecycleHandler ... handlers) { public ApplicationHandlers setLifecycleHandlers(List<ApplicationLifecycleHandler> handlers) {
this.lifecycleHandlers = new LifecycleHandlers(Arrays.asList(handlers)); this.lifecycleHandlers = handlers;
return this; return this;
} }
@ -58,7 +48,7 @@ public class ApplicationHandlers {
* @return the lifetime handlers * @return the lifetime handlers
*/ */
public List<RequestHandler> requestHandlers() { public List<RequestHandler> requestHandlers() {
return requestHandlers.values; return requestHandlers;
} }
/** /**
@ -66,75 +56,11 @@ public class ApplicationHandlers {
* @param handlers the request handlers * @param handlers the request handlers
* @return this configuration * @return this configuration
*/ */
public ApplicationHandlers set(RequestHandler ... handlers) { public ApplicationHandlers setRequetHandlers(List<RequestHandler> handlers) {
this.requestHandlers = new RequestHandlers(Arrays.asList(handlers)); this.requestHandlers = handlers;
return this; 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){ public void mergeWith(ApplicationHandlers other){
List<ApplicationLifecycleHandler> lifecycles = other.lifecycleHandlers(); List<ApplicationLifecycleHandler> lifecycles = other.lifecycleHandlers();

View File

@ -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)
*/
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);
}
}

View File

@ -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)
*
*/
@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());
}
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,106 @@
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;
@NotNull @NotEmpty
private Boolean checkTokenExpiration = false;
public boolean checkTokenExpiration() {
return checkTokenExpiration;
}
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
+ "]";
}
}

View File

@ -1,20 +1,10 @@
package org.gcube.smartgears.configuration.container; 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.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; 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.ValidationError;
import org.gcube.common.validator.Validator; import org.gcube.common.validator.Validator;
@ -22,11 +12,11 @@ import org.gcube.common.validator.ValidatorFactory;
import org.gcube.common.validator.annotations.IsValid; import org.gcube.common.validator.annotations.IsValid;
import org.gcube.common.validator.annotations.NotEmpty; import org.gcube.common.validator.annotations.NotEmpty;
import org.gcube.common.validator.annotations.NotNull; import org.gcube.common.validator.annotations.NotNull;
import org.gcube.smartgears.configuration.AuthorizationProviderConfiguration;
import org.gcube.smartgears.configuration.Mode; 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.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. * The configuration of the container.
@ -34,76 +24,77 @@ import org.gcube.smartgears.persistence.Persistence;
* @author Fabio Simeoni * @author Fabio Simeoni
* @author Luca Frosini (ISTI - CNR) * @author Luca Frosini (ISTI - CNR)
*/ */
@XmlRootElement(name="container")
public class ContainerConfiguration { public class ContainerConfiguration {
@XmlAttribute
private Mode mode = Mode.online;
@XmlElement
@NotNull @IsValid @NotNull @IsValid
String hostname; private BaseConfiguration baseConfiguration;
@XmlElement
@NotNull
Integer port;
@XmlElement(name ="authentication-endpoint")
String authenticationEnpoint = null;
@XmlElement(name ="protocol")
@NotNull @IsValid
String protocol="http";
@XmlElement
boolean authorizeChildrenContext = false;
@XmlElement
@NotNull@IsValid
String infrastructure;
@XmlElement
@NotNull @IsValid
Site site;
@XmlElement(name="token")
@NotNull @NotEmpty
List<String> tokens = new ArrayList<String>();
@XmlTransient
Set<String> allowedContext = new HashSet<String>();
@XmlElementRef(type=DefaultApplicationConfiguration.class)
List<ApplicationConfiguration> apps = new ArrayList<ApplicationConfiguration>();
@XmlElement(name="property")
@IsValid @IsValid
List<Property> properties = new ArrayList<Property>(); private Map<String,String> properties = new HashMap<String, String>();
@XmlElement(name="publication-frequency") @NotNull @IsValid
long publicationFrequency = default_container_publication_frequency_in_seconds; private Site site;
@XmlElementRef(type=DefaultPersistence.class)
@IsValid @IsValid
private Persistence persistenceManager; private ProxyAddress proxy;
@NotEmpty @NotNull
private String accountingFallbackLocation;
private List<ApplicationConfiguration> apps = new ArrayList<ApplicationConfiguration>();
@NotNull @IsValid
private PersistenceConfiguration persistenceConfiguration;
@NotNull @IsValid
private AuthorizationProviderConfiguration authorizationConfiguration;
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. * Returns the management mode for the container.
* @return the management mode * @return the management mode
*/ */
public Mode mode() { public Mode mode() {
return mode; return baseConfiguration.getMode();
} }
/** public boolean checkTokenExpiration() {
* Sets the management mode for the container. return baseConfiguration.checkTokenExpiration();
* @param mode the management mode
* @return this configuration
*/
public ContainerConfiguration mode(Mode mode) {
this.mode=mode;
return this;
} }
/** /**
@ -155,52 +146,22 @@ public class ContainerConfiguration {
return site; 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. * Returns the infrastructure in which the container is running.
* @return the infrastructure * @return the infrastructure
*/ */
public String 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. * Returns the host name of the container.
* @return the host name; * @return the host name;
*/ */
public String hostname() { 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;
} }
/** /**
@ -208,7 +169,7 @@ public class ContainerConfiguration {
* @return the port * @return the port
*/ */
public int port() { public int port() {
return port; return baseConfiguration.getPort();
} }
@ -217,81 +178,45 @@ public class ContainerConfiguration {
* @return the port * @return the port
*/ */
public String protocol() { public String protocol() {
return protocol; return baseConfiguration.getProtocol();
} }
public String authenticationEnpoint() {
return authenticationEnpoint;
}
public ContainerConfiguration authenticationEnpoint(String endpoint) {
this.authenticationEnpoint = endpoint;
return this;
}
/**
* 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() { public boolean authorizeChildrenContext() {
return authorizeChildrenContext; return baseConfiguration.isAuthorizeChildrenContext();
}
public ContainerConfiguration authorizeChildrenContext(boolean authorizeChildrenContext) {
this.authorizeChildrenContext = authorizeChildrenContext;
return this;
} }
/** /**
* Returns the VOs in which the container initially operates. * Returns the proxy of the container.
* @return the VOs * @return the proxy
*/ */
public List<String> startTokens() { public ProxyAddress proxy() {
return tokens; return proxy;
} }
/** /**
* Sets the VOs in which the container initially operates. * Returns the persistence manager of the container.
* @param vos the VOs * @return the manager
* @return this configuration
*/ */
public ContainerConfiguration startTokens(List<String> tokens) { public PersistenceConfiguration persistenceConfiguration() {
return this.persistenceConfiguration;
notNull("start Tokens",tokens);
this.tokens = tokens;
return this;
} }
/** /**
* Returns the persistence manager of the container. * Returns the persistence manager of the container.
* @return the manager * @return the manager
*/ */
public Persistence persistence() { public String accountingFallbackLocation() {
return persistenceManager; return accountingFallbackLocation;
} }
/** /**
* Sets the persistence manager of the container. * Returns the authorization configuration.
* @param manager the manager * @return AuthorizationProviderConfiguration the configuration
* @return this configuration
*/ */
public ContainerConfiguration persistence(Persistence manager) { public AuthorizationProviderConfiguration authorizationConfiguration() {
this.persistenceManager=manager; return authorizationConfiguration;
return this;
} }
/** /**
@ -299,21 +224,7 @@ public class ContainerConfiguration {
* @return the properties * @return the properties
*/ */
public Map<String,String> properties() { public Map<String,String> properties() {
Map<String,String> map = new HashMap<String, String>(); return Collections.unmodifiableMap(properties);
for (Property prop : properties)
map.put(prop.name, prop.value);
return map;
}
/**
* 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;
} }
/** /**
@ -321,26 +232,7 @@ public class ContainerConfiguration {
* @return the frquency; * @return the frquency;
*/ */
public long publicationFrequency() { public long publicationFrequency() {
return publicationFrequency; return baseConfiguration.getPublicationFrequencyInSeconds();
}
/**
* 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;
} }
/** /**
@ -362,159 +254,14 @@ public class ContainerConfiguration {
} }
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 @Override
public String toString() { public String toString() {
return "ContainerConfiguration [mode=" + mode + ", hostname=" + hostname + ", port=" + port + ", authenticationEnpoint=" + authenticationEnpoint + ", protocol=" + protocol return "ContainerConfiguration [baseConfiguration=" + baseConfiguration + ", properties=" + properties
+ ", authorizeChildrenContext=" + authorizeChildrenContext + ", infrastructure=" + infrastructure + ", site=" + site + ", proxy=" + proxy + ", accountingFallbackLocation=" + accountingFallbackLocation
+ ", site=" + site + ", tokens=" + tokens + ", allowedContext=" + allowedContext + ", apps=" + apps + ", persistence=" + persistenceConfiguration.getImplementationClass().getSimpleName()
+ ", properties=" + properties + ", publicationFrequency=" + publicationFrequency + ", authorizationProvider=" + authorizationConfiguration + "]";
+ ", persistenceManager=" + persistenceManager + "]";
} }
} }

View File

@ -1,18 +1,31 @@
package org.gcube.smartgears.configuration.container; package org.gcube.smartgears.configuration.container;
import static org.gcube.smartgears.utils.Utils.*; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.Modifier; import java.util.Collections;
import java.util.HashSet; import java.util.LinkedList;
import java.util.ServiceLoader; import java.util.List;
import java.util.Set; import java.util.Map.Entry;
import java.util.stream.Collectors;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import org.gcube.common.security.credentials.Credentials;
import org.gcube.common.security.factories.AuthorizationProviderFactory;
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.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.defaults.DefaultAuthorizationProviderFactory;
import org.gcube.smartgears.utils.Utils; import org.gcube.smartgears.utils.Utils;
import org.ini4j.Ini;
import org.ini4j.Profile.Section;
/** /**
* Binds {@link ContainerConfiguration}s to and from XML serialisations. * Binds {@link ContainerConfiguration}s to and from XML serialisations.
@ -22,31 +35,135 @@ import org.gcube.smartgears.utils.Utils;
*/ */
public class ContainerConfigurationBinder { public class ContainerConfigurationBinder {
/** public ContainerConfiguration load(InputStream stream) {
* 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) {
try { try {
Ini configurator = new Ini(stream);
ContainerConfiguration conf = new ContainerConfiguration();
JAXBContext ctx = JAXBContext.newInstance(ContainerConfiguration.class); Section nodeSection = configurator.get("node");
if (nodeSection != null) {
BaseConfiguration nodeConf = new BaseConfiguration();
nodeSection.to(nodeConf);
conf.setBaseConfiguration(nodeConf);
}
ContainerConfiguration config = (ContainerConfiguration) ctx.createUnmarshaller().unmarshal(stream); Section propertiesSection = configurator.get("properties");
if (propertiesSection != null)
conf.setProperties(propertiesSection.entrySet().stream()
.collect(Collectors.toMap(Entry::getKey, Entry::getValue)));
return config; Section siteSection = configurator.get("site");
if (siteSection != null) {
Site siteConf = new Site();
siteSection.to(siteConf);
conf.setSite(siteConf);
}
} catch (JAXBException e) { initAuthorizationPart(configurator, conf);
throw new RuntimeException("invalid container configuration", e); 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);
}
}
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);
}
}
@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)));
}
} }
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));
} }
} }
@ -57,45 +174,35 @@ public class ContainerConfigurationBinder {
* @return the handlers * @return the handlers
* @throws RuntimeException if the serialisation is invalid * @throws RuntimeException if the serialisation is invalid
*/ */
public ContainerHandlers bindHandlers(InputStream stream) { public List<ContainerHandler> bindHandlers(ClassLoader classloader) {
//collects handler classes LinkedList<ContainerHandler> handlers = new LinkedList<ContainerHandler>();
Set<Class<?>> classes = scanForConfigurationElements();
// ADDING BASE Handlers (order is important)
handlers.add(new AccountingManager());
handlers.add(new ContainerProfileManager());
handlers.addAll(scanForContainerHadlers(classloader));
return handlers;
}
private List<? extends ContainerHandler> scanForContainerHadlers(ClassLoader classloader) throws RuntimeException {
// TODO: scan for Container Handler
return Collections.emptyList();
}
public SmartgearsConfiguration loadSmartgearsProperty() {
try { try {
Ini configurator = new Ini(this.getClass().getResourceAsStream("/META-INF/smartgears-config.ini"));
JAXBContext ctx = JAXBContext.newInstance(classes.toArray(new Class<?>[0])); String version = configurator.get("smartgears").get("version");
return new SmartgearsConfiguration(version);
return (ContainerHandlers) ctx.createUnmarshaller().unmarshal(stream); }catch (Exception e) {
throw new RuntimeException(e);
} catch (JAXBException e) {
throw unchecked(e);
} }
} }
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);
}
//add top-level configuration
scanned.add(ContainerHandlers.class);
return scanned;
}
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -1,13 +1,19 @@
package org.gcube.smartgears.context.application; package org.gcube.smartgears.context.application;
import javax.servlet.ServletContext; import java.nio.file.Path;
import java.util.List;
import org.gcube.common.events.Hub; import org.gcube.common.events.Hub;
import org.gcube.common.security.factories.AuthorizationProvider;
import org.gcube.common.security.secrets.Secret;
import org.gcube.smartgears.configuration.application.ApplicationConfiguration; import org.gcube.smartgears.configuration.application.ApplicationConfiguration;
import org.gcube.smartgears.context.Properties; import org.gcube.smartgears.context.Properties;
import org.gcube.smartgears.context.container.ContainerContext; import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.lifecycle.application.ApplicationLifecycle; import org.gcube.smartgears.lifecycle.application.ApplicationLifecycle;
import org.gcube.smartgears.persistence.Persistence; import org.gcube.smartgears.persistence.PersistenceWriter;
import org.gcube.smartgears.security.secrets.SecretFactory;
import jakarta.servlet.ServletContext;
/** /**
* The management context of an application. * The management context of an application.
@ -34,9 +40,6 @@ public interface ApplicationContext {
*/ */
ApplicationConfiguration configuration(); ApplicationConfiguration configuration();
<T> T profile(Class<T> type);
/** /**
* Returns the lifecycle of the application. * Returns the lifecycle of the application.
* *
@ -56,7 +59,7 @@ public interface ApplicationContext {
* *
* @return the manager * @return the manager
*/ */
Persistence persistence(); PersistenceWriter persistence();
/** /**
* Returns the servlet context of the application. * Returns the servlet context of the application.
@ -79,4 +82,24 @@ public interface ApplicationContext {
*/ */
Properties 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();
/**
* returns the list of factory secrets ordered by priority
*
* @returnA Linked List
*/
List<SecretFactory<? extends Secret>> allowedSecretFactories();
} }

View File

@ -1,16 +1,35 @@
package org.gcube.smartgears.context.application; 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.lang.reflect.InvocationTargetException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import org.gcube.common.events.Hub; import org.gcube.common.events.Hub;
import org.gcube.common.resources.gcore.GCoreEndpoint; import org.gcube.common.security.factories.AuthorizationProvider;
import org.gcube.common.security.secrets.Secret;
import org.gcube.smartgears.configuration.PersistenceConfiguration;
import org.gcube.smartgears.configuration.application.ApplicationConfiguration; import org.gcube.smartgears.configuration.application.ApplicationConfiguration;
import org.gcube.smartgears.context.Properties; import org.gcube.smartgears.context.Properties;
import org.gcube.smartgears.context.container.ContainerContext; import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.lifecycle.application.ApplicationLifecycle; import org.gcube.smartgears.lifecycle.application.ApplicationLifecycle;
import org.gcube.smartgears.persistence.Persistence; import org.gcube.smartgears.persistence.PersistenceWriter;
import org.gcube.smartgears.security.secrets.GCubeKeyCloakSecretFactory;
import org.gcube.smartgears.security.secrets.LegacyGCubeTokenSecretFactory;
import org.gcube.smartgears.security.secrets.SecretFactory;
import org.gcube.smartgears.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.servlet.ServletContext;
/** /**
* Default {@link ApplicationContext} implementation. * Default {@link ApplicationContext} implementation.
@ -20,13 +39,24 @@ import org.gcube.smartgears.persistence.Persistence;
*/ */
public class DefaultApplicationContext implements ApplicationContext { public class DefaultApplicationContext implements ApplicationContext {
private static Logger log = LoggerFactory.getLogger(DefaultApplicationContext.class);
private final ContainerContext container; private final ContainerContext container;
private final ServletContext sctx; private final ServletContext sctx;
private final ApplicationConfiguration configuration; private final ApplicationConfiguration configuration;
private final ApplicationLifecycle lifecycle; private final ApplicationLifecycle lifecycle;
private final Properties properties; private final Properties properties;
private final Hub hub; private final Hub hub;
private final PersistenceWriter persistenceWriter;
private final String id; private final String id;
private Path appSpecificConfigurationFolder;
private final static String APPS_CONFIG__DIR = "config/apps";
private static final List<SecretFactory<? extends Secret>> defaultSecretFactories =
new LinkedList<SecretFactory<? extends Secret>>(Arrays.asList(new GCubeKeyCloakSecretFactory(), new LegacyGCubeTokenSecretFactory()));
private List<SecretFactory<? extends Secret>> allowedSecretFactories = null;
/** /**
* Crates an intance with mandatory parameters * Crates an intance with mandatory parameters
@ -37,14 +67,59 @@ public class DefaultApplicationContext implements ApplicationContext {
* @param lifecycle the lifecycle * @param lifecycle the lifecycle
* @param properties the properties * @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.id = id;
this.container=container; this.container=container;
this.sctx = sctx; this.sctx = sctx;
this.configuration=configuration; this.configuration=configuration;
this.hub=hub; this.hub=hub;
this.lifecycle = lifecycle; this.lifecycle = lifecycle;
this.appSpecificConfigurationFolder = getApplicationSpecificConfig();
this.properties=properties; this.properties=properties;
if (this.configuration.allowedSecrets()!=null && this.configuration.allowedSecrets().size()>0) {
this.allowedSecretFactories = new LinkedList<SecretFactory<? extends Secret>>();
for (String clazz : this.configuration.allowedSecrets() )
try {
Object obj = Class.forName(clazz).getConstructor().newInstance();
@SuppressWarnings("unchecked")
SecretFactory<? extends Secret> factory = SecretFactory.class.cast(obj);
this.allowedSecretFactories.add(factory);
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException | NoSuchMethodException | SecurityException
| ClassNotFoundException e) {
log.warn("declared secret factory {} instantiation error",clazz, e);
} catch (ClassCastException cc) {
log.warn("declared secret factory {} is not implementation of SecretFacory class",clazz, cc);
}
}
if (this.allowedSecretFactories == null || this.allowedSecretFactories.size()==0 )
this.allowedSecretFactories = defaultSecretFactories;
} }
/** /**
@ -52,7 +127,20 @@ public class DefaultApplicationContext implements ApplicationContext {
* @param context the other instance * @param context the other instance
*/ */
public DefaultApplicationContext(ApplicationContext context) { 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()), context.allowedSecretFactories());
}
private DefaultApplicationContext(String id, PersistenceWriter writer, ContainerContext container,ServletContext sctx,ApplicationConfiguration configuration, Hub hub, ApplicationLifecycle lifecycle, Properties properties, List<SecretFactory<? extends Secret>> allowedSecretFactories) {
this.id = id;
this.container=container;
this.sctx = sctx;
this.configuration=configuration;
this.hub=hub;
this.lifecycle = lifecycle;
this.properties=properties;
this.persistenceWriter = writer;
this.allowedSecretFactories = allowedSecretFactories;
} }
@Override @Override
@ -65,16 +153,6 @@ public class DefaultApplicationContext implements ApplicationContext {
return container; 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 @Override
public String name() { //little shortcut for ease of logging public String name() { //little shortcut for ease of logging
return configuration.name(); return configuration.name();
@ -96,8 +174,8 @@ public class DefaultApplicationContext implements ApplicationContext {
} }
@Override @Override
public Persistence persistence() { public PersistenceWriter persistence() {
return configuration.persistence(); return persistenceWriter;
} }
@Override @Override
@ -110,4 +188,45 @@ public class DefaultApplicationContext implements ApplicationContext {
return id; 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");
Path appSpecificConfigurationPath = Paths.get(home, APPS_CONFIG__DIR, this.sctx.getContextPath());
File appSpecificConfiguration = appSpecificConfigurationPath.toFile();
if (!(appSpecificConfiguration.exists() && appSpecificConfiguration.isDirectory() && appSpecificConfiguration.canRead())) {
log.warn("specific configuration folder for {} not found", this.sctx.getContextPath());
return null;
}
log.info("reading specific app configuration folder @ {} ", appSpecificConfiguration.getAbsolutePath());
return appSpecificConfigurationPath;
}
@Override
public List<SecretFactory<? extends Secret>> allowedSecretFactories() {
return this.allowedSecretFactories;
}
} }

View File

@ -1,10 +1,11 @@
package org.gcube.smartgears.context.container; package org.gcube.smartgears.context.container;
import org.gcube.common.events.Hub; import org.gcube.common.events.Hub;
import org.gcube.common.security.factories.AuthorizationProvider;
import org.gcube.smartgears.configuration.container.ContainerConfiguration; import org.gcube.smartgears.configuration.container.ContainerConfiguration;
import org.gcube.smartgears.context.Properties; import org.gcube.smartgears.context.Properties;
import org.gcube.smartgears.lifecycle.container.ContainerLifecycle; import org.gcube.smartgears.lifecycle.container.ContainerLifecycle;
import org.gcube.smartgears.persistence.Persistence; import org.gcube.smartgears.persistence.PersistenceWriter;
/** /**
* The management context of the container. * The management context of the container.
@ -21,11 +22,6 @@ public interface ContainerContext {
*/ */
ContainerConfiguration configuration(); 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 * Returns the lifecycle of the container
@ -43,7 +39,7 @@ public interface ContainerContext {
* Returns the persistence manager of the container. * Returns the persistence manager of the container.
* @return the manager * @return the manager
*/ */
Persistence persistence(); PersistenceWriter persistenceWriter();
/** /**
* Returns the properties of the container. * Returns the properties of the container.
@ -53,4 +49,8 @@ public interface ContainerContext {
String id(); String id();
AuthorizationProvider authorizationProvider();
} }

View File

@ -1,12 +1,21 @@
package org.gcube.smartgears.context.container; 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.events.Hub;
import org.gcube.common.resources.gcore.HostingNode; import org.gcube.common.security.factories.AuthorizationProvider;
import org.gcube.smartgears.configuration.PersistenceConfiguration;
import org.gcube.smartgears.configuration.container.ContainerConfiguration; import org.gcube.smartgears.configuration.container.ContainerConfiguration;
import org.gcube.smartgears.context.Properties; import org.gcube.smartgears.context.Properties;
import org.gcube.smartgears.lifecycle.container.ContainerLifecycle; import org.gcube.smartgears.lifecycle.container.ContainerLifecycle;
import org.gcube.smartgears.persistence.Persistence; import org.gcube.smartgears.persistence.PersistenceWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* Default {@link ContainerContext} implementation. * Default {@link ContainerContext} implementation.
@ -16,11 +25,15 @@ import org.gcube.smartgears.persistence.Persistence;
*/ */
public class DefaultContainerContext implements ContainerContext { public class DefaultContainerContext implements ContainerContext {
private static Logger log = LoggerFactory.getLogger(DefaultContainerContext.class);
private final ContainerConfiguration configuration; private final ContainerConfiguration configuration;
private final ContainerLifecycle lifecycle; private final ContainerLifecycle lifecycle;
private final Properties properties; private final Properties properties;
private final Hub hub; private final Hub hub;
private final AuthorizationProvider authorizationProvider;
private final String id; private final String id;
private final PersistenceWriter persistenceWriter;
/** /**
* Creates an instance with mandatory parameters. * Creates an instance with mandatory parameters.
* @param configuration the configuration * @param configuration the configuration
@ -28,23 +41,49 @@ public class DefaultContainerContext implements ContainerContext {
* @param lifecycle the lifecycle * @param lifecycle the lifecycle
* @param properties the properties * @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) { Properties properties) {
this.id = id;
this.configuration=configuration; this.configuration=configuration;
this.hub=hub; this.hub=hub;
this.lifecycle = lifecycle; this.lifecycle = lifecycle;
this.properties=properties; 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);
} }
@SuppressWarnings("all") File file = persistenceWriter.file(container_profile_file_path);
public <T> T profile(Class<T> type) {
if (type==HostingNode.class) String id = null;
return (T) properties().lookup(container_profile_property).value(HostingNode.class); 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);
}
throw new IllegalArgumentException("unsupported profile type: "+type); }
}; if (id == null) {
id = UUID.randomUUID().toString();
log.info("container id created is {}", id);
}
this.id = id;
}
/*
public HostingNode profile() {
return properties().lookup(container_profile_property).value(HostingNode.class);
};*/
@Override @Override
public ContainerConfiguration configuration() { public ContainerConfiguration configuration() {
@ -62,8 +101,8 @@ public class DefaultContainerContext implements ContainerContext {
} }
@Override @Override
public Persistence persistence() { public PersistenceWriter persistenceWriter() {
return configuration.persistence(); return persistenceWriter;
} }
@Override @Override
@ -76,6 +115,8 @@ public class DefaultContainerContext implements ContainerContext {
return id; return id;
} }
public AuthorizationProvider authorizationProvider() {
return authorizationProvider;
}
} }

View File

@ -4,12 +4,12 @@ import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.Set; import java.util.Set;
import javax.servlet.ServletException; import jakarta.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.gcube.smartgears.Constants; 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}. * A resource-specifc API handled by an {@link HttpController}.
@ -53,8 +53,8 @@ public abstract class ApiResource extends HttpExtension {
} }
@Override @Override
public Set<Exclude> excludes() { public Set<GCubeExclude> excludes() {
return Collections.singleton(new Exclude(Constants.root_mapping+mapping())); return Collections.singleton(new GCubeExclude(Constants.root_mapping+mapping()));
} }
/** /**

View File

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

View File

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

View File

@ -1,17 +1,15 @@
package org.gcube.smartgears.extensions; 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.HashSet;
import java.util.Set; import java.util.Set;
import javax.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServlet;
import javax.xml.bind.annotation.XmlAttribute;
import org.gcube.common.validator.annotations.NotEmpty; 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; import org.gcube.smartgears.context.application.ApplicationContext;
/** /**
* An {@link ApplicationExtension} that implements the {@link HttpServlet} interface * 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 { 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;
} }
@XmlAttribute @NotEmpty
public String getValue() {
return this.value;
}
}
@NotEmpty
private String name; private String name;
@XmlAttribute @NotEmpty @NotEmpty
private String mapping; private String mapping;
private ApplicationContext context; private ApplicationContext context;
@ -50,7 +66,7 @@ public abstract class HttpExtension extends HttpServlet implements ApplicationEx
} }
//extensions use init(context) instead //extensions use init(context) instead
public final void init() throws javax.servlet.ServletException { public final void init() throws jakarta.servlet.ServletException {
}; };
@Override @Override
@ -59,8 +75,11 @@ public abstract class HttpExtension extends HttpServlet implements ApplicationEx
} }
@Override @Override
public Set<Exclude> excludes() { public void stop() {}
return new HashSet<Exclude>(); //all managed by default
@Override
public Set<GCubeExclude> excludes() {
return new HashSet<GCubeExclude>(); //all managed by default
} }
protected ApplicationContext context() { protected ApplicationContext context() {

View File

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

View File

@ -1,17 +1,16 @@
package org.gcube.smartgears.extensions.resource; 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 static org.gcube.smartgears.extensions.HttpExtension.Method.GET;
import java.io.IOException; import java.io.IOException;
import javax.servlet.ServletException; import jakarta.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; 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.ApplicationConfiguration;
import org.gcube.smartgears.configuration.application.BridgedApplicationConfiguration;
import org.gcube.smartgears.extensions.ApiResource; import org.gcube.smartgears.extensions.ApiResource;
import org.gcube.smartgears.extensions.ApiSignature; import org.gcube.smartgears.extensions.ApiSignature;
@ -27,7 +26,7 @@ public class ConfigurationResource extends ApiResource {
public static final String mapping = "/configuration"; 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() { ConfigurationResource() {
super(signature); super(signature);
@ -37,8 +36,8 @@ public class ConfigurationResource extends ApiResource {
@Override @Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ApplicationConfiguration config = BridgedApplicationConfiguration.class.cast(context().configuration()).inner(); ApplicationConfiguration config = context().configuration();
Resources.marshal(config,resp.getWriter()); new ObjectMapper().writeValue(resp.getWriter(), config);
} }
} }

View File

@ -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.Constants.frontpage_file_path;
import static org.gcube.smartgears.extensions.HttpExtension.Method.GET; 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.handlers.application.request.RequestError.application_error;
import static org.gcube.smartgears.provider.ProviderFactory.provider;
import static org.gcube.smartgears.utils.Utils.closeSafely; import static org.gcube.smartgears.utils.Utils.closeSafely;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.servlet.ServletException; import jakarta.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; 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.ApiResource;
import org.gcube.smartgears.extensions.ApiSignature; import org.gcube.smartgears.extensions.ApiSignature;
import org.gcube.smartgears.provider.ProviderFactory;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -93,42 +88,12 @@ public class FrontPageResource extends ApiResource {
values.put("version", context().configuration().version()); values.put("version", context().configuration().version());
String infrastructure = context().container().configuration().infrastructure(); 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("infra", infrastructure);
values.put("vos", voValue.toString());
values.put("status", context().lifecycle().state().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; return values;
} }

View File

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

View File

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

View File

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

View File

@ -5,12 +5,10 @@ import static org.gcube.smartgears.extensions.HttpExtension.Method.GET;
import java.io.IOException; import java.io.IOException;
import javax.servlet.ServletException; import jakarta.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; 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.ApiResource;
import org.gcube.smartgears.extensions.ApiSignature; import org.gcube.smartgears.extensions.ApiSignature;
@ -36,7 +34,8 @@ public class ProfileResource extends ApiResource {
@Override @Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 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
} }
} }

View File

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

View File

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

View File

@ -4,20 +4,13 @@ import java.util.Collection;
public interface ProfilePublisher { public interface ProfilePublisher {
/** void addTo(Collection<String> contexts);
* Adds for the first time the current resource profile of the application in one or more scopes.
* @param scopes the scopes
*/
void addTo(Collection<String> tokens);
void addToAll(); void addToAll();
void update(); void update();
/** void removeFrom(Collection<String> contexts);
* Removes the application from one or more scopes.
* @param scopes the scopes
*/
void removeFrom(Collection<String> tokens);
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -1,72 +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;
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));
}
}
}

View File

@ -1,231 +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.configuration.Mode;
import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.handlers.ProfilePublisher;
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 ProfilePublisherImpl implements ProfilePublisher {
private static final Logger log = LoggerFactory.getLogger(ProfilePublisherImpl.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 ProfilePublisherImpl(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
*/
@Override
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();
log.debug("using context {}",contextCL.getClass().getSimpleName());
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]);
if (context.container().configuration().mode()!=Mode.root) Thread.currentThread().setContextClassLoader(ProfilePublisherImpl.class.getClassLoader());
profile = publisher.create(profile, resolveScopesFromTokens(tokens));
} catch (Exception e) {
rethrowUnchecked(e);
} finally{
SecurityTokenProvider.instance.set(previousToken);
if (context.container().configuration().mode()!=Mode.root) Thread.currentThread().setContextClassLoader(contextCL);
}
sharePublished(profile);
log.debug("shared profile with scopes {}", profile.scopes().asCollection());
}
@Override
public void addToAll() {
this.addTo(context.configuration().startTokens());
}
@Override
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();
log.debug("using context {}",contextCL.getClass().getSimpleName());
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]);
if (context.container().configuration().mode()!=Mode.root)
Thread.currentThread().setContextClassLoader(ProfilePublisherImpl.class.getClassLoader());
profile = publisher.update(profile);
} catch (Exception e) {
rethrowUnchecked(e);
} finally{
SecurityTokenProvider.instance.set(previousToken);
if (context.container().configuration().mode()!=Mode.root)
Thread.currentThread().setContextClassLoader(contextCL);
}
sharePublished(profile);
}
/**
* Removes the application from one or more scopes.
* @param scopes the scopes
*/
@Override
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();
log.debug("using context {}",contextCL.getClass().getSimpleName());
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]);
if (context.container().configuration().mode()!=Mode.root)
Thread.currentThread().setContextClassLoader(ProfilePublisherImpl.class.getClassLoader());
profile = publisher.remove(profile, resolveScopesFromTokens(tokens));
} catch (Exception e) {
rethrowUnchecked(e);
} finally{
SecurityTokenProvider.instance.set(previousToken);
if (context.container().configuration().mode()!=Mode.root)
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;
}
}

View File

@ -1,16 +1,10 @@
package org.gcube.smartgears.handlers.application.request; 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.UsageRecord.OperationResult;
import org.gcube.accounting.datamodel.usagerecords.ServiceUsageRecord; import org.gcube.accounting.datamodel.usagerecords.ServiceUsageRecord;
import org.gcube.accounting.persistence.AccountingPersistence; import org.gcube.accounting.persistence.AccountingPersistence;
import org.gcube.accounting.persistence.AccountingPersistenceFactory; import org.gcube.accounting.persistence.AccountingPersistenceFactory;
import org.gcube.common.authorization.library.provider.AuthorizationProvider; import org.gcube.common.security.providers.SecretManagerProvider;
import org.gcube.common.authorization.library.provider.SecurityTokenProvider;
import org.gcube.common.scope.api.ScopeProvider;
import org.gcube.smartgears.Constants; import org.gcube.smartgears.Constants;
import org.gcube.smartgears.configuration.Mode; import org.gcube.smartgears.configuration.Mode;
import org.gcube.smartgears.context.application.ApplicationContext; import org.gcube.smartgears.context.application.ApplicationContext;
@ -21,13 +15,13 @@ import org.gcube.smartgears.utils.InnerMethodName;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@XmlRootElement(name = Constants.request_accounting)
public class RequestAccounting extends RequestHandler { public class RequestAccounting extends RequestHandler {
private static Logger log = LoggerFactory.getLogger(RequestAccounting.class); 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 @Override
public String getName() { public String getName() {
@ -36,37 +30,41 @@ public class RequestAccounting extends RequestHandler {
@Override @Override
public void handleRequest(RequestEvent e) { public void handleRequest(RequestEvent e) {
ApplicationContext context = e.context(); ApplicationContext appContext = e.context();
String calledMethod = e.request().getHeader(called_method_header); String context = getContext(appContext);
if (calledMethod==null){
calledMethod = e.request().getRequestURI().substring(e.request().getContextPath().length()); if (InnerMethodName.get() == null) {
String calledMethod = e.request().getRequestURI().substring(e.request().getContextPath().length());
if (calledMethod.isEmpty()) if (calledMethod.isEmpty())
calledMethod = "/"; calledMethod = "/";
calledMethod = e.request().getMethod() + " " + 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()); startCallThreadLocal.set(System.currentTimeMillis());
log.info("REQUEST START ON {}:{}({}) CALLED FROM {}@{} IN SCOPE {} ",
context.configuration().name(),context.configuration().serviceClass(), InnerMethodName.instance.get(), log.info("REQUEST START ON {}:{}({}) CALLED FROM {}@{} IN SCOPE {} ", appContext.configuration().name(),
caller, e.request().getRemoteHost(), ScopeProvider.instance.get()); appContext.configuration().group(), InnerMethodName.get(), caller,
e.request().getRemoteHost(), context);
} }
@Override @Override
public void handleResponse(ResponseEvent e) { public void handleResponse(ResponseEvent e) {
ApplicationContext context = e.context(); ApplicationContext appContext = e.context();
boolean resetScope = false; try {
if (ScopeProvider.instance.get()==null && SecurityTokenProvider.instance.get()==null){
String infrastructure = e.context().container().configuration().infrastructure();
ScopeProvider.instance.set("/"+infrastructure);
resetScope = true;
}
String caller = AuthorizationProvider.instance.get()!=null? AuthorizationProvider.instance.get().getClient().getId(): "UNKNOWN"; String context = getContext(appContext);
String callerQualifier = AuthorizationProvider.instance.get()!=null? AuthorizationProvider.instance.get().getTokenQualifier(): "UNKNOWN";
String caller = SecretManagerProvider.get() != null
? SecretManagerProvider.get().getOwner().getId()
: UNKNOWN;
String callerQualifier = UNKNOWN;
// retieves caller Ip when there is a proxy // retieves caller Ip when there is a proxy
String callerIp = e.request().getHeader("x-forwarded-for"); String callerIp = e.request().getHeader("x-forwarded-for");
if (callerIp == null) if (callerIp == null)
@ -74,32 +72,55 @@ public class RequestAccounting extends RequestHandler {
boolean success = e.response().getStatus() < 400; boolean success = e.response().getStatus() < 400;
if (context.container().configuration().mode()!=Mode.offline) if (appContext.container().configuration().mode() != Mode.offline)
generateAccounting(caller,callerQualifier,callerIp==null?"UNKNOWN":callerIp , success, context); 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", log.info("REQUEST SERVED ON {}:{}({}) CALLED FROM {}@{} IN SCOPE {} {}(CODE {}) IN {} millis",
context.configuration().name(),context.configuration().serviceClass(), InnerMethodName.instance.get(), appContext.configuration().name(), appContext.configuration().group(),
caller, callerIp, ScopeProvider.instance.get(), success?"SUCCEDED":"FAILED", e.response().getStatus(), System.currentTimeMillis()-startCallThreadLocal.get()); 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(); startCallThreadLocal.remove();
InnerMethodName.instance.reset(); InnerMethodName.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(); AccountingPersistence persistence = AccountingPersistenceFactory.getPersistence();
ServiceUsageRecord serviceUsageRecord = new ServiceUsageRecord(); ServiceUsageRecord serviceUsageRecord = new ServiceUsageRecord();
try { try {
serviceUsageRecord.setConsumerId(caller); serviceUsageRecord.setConsumerId(caller);
serviceUsageRecord.setCallerQualifier(callerQualifier); serviceUsageRecord.setCallerQualifier(callerQualifier);
serviceUsageRecord.setScope(ScopeProvider.instance.get()); serviceUsageRecord.setScope(gcubeContext);
serviceUsageRecord.setServiceClass(context.configuration().serviceClass()); serviceUsageRecord.setServiceClass(appContext.configuration().group());
serviceUsageRecord.setServiceName(context.configuration().name()); serviceUsageRecord.setServiceName(appContext.configuration().name());
serviceUsageRecord.setHost(context.container().configuration().hostname()+":"+context.container().configuration().port()); serviceUsageRecord.setHost(appContext.container().configuration().hostname() + ":"
serviceUsageRecord.setCalledMethod(InnerMethodName.instance.get()); + appContext.container().configuration().port());
serviceUsageRecord.setCalledMethod(InnerMethodName.get());
serviceUsageRecord.setCallerHost(remoteHost); serviceUsageRecord.setCallerHost(remoteHost);
serviceUsageRecord.setOperationResult(success ? OperationResult.SUCCESS : OperationResult.FAILED); serviceUsageRecord.setOperationResult(success ? OperationResult.SUCCESS : OperationResult.FAILED);
serviceUsageRecord.setDuration(System.currentTimeMillis() - startCallThreadLocal.get()); serviceUsageRecord.setDuration(System.currentTimeMillis() - startCallThreadLocal.get());
@ -110,11 +131,17 @@ public class RequestAccounting extends RequestHandler {
} }
} }
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 @Override
public String toString() { public String toString() {
return getName(); return getName();
} }
} }

View File

@ -1,160 +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 java.util.Base64;
import javax.xml.bind.annotation.XmlRootElement;
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
import org.gcube.common.authorization.client.exceptions.ObjectNotFound;
import org.gcube.common.authorization.library.AuthorizationEntry;
import org.gcube.common.authorization.library.provider.AccessTokenProvider;
import org.gcube.common.authorization.library.provider.AuthorizationProvider;
import org.gcube.common.authorization.library.provider.ClientInfo;
import org.gcube.common.authorization.library.provider.ExternalServiceInfo;
import org.gcube.common.authorization.library.provider.SecurityTokenProvider;
import org.gcube.common.authorization.library.provider.UserInfo;
import org.gcube.common.authorization.library.utils.Caller;
import org.gcube.common.scope.api.ScopeProvider;
import org.gcube.common.scope.impl.ScopeBean;
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.gcube.smartgears.utils.GcubeJwt;
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);
String authHeader = call.request().getHeader(Constants.authorization_header);
log.trace("authorization header is {}",authHeader);
log.trace("token header is {}",token);
log.trace("scope header is {}",scope);
String retrievedUser = null;
String accessToken = null;
if (authHeader!=null && !authHeader.isEmpty()) {
if (authHeader.startsWith(BEARER_AUTH_PREFIX))
accessToken = authHeader.substring(BEARER_AUTH_PREFIX.length()).trim();
else if (token==null && authHeader.startsWith(BASIC_AUTH_PREFIX)) {
String basicAuthToken = authHeader.substring(BASIC_AUTH_PREFIX.length()).trim();
String decodedAuth = new String(Base64.getDecoder().decode(basicAuthToken.getBytes()));
String[] splitAuth = decodedAuth.split(":");
token = splitAuth[1];
retrievedUser = splitAuth[0];
}
}
//Gives priority to the umaToken
if (accessToken!=null) {
this.retreiveAndSetInfoUmaToken(accessToken, token, call);
} else if (token!=null)
this.retreiveAndSetInfoGcubeToken(token, retrievedUser, call);
else if (scope!=null)
ScopeProvider.instance.set(scope);
}
@Override
public void handleResponse(ResponseEvent e) {
SecurityTokenProvider.instance.reset();
AuthorizationProvider.instance.reset();
AccessTokenProvider.instance.reset();
ScopeProvider.instance.reset();
log.debug("resetting all the Thread local for this call.");
}
private void retreiveAndSetInfoGcubeToken(String token, String retrievedUser, RequestEvent call){
log.trace("retrieving context using token {} ", token);
AuthorizationEntry authEntry = null;
try{
authEntry = authorizationService().get(token);
if (retrievedUser != null && !authEntry.getClientInfo().getId().equals(retrievedUser))
throw new Exception("user and token owner are not the same");
}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());
}
private void retreiveAndSetInfoUmaToken(String accessToken, String gcubeToken, RequestEvent call){
log.debug("using UMA token for authorization");
log.trace("retrieving context using uma token {} ", accessToken);
AccessTokenProvider.instance.set(accessToken);
parseAccessTokenAndSet(accessToken);
log.info("retrieved request authorization info {} in scope {} ", AuthorizationProvider.instance.get(), ScopeProvider.instance.get());
}
private void parseAccessTokenAndSet(String umaToken) {
String realUmaTokenEncoded = umaToken.split("\\.")[1];
String realUmaToken = new String(Base64.getDecoder().decode(realUmaTokenEncoded.getBytes()));
ObjectMapper mapper = new ObjectMapper();
GcubeJwt jwt = null;
try {
jwt = mapper.readValue(realUmaToken, GcubeJwt.class);
}catch(Exception e){
log.error("error decoding uma token",e);
internal_server_error.fire("error parsing access token");
}
ScopeBean scopeBean = null;
try {
scopeBean = new ScopeBean(jwt.getContext());
}catch(Exception e){
log.error("error decoding uma token",e);
internal_server_error.fire("invalid context in access token");
}
ClientInfo clientInfo;
if (!jwt.isExternalService())
clientInfo = new UserInfo(jwt.getUsername(), jwt.getRoles(), jwt.getEmail(), jwt.getFirstName(), jwt.getLastName());
else
clientInfo = new ExternalServiceInfo(jwt.getUsername(), "unknown", jwt.getRoles());
log.info("caller type is {}",clientInfo.getType());
AuthorizationProvider.instance.set(new Caller(clientInfo, "token"));
ScopeProvider.instance.set(scopeBean.toString());
}
}

View File

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

View File

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

View File

@ -1,47 +1,33 @@
package org.gcube.smartgears.handlers.application.request; package org.gcube.smartgears.handlers.application.request;
import static org.gcube.common.authorization.client.Constants.authorizationService;
import static org.gcube.smartgears.handlers.application.request.RequestError.application_failed_error; 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.application_unavailable_error;
import static org.gcube.smartgears.handlers.application.request.RequestError.invalid_request_error; import static org.gcube.smartgears.handlers.application.request.RequestError.invalid_request_error;
import java.util.Collections; import java.util.Objects;
import java.util.List; import java.util.Set;
import javax.xml.bind.annotation.XmlAttribute; import org.gcube.common.security.ContextBean;
import javax.xml.bind.annotation.XmlRootElement; import org.gcube.common.security.ContextBean.Type;
import org.gcube.common.security.providers.SecretManagerProvider;
import org.gcube.common.authorization.library.PolicyUtils; import org.gcube.common.security.secrets.Secret;
import org.gcube.common.authorization.library.policies.Policy;
import org.gcube.common.authorization.library.policies.User2ServicePolicy;
import org.gcube.common.authorization.library.policies.UserEntity;
import org.gcube.common.authorization.library.provider.AccessTokenProvider;
import org.gcube.common.authorization.library.provider.AuthorizationProvider;
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 org.gcube.smartgears.Constants; import org.gcube.smartgears.Constants;
import org.gcube.smartgears.configuration.Mode; import org.gcube.smartgears.configuration.Mode;
import org.gcube.smartgears.configuration.container.ContainerConfiguration; import org.gcube.smartgears.configuration.container.ContainerConfiguration;
import org.gcube.smartgears.context.application.ApplicationContext; import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.handlers.application.RequestEvent; import org.gcube.smartgears.handlers.application.RequestEvent;
import org.gcube.smartgears.handlers.application.RequestHandler; 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.secrets.SecretFactory;
import org.gcube.smartgears.security.secrets.exceptions.SecretNotFoundException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@XmlRootElement(name = Constants.request_validation)
public class RequestValidator extends RequestHandler { public class RequestValidator extends RequestHandler {
@XmlAttribute(required=false, name="oauth")
@Deprecated
boolean oauthCompatibility = false;
private static Logger log = LoggerFactory.getLogger(RequestValidator.class); private static Logger log = LoggerFactory.getLogger(RequestValidator.class);
private ApplicationContext context; private ApplicationContext appContext;
@Override @Override
public String getName() { public String getName() {
@ -53,27 +39,30 @@ public class RequestValidator extends RequestHandler {
log.trace("executing request validator ON REQUEST"); log.trace("executing request validator ON REQUEST");
log.trace("accessToken is null? {} \nGcubeToken is null ? {} \nscope rpvideris null? {}", appContext = call.context();
AccessTokenProvider.instance.get()==null,
SecurityTokenProvider.instance.get()==null,
ScopeProvider.instance.get()==null);
context = call.context(); SecretManagerProvider.set(getSecret(call));
validateAgainstLifecycle(call); validateAgainstLifecycle(call);
rejectUnauthorizedCalls(call); rejectUnauthorizedCalls(call);
if (context.container().configuration().mode()!=Mode.offline) { if (appContext.container().configuration().mode()!=Mode.offline) {
validateScopeCall(); validateScopeCall();
validatePolicy(ScopeProvider.instance.get(), call);
} }
} }
@Override
public void handleResponse(ResponseEvent e) {
log.debug("resetting all the Thread local for this call.");
SecretManagerProvider.reset();
}
private void validateAgainstLifecycle(RequestEvent call) { private void validateAgainstLifecycle(RequestEvent call) {
switch(context.lifecycle().state()) { switch(appContext.lifecycle().state()) {
case stopped : case stopped :
application_unavailable_error.fire(); break; application_unavailable_error.fire(); break;
@ -85,36 +74,36 @@ public class RequestValidator extends RequestHandler {
//nothing to do, but avoids warnings //nothing to do, but avoids warnings
} }
} }
private void validateScopeCall() { private void validateScopeCall() {
String scope = ScopeProvider.instance.get(); String context = SecretManagerProvider.get().getContext();
if (scope == null) { if (context == null) {
log.warn("rejecting unscoped call to {}",context.name()); log.warn("rejecting unscoped call to {}",appContext.name());
invalid_request_error.fire("call is unscoped"); invalid_request_error.fire("call is unscoped");
} }
ScopeBean bean = new ScopeBean(scope); ContextBean bean = new ContextBean(context);
ContainerConfiguration conf = context.container().configuration(); ContainerConfiguration conf = appContext.container().configuration();
if (!conf.allowedContexts().contains(scope) && Set<String> allowedContexts =appContext.authorizationProvider().getContexts();
!(conf.authorizeChildrenContext() && bean.is(Type.VRE) && conf.allowedContexts().contains(bean.enclosingScope().toString()) ) ) { if (!allowedContexts.contains(context) &&
log.warn("rejecting call to {} in invalid context {}, allowed context are {}",context.name(),scope,context.container().configuration().allowedContexts()); !(conf.authorizeChildrenContext() && bean.is(Type.VRE)
invalid_request_error.fire(context.name()+" cannot be called in scope "+scope); && 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){ private void rejectUnauthorizedCalls(RequestEvent call){
String token = SecurityTokenProvider.instance.get(); Secret secret = SecretManagerProvider.get();
String accessToken = AccessTokenProvider.instance.get();
if (token == null && accessToken==null){ if (secret == null){
log.warn("rejecting call to {}, authorization required",context.name(),token); log.warn("rejecting call to {}, authorization required",appContext.name());
RequestError.request_not_authorized_error.fire(context.name()+": authorization required"); RequestError.request_not_authorized_error.fire(appContext.name()+": authorization required");
} }
} }
@ -123,45 +112,32 @@ public class RequestValidator extends RequestHandler {
return getName(); return getName();
} }
private void validatePolicy(String scope, RequestEvent call){
log.info("accessing policy validator in scope {} ", scope);
ServiceIdentifier serviceIdentifier = Utils.getServiceInfo(call.context()).getServiceIdentifier(); private Secret getSecret(RequestEvent call){
String previousToken = SecurityTokenProvider.instance.get(); Secret secret = null;
for (SecretFactory<? extends Secret> factory: call.context().allowedSecretFactories()) {
try { try {
String serviceToken = context.configuration().startTokens().stream().findFirst().get(); secret = factory.create(call.request());
SecurityTokenProvider.instance.set(serviceToken); break;
String callerId = AuthorizationProvider.instance.get().getClient().getId(); } catch (SecretNotFoundException e) {
log.info("authorization for secret {} not found", factory.getSecretClass().getName());
List<Policy> policies = Collections.emptyList(); } catch (Throwable t) {
try { log.warn("generic error creating secret {}", factory.getSecretClass().getName(), t);
policies = authorizationService().getPolicies(scope);
}catch (Exception e) {
log.error("error contacting authorization services for policies");
}
for (Policy policy: policies) {
log.debug("policy: {}", policy.getPolicyAsString() );
if (PolicyUtils.isPolicyValidForClient(policy.getServiceAccess(), serviceIdentifier )) {
boolean toReject = false;
UserEntity entity = (((User2ServicePolicy) policy).getEntity());
if (entity.getIdentifier()!=null)
toReject = entity.getIdentifier().equals(callerId);
else if (entity.getExcludes().isEmpty())
toReject = true;
else toReject = !entity.getExcludes().contains(callerId);
if (toReject) {
log.error("rejecting call to {} : {} is not allowed to contact the service ",context.name(), callerId);
RequestError.request_not_authorized_error.fire("rejecting call to "+context.name()+" for polices: "+callerId+" is not allowed to contact the service: "+serviceIdentifier.getServiceName() );
}
} }
} }
}finally {
SecurityTokenProvider.instance.set(previousToken); if (Objects.isNull(secret))
} RequestError.request_not_authorized_error.fire("call not authorized");
if (!secret.isValid())
RequestError.request_not_authorized_error.fire("authorization with secret "+secret.getClass().getSimpleName()+": token not valid ");
if (call.context().container().configuration().checkTokenExpiration() && secret.isExpired())
RequestError.request_not_authorized_error.fire("authorization with secret "+secret.getClass().getSimpleName()+": token expired ");
return secret;
} }

View File

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

View File

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

View File

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

View File

@ -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)
*
*/
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];
}
}

View File

@ -1,294 +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.configuration.Mode;
import org.gcube.smartgears.context.Property;
import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.handlers.OfflineProfilePublisher;
import org.gcube.smartgears.handlers.ProfilePublisher;
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 = context.configuration().mode()!=Mode.offline?
new ProfilePublisherImpl(context):
new OfflineProfilePublisher();
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;
}
}

View File

@ -1,241 +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.configuration.Mode;
import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.handlers.ProfileEvents;
import org.gcube.smartgears.handlers.ProfilePublisher;
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 ProfilePublisherImpl implements ProfilePublisher {
private static final Logger log = LoggerFactory.getLogger(ProfilePublisherImpl.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 ProfilePublisherImpl(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();
log.debug("using context {}",contextCL.getClass().getSimpleName());
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]);
if (context.configuration().mode()!=Mode.root)
Thread.currentThread().setContextClassLoader(ProfilePublisherImpl.class.getClassLoader());
profile = publisher.create(profile, resolveScopesFromTokens(tokens));
} catch (Exception e) {
rethrowUnchecked(e);
} finally {
SecurityTokenProvider.instance.set(previousToken);
if (context.configuration().mode()!=Mode.root)
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();
log.debug("using context {}",contextCL.getClass().getSimpleName());
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]);
if (context.configuration().mode()!=Mode.root)
Thread.currentThread().setContextClassLoader(ProfilePublisherImpl.class.getClassLoader());
profile = publisher.update(profile);
} catch (Exception e) {
rethrowUnchecked(e);
} finally {
SecurityTokenProvider.instance.set(previousToken);
if (context.configuration().mode()!=Mode.root)
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();
log.debug("using context {}",contextCL.getClass().getSimpleName());
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]);
if (context.configuration().mode()!=Mode.root)
Thread.currentThread().setContextClassLoader(ProfilePublisherImpl.class.getClassLoader());
profile = publisher.remove(profile, resolveScopesFromTokens(tokens));
} catch (Exception e) {
rethrowUnchecked(e);
} finally {
SecurityTokenProvider.instance.set(previousToken);
if (context.configuration().mode()!=Mode.root)
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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -12,36 +12,27 @@ import static org.gcube.smartgears.provider.ProviderFactory.provider;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.stream.Collectors;
import javax.servlet.FilterRegistration; import jakarta.servlet.FilterRegistration;
import javax.servlet.ServletContext; import jakarta.servlet.ServletContext;
import javax.servlet.ServletContextEvent; import jakarta.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener; import jakarta.servlet.ServletContextListener;
import javax.servlet.ServletRegistration; 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.common.events.Observes;
import org.gcube.smartgears.Constants;
import org.gcube.smartgears.configuration.Mode;
import org.gcube.smartgears.configuration.application.ApplicationExtensions;
import org.gcube.smartgears.configuration.application.ApplicationHandlers; import org.gcube.smartgears.configuration.application.ApplicationHandlers;
import org.gcube.smartgears.context.application.ApplicationContext; import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.context.container.ContainerContext; import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.extensions.ApplicationExtension; import org.gcube.smartgears.extensions.ApplicationExtension;
import org.gcube.smartgears.extensions.RequestExceptionBarrier; 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.ApplicationLifecycleEvent;
import org.gcube.smartgears.handlers.application.ApplicationLifecycleHandler; import org.gcube.smartgears.handlers.application.ApplicationLifecycleHandler;
import org.gcube.smartgears.handlers.application.ApplicationPipeline; import org.gcube.smartgears.handlers.application.ApplicationPipeline;
import org.gcube.smartgears.handlers.application.RequestHandler; import org.gcube.smartgears.handlers.application.RequestHandler;
import org.gcube.smartgears.lifecycle.application.ApplicationLifecycle; import org.gcube.smartgears.lifecycle.application.ApplicationLifecycle;
import org.gcube.smartgears.lifecycle.container.ContainerLifecycle; import org.gcube.smartgears.lifecycle.container.ContainerLifecycle;
import org.gcube.smartgears.utils.Utils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -59,6 +50,8 @@ public class ApplicationManager {
private ApplicationContext context; private ApplicationContext context;
private List<ApplicationExtension> extensions;
/** /**
* Starts application management. * Starts application management.
* *
@ -75,18 +68,8 @@ public class ApplicationManager {
for (Entry<String,? extends ServletRegistration> servlet : application.getServletRegistrations().entrySet()) for (Entry<String,? extends ServletRegistration> servlet : application.getServletRegistrations().entrySet())
log.trace("servlet {} : {} {} ", application.getServletContextName(),servlet.getKey(), servlet.getValue().getMappings()); log.trace("servlet {} : {} {} ", application.getServletContextName(),servlet.getKey(), servlet.getValue().getMappings());
/* 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()));
*/
if (context.container().configuration().mode()!=Mode.offline) {
context.configuration().startTokens(generateTokensForApplication(container).stream().collect(Collectors.toSet()));
context.configuration().validate(); context.configuration().validate();
}
saveApplicationState(); saveApplicationState();
// make context available to application in case it is gcube-aware // make context available to application in case it is gcube-aware
@ -96,13 +79,8 @@ public class ApplicationManager {
registerObservers(); registerObservers();
ApplicationHandlers handlers = provider().handlersFor(context); ApplicationHandlers handlers = provider().handlersFor(context);
handlers.validate();
ApplicationExtensions extensions = provider().extensionsFor(context);
extensions.validate();
List<ApplicationLifecycleHandler> lifecycleHandlers = handlers.lifecycleHandlers(); List<ApplicationLifecycleHandler> lifecycleHandlers = handlers.lifecycleHandlers();
List<RequestHandler> requestHandlers = handlers.requestHandlers(); List<RequestHandler> requestHandlers = handlers.requestHandlers();
@ -111,11 +89,12 @@ public class ApplicationManager {
log.trace("managing {} requests with {}", context.name(), requestHandlers); log.trace("managing {} requests with {}", context.name(), requestHandlers);
log.trace("extending {} API with {}", context.name(), extensions); log.trace("extending {} API with {}", context.name(), extensions);
extensions = provider().extensionsFor(context);
// order is important here: first add APIs // order is important here: first add APIs
register(extensions); registerExtension(extensions);
// then intercept them all // then intercept them all
register(requestHandlers); registerHandlersAsFilter(requestHandlers);
// start lifecycle management // start lifecycle management
start(lifecycleHandlers); start(lifecycleHandlers);
@ -123,6 +102,7 @@ public class ApplicationManager {
//adding the context name to the configuration //adding the context name to the configuration
context.configuration().context(application.getContextPath()); context.configuration().context(application.getContextPath());
// we're in business // we're in business
context.lifecycle().moveTo(active); context.lifecycle().moveTo(active);
@ -143,43 +123,8 @@ public class ApplicationManager {
} }
private List<String> generateTokensForApplication(ContainerContext container){
log.info("generating token for app {}",context.configuration().name());
SecurityTokenProvider.instance.set(container.configuration().startTokens().get(0));
try {
AuthorizationProxy authProxy = provider().authorizationProxy();
try {
return authProxy.generateServiceToken(Utils.getServiceInfo(context), container.configuration().startTokens());
}catch (Exception e) {
log.error("error generating service token",e);
throw new RuntimeException(e);
}
} catch (Exception e) {
throw new RuntimeException("error contacting authorization service",e);
} finally{
SecurityTokenProvider.instance.reset();
}
}
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() { 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))){ try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file))){
oos.writeObject(context.id()); oos.writeObject(context.id());
}catch (Exception e) { }catch (Exception e) {
@ -206,6 +151,9 @@ public class ApplicationManager {
context.events().fire(context, ApplicationLifecycle.stop); context.events().fire(context, ApplicationLifecycle.stop);
if (extensions != null)
unregister(extensions);
stopLifecycleHandlers(); stopLifecycleHandlers();
log.info("stopping application events for {}", context.name()); log.info("stopping application events for {}", context.name());
@ -219,38 +167,25 @@ public class ApplicationManager {
} }
private void register(List<RequestHandler> rqHandlers) { private void registerHandlersAsFilter(List<RequestHandler> rqHandlers) {
ServletContext app = context.application(); ServletContext app = context.application();
// attach filters based on request pipeline to each servlet String appName = app.getContextPath().replace("/", "");
Collection<? extends ServletRegistration> servlets = app.getServletRegistrations().values();
for (ServletRegistration servlet : servlets) { RequestManager requestFilter = new RequestManager(context, appName, rqHandlers);
String name = servlet.getName(); FilterRegistration.Dynamic filter = app.addFilter(appName + "-filter", requestFilter);
if (name.equals("default") || name.equals("jsp")) // skip page-resolving servlets filter.addMappingForUrlPatterns(null, false, "/*");
continue;
for (String mapping : servlet.getMappings()) {
RequestManager requestFilter = new RequestManager(context, name, rqHandlers);
FilterRegistration.Dynamic filter = app.addFilter(name + "-filter-"+mapping.replaceAll("/", ""), requestFilter);
log.trace("filter {} for requestfilter {} in contextPath {} is null ?? {} ",name ,requestFilter, mapping, (filter==null));
filter.addMappingForUrlPatterns(null, false, mapping);
}
}
} }
private void register(ApplicationExtensions extensions) { private void registerExtension(List<ApplicationExtension> extensions) {
ServletContext application = context.application(); ServletContext application = context.application();
for (ApplicationExtension extension : extensions.extensions()) for (ApplicationExtension extension : extensions)
try { try {
@ -283,6 +218,14 @@ public class ApplicationManager {
} }
private void unregister(List<ApplicationExtension> extensions) {
for (ApplicationExtension extension : extensions)
extension.stop();
}
private void start(List<ApplicationLifecycleHandler> handlers) { private void start(List<ApplicationLifecycleHandler> handlers) {
try { try {
@ -320,28 +263,6 @@ public class ApplicationManager {
log.warn("cannot stop {} after container has stopped", context.name()); 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);
saveApplicationState();
}
@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);
saveApplicationState();
}
}; };
context.container().events().subscribe(observer); context.container().events().subscribe(observer);
@ -352,7 +273,8 @@ public class ApplicationManager {
@Override @Override
public void contextInitialized(ServletContextEvent sce) { public void contextInitialized(ServletContextEvent sce) {
log.info("initilizing context {} ",context.name()); 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()); log.info("webApp {} initialized ",context.name());
} }

View File

@ -10,22 +10,14 @@ import static org.gcube.smartgears.provider.ProviderFactory.provider;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; 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;
import org.gcube.common.events.Observes.Kind; import org.gcube.common.events.Observes.Kind;
import org.gcube.smartgears.configuration.Mode; import org.gcube.smartgears.configuration.Mode;
import org.gcube.smartgears.configuration.container.ContainerHandlers;
import org.gcube.smartgears.context.application.ApplicationContext; import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.context.container.ContainerContext; import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.handlers.ProfileEvents;
import org.gcube.smartgears.handlers.container.ContainerHandler; import org.gcube.smartgears.handlers.container.ContainerHandler;
import org.gcube.smartgears.handlers.container.ContainerLifecycleEvent; import org.gcube.smartgears.handlers.container.ContainerLifecycleEvent;
import org.gcube.smartgears.handlers.container.ContainerPipeline; import org.gcube.smartgears.handlers.container.ContainerPipeline;
@ -47,8 +39,6 @@ public class ContainerManager {
public static ContainerManager instance = new ContainerManager(); public static ContainerManager instance = new ContainerManager();
private AuthorizationProxy authProvider = provider().authorizationProxy();
private ContainerContext context; private ContainerContext context;
private ContainerPipeline pipeline; private ContainerPipeline pipeline;
@ -70,21 +60,17 @@ public class ContainerManager {
saveContainerState(); 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); context.lifecycle().moveTo(active);
log.trace("loading keys for starting token ...");
//loadKeyForToken(context.configuration().startTokens());
log.trace("keys loaded for starting token ...");
return context; return context;
} } catch (RuntimeException e) {
catch(RuntimeException e) {
log.error("cannot manage container (see cause)", e); log.error("cannot manage container (see cause)", e);
@ -96,12 +82,10 @@ public class ContainerManager {
} }
private void saveContainerState() { private void saveContainerState() {
File file = context.configuration().persistence().file(container_profile_file_path); File file = context.persistenceWriter().file(container_profile_file_path);
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file))) { try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file))) {
oos.writeObject(context.id()); oos.writeObject(context.id());
oos.writeObject(context.configuration().startTokens());
} catch (Exception e) { } catch (Exception e) {
log.error("error serializing cointainer state"); log.error("error serializing cointainer state");
throw new RuntimeException(e); throw new RuntimeException(e);
@ -112,59 +96,24 @@ public class ContainerManager {
private void validateContainer(ContainerContext context) { private void validateContainer(ContainerContext context) {
// List<String> tokensToRemove = new ArrayList<String>(); // List<String> tokensToRemove = new ArrayList<String>();
context.configuration().validate(); context.configuration().validate();
Set<String> foundContexts= new HashSet<String>(); Set<String> foundContexts;
try { try {
List<AuthorizationEntry> entries = authProvider.get(context.configuration().startTokens()); foundContexts = context.authorizationProvider().getContexts();
log.info("requesting auth on {} tokens returned {} entries", context.configuration().startTokens().size(),entries.size());
for (AuthorizationEntry entry : entries ) {
log.info("the container will be started in context {}",entry.getContext());
foundContexts.add(entry.getContext());
}
} catch (Exception e) { } catch (Exception e) {
log.error("error contacting auth service on container",e); log.error("error authorizing container", e);
throw new RuntimeException("error authorizing container, moving the container to failed", e);
} }
if (foundContexts.isEmpty()) { if (foundContexts.isEmpty()) {
log.error("no valid starting token are specified, moving the container to failed"); log.error("no valid contexts found, moving the container to failed");
throw new RuntimeException("no valid starting token are specified"); throw new RuntimeException("no valid contexts found, moving the container to failed");
} }
//context.configuration().startTokens().removeAll(tokensToRemove);
context.configuration().allowedContexts(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);
} catch (Exception e) {
log.error("error contacting authorization for token {}",token,e);
}
return null;
} }
public void manage(ApplicationContext app) { public void manage(ApplicationContext app) {
app.events().subscribe(this); app.events().subscribe(this);
} }
@Observes(value = { ApplicationLifecycle.failure, ApplicationLifecycle.stop }, kind = Kind.critical) @Observes(value = { ApplicationLifecycle.failure, ApplicationLifecycle.stop }, kind = Kind.critical)
@ -172,42 +121,6 @@ public class ContainerManager {
context.lifecycle().tryMoveTo(ContainerState.partActive); 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. * Stops container management on remote request.
* *
@ -217,18 +130,20 @@ public class ContainerManager {
stop(false); stop(false);
} }
/** /**
* Stops container management on remote request or container shutdown. * Stops container management on remote request or container shutdown.
* *
*/ */
public void stop(boolean 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 // 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. // if other app have been already notified, the container may already be
//apps still to notify will listen only on stop, hence won't react to this but will go down when their turn arrives. // 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) if (context == null)
return; return;
@ -246,16 +161,13 @@ public class ContainerManager {
context.events().stop(); context.events().stop();
Utils.scheduledServicePool.shutdownNow(); Utils.scheduledServicePool.shutdownNow();
} } catch (RuntimeException e) {
catch (RuntimeException e) {
log.warn("cannot stop container management (see cause)", e); log.warn("cannot stop container management (see cause)", e);
} }
} }
// helpers // helpers
private void startHandlers(List<ContainerHandler> handlers) { private void startHandlers(List<ContainerHandler> handlers) {
@ -273,7 +185,6 @@ public class ContainerManager {
} }
} }
private void stopHandlers() { private void stopHandlers() {
if (pipeline == null) if (pipeline == null)
@ -287,45 +198,31 @@ public class ContainerManager {
} }
/* /*
private void loadKeyForToken(List<String> tokens) { * private void loadKeyForToken(List<String> tokens) { String initialToken =
String initialToken = SecurityTokenProvider.instance.get(); * SecurityTokenProvider.instance.get();
*
//TODO: change this * //TODO: change this String filePath = "/tmp/keys"; File PathDirs = new
String filePath = "/tmp/keys"; * File(filePath+"/"); PathDirs.mkdirs(); try{ for (String token : tokens) {
File PathDirs = new File(filePath+"/"); * try{ SecurityTokenProvider.instance.set(token); File key =
PathDirs.mkdirs(); * authProvider.getSymmKey(filePath);
try{ * log.trace("loading key {} file name ",key.getAbsolutePath());
for (String token : tokens) { * log.trace("loaded key {} file name ",key.getAbsolutePath()); }catch(Exception
try{ * e){ log.warn("error loading key for token {}", token, e); } }
SecurityTokenProvider.instance.set(token); * loadFileIntoClasspath(PathDirs); }finally{
File key = authProvider.getSymmKey(filePath); * SecurityTokenProvider.instance.set(initialToken); } }
log.trace("loading key {} file name ",key.getAbsolutePath()); *
log.trace("loaded key {} file name ",key.getAbsolutePath()); *
}catch(Exception e){ * private void loadFileIntoClasspath(File file){ try { URL url =
log.warn("error loading key for token {}", token, e); * file.toURI().toURL();
} *
} * ClassLoader currentClassloader =
loadFileIntoClasspath(PathDirs); * Thread.currentThread().getContextClassLoader().getParent()==null?
}finally{ * Thread.currentThread().getContextClassLoader() :
SecurityTokenProvider.instance.set(initialToken); * Thread.currentThread().getContextClassLoader().getParent();
} *
} * URLClassLoader classLoader = (URLClassLoader)currentClassloader; Method
* method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
* method.setAccessible(true); method.invoke(classLoader, url); } catch
private void loadFileIntoClasspath(File file){ * (Exception ex) { log.error("error loading file into classpath",ex); } }
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);
}
}
*/ */
} }

View File

@ -2,12 +2,9 @@ package org.gcube.smartgears.managers;
public class ContextEvents { 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";
} }

View File

@ -8,19 +8,21 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.Filter; import jakarta.servlet.Filter;
import javax.servlet.FilterChain; import jakarta.servlet.FilterChain;
import javax.servlet.FilterConfig; import jakarta.servlet.FilterConfig;
import javax.servlet.ServletException; import jakarta.servlet.ServletException;
import javax.servlet.ServletRequest; import jakarta.servlet.ServletRequest;
import javax.servlet.ServletResponse; import jakarta.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.gcube.common.authorization.library.exception.AuthorizationException; import org.gcube.common.authorization.library.exception.AuthorizationException;
import org.gcube.smartgears.configuration.application.Exclude; import org.gcube.smartgears.Constants;
import org.gcube.smartgears.configuration.application.Include; 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.ApplicationContext;
import org.gcube.smartgears.context.application.DefaultApplicationContext; import org.gcube.smartgears.context.application.DefaultApplicationContext;
import org.gcube.smartgears.handlers.application.ApplicationPipeline; import org.gcube.smartgears.handlers.application.ApplicationPipeline;
@ -123,40 +125,49 @@ public class RequestManager implements Filter {
String query = request.getQueryString(); 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(); return Collections.emptyList();
String path = request.getServletPath()==null?"":request.getServletPath(); String path = request.getServletPath()==null?"":request.getServletPath();
path += request.getPathInfo() ==null?"":request.getPathInfo(); path += request.getPathInfo() ==null?"":request.getPathInfo();
log.trace("check which handler should be excluded {}", path);
log.debug("check wich handler should be excluded {}", path);
if (!context.configuration().excludes().isEmpty()) { 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(); String excludePath= exclude.getPath();
log.trace("exclude is {}",exclude); log.debug("exclude is {}",exclude);
if ( if ((WILDCARD).equals(excludePath) ||
(WILDCARD).equals(excludePath) ||
(excludePath.endsWith(WILDCARD) && path!=null && path.startsWith(excludePath.substring(0,excludePath.length()-2))) || (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))) excludePath.equals(path) || (path.endsWith("/") && excludePath.equals(path.substring(0, path.length()-1)))
){ ){
//ALL handler are filtered //ALL handler are filtered except for unfilterable
if (exclude.getHandlers().isEmpty()) return Collections.emptyList(); 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<>(); List<RequestHandler> filteredHandlers = new ArrayList<>();
for (RequestHandler rh : handlersToFilter){ for (RequestHandler rh : handlersToFilter)
if (!exclude.getHandlers().contains(rh.getName())) if (rh.isUnfiltrable() || !exclude.getHandlers().contains(rh.getName()))
filteredHandlers.add(rh); filteredHandlers.add(rh);
}
return filteredHandlers; return filteredHandlers;
} }
} }
} else if (!context.configuration().includes().isEmpty()) { } else if (!context.configuration().includes().isEmpty()) {
for (Include include : context.configuration().includes()){ for (GCubeInclude include : context.configuration().includes()){
String includePath= include.getPath(); String includePath= include.getPath();
log.trace("include is {}",include); log.trace("include is {}",include);
if ( if (
@ -168,16 +179,16 @@ public class RequestManager implements Filter {
if (include.getHandlers().isEmpty()) return handlersToFilter; if (include.getHandlers().isEmpty()) return handlersToFilter;
List<RequestHandler> filteredHandlers = new ArrayList<>(); List<RequestHandler> filteredHandlers = new ArrayList<>();
for (RequestHandler rh : handlersToFilter){ for (RequestHandler rh : handlersToFilter)
if (include.getHandlers().contains(rh.getName())) if (rh.isUnfiltrable() || include.getHandlers().contains(rh.getName()))
filteredHandlers.add(rh); filteredHandlers.add(rh);
}
return filteredHandlers; return filteredHandlers;
} }
} }
return new ArrayList<>(); return new ArrayList<>();
} }
log.trace("returning original handlers");
return handlersToFilter; 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 { private void handleError(HttpServletRequest request, HttpServletResponse response,Throwable t) throws IOException {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,66 +1,52 @@
package org.gcube.smartgears.provider; 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.configuration_file_path;
import static org.gcube.smartgears.Constants.container_configuraton_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_env;
import static org.gcube.smartgears.Constants.ghn_home_property; 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.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.InputStream; import java.io.InputStream;
import java.io.ObjectInputStream; import java.util.ArrayList;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List; 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.Hub;
import org.gcube.common.events.impl.DefaultHub; import org.gcube.common.events.impl.DefaultHub;
import org.gcube.common.scan.ClasspathScanner; import org.gcube.common.security.credentials.Credentials;
import org.gcube.common.scan.ClasspathScannerFactory; import org.gcube.common.security.factories.AuthorizationProvider;
import org.gcube.common.scan.matchers.NameMatcher; import org.gcube.common.security.factories.AuthorizationProviderFactory;
import org.gcube.common.scan.resources.ClasspathResource; import org.gcube.smartgears.configuration.SmartgearsConfiguration;
import org.gcube.informationsystem.publisher.RegistryPublisherFactory;
import org.gcube.informationsystem.publisher.ScopedPublisher;
import org.gcube.smartgears.configuration.Mode;
import org.gcube.smartgears.configuration.application.ApplicationConfiguration; import org.gcube.smartgears.configuration.application.ApplicationConfiguration;
import org.gcube.smartgears.configuration.application.ApplicationConfigurationBinder; 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.ApplicationHandlers;
import org.gcube.smartgears.configuration.application.BridgedApplicationConfiguration;
import org.gcube.smartgears.configuration.container.ContainerConfiguration; import org.gcube.smartgears.configuration.container.ContainerConfiguration;
import org.gcube.smartgears.configuration.container.ContainerConfigurationBinder; 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.Properties;
import org.gcube.smartgears.context.application.ApplicationContext; import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.context.application.DefaultApplicationContext; import org.gcube.smartgears.context.application.DefaultApplicationContext;
import org.gcube.smartgears.context.container.ContainerContext; import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.context.container.DefaultContainerContext; 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.application.ApplicationLifecycle;
import org.gcube.smartgears.lifecycle.container.ContainerLifecycle; 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.utils.Utils; import org.gcube.smartgears.utils.Utils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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. * Default implementation of the {@link Provider} interface.
* *
@ -74,105 +60,54 @@ public class DefaultProvider implements Provider {
private ContainerContext containerContext; private ContainerContext containerContext;
// TODO: do the same with applicationContext (with a map) // TODO: do the same with applicationContext (with a map)
protected DefaultProvider(){}; private File configFile = null;
protected DefaultProvider(File configFile) {
this.configFile = configFile;
}
List<Publisher> publishers;
protected DefaultProvider() {
};
@SuppressWarnings("unchecked")
@Override @Override
public ContainerContext containerContext() { public ContainerContext containerContext() {
if (containerContext == null) { if (containerContext == null) {
ContainerConfiguration configuration = containerConfiguration(); 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(); Hub hub = new DefaultHub();
ContainerLifecycle lifecycle = new ContainerLifecycle(hub); ContainerLifecycle lifecycle = new ContainerLifecycle(hub);
File file = configuration.persistence().file(container_profile_file_path); AuthorizationProviderFactory<?> authfactory = configuration.authorizationConfiguration()
.getAuthProviderFactory();
Credentials credentials = configuration.authorizationConfiguration().getCredentials();
String id = null; AuthorizationProvider authProvider = authfactory.connect(credentials);
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);
}
} containerContext = new DefaultContainerContext(configuration, hub, lifecycle, authProvider,
if (id==null){ new Properties());
id = UUID.randomUUID().toString();
log.info("container id created is {}",id);
}
if (tokens!=null)
configuration.startTokens(tokens);
containerContext = new DefaultContainerContext(id, configuration, hub, lifecycle, new Properties());
} }
return containerContext; return containerContext;
} }
@Override @Override
public ContainerHandlers containerHandlers() { public List<ContainerHandler> containerHandlers() {
try { 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(); ContainerConfigurationBinder binder = new ContainerConfigurationBinder();
ContainerHandlers defaultHandlers = binder.bindHandlers(config);
ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader(); 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"); log.trace("probably i'm in a webapp classloader");
currentClassLoader = currentClassLoader.getParent(); currentClassLoader = currentClassLoader.getParent();
} }
List<ContainerHandler> defaultHandlers = binder.bindHandlers(currentClassLoader);
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);
}
return defaultHandlers; return defaultHandlers;
@ -186,63 +121,20 @@ public class DefaultProvider implements Provider {
@Override @Override
public ApplicationContext contextFor(ContainerContext context, ServletContext application) { public ApplicationContext contextFor(ContainerContext context, ServletContext application) {
ApplicationConfiguration configuration = null;
ApplicationConfiguration embedded = configurationFor(application); ApplicationConfiguration embedded = configurationFor(application);
ApplicationConfiguration external = context.configuration().app(application.getContextPath());
// shouldn't happen: management shouldn't have started at all // shouldn't happen: management shouldn't have started at all
if (embedded==null && external==null) if (embedded == null)
throw new AssertionError("application @ " + application.getContextPath() + " is not distributed with " 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); + 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);
Hub hub = new DefaultHub(); Hub hub = new DefaultHub();
ApplicationLifecycle lifecycle = new ApplicationLifecycle(hub, configuration.name()); ApplicationLifecycle lifecycle = new ApplicationLifecycle(hub, embedded.name());
File file = bridgedConfiguration.persistence().file(profile_file_path); return new DefaultApplicationContext(context, application, embedded, hub, lifecycle,
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();
return new DefaultApplicationContext(id, context, application, bridgedConfiguration, hub, lifecycle,
new Properties()); new Properties());
} }
@ -251,128 +143,34 @@ public class DefaultProvider implements Provider {
try { 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(); 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(); 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"); log.trace("probably i'm in a webapp classloader");
currentClassLoader = currentClassLoader.getParent(); currentClassLoader = currentClassLoader.getParent();
} }
ApplicationHandlers defaultHandlers = binder.bindHandlers(currentClassLoader);
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; return defaultHandlers;
} catch (RuntimeException e) { } catch (RuntimeException e) {
throw new RuntimeException("cannot install handlers for application @ " + context.name()+" (see cause) ", e); throw new RuntimeException("cannot install handlers for application @ " + context.name() + " (see cause) ",
e);
} }
} }
public List<ApplicationExtension> extensionsFor(ApplicationContext context){
@Override return List.of(new RemoteResource());
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);
} catch (RuntimeException e) {
throw new RuntimeException("cannot install extensions 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);
}
} }
// helpers // helpers
private ApplicationConfiguration configurationFor(ServletContext application) { private ApplicationConfiguration configurationFor(ServletContext application) {
try { try {
@ -384,7 +182,7 @@ public class DefaultProvider implements Provider {
ApplicationConfigurationBinder binder = new ApplicationConfigurationBinder(); ApplicationConfigurationBinder binder = new ApplicationConfigurationBinder();
return binder.bind(config); return binder.load(config);
} catch (RuntimeException e) { } catch (RuntimeException e) {
@ -395,6 +193,8 @@ public class DefaultProvider implements Provider {
private ContainerConfiguration containerConfiguration() { private ContainerConfiguration containerConfiguration() {
if (configFile == null) {
String home = Utils.home(); String home = Utils.home();
if (home == null) if (home == null)
@ -404,67 +204,90 @@ public class DefaultProvider implements Provider {
File homeDir = new File(home); File homeDir = new File(home);
if (!(homeDir.exists() && homeDir.isDirectory() && homeDir.canRead() && homeDir.canWrite())) 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"); 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 config = new File(homeDir,container_configuraton_file_path); configFile = new File(homeDir, container_configuraton_file_path);
if (!(config.exists() && config.canRead())) log.trace("reading container configuration @ {} ", configFile.getAbsolutePath());
throw new IllegalStateException("invalid node configuration: file "+config.getAbsolutePath()+" does not exist or cannot be accessed");
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);
} }
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 { ContainerConfiguration configuration;
stream.close(); try (InputStream stream = new FileInputStream(configFile)) {
} configuration = new ContainerConfigurationBinder().load(stream);
catch(Exception e) { } catch (Exception e) {
log.warn("could not close stream when reading container configuration @ "+config.getAbsolutePath()+" (see cause)",e); throw new IllegalStateException(
"invalid node configuration: file " + configFile.getAbsolutePath() + " is invalid", e);
} }
return configuration; return configuration;
} }
@Override
public synchronized List<Publisher> publishers() {
if (this.publishers == null) {
Set<Class<?>> annotatedPublishers;
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());
}
/* /*
@Override * Collection<URL> urls =
public RegistryPublisher publisherFor(ContainerContext context) { * ClasspathHelper.forClassLoader(Thread.currentThread().getContextClassLoader()
return context.configuration().mode()==Mode.online? * ); urls.removeIf(url -> url.toString().endsWith(".so") ||
RegistryPublisherFactory.create(): new OfflinePublisher(); * url.toString().endsWith(".zip") );
} *
*
* ConfigurationBuilder reflectionConf = new
* ConfigurationBuilder().addUrls(urls).setScanners(new
* TypeAnnotationsScanner(), new SubTypesScanner());
*
* Reflections reflection = new Reflections(reflectionConf);
*
* = reflection.getTypesAnnotatedWith(SmartgearsProfilePublisher.class);
*/
@Override List<Publisher> foundPublishers = new ArrayList<Publisher>();
public RegistryPublisher publisherFor(ApplicationContext context) { for (Class<?> annotatedPublisher : annotatedPublishers) {
return context.configuration().mode()==Mode.online? try {
RegistryPublisherFactory.create(): new OfflinePublisher(); foundPublishers.add((Publisher) annotatedPublisher.getDeclaredConstructor().newInstance());
}*/ log.info("added class {} to publishers", annotatedPublisher);
} catch (Throwable e) {
@Override log.error("publisher class {} cannot be instantiated", annotatedPublisher.getCanonicalName(), e);
public ScopedPublisher publisherFor(ContainerContext context) {
return context.configuration().mode()==Mode.online? RegistryPublisherFactory.scopedPublisher()
: new OfflinePublisher();
}
@Override
public ScopedPublisher publisherFor(ApplicationContext context) {
return context.configuration().mode()==Mode.online? RegistryPublisherFactory.scopedPublisher()
: new OfflinePublisher();
}
@Override
public AuthorizationProxy authorizationProxy() {
return authorizationService();
} }
}
this.publishers = foundPublishers;
if (foundPublishers.isEmpty())
log.warn("no publishers found in classloader");
}
return this.publishers;
}
@Override
public SmartgearsConfiguration smartgearsConfiguration() {
ContainerConfigurationBinder binder = new ContainerConfigurationBinder();
return binder.loadSmartgearsProperty();
}
/*
* @Override public AuthorizationProvider authorizationProvider() { return
* containerContext.authorizationProvider(); }
*/
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -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 + "]";
}
}

View File

@ -0,0 +1,77 @@
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.factories.AuthorizationProvider;
import org.gcube.common.security.secrets.Secret;
import org.gcube.common.security.secrets.UmaTokenSecret;
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);
}
}
@Deprecated
public SimpleCredentials getCredentials() {
return credentials;
}
}

View File

@ -0,0 +1,43 @@
package org.gcube.smartgears.security.defaults;
import java.util.List;
import org.gcube.common.security.credentials.Credentials;
import org.gcube.common.security.factories.AuthorizationProviderFactory;
import org.gcube.common.validator.ValidationError;
import org.gcube.common.validator.ValidatorFactory;
import org.gcube.common.validator.annotations.NotEmpty;
import org.gcube.smartgears.security.SimpleCredentials;
public class DefaultAuthorizationProviderFactory implements AuthorizationProviderFactory<DefaultAuthorizationProvider>{
@NotEmpty
private String endpoint;
@Override
public DefaultAuthorizationProvider connect(Credentials credentials) {
if (!SimpleCredentials.class.isInstance(credentials))
throw new IllegalArgumentException("invalid credential type passed");
List<ValidationError> errors = ValidatorFactory.validator().validate(credentials);
if (!errors.isEmpty())
throw new IllegalArgumentException(String.format("invalid credential: %s", errors));
if (this.endpoint == null || this.endpoint.isEmpty())
throw new IllegalArgumentException("invalid enpoint passed");
return new DefaultAuthorizationProvider((SimpleCredentials)credentials, this.endpoint);
}
@Override
public String toString() {
return "DefaultAuthorizationProviderFactory [endpoint=" + endpoint + "]";
}
public String getEndpoint() {
return endpoint;
}
public void setEndpoint(String endpoint) {
this.endpoint = endpoint;
}
}

View File

@ -0,0 +1,28 @@
package org.gcube.smartgears.security.secrets;
import org.gcube.common.security.secrets.UmaTokenSecret;
import org.gcube.smartgears.Constants;
import org.gcube.smartgears.security.secrets.exceptions.SecretNotFoundException;
import jakarta.servlet.http.HttpServletRequest;
public class GCubeKeyCloakSecretFactory implements SecretFactory<UmaTokenSecret> {
private static final String BEARER_AUTH_PREFIX ="Bearer";
@Override
public UmaTokenSecret create(HttpServletRequest request) throws SecretNotFoundException {
String authHeader = request.getHeader(Constants.authorization_header);
String umaToken = null;
if (authHeader!=null && !authHeader.isEmpty() && authHeader.startsWith(BEARER_AUTH_PREFIX)) {
umaToken = authHeader.substring(BEARER_AUTH_PREFIX.length()).trim();
return new UmaTokenSecret(umaToken);
} else throw new SecretNotFoundException();
}
@Override
public Class<UmaTokenSecret> getSecretClass() {
return UmaTokenSecret.class;
}
}

View File

@ -0,0 +1,26 @@
package org.gcube.smartgears.security.secrets;
import static org.gcube.smartgears.Constants.token_header;
import java.util.Objects;
import org.gcube.common.security.secrets.GCubeSecret;
import org.gcube.smartgears.security.secrets.exceptions.SecretNotFoundException;
import jakarta.servlet.http.HttpServletRequest;
public class LegacyGCubeTokenSecretFactory implements SecretFactory<GCubeSecret> {
@Override
public GCubeSecret create(HttpServletRequest request) throws SecretNotFoundException {
String token = request.getParameter(token_header)==null? request.getHeader(token_header):request.getParameter(token_header);
if (Objects.isNull(token) || token.isBlank()) throw new SecretNotFoundException();
return new GCubeSecret(token);
}
@Override
public Class<GCubeSecret> getSecretClass() {
return GCubeSecret.class;
}
}

View File

@ -0,0 +1,14 @@
package org.gcube.smartgears.security.secrets;
import org.gcube.common.security.secrets.Secret;
import org.gcube.smartgears.security.secrets.exceptions.SecretNotFoundException;
import jakarta.servlet.http.HttpServletRequest;
public interface SecretFactory<T extends Secret> {
Class<T> getSecretClass();
T create(HttpServletRequest request) throws SecretNotFoundException;
}

View File

@ -0,0 +1,7 @@
package org.gcube.smartgears.security.secrets.exceptions;
public class SecretNotFoundException extends Exception {
private static final long serialVersionUID = 1L;
}

View File

@ -1,85 +0,0 @@
package org.gcube.smartgears.utils;
import java.io.IOException;
import javax.servlet.ServletException;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.ValveBase;
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.scope.api.ScopeProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class GcubeAccountingValve extends ValveBase {
private static Logger log = LoggerFactory.getLogger(GcubeAccountingValve.class);
private String infra;
private String serviceClass;
private String serviceName;
private String hostAndPort;
public void setInfra(String infra) {
this.infra = infra;
}
public void setServiceClass(String serviceClass) {
this.serviceClass = serviceClass;
}
public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}
public void setHostAndPort(String hostAndPort) {
this.hostAndPort = hostAndPort;
}
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
try {
String callerIp = request.getHeader("x-forwarded-for");
if (callerIp == null) {
callerIp = request.getRemoteAddr();
}
boolean success = response.getStatus()<400;
ScopeProvider.instance.set(infra);
AccountingPersistenceFactory.setFallbackLocation("/tmp");
AccountingPersistence persistence = AccountingPersistenceFactory.getPersistence();
ServiceUsageRecord serviceUsageRecord = new ServiceUsageRecord();
try{
serviceUsageRecord.setConsumerId("UNKNOWN");
serviceUsageRecord.setCallerQualifier("UNKNOWN");
serviceUsageRecord.setScope(infra);
serviceUsageRecord.setServiceClass(serviceClass);
serviceUsageRecord.setServiceName(serviceName);
serviceUsageRecord.setDuration(200l);
serviceUsageRecord.setHost(hostAndPort);
serviceUsageRecord.setCalledMethod(request.getRequestURI());
serviceUsageRecord.setCallerHost(callerIp);
serviceUsageRecord.setOperationResult(success?OperationResult.SUCCESS:OperationResult.FAILED);
persistence.account(serviceUsageRecord);
log.info("Request: {} {} {} {} ", infra, request.getContextPath(), request.getRequestURI(), success);
}catch(Exception ex){
log.warn("invalid record passed to accounting ",ex);
}finally {
ScopeProvider.instance.reset();
}
}catch (Exception e) {
log.error("error executing valve", e);
}
getNext().invoke(request, response);
}
}

View File

@ -1,86 +0,0 @@
package org.gcube.smartgears.utils;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.gcube.com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.gcube.com.fasterxml.jackson.annotation.JsonProperty;
@JsonIgnoreProperties(ignoreUnknown = true)
public class GcubeJwt {
protected final static List<String> MINIMAL_ROLES = Arrays.asList("Member");
@JsonProperty("aud")
private String context;
@JsonProperty("resource_access")
private Map<String, Roles> contextAccess = new HashMap<>();
@JsonProperty("preferred_username")
private String username;
@JsonProperty("given_name")
private String firstName;
@JsonProperty("family_name")
private String lastName;
@JsonProperty("clientId")
private String clientId;
@JsonProperty("email")
private String email;
public List<String> getRoles(){
return contextAccess.get(this.context) == null ? MINIMAL_ROLES : contextAccess.get(this.context).roles;
}
public String getContext() {
try {
return URLDecoder.decode(context, StandardCharsets.UTF_8.toString());
}catch (UnsupportedEncodingException e) {
return context;
}
}
public String getUsername() {
return username;
}
public boolean isExternalService() {
return clientId != null;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getEmail() {
return email;
}
@Override
public String toString() {
return "GcubeJwt [context=" + getContext() + ", roles=" + getRoles() + ", username=" + username
+ ", firstName=" + firstName + ", lastName=" + lastName + ", email=" + email + "]";
}
public static class Roles {
@JsonProperty("roles")
List<String> roles = new ArrayList<>();
}
}

View File

@ -1,40 +1,37 @@
package org.gcube.smartgears.utils; package org.gcube.smartgears.utils;
import org.gcube.common.authorization.library.provider.CalledMethodProvider;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
public class InnerMethodName { public class InnerMethodName {
public static InnerMethodName instance = new InnerMethodName(); private static Logger logger = LoggerFactory.getLogger(InnerMethodName.class);
private static Logger logger = LoggerFactory.getLogger(CalledMethodProvider.class);
// Thread local variable containing each thread's ID // Thread local variable containing each thread's ID
private static final InheritableThreadLocal<String> threadMethod = private static final InheritableThreadLocal<String> threadMethod =
new InheritableThreadLocal<String>() { new InheritableThreadLocal<String>() {
@Override protected String initialValue() { @Override protected String initialValue() {
return "UNKNOWN"; return null;
} }
}; };
private InnerMethodName(){} private InnerMethodName(){}
public String get(){ public static String get(){
String calledMethod = threadMethod.get(); String calledMethod = threadMethod.get();
logger.trace("getting InnerMethodName as "+calledMethod+" in thread "+Thread.currentThread().getId() ); logger.trace("getting InnerMethodName as "+calledMethod+" in thread "+Thread.currentThread().getId() );
return calledMethod; return calledMethod;
} }
public void set(String calledMethod){ public static void set(String calledMethod){
if (calledMethod==null) return; if (calledMethod==null) return;
threadMethod.set(calledMethod); threadMethod.set(calledMethod);
logger.trace("setting InnerMethodName as "+calledMethod+" in thread "+Thread.currentThread().getId() ); logger.trace("setting InnerMethodName as "+calledMethod+" in thread "+Thread.currentThread().getId() );
} }
public void reset(){ public static void reset(){
threadMethod.remove(); threadMethod.remove();
} }
} }

View File

@ -16,8 +16,8 @@ import java.util.Collection;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import javax.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.gcube.common.authorization.library.provider.ServiceIdentifier; import org.gcube.common.authorization.library.provider.ServiceIdentifier;
import org.gcube.common.authorization.library.provider.ServiceInfo; import org.gcube.common.authorization.library.provider.ServiceInfo;
@ -214,7 +214,7 @@ public class Utils {
public static ServiceInfo getServiceInfo(ApplicationContext application){ public static ServiceInfo getServiceInfo(ApplicationContext application){
String hostedin = String.format("%s_%d", application.container().configuration().hostname(), application.container().configuration().port()); String hostedin = String.format("%s_%d", application.container().configuration().hostname(), application.container().configuration().port());
return return
new ServiceInfo(new ServiceIdentifier(application.configuration().serviceClass(), application.configuration().name(), hostedin)); new ServiceInfo(new ServiceIdentifier(application.configuration().group(), application.configuration().name(), hostedin));
} }
public static Throwable getCause(Throwable e) { public static Throwable getCause(Throwable e) {

View File

@ -1 +0,0 @@
/smartgears-config.xml

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