Compare commits

..

No commits in common. "master" and "r4.15.0" have entirely different histories.

141 changed files with 7286 additions and 3885 deletions

43
.classpath Normal file
View File

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

4
.gitignore vendored
View File

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

View File

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

View File

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

View File

@ -1,26 +0,0 @@
# 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

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

View File

@ -1,59 +0,0 @@
# Common Smartgears
A core gCube library which empower a servlet container (e.g. tomcat) with a set of functionality such as:
- node and application infrastructure registration
- authorization
- accounting
## Built With
* [OpenJDK](https://openjdk.java.net/) - The JDK used
* [Maven](https://maven.apache.org/) - Dependency Management
## Documentation
[SmartGears](https://wiki.gcube-system.org/gcube/SmartGears)
## Change log
See [Releases](https://code-repo.d4science.org/gCubeSystem/common-smartgears/releases).
## Authors
* **Luca Frosini** ([ORCID](https://orcid.org/0000-0003-3183-2291)) - [ISTI-CNR Infrascience Group](http://nemis.isti.cnr.it/groups/infrascience)
* **Lucio Lelii** - [ISTI-CNR Infrascience Group](http://nemis.isti.cnr.it/groups/infrascience)
* **Fabio Simeoni** - FAO of the UN, Italy
## How to Cite this Software
Tell people how to cite this software.
* Cite an associated paper?
* Use a specific BibTeX entry for the software?
@Manual{,
title = {Common Smartgears},
author = {{Frosini, Luca}, {Lelii, Lucio}, {Simeoni, Fabio}},
organization = {{ISTI - CNR}, {FAO}},
address = {{Pisa, Italy}, {Roma, Italy}},
year = 2019,
url = {http://www.gcube-system.org/}
}
## License
This project is licensed under the EUPL V.1.1 License - see the [LICENSE.md](LICENSE.md) file for details.
## About the gCube Framework
This software is part of the [gCubeFramework](https://www.gcube-system.org/ "gCubeFramework"): an
open-source software toolkit used for building and operating Hybrid Data
Infrastructures enabling the dynamic deployment of Virtual Research Environments
by favouring the realisation of reuse oriented policies.
The projects leading to this software have received funding from a series of European Union programmes see [FUNDING.md](FUNDING.md)

4
distro/LICENSE Normal file
View File

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

70
distro/README Normal file
View File

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

68
distro/changelog.xml Normal file
View File

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

31
distro/descriptor.xml Normal file
View File

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

29
distro/profile.xml Normal file
View File

@ -0,0 +1,29 @@
<?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

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

BIN
endpoint.xml Normal file

Binary file not shown.

View File

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

252
pom.xml
View File

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

View File

@ -1,12 +1,10 @@
package org.gcube.smartgears;
import static java.util.Collections.emptyList;
import java.util.Set;
import jakarta.servlet.ServletContainerInitializer;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.context.container.ContainerContext;
@ -16,19 +14,6 @@ import org.gcube.smartgears.provider.ProviderFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.binder.jvm.ClassLoaderMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmInfoMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics;
import io.micrometer.core.instrument.binder.logging.LogbackMetrics;
import io.micrometer.core.instrument.binder.system.ProcessorMetrics;
import io.micrometer.core.instrument.binder.system.UptimeMetrics;
import io.micrometer.core.instrument.binder.tomcat.TomcatMetrics;
import io.micrometer.prometheus.PrometheusConfig;
import io.micrometer.prometheus.PrometheusMeterRegistry;
/**
* Bootstraps management of all deployed applications which require it.
*
@ -49,8 +34,6 @@ public class Bootstrap implements ServletContainerInitializer {
public Bootstrap() {
log.info("bootstrap started the container");
if (smartgearsHasStarted)
return;
@ -68,8 +51,6 @@ public class Bootstrap implements ServletContainerInitializer {
ApplicationManager appManager = new ApplicationManager();
log.info("check if is managed @ {}", application.getContextPath());
//act only on resources
if (isResource(application)) {
@ -91,40 +72,20 @@ public class Bootstrap implements ServletContainerInitializer {
+ " (see cause)", t);
}
} else log.info("is not managed @ {}", application.getContextPath());
}
}
// helpers
@SuppressWarnings("resource")
private void initialiseContainer() {
ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
try {
// TODO Ask why is needed?
Thread.currentThread().setContextClassLoader(ContainerManager.class.getClassLoader());
log.trace("smartgears is starting");
/* Get the ContainerContext. Look at DefaultProvider */
context = ProviderFactory.provider().containerContext();
PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
new ClassLoaderMetrics().bindTo(registry);
new JvmMemoryMetrics().bindTo(registry);
new JvmGcMetrics().bindTo(registry);
new ProcessorMetrics().bindTo(registry);
new JvmThreadMetrics().bindTo(registry);
new UptimeMetrics().bindTo(registry);
new ProcessorMetrics().bindTo(registry);
new TomcatMetrics(null, emptyList()).bindTo(registry);
new LogbackMetrics().bindTo(registry);
new JvmInfoMetrics().bindTo(registry);
Metrics.addRegistry(registry);
/* Validate the configuration retrieved by ContainerContext
* using gcube facilities annotation based
* ( i.e org.gcube.common.validator.annotations)
@ -139,8 +100,6 @@ public class Bootstrap implements ServletContainerInitializer {
//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;
import org.gcube.smartgears.extensions.resource.RemoteResource;
import org.gcube.smartgears.handlers.application.request.RequestAccounting;
import org.gcube.smartgears.handlers.application.lifecycle.ProfileManager;
import org.gcube.smartgears.handlers.application.request.RequestValidator;
import org.gcube.smartgears.handlers.container.lifecycle.AccountingManager;
@ -27,8 +27,8 @@ public class Constants {
/**
* The container configuration file path, relative to the container configuration directory.
*/
public static final String container_configuraton_file_path = "container.ini";
*/
public static final String container_configuraton_file_path = "container.xml";
/**
@ -36,15 +36,25 @@ public class Constants {
*/
public static final String container_profile_file_path = "ghn.xml";
/*
public static final String container_profile_file_path_copy = "ghn.xml.copy";
*/
/**
* The container lifecycle configuration resource path.
*/
public static final String container_handlers_file_path = "/META-INF/container-handlers.xml";
public static final String container_handlers_file_name = "gcube-container-handlers.xml";
/**
* The library configuration resource path.
*/
public static final String library_configuration_file_path = "/META-INF/smartgears-config.xml";
/**
* The name of the context property that contains the node profile.
*/
public static final String container_profile_property = "ghn-profile";
*/
/**
* The default value of for the container publication frequency.
@ -52,11 +62,24 @@ public class Constants {
public static final long default_container_publication_frequency_in_seconds = 60;
/**
* The application configuration resource path.
*/
public static final String configuration_file_path = "/WEB-INF/application.yaml";
public static final String configuration_file_path = "/WEB-INF/gcube-app.xml";
/**
* 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.
@ -69,6 +92,16 @@ public class Constants {
*/
public static final String root_mapping = "/gcube/resource";
/**
* The application extensions configuration resource path.
*/
public static final String extensions_file_path = "/WEB-INF/gcube-extensions.xml";
/**
* The default application extensions configuration resource path.
*/
public static final String default_extensions_file_path = "/META-INF/default-extensions.xml";
/**
* The application frontpage resource path.
*/
@ -84,11 +117,6 @@ public class Constants {
*/
public static final String request_validation = "request-validation";
/**
* The configuration name of {@link RequestMetrics}s.
*/
public static final String request_metrics = "request-metrics";
/**
* The configuration name of {@link RequestValidator}s.
*/
@ -112,6 +140,8 @@ public class Constants {
public static final String remote_management = "remote-management";
/**
* The path of the application profile file, relative to the service configuration directory.
*/

View File

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

View File

@ -1,11 +0,0 @@
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

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

View File

@ -1,14 +0,0 @@
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

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

View File

@ -1,37 +0,0 @@
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

@ -1,42 +0,0 @@
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

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,97 @@
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

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

View File

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

View File

@ -1,31 +1,18 @@
package org.gcube.smartgears.configuration.container;
import java.io.File;
import java.io.InputStream;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import static org.gcube.smartgears.utils.Utils.*;
import java.io.InputStream;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.ServiceLoader;
import java.util.Set;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import org.gcube.common.security.credentials.Credentials;
import org.gcube.smartgears.configuration.AuthorizationProviderConfiguration;
import org.gcube.smartgears.configuration.ComponentConfiguration;
import org.gcube.smartgears.configuration.ConfiguredWith;
import org.gcube.smartgears.configuration.PersistenceConfiguration;
import org.gcube.smartgears.configuration.ProxyAddress;
import org.gcube.smartgears.configuration.SmartgearsConfiguration;
import org.gcube.smartgears.handlers.container.ContainerHandler;
import org.gcube.smartgears.handlers.container.lifecycle.AccountingManager;
import org.gcube.smartgears.handlers.container.lifecycle.ContainerProfileManager;
import org.gcube.smartgears.persistence.LocalWriter;
import org.gcube.smartgears.persistence.LocalWriterConfiguration;
import org.gcube.smartgears.persistence.PersistenceWriter;
import org.gcube.smartgears.security.AuthorizationProviderFactory;
import org.gcube.smartgears.security.defaults.DefaultAuthorizationProviderFactory;
import org.gcube.smartgears.utils.Utils;
import org.ini4j.Ini;
import org.ini4j.Profile.Section;
/**
* Binds {@link ContainerConfiguration}s to and from XML serialisations.
@ -35,138 +22,34 @@ import org.ini4j.Profile.Section;
*/
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 {
Ini configurator = new Ini(stream);
ContainerConfiguration conf = new ContainerConfiguration();
Section nodeSection = configurator.get("node");
if (nodeSection != null) {
BaseConfiguration nodeConf = new BaseConfiguration();
nodeSection.to(nodeConf);
conf.setBaseConfiguration(nodeConf);
}
JAXBContext ctx = JAXBContext.newInstance(ContainerConfiguration.class);
Section propertiesSection = configurator.get("properties");
if (propertiesSection != null)
conf.setProperties(propertiesSection.entrySet().stream()
.collect(Collectors.toMap(Entry::getKey, Entry::getValue)));
ContainerConfiguration config = (ContainerConfiguration) ctx.createUnmarshaller().unmarshal(stream);
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);
}
finally {
Utils.closeSafely(stream);
}
}
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)));
}
}
private void initAuthorizationPart(Ini configurator, ContainerConfiguration conf) throws Exception {
Section authorizationSection = configurator.get("authorization");
if (authorizationSection != null) {
String provider = authorizationSection.get("factory");
AuthorizationProviderFactory<?> authProviderFactory;
if (provider != null) {
try {
Object authProviderImpl = Class.forName(provider).getDeclaredConstructor().newInstance();
authProviderFactory = AuthorizationProviderFactory.class.cast(authProviderImpl);
} catch (Exception e) {
throw new Exception("ini file error: invalid provider type in \"authorization\" section", e);
}
} else
authProviderFactory = new DefaultAuthorizationProviderFactory();
authorizationSection.to(authProviderFactory, "factory.");
String type = authorizationSection.get("credentials.class");
if (type == null)
throw new Exception("ini file error: credentials type not found in \"authorization\" section");
Credentials credentials;
try {
Object credentialsImpl = Class.forName(type).getDeclaredConstructor().newInstance();
credentials = Credentials.class.cast(credentialsImpl);
} catch (Exception e) {
throw new Exception("ini file error: invalid credentials type in \"authorization\" section", e);
}
authorizationSection.to(credentials, "credentials.");
conf.setAuthorizationProviderConfiguration(
new AuthorizationProviderConfiguration(authProviderFactory, credentials));
}
}
/**
* Returns the handlers of the container from their XML serialisation.
*
@ -174,35 +57,45 @@ public class ContainerConfigurationBinder {
* @return the handlers
* @throws RuntimeException if the serialisation is invalid
*/
public List<ContainerHandler> bindHandlers(ClassLoader classloader) {
public ContainerHandlers bindHandlers(InputStream stream) {
LinkedList<ContainerHandler> handlers = new LinkedList<ContainerHandler>();
//collects handler classes
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 {
Ini configurator = new Ini(this.getClass().getResourceAsStream("/META-INF/smartgears-config.ini"));
String version = configurator.get("smartgears").get("version");
return new SmartgearsConfiguration(version);
}catch (Exception e) {
throw new RuntimeException(e);
JAXBContext ctx = JAXBContext.newInstance(classes.toArray(new Class<?>[0]));
return (ContainerHandlers) ctx.createUnmarshaller().unmarshal(stream);
} 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

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

View File

@ -0,0 +1,79 @@
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

@ -0,0 +1,47 @@
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,19 +1,13 @@
package org.gcube.smartgears.context.application;
import java.nio.file.Path;
import java.util.List;
import javax.servlet.ServletContext;
import org.gcube.common.events.Hub;
import org.gcube.common.security.secrets.Secret;
import org.gcube.smartgears.configuration.application.ApplicationConfiguration;
import org.gcube.smartgears.context.Properties;
import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.lifecycle.application.ApplicationLifecycle;
import org.gcube.smartgears.persistence.PersistenceWriter;
import org.gcube.smartgears.security.AuthorizationProvider;
import org.gcube.smartgears.security.secrets.SecretFactory;
import jakarta.servlet.ServletContext;
import org.gcube.smartgears.persistence.Persistence;
/**
* The management context of an application.
@ -39,6 +33,9 @@ public interface ApplicationContext {
* @return the configuration
*/
ApplicationConfiguration configuration();
<T> T profile(Class<T> type);
/**
* Returns the lifecycle of the application.
@ -59,7 +56,7 @@ public interface ApplicationContext {
*
* @return the manager
*/
PersistenceWriter persistence();
Persistence persistence();
/**
* Returns the servlet context of the application.
@ -81,25 +78,5 @@ public interface ApplicationContext {
* @return the properties
*/
Properties properties();
/**
* Returns the authorization provider.
* @return the AuhtorizationProvider
**/
AuthorizationProvider authorizationProvider();
/**
* Returns the Path to the configuration Folder for the current app
*
* @return the Path to the folder , null if the Path is not present
*/
Path appSpecificConfigurationFolder();
/**
* returns the list of factory secrets ordered by priority
*
* @returnA Linked List
*/
List<SecretFactory<? extends Secret>> allowedSecretFactories();
}

View File

@ -1,35 +1,16 @@
package org.gcube.smartgears.context.application;
import static org.gcube.smartgears.Constants.profile_file_path;
import static org.gcube.smartgears.Constants.profile_property;
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 javax.servlet.ServletContext;
import org.gcube.common.events.Hub;
import org.gcube.common.security.secrets.Secret;
import org.gcube.smartgears.configuration.PersistenceConfiguration;
import org.gcube.common.resources.gcore.GCoreEndpoint;
import org.gcube.smartgears.configuration.application.ApplicationConfiguration;
import org.gcube.smartgears.context.Properties;
import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.lifecycle.application.ApplicationLifecycle;
import org.gcube.smartgears.persistence.PersistenceWriter;
import org.gcube.smartgears.security.AuthorizationProvider;
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;
import org.gcube.smartgears.persistence.Persistence;
/**
* Default {@link ApplicationContext} implementation.
@ -39,24 +20,13 @@ import jakarta.servlet.ServletContext;
*/
public class DefaultApplicationContext implements ApplicationContext {
private static Logger log = LoggerFactory.getLogger(DefaultApplicationContext.class);
private final ContainerContext container;
private final ServletContext sctx;
private final ApplicationConfiguration configuration;
private final ApplicationLifecycle lifecycle;
private final Properties properties;
private final Hub hub;
private final PersistenceWriter persistenceWriter;
private final String id;
private Path appSpecificConfigurationFolder;
private final static String APPS_CONFIG__DIR = "config/apps";
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
@ -67,59 +37,14 @@ public class DefaultApplicationContext implements ApplicationContext {
* @param lifecycle the lifecycle
* @param properties the 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();
public DefaultApplicationContext(String id,ContainerContext container,ServletContext sctx,ApplicationConfiguration configuration, Hub hub, ApplicationLifecycle lifecycle, Properties properties) {
this.id = id;
this.container=container;
this.sctx = sctx;
this.configuration=configuration;
this.hub=hub;
this.lifecycle = lifecycle;
this.appSpecificConfigurationFolder = getApplicationSpecificConfig();
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;
this.properties=properties;
}
/**
@ -127,22 +52,9 @@ public class DefaultApplicationContext implements ApplicationContext {
* @param context the other instance
*/
public DefaultApplicationContext(ApplicationContext context) {
this(context.id(), context.persistence(), context.container(),context.application(),context.configuration(),context.events(), context.lifecycle(), new Properties(context.properties()), context.allowedSecretFactories());
this(context.id(), context.container(),context.application(),context.configuration(),context.events(), context.lifecycle(), new Properties(context.properties()));
}
private DefaultApplicationContext(String id, PersistenceWriter writer, ContainerContext container,ServletContext sctx,ApplicationConfiguration configuration, Hub hub, ApplicationLifecycle lifecycle, Properties properties, 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
public ServletContext application() {
return sctx;
@ -153,6 +65,16 @@ public class DefaultApplicationContext implements ApplicationContext {
return container;
}
@Override
@SuppressWarnings("all")
public <T> T profile(Class<T> type) {
if (type==GCoreEndpoint.class)
return (T) properties().lookup(profile_property).value(GCoreEndpoint.class);
throw new IllegalArgumentException("unsupported profile type: "+type);
}
@Override
public String name() { //little shortcut for ease of logging
return configuration.name();
@ -174,10 +96,10 @@ public class DefaultApplicationContext implements ApplicationContext {
}
@Override
public PersistenceWriter persistence() {
return persistenceWriter;
public Persistence persistence() {
return configuration.persistence();
}
@Override
public Properties properties() {
return properties;
@ -188,45 +110,4 @@ public class DefaultApplicationContext implements ApplicationContext {
return id;
}
/**
* Returns the authorization provider.
* @return the AuhtorizationProvider
**/
public AuthorizationProvider authorizationProvider() {
return container().authorizationProvider();
}
@Override
public Path appSpecificConfigurationFolder() {
return this.appSpecificConfigurationFolder;
}
private Path getApplicationSpecificConfig(){
String home = Utils.home();
File homeDir = new File(home);
if (!(homeDir.exists() && homeDir.isDirectory() && homeDir.canRead()))
throw new IllegalStateException("invalid node configuration: home " + home
+ " does not exist or is not a directory or cannot be accessed in read mode");
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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,38 +0,0 @@
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,10 +5,12 @@ 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 javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.gcube.common.resources.gcore.GCoreEndpoint;
import org.gcube.common.resources.gcore.Resources;
import org.gcube.smartgears.extensions.ApiResource;
import org.gcube.smartgears.extensions.ApiSignature;
@ -34,8 +36,7 @@ public class ProfileResource extends ApiResource {
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//Resources.marshal(context().profile(),resp.getWriter());
//TODO: return something to show
Resources.marshal(context().profile(GCoreEndpoint.class),resp.getWriter());
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,48 +0,0 @@
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,69 +1,71 @@
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_unavailable_error;
import static org.gcube.smartgears.handlers.application.request.RequestError.internal_server_error;
import static org.gcube.smartgears.handlers.application.request.RequestError.invalid_request_error;
import java.util.Objects;
import java.util.Set;
import java.io.IOException;
import org.gcube.common.security.ContextBean;
import org.gcube.common.security.ContextBean.Type;
import org.gcube.common.security.providers.SecretManagerProvider;
import org.gcube.common.security.secrets.Secret;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import org.gcube.common.authorization.client.exceptions.ObjectNotFound;
import org.gcube.common.authorization.library.AuthorizationEntry;
import org.gcube.common.authorization.library.PolicyUtils;
import org.gcube.common.authorization.library.policies.Policy;
import org.gcube.common.authorization.library.provider.SecurityTokenProvider;
import org.gcube.common.authorization.library.provider.ServiceIdentifier;
import org.gcube.common.scope.api.ScopeProvider;
import org.gcube.common.scope.impl.ScopeBean;
import org.gcube.common.scope.impl.ScopeBean.Type;
import org.gcube.smartgears.Constants;
import org.gcube.smartgears.configuration.Mode;
import org.gcube.smartgears.configuration.container.ContainerConfiguration;
import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.handlers.application.RequestEvent;
import org.gcube.smartgears.handlers.application.RequestHandler;
import org.gcube.smartgears.handlers.application.ResponseEvent;
import org.gcube.smartgears.security.secrets.SecretFactory;
import org.gcube.smartgears.security.secrets.exceptions.SecretNotFoundException;
import org.gcube.smartgears.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@XmlRootElement(name = Constants.request_validation)
public class RequestValidator extends RequestHandler {
@XmlAttribute(required=false, name="oauth")
@Deprecated
boolean oauthCompatibility = false;
private static Logger log = LoggerFactory.getLogger(RequestValidator.class);
private ApplicationContext appContext;
private ApplicationContext context;
@Override
public String getName() {
return Constants.request_validation;
}
@Override
public void handleRequest(RequestEvent call) {
log.trace("executing request validator ON REQUEST");
appContext = call.context();
SecretManagerProvider.set(getSecret(call));
context = call.context();
validateAgainstLifecycle(call);
rejectUnauthorizedCalls(call);
if (appContext.container().configuration().mode()!=Mode.offline) {
validateScopeCall();
validatePolicy(call);
}
validateScopeCall();
if (SecurityTokenProvider.instance.get()!=null)
validatePolicy(SecurityTokenProvider.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) {
switch(appContext.lifecycle().state()) {
switch(context.lifecycle().state()) {
case stopped :
application_unavailable_error.fire(); break;
@ -75,36 +77,49 @@ public class RequestValidator extends RequestHandler {
//nothing to do, but avoids warnings
}
}
private void validateScopeCall() {
String context = SecretManagerProvider.get().getContext();
if (context == null) {
log.warn("rejecting unscoped call to {}",appContext.name());
String scope = ScopeProvider.instance.get();
if (scope == null) {
log.warn("rejecting unscoped call to {}",context.name());
invalid_request_error.fire("call is unscoped");
}
ContextBean bean = new ContextBean(context);
ContainerConfiguration conf = appContext.container().configuration();
Set<String> allowedContexts =appContext.authorizationProvider().getContexts();
if (!allowedContexts.contains(context) &&
!(conf.authorizeChildrenContext() && bean.is(Type.VRE)
&& allowedContexts.contains(bean.enclosingScope().toString()) ) ) {
log.warn("rejecting call to {} in invalid context {}, allowed context are {}",appContext.name(),context,allowedContexts);
invalid_request_error.fire(appContext.name()+" cannot be called in scope "+context);
ScopeBean bean = new ScopeBean(scope);
ContainerConfiguration conf = context.container().configuration();
if (!conf.allowedContexts().contains(scope) &&
!(conf.authorizeChildrenContext() && bean.is(Type.VRE) && conf.allowedContexts().contains(bean.enclosingScope().toString()) ) ) {
log.warn("rejecting call to {} in invalid context {}, allowed context are {}",context.name(),scope,context.container().configuration().allowedContexts());
invalid_request_error.fire(context.name()+" cannot be called in scope "+scope);
}
}
private void rejectUnauthorizedCalls(RequestEvent call){
String token = SecurityTokenProvider.instance.get();
String scope = ScopeProvider.instance.get();
if (token == null && scope==null){
log.warn("rejecting call to {}, authorization required",context.name(),token);
if (call.context().container().configuration().authenticationEnpoint()==null){
log.warn("rejecting call to {}, authorization required",context.name(),token);
RequestError.request_not_authorized_error.fire(context.name()+": authorization required");
}else {
log.info("authorization enpoint found on configuration, redirecting the call");
String recallLocation = String.format("http://%s:%d%s", call.context().container().configuration().hostname(), call.context().container().configuration().port(), call.uri());
//call.response().setHeader("Allowed-Contexts", call.context().container().configuration().allowedContexts().toString());
try {
call.response().sendRedirect(context.container().configuration().authenticationEnpoint()+"?Recall-Location="+recallLocation);
} catch (IOException e) {
log.error("errror redirecting call",e );
}
}
Secret secret = SecretManagerProvider.get();
if (secret == null){
log.warn("rejecting call to {}, authorization required",appContext.name());
RequestError.request_not_authorized_error.fire(appContext.name()+": authorization required");
}
}
@ -113,31 +128,29 @@ public class RequestValidator extends RequestHandler {
return getName();
}
private void validatePolicy(RequestEvent call){
//TODO: must be re-thought
}
private Secret getSecret(RequestEvent call){
Secret secret = null;
for (SecretFactory<? extends Secret> factory: call.context().allowedSecretFactories()) {
try {
secret = factory.create(call.request());
if (!secret.isValid())
RequestError.request_not_authorized_error.fire("authorization with secret "+factory.getSecretClass().getName()+" is not valid ");
} catch (SecretNotFoundException e) {
log.info("authorization with secret {} not found", factory.getSecretClass().getName());
} catch (Throwable t) {
log.warn("geenric error with secret {}", factory.getSecretClass().getName(), t);
}
private void validatePolicy(String token, RequestEvent call){
log.info("accessing policy validator with token {} ", token);
AuthorizationEntry authEntry = null;
try{
authEntry = authorizationService().get(token);
}catch(ObjectNotFound onf){
log.warn("rejecting call to {}, invalid token {}",context.name(),token);
invalid_request_error.fire(context.name()+" invalid token : "+token);
}catch(Exception e){
log.error("error contacting authorization service",e);
internal_server_error.fire("error contacting authorization service");
}
if (Objects.isNull(secret))
RequestError.request_not_authorized_error.fire("call not authorized");
return secret;
ServiceIdentifier serviceIdentifier = Utils.getServiceInfo(call.context()).getServiceIdentifier();
for (Policy policy: authEntry.getPolicies())
if (PolicyUtils.isPolicyValidForClient(policy.getServiceAccess(), serviceIdentifier)){
log.error("rejecting call to {} : {} is not allowed to contact the service ",context.name(),authEntry.getClientInfo().getId());
invalid_request_error.fire("rejecting call to "+context.name()+": "+authEntry.getClientInfo().getId()+" is not allowed to contact the service");
}
}
}

View File

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

View File

@ -1,218 +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.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

@ -1,93 +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.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

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

View File

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

View File

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

View File

@ -1,19 +0,0 @@
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

@ -1,35 +0,0 @@
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

@ -1,49 +0,0 @@
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

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,96 @@
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

@ -1,68 +0,0 @@
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

@ -1,24 +0,0 @@
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

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

View File

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

View File

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

View File

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

@ -1,63 +0,0 @@
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

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

View File

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

View File

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

View File

@ -1,68 +0,0 @@
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 + "]";
}
}

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