branch for release 4.2
git-svn-id: https://svn.d4science-ii.research-infrastructures.eu/gcube/branches/common/common-smartgears/2.1@134815 82a268e6-3cf1-43bd-a215-b396298e98cf
This commit is contained in:
commit
702a724422
|
@ -0,0 +1,41 @@
|
|||
<?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"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" 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.7">
|
||||
<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>
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>common-service</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
|
@ -0,0 +1 @@
|
|||
${gcube.license}
|
|
@ -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.
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<ReleaseNotes>
|
||||
<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>
|
|
@ -0,0 +1,32 @@
|
|||
<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>/</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/${build.finalName}.${project.packaging}</source>
|
||||
<outputDirectory>/${artifactId}</outputDirectory>
|
||||
</file>
|
||||
|
||||
</files>
|
||||
</assembly>
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<ID />
|
||||
<Type>Service</Type>
|
||||
<Profile>
|
||||
<Description>${description}</Description>
|
||||
<Class>Common</Class>
|
||||
<Name>${artifactId}</Name>
|
||||
<Version>1.0.0</Version>
|
||||
<Packages>
|
||||
<Software>
|
||||
<Name>${artifactId}</Name>
|
||||
<Version>${version}</Version>
|
||||
<MavenCoordinates>
|
||||
<groupId>${groupId}</groupId>
|
||||
<artifactId>${artifactId}</artifactId>
|
||||
<version>${version}</version>
|
||||
</MavenCoordinates>
|
||||
<Files>
|
||||
<File>${build.finalName}.jar</File>
|
||||
</Files>
|
||||
</Software>
|
||||
</Packages>
|
||||
</Profile>
|
||||
</Resource>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<smartgears version="${project.version}" />
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,612 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>ActiveLayerIndex</key>
|
||||
<integer>0</integer>
|
||||
<key>ApplicationVersion</key>
|
||||
<array>
|
||||
<string>com.omnigroup.OmniGrafflePro</string>
|
||||
<string>138.17.0.133677</string>
|
||||
</array>
|
||||
<key>AutoAdjust</key>
|
||||
<true/>
|
||||
<key>BackgroundGraphic</key>
|
||||
<dict>
|
||||
<key>Bounds</key>
|
||||
<string>{{0, 0}, {559, 783}}</string>
|
||||
<key>Class</key>
|
||||
<string>SolidGraphic</string>
|
||||
<key>ID</key>
|
||||
<integer>2</integer>
|
||||
<key>Style</key>
|
||||
<dict>
|
||||
<key>shadow</key>
|
||||
<dict>
|
||||
<key>Draws</key>
|
||||
<string>NO</string>
|
||||
</dict>
|
||||
<key>stroke</key>
|
||||
<dict>
|
||||
<key>Draws</key>
|
||||
<string>NO</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>CanvasOrigin</key>
|
||||
<string>{0, 0}</string>
|
||||
<key>ColumnAlign</key>
|
||||
<integer>1</integer>
|
||||
<key>ColumnSpacing</key>
|
||||
<real>36</real>
|
||||
<key>CreationDate</key>
|
||||
<string>2013-08-26 14:09:56 +0000</string>
|
||||
<key>Creator</key>
|
||||
<string>fabio</string>
|
||||
<key>DisplayScale</key>
|
||||
<string>1 0/72 in = 1 0/72 in</string>
|
||||
<key>GraphDocumentVersion</key>
|
||||
<integer>6</integer>
|
||||
<key>GraphicsList</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>Bounds</key>
|
||||
<string>{{182, 101.9996}, {27.164064, 25.574612}}</string>
|
||||
<key>Class</key>
|
||||
<string>ShapedGraphic</string>
|
||||
<key>ID</key>
|
||||
<integer>8</integer>
|
||||
<key>Shape</key>
|
||||
<string>Circle</string>
|
||||
<key>Style</key>
|
||||
<dict>
|
||||
<key>fill</key>
|
||||
<dict>
|
||||
<key>Color</key>
|
||||
<dict>
|
||||
<key>b</key>
|
||||
<string>0.863685</string>
|
||||
<key>g</key>
|
||||
<string>0.863671</string>
|
||||
<key>r</key>
|
||||
<string>0.863697</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>shadow</key>
|
||||
<dict>
|
||||
<key>Draws</key>
|
||||
<string>NO</string>
|
||||
</dict>
|
||||
<key>stroke</key>
|
||||
<dict>
|
||||
<key>Color</key>
|
||||
<dict>
|
||||
<key>b</key>
|
||||
<string>0.491337</string>
|
||||
<key>g</key>
|
||||
<string>0.325267</string>
|
||||
<key>r</key>
|
||||
<string>0.192584</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>Text</key>
|
||||
<dict>
|
||||
<key>Text</key>
|
||||
<string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf390
|
||||
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Light;}
|
||||
{\colortbl;\red255\green255\blue255;\red21\green111\blue181;}
|
||||
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
|
||||
|
||||
\f0\fs42 \cf2 C}</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Bounds</key>
|
||||
<string>{{132.60156, 101.9996}, {27.164064, 25.574612}}</string>
|
||||
<key>Class</key>
|
||||
<string>ShapedGraphic</string>
|
||||
<key>ID</key>
|
||||
<integer>3</integer>
|
||||
<key>Shape</key>
|
||||
<string>Circle</string>
|
||||
<key>Style</key>
|
||||
<dict>
|
||||
<key>fill</key>
|
||||
<dict>
|
||||
<key>Color</key>
|
||||
<dict>
|
||||
<key>b</key>
|
||||
<string>0.863685</string>
|
||||
<key>g</key>
|
||||
<string>0.863671</string>
|
||||
<key>r</key>
|
||||
<string>0.863697</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>shadow</key>
|
||||
<dict>
|
||||
<key>Draws</key>
|
||||
<string>NO</string>
|
||||
</dict>
|
||||
<key>stroke</key>
|
||||
<dict>
|
||||
<key>Color</key>
|
||||
<dict>
|
||||
<key>b</key>
|
||||
<string>0.491337</string>
|
||||
<key>g</key>
|
||||
<string>0.325267</string>
|
||||
<key>r</key>
|
||||
<string>0.192584</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>Text</key>
|
||||
<dict>
|
||||
<key>Text</key>
|
||||
<string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf390
|
||||
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue-Light;}
|
||||
{\colortbl;\red255\green255\blue255;\red21\green111\blue181;}
|
||||
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
|
||||
|
||||
\f0\fs42 \cf2 P}</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
<key>GridInfo</key>
|
||||
<dict/>
|
||||
<key>GuidesLocked</key>
|
||||
<string>NO</string>
|
||||
<key>GuidesVisible</key>
|
||||
<string>YES</string>
|
||||
<key>HPages</key>
|
||||
<integer>1</integer>
|
||||
<key>ImageCounter</key>
|
||||
<integer>4</integer>
|
||||
<key>KeepToScale</key>
|
||||
<false/>
|
||||
<key>Layers</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>Lock</key>
|
||||
<string>NO</string>
|
||||
<key>Name</key>
|
||||
<string>Layer 1</string>
|
||||
<key>Print</key>
|
||||
<string>YES</string>
|
||||
<key>View</key>
|
||||
<string>YES</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>LayoutInfo</key>
|
||||
<dict>
|
||||
<key>Animate</key>
|
||||
<string>NO</string>
|
||||
<key>circoMinDist</key>
|
||||
<real>18</real>
|
||||
<key>circoSeparation</key>
|
||||
<real>0.0</real>
|
||||
<key>layoutEngine</key>
|
||||
<string>dot</string>
|
||||
<key>neatoSeparation</key>
|
||||
<real>0.0</real>
|
||||
<key>twopiSeparation</key>
|
||||
<real>0.0</real>
|
||||
</dict>
|
||||
<key>LinksVisible</key>
|
||||
<string>NO</string>
|
||||
<key>MagnetsVisible</key>
|
||||
<string>NO</string>
|
||||
<key>MasterSheets</key>
|
||||
<array/>
|
||||
<key>ModificationDate</key>
|
||||
<string>2013-08-27 13:29:54 +0000</string>
|
||||
<key>Modifier</key>
|
||||
<string>fabio</string>
|
||||
<key>NotesVisible</key>
|
||||
<string>NO</string>
|
||||
<key>Orientation</key>
|
||||
<integer>2</integer>
|
||||
<key>OriginVisible</key>
|
||||
<string>NO</string>
|
||||
<key>PageBreaks</key>
|
||||
<string>YES</string>
|
||||
<key>PrintInfo</key>
|
||||
<dict>
|
||||
<key>NSBottomMargin</key>
|
||||
<array>
|
||||
<string>float</string>
|
||||
<string>41</string>
|
||||
</array>
|
||||
<key>NSLeftMargin</key>
|
||||
<array>
|
||||
<string>float</string>
|
||||
<string>18</string>
|
||||
</array>
|
||||
<key>NSPaperSize</key>
|
||||
<array>
|
||||
<string>size</string>
|
||||
<string>{595, 842}</string>
|
||||
</array>
|
||||
<key>NSRightMargin</key>
|
||||
<array>
|
||||
<string>float</string>
|
||||
<string>18</string>
|
||||
</array>
|
||||
<key>NSTopMargin</key>
|
||||
<array>
|
||||
<string>float</string>
|
||||
<string>18</string>
|
||||
</array>
|
||||
</dict>
|
||||
<key>PrintOnePage</key>
|
||||
<false/>
|
||||
<key>QuickLookPreview</key>
|
||||
<data>
|
||||
JVBERi0xLjMKJcTl8uXrp/Og0MTGCjUgMCBvYmoKPDwgL0xlbmd0aCA2IDAgUiAvRmls
|
||||
dGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAGlk02O2zAMhfc+BZftIhqREiVq26Cb
|
||||
rjqIgR7A6KAYTAaY5v5An2Lrp+h0UTRGYvlFpMjHz2/0SG/kcakWyhbo53f6Rq/0cL4x
|
||||
bTfi+3Xb6OSd1g34vWKz9aeX/Wmp/7/s2/blj99jnmpOqTm9s+K5JH53ddsWVnXZslLK
|
||||
2Ykq40hO7HzhDE2cJI6TkqLjFAuNOC2OM/OyQfMuWoiUFLtyRlxkZxZsUkJymgtyH3GE
|
||||
uMBOsqKGI/sylF5Bj2t1Iq5nN9QZzaYKDuWd/jZ6gtFf8H3ebTpfYI7E7BUZ0IEPRThh
|
||||
pamKhS5nDA4O4jrVG0azbLCpWcceHkaYMlnH7F0OloZ1zOYswOARJ8GZWJmtY8EuEcS1
|
||||
5obSLOAjbrauZR/WjQp6XKtzso496lSWYV1ThnW9v40ugLjx6p03CSolVtfui4pZ9sVg
|
||||
IQHkyTWOybGFhBmbE8y3OvhpJTl8xf2kzpIm9J5dLPgIrVd6WFfQCPvXJ/rw9SOtz/R5
|
||||
RRUgO4UEkPZFZrwVVTGtJwtaZbg/US2YSMTLMKgeSqP6CFtmOEsBwlwmhAtwxYwmxdSV
|
||||
bEg9QW3epRyXxjTxLozjuUdNSPfcHel+fkP6j94q0feh3EnmImp1JEFUUvUnFg4h/w3j
|
||||
lq5P+YqhHFZ1hibloLiHTRC3UgeyrZ2htKZniA9nBsNdaC9Rj5oQ7rk7wv38prR59t52
|
||||
gv+N3KJODdP+H3DPA9zHX03XNKcKZW5kc3RyZWFtCmVuZG9iago2IDAgb2JqCjU1NApl
|
||||
bmRvYmoKMyAwIG9iago8PCAvVHlwZSAvUGFnZSAvUGFyZW50IDQgMCBSIC9SZXNvdXJj
|
||||
ZXMgNyAwIFIgL0NvbnRlbnRzIDUgMCBSIC9NZWRpYUJveCBbMCAwIDU1OSA3ODNdCj4+
|
||||
CmVuZG9iago3IDAgb2JqCjw8IC9Qcm9jU2V0IFsgL1BERiAvVGV4dCBdIC9Db2xvclNw
|
||||
YWNlIDw8IC9DczEgOCAwIFIgL0NzMiA5IDAgUiA+PiAvRm9udCA8PAovVFQxLjAgMTAg
|
||||
MCBSID4+ID4+CmVuZG9iagoxMSAwIG9iago8PCAvTGVuZ3RoIDEyIDAgUiAvTiAzIC9B
|
||||
bHRlcm5hdGUgL0RldmljZVJHQiAvRmlsdGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0K
|
||||
eAGFVd9v21QUPolvUqQWPyBYR4eKxa9VU1u5GxqtxgZJk6XtShal6dgqJOQ6N4mpGwfb
|
||||
6baqT3uBNwb8AUDZAw9IPCENBmJ72fbAtElThyqqSUh76MQPISbtBVXhu3ZiJ1PEXPX6
|
||||
yznfOec7517bRD1fabWaGVWIlquunc8klZOnFpSeTYrSs9RLA9Sr6U4tkcvNEi7BFffO
|
||||
6+EdigjL7ZHu/k72I796i9zRiSJPwG4VHX0Z+AxRzNRrtksUvwf7+Gm3BtzzHPDTNgQC
|
||||
qwKXfZwSeNHHJz1OIT8JjtAq6xWtCLwGPLzYZi+3YV8DGMiT4VVuG7oiZpGzrZJhcs/h
|
||||
L49xtzH/Dy6bdfTsXYNY+5yluWO4D4neK/ZUvok/17X0HPBLsF+vuUlhfwX4j/rSfAJ4
|
||||
H1H0qZJ9dN7nR19frRTeBt4Fe9FwpwtN+2p1MXscGLHR9SXrmMgjONd1ZxKzpBeA71b4
|
||||
tNhj6JGoyFNp4GHgwUp9qplfmnFW5oTdy7NamcwCI49kv6fN5IAHgD+0rbyoBc3SOjcz
|
||||
ohbyS1drbq6pQdqumllRC/0ymTtej8gpbbuVwpQfyw66dqEZyxZKxtHpJn+tZnpnEdrY
|
||||
BbueF9qQn93S7HQGGHnYP7w6L+YGHNtd1FJitqPAR+hERCNOFi1i1alKO6RQnjKUxL1G
|
||||
NjwlMsiEhcPLYTEiT9ISbN15OY/jx4SMshe9LaJRpTvHr3C/ybFYP1PZAfwfYrPsMBtn
|
||||
E6SwN9ib7AhLwTrBDgUKcm06FSrTfSj187xPdVQWOk5Q8vxAfSiIUc7Z7xr6zY/+hpqw
|
||||
Syv0I0/QMTRb7RMgBxNodTfSPqdraz/sDjzKBrv4zu2+a2t0/HHzjd2Lbcc2sG7GtsL4
|
||||
2K+xLfxtUgI7YHqKlqHK8HbCCXgjHT1cAdMlDetv4FnQ2lLasaOl6vmB0CMmwT/IPszS
|
||||
ueHQqv6i/qluqF+oF9TfO2qEGTumJH0qfSv9KH0nfS/9TIp0Wboi/SRdlb6RLgU5u++9
|
||||
nyXYe69fYRPdil1o1WufNSdTTsp75BfllPy8/LI8G7AUuV8ek6fkvfDsCfbNDP0dvRh0
|
||||
CrNqTbV7LfEEGDQPJQadBtfGVMWEq3QWWdufk6ZSNsjG2PQjp3ZcnOWWing6noonSInv
|
||||
i0/Ex+IzAreevPhe+CawpgP1/pMTMDo64G0sTCXIM+KdOnFWRfQKdJvQzV1+Bt8Ookmr
|
||||
dtY2yhVX2a+qrykJfMq4Ml3VR4cVzTQVz+UoNne4vcKLoyS+gyKO6EHe+75Fdt0Mbe5b
|
||||
RIf/wjvrVmhbqBN97RD1vxrahvBOfOYzoosH9bq94uejSOQGkVM6sN/7HelL4t10t9F4
|
||||
gPdVzydEOx83Gv+uNxo7XyL/FtFl8z9ZAHF4CmVuZHN0cmVhbQplbmRvYmoKMTIgMCBv
|
||||
YmoKMTA0NwplbmRvYmoKOCAwIG9iagpbIC9JQ0NCYXNlZCAxMSAwIFIgXQplbmRvYmoK
|
||||
MTMgMCBvYmoKPDwgL0xlbmd0aCAxNCAwIFIgL04gMyAvQWx0ZXJuYXRlIC9EZXZpY2VS
|
||||
R0IgL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngBnZZ3VFPZFofPvTe90BIi
|
||||
ICX0GnoJINI7SBUEUYlJgFAChoQmdkQFRhQRKVZkVMABR4ciY0UUC4OCYtcJ8hBQxsFR
|
||||
REXl3YxrCe+tNfPemv3HWd/Z57fX2Wfvfde6AFD8ggTCdFgBgDShWBTu68FcEhPLxPcC
|
||||
GBABDlgBwOFmZgRH+EQC1Py9PZmZqEjGs/buLoBku9ssv1Amc9b/f5EiN0MkBgAKRdU2
|
||||
PH4mF+UClFOzxRky/wTK9JUpMoYxMhahCaKsIuPEr2z2p+Yru8mYlybkoRpZzhm8NJ6M
|
||||
u1DemiXho4wEoVyYJeBno3wHZb1USZoA5fco09P4nEwAMBSZX8znJqFsiTJFFBnuifIC
|
||||
AAiUxDm8cg6L+TlongB4pmfkigSJSWKmEdeYaeXoyGb68bNT+WIxK5TDTeGIeEzP9LQM
|
||||
jjAXgK9vlkUBJVltmWiR7a0c7e1Z1uZo+b/Z3x5+U/09yHr7VfEm7M+eQYyeWd9s7Kwv
|
||||
vRYA9iRamx2zvpVVALRtBkDl4axP7yAA8gUAtN6c8x6GbF6SxOIMJwuL7OxscwGfay4r
|
||||
6Df7n4Jvyr+GOfeZy+77VjumFz+BI0kVM2VF5aanpktEzMwMDpfPZP33EP/jwDlpzcnD
|
||||
LJyfwBfxhehVUeiUCYSJaLuFPIFYkC5kCoR/1eF/GDYnBxl+nWsUaHVfAH2FOVC4SQfI
|
||||
bz0AQyMDJG4/egJ961sQMQrIvrxorZGvc48yev7n+h8LXIpu4UxBIlPm9gyPZHIloiwZ
|
||||
o9+EbMECEpAHdKAKNIEuMAIsYA0cgDNwA94gAISASBADlgMuSAJpQASyQT7YAApBMdgB
|
||||
doNqcADUgXrQBE6CNnAGXARXwA1wCwyAR0AKhsFLMAHegWkIgvAQFaJBqpAWpA+ZQtYQ
|
||||
G1oIeUNBUDgUA8VDiZAQkkD50CaoGCqDqqFDUD30I3Qaughdg/qgB9AgNAb9AX2EEZgC
|
||||
02EN2AC2gNmwOxwIR8LL4ER4FZwHF8Db4Uq4Fj4Ot8IX4RvwACyFX8KTCEDICAPRRlgI
|
||||
G/FEQpBYJAERIWuRIqQCqUWakA6kG7mNSJFx5AMGh6FhmBgWxhnjh1mM4WJWYdZiSjDV
|
||||
mGOYVkwX5jZmEDOB+YKlYtWxplgnrD92CTYRm40txFZgj2BbsJexA9hh7DscDsfAGeIc
|
||||
cH64GFwybjWuBLcP14y7gOvDDeEm8Xi8Kt4U74IPwXPwYnwhvgp/HH8e348fxr8nkAla
|
||||
BGuCDyGWICRsJFQQGgjnCP2EEcI0UYGoT3QihhB5xFxiKbGO2EG8SRwmTpMUSYYkF1Ik
|
||||
KZm0gVRJaiJdJj0mvSGTyTpkR3IYWUBeT64knyBfJQ+SP1CUKCYUT0ocRULZTjlKuUB5
|
||||
QHlDpVINqG7UWKqYup1aT71EfUp9L0eTM5fzl+PJrZOrkWuV65d7JU+U15d3l18unydf
|
||||
IX9K/qb8uAJRwUDBU4GjsFahRuG0wj2FSUWaopViiGKaYolig+I1xVElvJKBkrcST6lA
|
||||
6bDSJaUhGkLTpXnSuLRNtDraZdowHUc3pPvTk+nF9B/ovfQJZSVlW+Uo5RzlGuWzylIG
|
||||
wjBg+DNSGaWMk4y7jI/zNOa5z+PP2zavaV7/vCmV+SpuKnyVIpVmlQGVj6pMVW/VFNWd
|
||||
qm2qT9QwaiZqYWrZavvVLquNz6fPd57PnV80/+T8h+qwuol6uPpq9cPqPeqTGpoavhoZ
|
||||
GlUalzTGNRmabprJmuWa5zTHtGhaC7UEWuVa57VeMJWZ7sxUZiWzizmhra7tpy3RPqTd
|
||||
qz2tY6izWGejTrPOE12SLls3Qbdct1N3Qk9LL1gvX69R76E+UZ+tn6S/R79bf8rA0CDa
|
||||
YItBm8GooYqhv2GeYaPhYyOqkavRKqNaozvGOGO2cYrxPuNbJrCJnUmSSY3JTVPY1N5U
|
||||
YLrPtM8Ma+ZoJjSrNbvHorDcWVmsRtagOcM8yHyjeZv5Kws9i1iLnRbdFl8s7SxTLess
|
||||
H1kpWQVYbbTqsPrD2sSaa11jfceGauNjs86m3ea1rakt33a/7X07ml2w3Ra7TrvP9g72
|
||||
Ivsm+zEHPYd4h70O99h0dii7hH3VEevo4bjO8YzjByd7J7HTSaffnVnOKc4NzqMLDBfw
|
||||
F9QtGHLRceG4HHKRLmQujF94cKHUVduV41rr+sxN143ndsRtxN3YPdn9uPsrD0sPkUeL
|
||||
x5Snk+cazwteiJevV5FXr7eS92Lvau+nPjo+iT6NPhO+dr6rfS/4Yf0C/Xb63fPX8Of6
|
||||
1/tPBDgErAnoCqQERgRWBz4LMgkSBXUEw8EBwbuCHy/SXyRc1BYCQvxDdoU8CTUMXRX6
|
||||
cxguLDSsJux5uFV4fnh3BC1iRURDxLtIj8jSyEeLjRZLFndGyUfFRdVHTUV7RZdFS5dY
|
||||
LFmz5EaMWowgpj0WHxsVeyR2cqn30t1Lh+Ps4grj7i4zXJaz7NpyteWpy8+ukF/BWXEq
|
||||
HhsfHd8Q/4kTwqnlTK70X7l35QTXk7uH+5LnxivnjfFd+GX8kQSXhLKE0USXxF2JY0mu
|
||||
SRVJ4wJPQbXgdbJf8oHkqZSQlKMpM6nRqc1phLT4tNNCJWGKsCtdMz0nvS/DNKMwQ7rK
|
||||
adXuVROiQNGRTChzWWa7mI7+TPVIjCSbJYNZC7Nqst5nR2WfylHMEeb05JrkbssdyfPJ
|
||||
+341ZjV3dWe+dv6G/ME17msOrYXWrlzbuU53XcG64fW+649tIG1I2fDLRsuNZRvfbore
|
||||
1FGgUbC+YGiz7+bGQrlCUeG9Lc5bDmzFbBVs7d1ms61q25ciXtH1YsviiuJPJdyS699Z
|
||||
fVf53cz2hO29pfal+3fgdgh33N3puvNYmWJZXtnQruBdreXM8qLyt7tX7L5WYVtxYA9p
|
||||
j2SPtDKosr1Kr2pH1afqpOqBGo+a5r3qe7ftndrH29e/321/0wGNA8UHPh4UHLx/yPdQ
|
||||
a61BbcVh3OGsw8/rouq6v2d/X39E7Ujxkc9HhUelx8KPddU71Nc3qDeUNsKNksax43HH
|
||||
b/3g9UN7E6vpUDOjufgEOCE58eLH+B/vngw82XmKfarpJ/2f9rbQWopaodbc1om2pDZp
|
||||
e0x73+mA050dzh0tP5v/fPSM9pmas8pnS8+RzhWcmzmfd37yQsaF8YuJF4c6V3Q+urTk
|
||||
0p2usK7ey4GXr17xuXKp2737/FWXq2euOV07fZ19ve2G/Y3WHruell/sfmnpte9tvelw
|
||||
s/2W462OvgV95/pd+y/e9rp95Y7/nRsDiwb67i6+e/9e3D3pfd790QepD14/zHo4/Wj9
|
||||
Y+zjoicKTyqeqj+t/dX412apvfTsoNdgz7OIZ4+GuEMv/5X5r0/DBc+pzytGtEbqR61H
|
||||
z4z5jN16sfTF8MuMl9Pjhb8p/rb3ldGrn353+71nYsnE8GvR65k/St6ovjn61vZt52To
|
||||
5NN3ae+mp4req74/9oH9oftj9MeR6exP+E+Vn40/d3wJ/PJ4Jm1m5t/3hPP7CmVuZHN0
|
||||
cmVhbQplbmRvYmoKMTQgMCBvYmoKMjYxMgplbmRvYmoKOSAwIG9iagpbIC9JQ0NCYXNl
|
||||
ZCAxMyAwIFIgXQplbmRvYmoKNCAwIG9iago8PCAvVHlwZSAvUGFnZXMgL01lZGlhQm94
|
||||
IFswIDAgNjEyIDc5Ml0gL0NvdW50IDEgL0tpZHMgWyAzIDAgUiBdID4+CmVuZG9iagox
|
||||
NSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvT3V0bGluZXMgMiAwIFIgL1BhZ2VzIDQg
|
||||
MCBSID4+CmVuZG9iagoyIDAgb2JqCjw8IC9MYXN0IDE2IDAgUiAvRmlyc3QgMTcgMCBS
|
||||
ID4+CmVuZG9iagoxNyAwIG9iago8PCAvUGFyZW50IDE4IDAgUiAvQ291bnQgMCAvRGVz
|
||||
dCBbIDMgMCBSIC9YWVogMCA3ODMgbnVsbCBdIC9UaXRsZSAoQ2FudmFzIDEpCj4+CmVu
|
||||
ZG9iagoxOCAwIG9iago8PCA+PgplbmRvYmoKMTYgMCBvYmoKPDwgL1BhcmVudCAxOCAw
|
||||
IFIgL0NvdW50IDAgL0Rlc3QgWyAzIDAgUiAvWFlaIDAgNzgzIG51bGwgXSAvVGl0bGUg
|
||||
KENhbnZhcyAxKQo+PgplbmRvYmoKMTAgMCBvYmoKPDwgL1R5cGUgL0ZvbnQgL1N1YnR5
|
||||
cGUgL1RydWVUeXBlIC9CYXNlRm9udCAvWU5TU1ZWK0hlbHZldGljYU5ldWUtTGlnaHQg
|
||||
L0ZvbnREZXNjcmlwdG9yCjE5IDAgUiAvRW5jb2RpbmcgL01hY1JvbWFuRW5jb2Rpbmcg
|
||||
L0ZpcnN0Q2hhciA2NyAvTGFzdENoYXIgODAgL1dpZHRocyBbIDcwNAowIDAgMCAwIDAg
|
||||
MCAwIDAgMCAwIDAgMCA2MzAgXSA+PgplbmRvYmoKMTkgMCBvYmoKPDwgL1R5cGUgL0Zv
|
||||
bnREZXNjcmlwdG9yIC9Gb250TmFtZSAvWU5TU1ZWK0hlbHZldGljYU5ldWUtTGlnaHQg
|
||||
L0ZsYWdzIDMyIC9Gb250QkJveApbLTQzMCAtMzM3IDE0MjAgMTE2M10gL0l0YWxpY0Fu
|
||||
Z2xlIDAgL0FzY2VudCA5NjcgL0Rlc2NlbnQgLTIxMyAvQ2FwSGVpZ2h0Cjg1OSAvU3Rl
|
||||
bVYgMCAvTGVhZGluZyAyOSAvWEhlaWdodCA2NDQgL01heFdpZHRoIDE0ODAgL0ZvbnRG
|
||||
aWxlMiAyMCAwIFIgPj4KZW5kb2JqCjIwIDAgb2JqCjw8IC9MZW5ndGggMjEgMCBSIC9M
|
||||
ZW5ndGgxIDIyNDQgL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngBxVVdTFtl
|
||||
GH6/c9rS8lNaxv8Z3TkrrUA76Fpg/Agro/wMCDJAck4iwwZaYAGHkyEzM/ZGklVCTEzM
|
||||
TLzwysQrjzFZOm4gXvh7Y4xeaLg1WXZllpk441qf77Q2orhwscSvec73/n/v95zmPevX
|
||||
rseomBIkUnh+NbpGxrKEsbXOb6zLhkrse+yn42uLqzn9NyJT5+LKjXhWt9wkYg+XYtGF
|
||||
rE5/YG9fgiEX34q9fml1fTOrW7hevHJ1Pue3rEO3rkY3c+fTAXT5pehqLBtvvY3dv3b1
|
||||
FR6HZeVxDWvXYrl4pkJ/rfneuXe4N7vsaAmSSDepgF4FBMPuIgimTjLBy/3A4M7s/Fzp
|
||||
s7+yGvEej7nT/2iY79/cNr2Qnkx/bN42vQXVlqtg5IhfZHxUZ/4yPZmRzdtGJZ7y1xJT
|
||||
VO5LUaGPdpHFqNjHdtGKyIU9nH2J+qmVPFSFhDLfHplJpWHqoAaqNiy0h7Q5Gqce8lNt
|
||||
LkikJXoeiQGqywbtkgWZThzE5IHXl6sjKSIfXOX4dVEbReg8NHvmAhUKJioVPgW+plJ4
|
||||
CZH+0RTZJtRPGNvRUizzZooidXdxS3Hu8hlU9MvywHJEZy9CEfwwNCmQRL88qIuewUnV
|
||||
rclJOXlxISkPykvRBd3kMXY4YkmtRdZpSl3Gc1pV9LAm5cWYpnWhjonXQQrCkxoqXMlV
|
||||
wG6YWh4jyOwflXXRO6FeUvVERNLDEU1SFHlA359Q9f2IpGgaoiz5TtExZyHbcwF6tjTB
|
||||
b81WmUINlNCSSV4TmuBV9P1kUkriJobFraQY5Qy4KY8RPQMpFp5QuSvsViRucCtuBX1o
|
||||
EdS2+Uen1AF0ovBOCp9MaVG+UcQWo70ig9KSp0Sp/TiUlh6LUke+00OUOtGzg1Nalqc0
|
||||
LOmUp9St6Il/EEr/yXCe8vARlCeylCeOoPzEIcrLn0x5Rf4i6LoS7VcYlFc9Jcqrj0N5
|
||||
zbEor813eohyCT3XcspP/o+U1/2NcgwU9pCCwj55hQ3MLD4N+RzFPMc08mLvJ/u/5qER
|
||||
YDyyU/gk7dDvGHFBpH8Gk4j5XIShZG7BaGgJnPUoTsXjVJxsN51gifQme9vKfrGmcxOY
|
||||
kTfzQPhA+JEaaXWXZKSKGIKyo09CDzJVAl6gHRgEZoA4sAFsAe8CHwJ3gM+Bktk+M/0A
|
||||
4WdAmMXMlshhlJQcfMZ6DNniCJxloWbRfdouVJS7hFCwVzjntkNvFtpae6G7BGEyUtD2
|
||||
XDQ0/cZ0UxMe5xfHAgURc33XRHAo3nfqVF986Jmh7kb2oGOm29WzuDUyshXvaRi5ciEw
|
||||
1Vvfrr3c1b2mtVf5+/hHCtwKGua1FV+JWymqadkFrRjPfNw7oANlB6DsAM3/hO/AfUCY
|
||||
BQU2CDVAI9AJXAQ0YBm4AdwC3gM+Au4CXwEls9mqRd/iLRTiKBsO40fZwAB/t1wmMHDC
|
||||
GQpWVpRb3MwZgtiLqzcL7u37Q6o6lP4u8igw3uFydYwH2AqrHx4ZGWaXH7ezoNQ2FgiM
|
||||
tUn8XsbKvM/f/RFLhM1LITwZleX+Rxb+CdTGp6ZmZnzDsZWN2PryfHQ8dj12Zmx5cWn9
|
||||
T34W9U0KZW5kc3RyZWFtCmVuZG9iagoyMSAwIG9iagoxMTg2CmVuZG9iagoyMiAwIG9i
|
||||
agooTWFjIE9TIFggMTAuOC40IFF1YXJ0eiBQREZDb250ZXh0KQplbmRvYmoKMjMgMCBv
|
||||
YmoKKEQ6MjAxMzA4MjkxOTU4MjZaMDAnMDAnKQplbmRvYmoKMSAwIG9iago8PCAvUHJv
|
||||
ZHVjZXIgMjIgMCBSIC9DcmVhdGlvbkRhdGUgMjMgMCBSIC9Nb2REYXRlIDIzIDAgUiA+
|
||||
PgplbmRvYmoKeHJlZgowIDI0CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwNzEyNSAw
|
||||
MDAwMCBuIAowMDAwMDA1MDEyIDAwMDAwIG4gCjAwMDAwMDA2NjkgMDAwMDAgbiAKMDAw
|
||||
MDAwNDg2MyAwMDAwMCBuIAowMDAwMDAwMDIyIDAwMDAwIG4gCjAwMDAwMDA2NTAgMDAw
|
||||
MDAgbiAKMDAwMDAwMDc3MyAwMDAwMCBuIAowMDAwMDAyMDU1IDAwMDAwIG4gCjAwMDAw
|
||||
MDQ4MjcgMDAwMDAgbiAKMDAwMDAwNTI3NCAwMDAwMCBuIAowMDAwMDAwODg0IDAwMDAw
|
||||
IG4gCjAwMDAwMDIwMzQgMDAwMDAgbiAKMDAwMDAwMjA5MSAwMDAwMCBuIAowMDAwMDA0
|
||||
ODA2IDAwMDAwIG4gCjAwMDAwMDQ5NDYgMDAwMDAgbiAKMDAwMDAwNTE3OCAwMDAwMCBu
|
||||
IAowMDAwMDA1MDYwIDAwMDAwIG4gCjAwMDAwMDUxNTYgMDAwMDAgbiAKMDAwMDAwNTQ4
|
||||
NyAwMDAwMCBuIAowMDAwMDA1NzM0IDAwMDAwIG4gCjAwMDAwMDcwMTAgMDAwMDAgbiAK
|
||||
MDAwMDAwNzAzMSAwMDAwMCBuIAowMDAwMDA3MDgzIDAwMDAwIG4gCnRyYWlsZXIKPDwg
|
||||
L1NpemUgMjQgL1Jvb3QgMTUgMCBSIC9JbmZvIDEgMCBSIC9JRCBbIDxmZGRiMmE0NjQ3
|
||||
MmU0OGZmNWU4MjA5Mjk0Njg2N2Y0Yz4KPGZkZGIyYTQ2NDcyZTQ4ZmY1ZTgyMDkyOTQ2
|
||||
ODY3ZjRjPiBdID4+CnN0YXJ0eHJlZgo3MjAwCiUlRU9GCjEgMCBvYmoKPDwvQXV0aG9y
|
||||
IChmYWJpbykvQ3JlYXRpb25EYXRlIChEOjIwMTMwODI2MTQwOTAwWikvQ3JlYXRvciAo
|
||||
T21uaUdyYWZmbGUgUHJvZmVzc2lvbmFsIDUuMi4zKS9Nb2REYXRlIChEOjIwMTMwODI3
|
||||
MTMyOTAwWikvUHJvZHVjZXIgMjIgMCBSIC9UaXRsZSAob3JpZ2luYWxzLmdyYWZmbGUp
|
||||
Pj4KZW5kb2JqCnhyZWYKMSAxCjAwMDAwMDc4MzcgMDAwMDAgbiAKdHJhaWxlcgo8PC9J
|
||||
RCBbPGZkZGIyYTQ2NDcyZTQ4ZmY1ZTgyMDkyOTQ2ODY3ZjRjPiA8ZmRkYjJhNDY0NzJl
|
||||
NDhmZjVlODIwOTI5NDY4NjdmNGM+XSAvSW5mbyAxIDAgUiAvUHJldiA3MjAwIC9Sb290
|
||||
IDE1IDAgUiAvU2l6ZSAyND4+CnN0YXJ0eHJlZgo4MDE3CiUlRU9GCg==
|
||||
</data>
|
||||
<key>QuickLookThumbnail</key>
|
||||
<data>
|
||||
TU0AKgAAAJaAP+BP8AQWDQeEQmFQuGQaBv9xN1xABhuB4AB/goFAAXhMCgAUiMOAACAM
|
||||
Bwx4u51gBXs1zyQAycUB4FxiBsBgNAAOgCR8MBQGgAPgiCPl/zIQyOFr5eM0APgQBYAC
|
||||
4Bv4APYBgGbQJoNBsgAGhSageFPJ8P0ACoSB+GNZuN0AOZzPcAPIB2QZB8EgCAgAAA8B
|
||||
AAADAAAAAQANAAABAQADAAAAAQAEAAABAgADAAAABAAAAVABAwADAAAAAQAFAAABBgAD
|
||||
AAAAAQACAAABEQAEAAAAAQAAAAgBEgADAAAAAQABAAABFQADAAAAAQAEAAABFgADAAAA
|
||||
AQAEAAABFwAEAAAAAQAAAI0BHAADAAAAAQABAAABPQADAAAAAQACAAABUgADAAAAAQAB
|
||||
AAABUwADAAAABAAAAViHcwAHAAAatAAAAWAAAAAAAAgACAAIAAgAAQABAAEAAQAAGrRh
|
||||
cHBsAhAAAG1udHJSR0IgWFlaIAfdAAcABQATADIAOmFjc3BBUFBMAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAD21gABAAAAANMtYXBwbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEWRlc2MAAAFQAAAAYmRzY20AAAG0AAADuGNw
|
||||
cnQAAAVsAAAAJHd0cHQAAAWQAAAAFHJYWVoAAAWkAAAAFGdYWVoAAAW4AAAAFGJYWVoA
|
||||
AAXMAAAAFHJUUkMAAAXgAAAIDGFhcmcAAA3sAAAAIHZjZ3QAAA4MAAAGEm5kaW4AABQg
|
||||
AAAGPmNoYWQAABpgAAAALG1tb2QAABqMAAAAKGJUUkMAAAXgAAAIDGdUUkMAAAXgAAAI
|
||||
DGFhYmcAAA3sAAAAIGFhZ2cAAA3sAAAAIGRlc2MAAAAAAAAACERpc3BsYXkAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABtbHVjAAAAAAAAAB4AAAAMc2tTSwAAABYAAAF4
|
||||
Y2FFUwAAABgAAAGOaGVJTAAAABYAAAGmcHRCUgAAABgAAAG8aXRJVAAAABQAAAHUaHVI
|
||||
VQAAABQAAAHodWtVQQAAABwAAAH8a29LUgAAAAwAAAIYbmJOTwAAABIAAAIkY3NDWgAA
|
||||
ABYAAAI2emhUVwAAAAwAAAJMZGVERQAAABAAAAJYcm9STwAAABIAAAJoc3ZTRQAAABAA
|
||||
AAJ6emhDTgAAAAwAAAJMamFKUAAAAA4AAAKKYXIAAAAAABQAAAKYZWxHUgAAACIAAAKs
|
||||
cHRQVAAAABYAAALObmxOTAAAABYAAALkZnJGUgAAABYAAAL6ZXNFUwAAABIAAAJodGhU
|
||||
SAAAAAwAAAMQdHJUUgAAABQAAAMcZmlGSQAAABAAAAMwaHJIUgAAABQAAANAcGxQTAAA
|
||||
ABIAAANUcnVSVQAAACQAAANmZW5VUwAAABIAAAOKZGFESwAAABwAAAOcAEYAYQByAGUA
|
||||
YgBuAOkAIABMAEMARABMAEMARAAgAGUAbgAgAGMAbwBsAG8AciAPAEwAQwBEACAF5gXR
|
||||
BeIF1QXgBdkATABDAEQAIABDAG8AbABvAHIAaQBkAG8ATABDAEQAIABjAG8AbABvAHIA
|
||||
aQBTAHoA7QBuAGUAcwAgAEwAQwBEBBoEPgQ7BEwEPgRABD4EMgQ4BDkAIABMAEMARM7s
|
||||
t+wAIABMAEMARABGAGEAcgBnAGUALQBMAEMARABCAGEAcgBlAHYAbgD9ACAATABDAERf
|
||||
aYJyACAATABDAEQARgBhAHIAYgAtAEwAQwBEAEwAQwBEACAAYwBvAGwAbwByAEYA5ABy
|
||||
AGcALQBMAEMARDCrMOkw/AAgAEwAQwBEIA8ATABDAEQAIAZFBkQGSAZGBikDiAOzA8cD
|
||||
wQPJA7wDtwAgA78DuAPMA70DtwAgAEwAQwBEAEwAQwBEACAAYQAgAEMAbwByAGUAcwBL
|
||||
AGwAZQB1AHIAZQBuAC0ATABDAEQATABDAEQAIABjAG8AdQBsAGUAdQByAEwAQwBEACAO
|
||||
Kg41AFIAZQBuAGsAbABpACAATABDAEQAVgDkAHIAaQAtAEwAQwBEAEwAQwBEACAAdQAg
|
||||
AGIAbwBqAGkASwBvAGwAbwByACAATABDAEQEJgQyBDUEQgQ9BD4EOQAgBBYEGgAtBDQE
|
||||
OARBBD8EOwQ1BDkAQwBvAGwAbwByACAATABDAEQATABDAEQALQBmAGEAcgB2AGUAcwBr
|
||||
AOYAcgBtdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUsIEluYy4sIDIwMTMAWFlaIAAAAAAA
|
||||
APNSAAEAAAABFs9YWVogAAAAAAAAansAADcfAAABw1hZWiAAAAAAAABl1gAAut0AAAgK
|
||||
WFlaIAAAAAAAACaFAAAOBAAAyWBjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgA
|
||||
LQAyADYAOwBAAEUASgBPAFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8AowCo
|
||||
AK0AsgC3ALwAwQDGAMsA0ADVANsA4ADlAOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIB
|
||||
OAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGSAZoBoQGpAbEBuQHBAckB0QHZAeEB6QHy
|
||||
AfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2AsECywLVAuAC
|
||||
6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQT
|
||||
BCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcF
|
||||
hgWWBaYFtQXFBdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcr
|
||||
Bz0HTwdhB3QHhgeZB6wHvwfSB+UH+AgLCB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJ
|
||||
JQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9ClQKagqBCpgKrgrFCtwK8wsLCyILOQtR
|
||||
C2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4MpwzADNkM8w0NDSYNQA1aDXQNjg2pDcMN
|
||||
3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBDEGEQfhCb
|
||||
ELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QT
|
||||
xRPlFAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcd
|
||||
F0EXZReJF64X0hf3GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa
|
||||
7BsUGzsbYxuKG7Ib2hwCHCocUhx7HKMczBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7p
|
||||
HxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwhSCF1IaEhziH7IiciVSKCIq8i3SMKIzgj
|
||||
ZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtyboJxgnSSd6J6sn3CgN
|
||||
KD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizXLQwt
|
||||
QS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKb
|
||||
MtQzDTNGM38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4
|
||||
jDjIOQU5Qjl/Obw5+To2OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6g
|
||||
PuA/IT9hP6I/4kAjQGRApkDnQSlBakGsQe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJF
|
||||
VUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1JY0mpSfBKN0p9SsRLDEtTS5pL4kwq
|
||||
THJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR5lIxUnxSx1MTU19T
|
||||
qlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtF
|
||||
W5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Nj
|
||||
l2PrZEBklGTpZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/
|
||||
bFdsr20IbWBtuW4SbmtuxG8eb3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1
|
||||
KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnnekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5i
|
||||
fsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VHhauGDoZyhteHO4efiASI
|
||||
aYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7OjzaPnpAGkG6Q1pE/kaiSEZJ6
|
||||
kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPed
|
||||
ZJ3SnkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhS
|
||||
qMSpN6mpqhyqj6sCq3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660
|
||||
JbSctRO1irYBtnm28Ldot+C4WbjRuUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1
|
||||
wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dBx7/IPci8yTrJuco4yrfLNsu2zDXM
|
||||
tc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV1tjXXNfg2GTY6Nls
|
||||
2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN5pbn
|
||||
H+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC
|
||||
9VD13vZt9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//3BhcmEAAAAAAAMA
|
||||
AAACZmYAAPKnAAANWQAAE9AAAAoOdmNndAAAAAAAAAAAAAMBAAACAAAAAwANACAAPABi
|
||||
AJMAzgESAVgBqAICAm0C4wNlA/IEiwUwBeAGnQdqCEQJJgoOCwIMAw0RDisPSRByEaES
|
||||
3RQeFWIWshgEGWAawBwfHYke8SBdIcsjOiSlJhQngCjnKk4rry0OLmMvszD7MjszczSf
|
||||
NcQ24Tf5OQw6GjsqPDg9RD5OP1pAaEF2QoZDlkSoRbpGykfaSOhJ90sETBBNHE4nTzFQ
|
||||
OlFCUklTTlRSVVVWV1dYWFlZWlpaW1pcWl1aXlhfVmBSYU1iRmM+ZDVlKmYfZxNoB2j7
|
||||
ae5q4WvUbMdtuW6rb51wjXF5cmFzQXQZdO51wXaXd294Snkjef1613uvfIh9YH43fw5/
|
||||
5YC7gZKCaIM9hBOE6IW+hpOHaIg8iRaJ/YsEjD6NrY84kLKR/ZMflCuVLpYwlzGYM5k0
|
||||
mjWbNZw2nTqeRp9loJih2KMVpEGlXaZup3qohqmRqpurpayvrbmuw6/SsO2yIbNwtNC2
|
||||
KbdsuJi5trrPu+e8/r4WvzHAVsGSwujESMWbxs3H4cjwygbLJcxHzWTOc89s0FXRN9Ie
|
||||
0wrT+dTj1c3Wv9e62LzZv9rB277cr92R3mzfQuAZ4PLh0OK546jkm+WQ5oXneeht6WDq
|
||||
Tesz7BLs6+3G7qTvhvBq8U7yNPMi9Cn1Xfa++DD5lPrf/Bn9WP6k//8AAAACAAsAGwAy
|
||||
AFIAegCsAOYBJwFvAcACGwKCAvIDcAP4BIgFIQXFBnQHLgfzCMIJnQp9C2QMWA1TDlgP
|
||||
YhB1EYsSqBPQFPgWJxddGJMZzxsNHEwdjB7OIA0hUSKRI9AlCyZFJ3YopinRKvQsEC0o
|
||||
LjQvOTA5MTMyKTMeNBA1ATX0NuU31zjIObk6qTuaPIs9ez5qP1tATkFCQjdDLkQkRRpG
|
||||
EEcGR/tI8EnlSthLzEy+TbBOok+TUIRRc1JhU1BUPlUrVhhXBVfxWNxZyFqzW59ciV1z
|
||||
Xl1fRWAsYRNh+GLdY8FkpGWIZmtnTWgvaRFp82rVa7ZsmG15blhvNnAScOlxv3KTc2R0
|
||||
NXUFddZ2pXd1eEV5FHnjerJ7gHxOfRx96n66f42AaoFSgkSDPYQ6hTaGMIcriCWJH4oZ
|
||||
ixKMC40Ejf2O9o/xkPKR+ZMHlBiVKJY3l0SYUJlbmmebcpx9nYeekp+coKehuKLSo/ml
|
||||
KaZcp4uotanaqv6sIa1FrmivjbC3se6zNbSFtdG3Dbg9uWm6mbvLvP++Nb9swKTB3MMR
|
||||
xEDFacaLx6rIyMnpyw3MM81azoLPrNDX0gbTNNRf1YTWpNfE2ObaCtsv3FTded6e38fg
|
||||
9+Ix43Lks+Xy5yzoZ+mj6t/sG+1Y7pzv8PF28y31EvcK+QL66/y7/mn//wAAAAIACgAX
|
||||
ACwASABrAJcAygEGAUcBkQHjAj0CoQMNA4UEBwSSBSYFwAZkBxIHyQiICU4KHwr5C9QM
|
||||
uA2lDpkPkhCPEZISmhOlFLYVyBbeF/kZEhotG0YcYR18HpcfriDCIdQi5SPtJPIl8Cbo
|
||||
J9gowimlKoMrWiwvLQEt0i6kL3cwSjEgMfcyzjOmNH41VTYsNwI32DitOYI6VjsqO/08
|
||||
0D2jPnc/SkAfQPVBzEKlQ35EVkUuRgVG3Ee0SIpJYEo2SwtL4Ey1TYlOXU8xUAVQ2VGs
|
||||
UoBTU1QlVPZVyFaaV2tYO1kLWdtaq1t6XEhdFl3kXrFff2BLYRdh42KuY3pkRmURZdtm
|
||||
pmdxaDtpBGnOapdrYGwpbPFtuG6Ab0ZwCXDMcYxySnMIc8V0gnU+dft2t3dzeC546Xmk
|
||||
el97GnvUfI59SH4Cfr5/foBIgR2B+4Lgg8iEsYWbhoSHbYhWiT6KJosNi/WM3I3DjqqP
|
||||
lJCBkXeScpNwlHGVcZZxl3GYcJlummmbYJxTnUqeTJ9doHmhnaLFo+6lFaY3p0yoUqlR
|
||||
qlure6y0rfuvR7CTsd+zK7R2tcG3C7hTuZi627whvWy+vcATwWrCw8QbxXTGzsgmyX3K
|
||||
1MwqzYLO49BW0dbTY9T11ojYGtmt20Dc1t6A4E3iU+SQ5vfplOzF8VT32f//AABuZGlu
|
||||
AAAAAAAABjYAAKVGAABWlAAAUzkAAKWOAAAlqAAADTwAAFANAABUOQACGZkAAb1wAAFc
|
||||
KAADAQAAAgAAAB4AQQBiAIMApADFAOYBBgEnAUkBawGNAa8B0QH0AhcCOwJfAoMCqALO
|
||||
AvMDGgNBA2gDkAO4A+IECwQ2BGEEjQS5BOcFFQVFBXUFpgXYBgwGQQZ3Bq4G6AciB18H
|
||||
ngfeCCIIaAiwCP0JTAmgCfYKUAqtCw0LbgvSDDgMoQ0LDXYN4w5RDsEPMg+kEBkQkBEJ
|
||||
EYUSAhKCEwUTiRQQFJkVJRWzFkQW2BdvGAgYpBlDGeQahxstG9Ucfx0sHdwejh9EH/0g
|
||||
uiF6Ij4jBSPPJJwlayY9JxIn6ijEKaIqgitlLEwtOS4uLy8wPjFVMmwzgzSaNbU20zf2
|
||||
OR06Rzt2PKk93z8ZQFdBmULfRChFdUbGSBpJZ0qVS5dMdk1ATf9OwU+MUGpRaVKEU7BU
|
||||
5FYaV1NYjlnMWw1cUV2WXthgD2EzYkdjVWRkZX9mq2foaTFqgWvTbShugG/acTdylnP4
|
||||
dVd2rHfteRd6MHtCfFl9f365gAmBaILNhDaFoocPiH6J54tBjISNtY7gkA6RTpKwlDSV
|
||||
xZdOmM2aSJvGnVGe+KDIor6ksqaXqHqqaaxbrkGwFbHhs621f7deuVq7fL23v/nCOcRt
|
||||
xorImcqkzK/OvtDR0ufVCNc92Y3b9N5a4LXjC+Vh57rqCOwn7gDvnfEe8pj0HPW29235
|
||||
Nfr8/Lb+X///AAAAJABLAHEAlgC8AOIBCAEuAVQBegGiAckB8QIZAkICawKVAsAC6wMW
|
||||
A0MDcAOdA8wD+wQrBFwEjgTBBPUFKgVhBZgF0QYLBkcGhQbEBwYHSgePB9gIJAhzCMUJ
|
||||
HAl3CdYKOQqgCwoLdwvnDFgMzQ1DDbwOOA62DzYPuRA/EMYRTxHZEmUS8hOCFBQUqBU/
|
||||
FdkWdRcUF7YYWhkCGawaWRsJG7wccx0sHegepx9pIC4g9iHBIo8jXyQzJQol5CbCJ6Qo
|
||||
iSlzKmArUSxFLTwuNy81MDYxOjJCM000WzVuNoY3pjjPOf47NDxvPa4+8kA5QYRC00Qn
|
||||
RX9G20g7SZ1K/UxQTZNOyk/8US5SZFOeVNpWGldcWKFZ6ls1XINd018lYHNhvGL+ZD1l
|
||||
fWbBaAlpVmqla/dtTG6jb/xxWXK4dBl1fHbaeDB5e3q9e/p9On6Bf86BIoJ5g9OFMIaO
|
||||
h+6JToqqi/uNQI58j7WQ9ZJEk6KVCZZyl9iZPpqjnAidb57WoD2hpqMUpImmBqeOqR6q
|
||||
tKxNreOveLELsp+0NLXKt2C49bqIvBy9tb9XwQLCtcRpxhzHzsmAyzTM6s6j0FvSEdO/
|
||||
1WPXANib2jnb3d2H3zLg3+KM5Dvl7Oee6U7q9eyI7fnvSfCF8bHyzvPo9P/2GPcz+FT5
|
||||
fPqq++r9OP6V//8AAAApAFQAfwCpANMA/gEpAVQBgAGsAdgCBgI0AmICkQLBAvIDIwNW
|
||||
A4kDvQPzBCkEYASZBNMFDwVMBYsFywYNBlIGmQbiBy0HfQfPCCYIgQjhCUcJsgokCpsL
|
||||
FguVDBYMmg0fDaYOMA68D0wP3xB1EQ8RrBJNEvETmRREFPIVoxZWFwsXwRh6GTYZ9hq5
|
||||
G38cSR0XHegevR+VIHIhUSI0IxokBCTyJeMm2SfSKM8p0CrVK94s6y38LxIwLDFKMmwz
|
||||
kzS+Ne03IDhXOZI60jwVPV0+qj/7QVBCqkQJRW1G10hKScZLTEzYTmlQAFGbUzxU4laO
|
||||
WD9Z9VuxXXJfNmDyYptkK2WtZypopmoma6ltL265cEdx2HNtdQZ2onhBed97eH0Gfo6A
|
||||
FIGZgyKErYY7h82JY4sBjKuOXJAGkZuTHpSVlgeXd5jnmlqb051YnvSgqaJlpAeliqb2
|
||||
qFiptasUrHSt1q85sJ6yBbNutNm2R7e5uTC6qrwkvZu/DsB9werDV8TExjLHockSyoTL
|
||||
981szuTQXdHZ01fU19ZU18nZMtqS2+vdPt6O393hLeJ+49DlI+Z4583pI+p268Ds/u4x
|
||||
70rwWfFZ8k7zPPQj9Qf14va694D4Pfjw+YH6Evqg+wn7cfvZ/EL8mfzo/Tf9hf3U/iP+
|
||||
af6t/vD/NP94/7v//wAAc2YzMgAAAAAAAQxCAAAF3v//8yYAAAeSAAD9kf//+6L///2j
|
||||
AAAD3AAAwGxtbW9kAAAAAAAABhAAAJzFAAAAAMZ644AAAAAAAAAAAAAAAAAAAAAA
|
||||
</data>
|
||||
<key>ReadOnly</key>
|
||||
<string>NO</string>
|
||||
<key>RowAlign</key>
|
||||
<integer>1</integer>
|
||||
<key>RowSpacing</key>
|
||||
<real>36</real>
|
||||
<key>SheetTitle</key>
|
||||
<string>Canvas 1</string>
|
||||
<key>SmartAlignmentGuidesActive</key>
|
||||
<string>YES</string>
|
||||
<key>SmartDistanceGuidesActive</key>
|
||||
<string>YES</string>
|
||||
<key>UniqueID</key>
|
||||
<integer>1</integer>
|
||||
<key>UseEntirePage</key>
|
||||
<false/>
|
||||
<key>VPages</key>
|
||||
<integer>1</integer>
|
||||
<key>WindowInfo</key>
|
||||
<dict>
|
||||
<key>CurrentSheet</key>
|
||||
<integer>0</integer>
|
||||
<key>ExpandedCanvases</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>Canvas 1</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>Frame</key>
|
||||
<string>{{320, -1}, {693, 774}}</string>
|
||||
<key>ListView</key>
|
||||
<true/>
|
||||
<key>OutlineWidth</key>
|
||||
<integer>142</integer>
|
||||
<key>RightSidebar</key>
|
||||
<false/>
|
||||
<key>ShowRuler</key>
|
||||
<true/>
|
||||
<key>Sidebar</key>
|
||||
<true/>
|
||||
<key>SidebarWidth</key>
|
||||
<integer>120</integer>
|
||||
<key>VisibleRegion</key>
|
||||
<string>{{0, 0}, {558, 619}}</string>
|
||||
<key>Zoom</key>
|
||||
<real>1</real>
|
||||
<key>ZoomValues</key>
|
||||
<array>
|
||||
<array>
|
||||
<string>Canvas 1</string>
|
||||
<real>1</real>
|
||||
<real>1</real>
|
||||
</array>
|
||||
</array>
|
||||
</dict>
|
||||
<key>saveQuickLookFiles</key>
|
||||
<string>YES</string>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,270 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>maven-parent</artifactId>
|
||||
<groupId>org.gcube.tools</groupId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath />
|
||||
</parent>
|
||||
|
||||
<groupId>org.gcube.core</groupId>
|
||||
<artifactId>common-smartgears</artifactId>
|
||||
<version>2.1.0-SNAPSHOT</version>
|
||||
<name>SmartGears</name>
|
||||
|
||||
<properties>
|
||||
<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>
|
||||
</properties>
|
||||
|
||||
<scm>
|
||||
<connection>scm:svn:http://svn.d4science.research-infrastructures.eu/gcube/trunk/Common/${project.artifactId}</connection>
|
||||
<developerConnection>scm:svn:https://svn.d4science.research-infrastructures.eu/gcube/trunk/Common/${project.artifactId}</developerConnection>
|
||||
<url>http://svn.d4science.research-infrastructures.eu/gcube/trunk/Common/${project.artifactId}</url>
|
||||
</scm>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.gcube.common</groupId>
|
||||
<artifactId>authorization-client</artifactId>
|
||||
<version>[2.0.0-SNAPSHOT,3.0.0-SNAPSHOT)</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.gcube.common</groupId>
|
||||
<artifactId>common-authorization</artifactId>
|
||||
<version>[2.0.0-SNAPSHOT,3.0.0-SNAPSHOT)</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.gcube.data.publishing</groupId>
|
||||
<artifactId>document-store-lib</artifactId>
|
||||
<version>[1.0.0-SNAPSHOT,2.0.0-SNAPSHOT)</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.gcube.accounting</groupId>
|
||||
<artifactId>accounting-lib</artifactId>
|
||||
<version>[2.0.0-SNAPSHOT,3.0.0-SNAPSHOT)</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.7.5</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.gcube.resources</groupId>
|
||||
<artifactId>registry-publisher</artifactId>
|
||||
<version>[1.0.0-SNAPSHOT, 2.0.0-SNAPSHOT)</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.gcube.resources</groupId>
|
||||
<artifactId>common-gcore-resources</artifactId>
|
||||
<version>[1.0.0-SNAPSHOT, 2.0.0-SNAPSHOT)</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.gcube.core</groupId>
|
||||
<artifactId>common-validator</artifactId>
|
||||
<version>[1.0.0-SNAPSHOT,2.0.0-SNAPSHOT)</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.gcube.core</groupId>
|
||||
<artifactId>common-scope</artifactId>
|
||||
<version>[1.0.0-SNAPSHOT, 2.0.0-SNAPSHOT)</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.gcube.core</groupId>
|
||||
<artifactId>common-events</artifactId>
|
||||
<version>[1.0.0-SNAPSHOT,2.0.0-SNAPSHOT)</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- ***************** test ******************* -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jboss.shrinkwrap.resolver</groupId>
|
||||
<artifactId>shrinkwrap-resolver-depchain</artifactId>
|
||||
<version>2.0.0-beta-2</version>
|
||||
<type>pom</type>
|
||||
<scope>test</scope>
|
||||
</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.0.13</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>1.9.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>2.2</version>
|
||||
<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>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>default-jar</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>**/probe/**/*</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!-- include probe in attached war -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
<configuration>
|
||||
<primaryArtifact>false</primaryArtifact>
|
||||
<classifier>probe</classifier>
|
||||
|
||||
<packagingIncludes>WEB-INF/classes/org/gcube/smartgears/probe/**/*</packagingIncludes>
|
||||
<failOnMissingWebXml>false</failOnMissingWebXml>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>probe-war</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>war</goal>
|
||||
</goals>
|
||||
</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>
|
|
@ -0,0 +1,149 @@
|
|||
package org.gcube.smartgears;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
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;
|
||||
import org.gcube.smartgears.managers.ApplicationManager;
|
||||
import org.gcube.smartgears.managers.ContainerManager;
|
||||
import org.gcube.smartgears.provider.ProviderFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Bootstraps management of all deployed applications which require it.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class Bootstrap implements ServletContainerInitializer {
|
||||
|
||||
private static Logger log = LoggerFactory.getLogger(Bootstrap.class);
|
||||
|
||||
private static boolean smartgearsHasStarted = false;
|
||||
|
||||
private static boolean containerHasFailed = false;
|
||||
|
||||
private static ContainerManager manager;
|
||||
|
||||
private static ContainerContext context;
|
||||
|
||||
public Bootstrap() {
|
||||
|
||||
if (smartgearsHasStarted)
|
||||
return;
|
||||
|
||||
smartgearsHasStarted = true;
|
||||
|
||||
initialiseContainer();
|
||||
|
||||
//this can fail the app: managed resources need a working container
|
||||
startContainerIfItHasntAlreadyFailed();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartup(Set<Class<?>> c, ServletContext application) throws ServletException {
|
||||
|
||||
ApplicationManager appManager = new ApplicationManager();
|
||||
|
||||
//act only on resources
|
||||
if (isResource(application)) {
|
||||
|
||||
try {
|
||||
|
||||
log.info("starting management of application @ {}", application.getContextPath());
|
||||
|
||||
ApplicationContext app = appManager.start(context, application);
|
||||
|
||||
manager.manage(app);
|
||||
|
||||
context.configuration().app(app.configuration());
|
||||
|
||||
} catch (Throwable t) {
|
||||
|
||||
appManager.stop();
|
||||
|
||||
throw new ServletException("cannot manage application @ " + application.getContextPath()
|
||||
+ " (see cause)", t);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// helpers
|
||||
private void initialiseContainer() {
|
||||
|
||||
try {
|
||||
|
||||
log.trace("smartgears is starting");
|
||||
|
||||
|
||||
/* Get the ContainerContext. Look at DefaultProvider */
|
||||
context = ProviderFactory.provider().containerContext();
|
||||
|
||||
/* Validate the configuration retrieved by ContainerContext
|
||||
* using gcube facilities annotation based
|
||||
* ( i.e org.gcube.common.validator.annotations)
|
||||
*/
|
||||
context.configuration().validate();
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
|
||||
containerHasFailed = true;
|
||||
|
||||
log.error("cannot start smartgears", e);
|
||||
|
||||
//we let the container continue
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void startContainerIfItHasntAlreadyFailed() {
|
||||
|
||||
if (containerHasFailed)
|
||||
throw new IllegalStateException("container is not managed due to previous failure");
|
||||
|
||||
|
||||
ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
|
||||
|
||||
|
||||
// we initialise the container in the same classloader as this
|
||||
// lib, lest container bind its resources to the current webapp
|
||||
try {
|
||||
|
||||
// TODO Ask why is needed?
|
||||
Thread.currentThread().setContextClassLoader(ContainerManager.class.getClassLoader());
|
||||
|
||||
manager = ContainerManager.instance;
|
||||
|
||||
context = manager.start(context);
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
|
||||
containerHasFailed = true;
|
||||
|
||||
throw new IllegalStateException("cannot manage container", e);
|
||||
|
||||
}
|
||||
|
||||
finally {//restore the classloader of the current application
|
||||
Thread.currentThread().setContextClassLoader(contextCL);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private boolean isResource(ServletContext application) {
|
||||
|
||||
//with care: smartgears may have already failed at this stage but we want to recognise
|
||||
//apps that would have been managed otherwise and give specific errors for those
|
||||
return (!containerHasFailed && context.configuration().app(application.getContextPath())!=null)
|
||||
||
|
||||
application.getResourceAsStream(Constants.configuration_file_path) != null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,222 @@
|
|||
package org.gcube.smartgears;
|
||||
|
||||
import org.gcube.smartgears.extensions.resource.RemoteResource;
|
||||
import org.gcube.smartgears.handlers.application.lifecycle.ProfileManager;
|
||||
import org.gcube.smartgears.handlers.application.request.RequestValidator;
|
||||
import org.gcube.smartgears.handlers.container.lifecycle.AccountingManager;
|
||||
|
||||
/**
|
||||
* Library-wide constants.
|
||||
*
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class Constants {
|
||||
|
||||
/**
|
||||
* The environment variable that points to the container configuration directory.
|
||||
*/
|
||||
public static final String ghn_home_env = "GHN_HOME";
|
||||
|
||||
/**
|
||||
* The system property that points to the container configuration directory.
|
||||
*/
|
||||
public static final String ghn_home_property = "ghn.home";
|
||||
|
||||
|
||||
/**
|
||||
* The container configuration file path, relative to the container configuration directory.
|
||||
*/
|
||||
public static final String container_configuraton_file_path = "container.xml";
|
||||
|
||||
|
||||
/**
|
||||
* The path of the application profile file, relative to the container configuration directory.
|
||||
*/
|
||||
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";
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public static final long default_container_publication_frequency_in_seconds = 60;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The application configuration resource path.
|
||||
*/
|
||||
public static final String configuration_file_path = "/WEB-INF/gcube-app.xml";
|
||||
|
||||
/**
|
||||
* 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";
|
||||
|
||||
|
||||
/**
|
||||
* The wildcard exclude directive.
|
||||
*/
|
||||
public static final String EXCLUDE_ALL = "*";
|
||||
|
||||
|
||||
/**
|
||||
* The mapping root of all extensions.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
public static final String frontpage_file_path = "/META-INF/frontpage.html";
|
||||
|
||||
/**
|
||||
* The configuration name of {@link ProfileManager}s.
|
||||
*/
|
||||
public static final String profile_management = "profile-management";
|
||||
|
||||
/**
|
||||
* The configuration name of {@link RequestValidator}s.
|
||||
*/
|
||||
public static final String request_validation = "request-validation";
|
||||
|
||||
|
||||
/**
|
||||
* The configuration name of {@link AccountingManager}s.
|
||||
*/
|
||||
public static final String accounting_management = "accounting-management";
|
||||
|
||||
/**
|
||||
* The configuration name of {@link RequestAccounting}s.
|
||||
*/
|
||||
public static final String request_accounting = "request-accounting";
|
||||
|
||||
|
||||
/**
|
||||
* The configuration name of {@link RemoteResource}s.
|
||||
*/
|
||||
public static final String remote_management = "remote-management";
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The path of the application profile file, relative to the service configuration directory.
|
||||
*/
|
||||
public static final String profile_file_path = "endpoint.xml";
|
||||
|
||||
/**
|
||||
* The name of the context property that contains the endpoint profile.
|
||||
*/
|
||||
public static final String profile_property = "endpoint-profile";
|
||||
|
||||
|
||||
/**
|
||||
* The name of the attribute in the servlet context that contains the context of an application.
|
||||
*/
|
||||
public static final String context_attribute ="gcube-application-context";
|
||||
|
||||
/**
|
||||
* The name of the HTTP header that contains the scope of requests
|
||||
*/
|
||||
public static final String scope_header="gcube-scope";
|
||||
|
||||
/**
|
||||
* The name of the HTTP header that contains the authorization token of requests
|
||||
*/
|
||||
public static final String token_header="gcube-token";
|
||||
|
||||
/**
|
||||
* The event for token registration for app.
|
||||
*/
|
||||
public static final String token_registered = "token-registered";
|
||||
|
||||
/**
|
||||
* The event for token removal for app.
|
||||
*/
|
||||
public static final String token_removed = "token-removed";
|
||||
|
||||
/**
|
||||
* The name of the HTTP header for standard HTTP basic authorization
|
||||
*/
|
||||
public static final String authorization_header ="Authorization";
|
||||
|
||||
/**
|
||||
* The name of the HTTP header that contains the called method of the current request
|
||||
*/
|
||||
public static final String called_method_header="gcube-method";
|
||||
|
||||
/**
|
||||
* The name of the Content-Type HTTP header
|
||||
*/
|
||||
public static final String content_type="Content-Type";
|
||||
|
||||
/**
|
||||
* The name of the Accept HTTP header
|
||||
*/
|
||||
public static final String accept="Accept";
|
||||
|
||||
/**
|
||||
* The name of the Allow HTTP header
|
||||
*/
|
||||
public static final String allow="Allow";
|
||||
|
||||
|
||||
/**
|
||||
* The name of the XML media type.
|
||||
*/
|
||||
public static final String plain_text="text/plain";
|
||||
|
||||
/**
|
||||
* The name of the XML media type.
|
||||
*/
|
||||
public static final String application_xml="application/xml";
|
||||
|
||||
|
||||
/**
|
||||
* The name of the XHTML media type.
|
||||
*/
|
||||
public static final String application_xhtml="application/xhtml+xml";
|
||||
|
||||
/**
|
||||
* The name of the Json media type.
|
||||
*/
|
||||
public static final String application_json="application/json";
|
||||
|
||||
public static final int application_republish_frequency_in_minutes = 20;
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package org.gcube.smartgears.configuration;
|
||||
|
||||
/**
|
||||
* The management mode the container or its applications.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public enum Mode {
|
||||
online,
|
||||
offline
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
package org.gcube.smartgears.configuration.application;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.gcube.smartgears.configuration.Mode;
|
||||
import org.gcube.smartgears.persistence.Persistence;
|
||||
|
||||
/**
|
||||
* The configuration of the application.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public interface ApplicationConfiguration {
|
||||
|
||||
|
||||
/**
|
||||
* Returns the management mode of the application.
|
||||
* @return the management mode
|
||||
*/
|
||||
Mode mode();
|
||||
|
||||
/**
|
||||
* Returns true if the application is secure (accessible only in https).
|
||||
* @return secure or not
|
||||
*/
|
||||
boolean secure();
|
||||
|
||||
/**
|
||||
* Returns true if the application is behind a proxy.
|
||||
* @return secure or not
|
||||
*/
|
||||
ApplicationConfiguration proxied(boolean proxied);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
|
||||
ApplicationConfiguration secure(boolean value);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
package org.gcube.smartgears.configuration.application;
|
||||
|
||||
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.smartgears.extensions.ApplicationExtension;
|
||||
import org.gcube.smartgears.handlers.application.ApplicationHandler;
|
||||
|
||||
/**
|
||||
* Binds {@link ApplicationConfiguration}s to and from XML serialisations.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class ApplicationConfigurationBinder {
|
||||
|
||||
/**
|
||||
* Returns the application configuration from its XML serialisation.
|
||||
*
|
||||
* @param stream the serialisation
|
||||
* @return the configuration
|
||||
* @throws RuntimeException if the serialisation is invalid
|
||||
*/
|
||||
public ApplicationConfiguration bind(InputStream stream) {
|
||||
|
||||
try {
|
||||
|
||||
JAXBContext ctx = JAXBContext.newInstance(DefaultApplicationConfiguration.class);
|
||||
|
||||
return (ApplicationConfiguration) ctx.createUnmarshaller().unmarshal(stream);
|
||||
|
||||
} catch (JAXBException e) {
|
||||
|
||||
throw new RuntimeException("invalid service configuration", e);
|
||||
|
||||
}
|
||||
finally {
|
||||
closeSafely(stream);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the handlers of the application from their XML serialisation.
|
||||
*
|
||||
* @param stream the serialisation
|
||||
* @return the handlers
|
||||
* @throws RuntimeException if the serialisation is invalid
|
||||
*/
|
||||
public ApplicationHandlers bindHandlers(InputStream stream) {
|
||||
|
||||
//collects handler classes
|
||||
Set<Class<?>> classes = scanForHandlers();
|
||||
|
||||
try {
|
||||
|
||||
JAXBContext ctx = JAXBContext.newInstance(classes.toArray(new Class<?>[0]));
|
||||
|
||||
return (ApplicationHandlers) ctx.createUnmarshaller().unmarshal(stream);
|
||||
|
||||
} catch (JAXBException e) {
|
||||
|
||||
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<?>> 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;
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
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.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.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
@XmlRootElement(name="handlers")
|
||||
public class ApplicationHandlers {
|
||||
|
||||
|
||||
@XmlElement(name="lifecycle") @IsValid
|
||||
private LifecycleHandlers lifecycleHandlers = new LifecycleHandlers();
|
||||
|
||||
@XmlElement(name="request") @IsValid
|
||||
private RequestHandlers requestHandlers = new RequestHandlers();
|
||||
|
||||
public ApplicationHandlers() {}
|
||||
|
||||
/**
|
||||
* Returns the {@link ApplicationLifecycleHandler}s for the service.
|
||||
* @return the lifecycle handlers
|
||||
*/
|
||||
public List<ApplicationLifecycleHandler> lifecycleHandlers() {
|
||||
return lifecycleHandlers.values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link ApplicationLifecycleHandler}s for the service.
|
||||
* @param handlers the lifecycle handlers
|
||||
* @return this configuration
|
||||
*/
|
||||
public ApplicationHandlers set(ApplicationLifecycleHandler ... handlers) {
|
||||
this.lifecycleHandlers = new LifecycleHandlers(Arrays.asList(handlers));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link RequestHandler}s for the service.
|
||||
* @return the lifetime handlers
|
||||
*/
|
||||
public List<RequestHandler> requestHandlers() {
|
||||
return requestHandlers.values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link RequestHandler}s for the service.
|
||||
* @param handlers the request handlers
|
||||
* @return this configuration
|
||||
*/
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
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());
|
||||
}
|
||||
|
||||
if (container.proxyAddress()==null)
|
||||
application.proxied(false);
|
||||
|
||||
}
|
||||
|
||||
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 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 void merge(ApplicationConfiguration config) {
|
||||
application.merge(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean secure() {
|
||||
return application.secure();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationConfiguration secure(boolean value) {
|
||||
return application.secure(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> startTokens() {
|
||||
return application.startTokens();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationConfiguration startTokens(Set<String> tokens) {
|
||||
return application.startTokens(tokens);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationConfiguration proxied(boolean proxied) {
|
||||
return application.proxied(proxied);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean proxied() {
|
||||
return application.proxied();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationConfiguration excludes(Exclude ... excludes) {
|
||||
return application.excludes(excludes);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,312 @@
|
|||
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="isSecure")
|
||||
private boolean secure = false;
|
||||
|
||||
@XmlAttribute(name="context")
|
||||
String context;
|
||||
|
||||
@XmlAttribute(name="proxied")
|
||||
private boolean proxied = true;
|
||||
|
||||
@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
|
||||
Set<Exclude> excludes= new LinkedHashSet<Exclude>();
|
||||
|
||||
@XmlElementRef(type=DefaultPersistence.class)
|
||||
@NotNull @IsValid
|
||||
private Persistence persistenceManager;
|
||||
|
||||
@Override
|
||||
public Set<Exclude> excludes() {
|
||||
return excludes;
|
||||
}
|
||||
|
||||
public DefaultApplicationConfiguration() {}
|
||||
|
||||
@Override
|
||||
public Mode mode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean secure() {
|
||||
return secure;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String context() {
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationConfiguration context(String context) {
|
||||
this.context=context;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationConfiguration excludes(Exclude ... excludes) {
|
||||
this.excludes=new HashSet<Exclude>(Arrays.asList(excludes));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationConfiguration name(String name) {
|
||||
this.name=name;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String serviceClass() {
|
||||
return group;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean proxied() {
|
||||
return proxied;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationConfiguration proxied(boolean proxied) {
|
||||
this.proxied = proxied;
|
||||
return this;
|
||||
}
|
||||
|
||||
@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 Persistence persistence() {
|
||||
return persistenceManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationConfiguration persistence(Persistence manager) {
|
||||
this.persistenceManager=manager;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationConfiguration secure(boolean value) {
|
||||
this.secure=value;
|
||||
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 (!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());
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DefaultApplicationConfiguration [mode=" + mode + ", secure="
|
||||
+ secure + ", context=" + context + ", proxied=" + proxied
|
||||
+ ", name=" + name + ", group=" + group + ", version="
|
||||
+ version + ", tokens=" + tokens + ", description="
|
||||
+ description + ", excludes=" + excludes
|
||||
+ ", persistenceManager=" + persistenceManager + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((context == null) ? 0 : context.hashCode());
|
||||
result = prime * result + ((description == null) ? 0 : description.hashCode());
|
||||
result = prime * result + ((excludes == null) ? 0 : excludes.hashCode());
|
||||
result = prime * result + ((group == null) ? 0 : group.hashCode());
|
||||
result = prime * result + ((mode == null) ? 0 : mode.hashCode());
|
||||
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||
result = prime * result + ((persistenceManager == null) ? 0 : persistenceManager.hashCode());
|
||||
result = prime * result + ((tokens == null) ? 0 : tokens.hashCode());
|
||||
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;
|
||||
DefaultApplicationConfiguration other = (DefaultApplicationConfiguration) obj;
|
||||
if (context == null) {
|
||||
if (other.context != null)
|
||||
return false;
|
||||
} else if (!context.equals(other.context))
|
||||
return false;
|
||||
if (description == null) {
|
||||
if (other.description != null)
|
||||
return false;
|
||||
} else if (!description.equals(other.description))
|
||||
return false;
|
||||
if (excludes == null) {
|
||||
if (other.excludes != null)
|
||||
return false;
|
||||
} else if (!excludes.equals(other.excludes))
|
||||
return false;
|
||||
if (group == null) {
|
||||
if (other.group != null)
|
||||
return false;
|
||||
} else if (!group.equals(other.group))
|
||||
return false;
|
||||
if (mode != other.mode)
|
||||
return false;
|
||||
if (name == null) {
|
||||
if (other.name != null)
|
||||
return false;
|
||||
} else if (!name.equals(other.name))
|
||||
return false;
|
||||
if (persistenceManager == null) {
|
||||
if (other.persistenceManager != null)
|
||||
return false;
|
||||
} else if (!persistenceManager.equals(other.persistenceManager))
|
||||
return false;
|
||||
if (tokens == null) {
|
||||
if (other.tokens != null)
|
||||
return false;
|
||||
} else if (!tokens.equals(other.tokens))
|
||||
return false;
|
||||
if (version == null) {
|
||||
if (other.version != null)
|
||||
return false;
|
||||
} else if (!version.equals(other.version))
|
||||
return false;
|
||||
if (secure!=other.secure)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package org.gcube.smartgears.configuration.application;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlValue;
|
||||
|
||||
@XmlRootElement(name="exclude")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class Exclude {
|
||||
|
||||
@XmlAttribute(name="handlers")
|
||||
private List<String> handlers = new ArrayList<String>();
|
||||
|
||||
@XmlValue
|
||||
private String path;
|
||||
|
||||
public List<String> getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
protected Exclude() {}
|
||||
|
||||
public Exclude(String path) {
|
||||
super();
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public Exclude(List<String> handlers, String path) {
|
||||
super();
|
||||
this.handlers = handlers;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((path == null) ? 0 : path.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;
|
||||
Exclude other = (Exclude) obj;
|
||||
if (handlers == null) {
|
||||
if (other.handlers != null)
|
||||
return false;
|
||||
} else if (!handlers.equals(other.handlers))
|
||||
return false;
|
||||
if (path == null) {
|
||||
if (other.path != null)
|
||||
return false;
|
||||
} else if (!path.equals(other.path))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Exclude [handlers=" + handlers + ", path=" + path + "]";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,501 @@
|
|||
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.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;
|
||||
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.Mode;
|
||||
import org.gcube.smartgears.configuration.application.ApplicationConfiguration;
|
||||
import org.gcube.smartgears.configuration.application.DefaultApplicationConfiguration;
|
||||
import org.gcube.smartgears.persistence.DefaultPersistence;
|
||||
import org.gcube.smartgears.persistence.Persistence;
|
||||
|
||||
/**
|
||||
* The configuration of the container.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
* @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/
|
||||
*/
|
||||
@XmlRootElement(name="container")
|
||||
public class ContainerConfiguration {
|
||||
|
||||
|
||||
@XmlAttribute
|
||||
private Mode mode = Mode.online;
|
||||
|
||||
@XmlElement
|
||||
@NotNull
|
||||
String hostname;
|
||||
|
||||
@XmlElement
|
||||
@NotNull
|
||||
Integer port;
|
||||
|
||||
@XmlElementRef
|
||||
@IsValid
|
||||
ProxyAddress proxyAddress;
|
||||
|
||||
@XmlElement(name ="authentication-endpoint")
|
||||
String authenticationEnpoint = null;
|
||||
|
||||
@XmlElement(name ="secure-port")
|
||||
Integer securePort;
|
||||
|
||||
@XmlElement
|
||||
@NotNull
|
||||
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
|
||||
List<Property> properties = new ArrayList<Property>();
|
||||
|
||||
@XmlElement(name="publication-frequency")
|
||||
long publicationFrequency = default_container_publication_frequency_in_seconds;
|
||||
|
||||
@XmlElementRef(type=DefaultPersistence.class)
|
||||
@IsValid
|
||||
private Persistence persistenceManager;
|
||||
|
||||
/**
|
||||
* Returns the management mode for the container.
|
||||
* @return the management mode
|
||||
*/
|
||||
public Mode mode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the management mode for the container.
|
||||
* @param mode the management mode
|
||||
* @return this configuration
|
||||
*/
|
||||
public ContainerConfiguration mode(Mode mode) {
|
||||
this.mode=mode;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the application configurations included in this configuration.
|
||||
* @return the application configurations
|
||||
*/
|
||||
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
|
||||
* @return this configuration
|
||||
*/
|
||||
public synchronized ContainerConfiguration app(ApplicationConfiguration app) {
|
||||
int indexToRemove =-1;
|
||||
int index =0;
|
||||
for (ApplicationConfiguration application : apps){
|
||||
if (app.context().equals(application.context()))
|
||||
indexToRemove = index;
|
||||
index++;
|
||||
}
|
||||
if(indexToRemove!=-1)
|
||||
apps.remove(indexToRemove);
|
||||
apps.add(app);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the geographical site of the container.
|
||||
* @return the site
|
||||
*/
|
||||
public Site site() {
|
||||
return site;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the geographical site of the container.
|
||||
* @param site the site
|
||||
* @return this configuration
|
||||
*/
|
||||
public ContainerConfiguration site(Site site) {
|
||||
this.site=site;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the infrastructure in which the container is running.
|
||||
* @return the infrastructure
|
||||
*/
|
||||
public String infrastructure() {
|
||||
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 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 port;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the port at which the container is listening for requests.
|
||||
* @return the port
|
||||
*/
|
||||
public Integer securePort() {
|
||||
return securePort;
|
||||
}
|
||||
|
||||
|
||||
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 securePort(int port) {
|
||||
this.securePort=port;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the VOs in which the container initially operates.
|
||||
* @return the VOs
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
public ProxyAddress proxyAddress() {
|
||||
return proxyAddress;
|
||||
}
|
||||
|
||||
public ContainerConfiguration setProxyaddress(ProxyAddress proxyaddress) {
|
||||
this.proxyAddress = proxyaddress;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the persistence manager of the container.
|
||||
* @return the manager
|
||||
*/
|
||||
public Persistence persistence() {
|
||||
return persistenceManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the persistence manager of the container.
|
||||
* @param manager the manager
|
||||
* @return this configuration
|
||||
*/
|
||||
public ContainerConfiguration persistence(Persistence manager) {
|
||||
this.persistenceManager=manager;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configuration properties of the container.
|
||||
* @return the properties
|
||||
*/
|
||||
public Map<String,String> properties() {
|
||||
Map<String,String> map = new HashMap<String, String>();
|
||||
for (Property prop : properties)
|
||||
map.put(prop.name, prop.value);
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a configuration property to the container.
|
||||
* @param the name of the property
|
||||
* @param the value of the property
|
||||
* @return this configuration
|
||||
*/
|
||||
public ContainerConfiguration property(String name, String value) {
|
||||
properties.add(new Property(name, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the publication frequency for the container's profile.
|
||||
* @return the frquency;
|
||||
*/
|
||||
public long publicationFrequency() {
|
||||
return publicationFrequency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the publication frequency for the container's profile.
|
||||
* @param frequency the frequency
|
||||
* @return this configuration
|
||||
*/
|
||||
public ContainerConfiguration publicationFrequency(long frequency) {
|
||||
this.publicationFrequency=frequency;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Set<String> allowedContexts() {
|
||||
return allowedContext;
|
||||
}
|
||||
|
||||
public void allowedContexts(Set<String> allowedContexts) {
|
||||
this.allowedContext = allowedContexts;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
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 + ((apps == null) ? 0 : apps.hashCode());
|
||||
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 + (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 (apps == null) {
|
||||
if (other.apps != null)
|
||||
return false;
|
||||
} else if (!apps.equals(other.apps))
|
||||
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 (securePort == null) {
|
||||
if (other.securePort != null)
|
||||
return false;
|
||||
} else if (!securePort.equals(other.securePort))
|
||||
return false;
|
||||
if (properties == null) {
|
||||
if (other.properties != null)
|
||||
return false;
|
||||
} else if (!properties.equals(other.properties))
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
package org.gcube.smartgears.configuration.container;
|
||||
|
||||
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.smartgears.handlers.container.ContainerHandler;
|
||||
import org.gcube.smartgears.utils.Utils;
|
||||
|
||||
/**
|
||||
* Binds {@link ContainerConfiguration}s to and from XML serialisations.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class ContainerConfigurationBinder {
|
||||
|
||||
/**
|
||||
* Returns a {@link ContainerConfiguration} from its XML serialisation.
|
||||
*
|
||||
* @param stream the serialisation
|
||||
* @return the configuration
|
||||
* @throws RuntimeException if the serialisation is invalid
|
||||
*/
|
||||
public ContainerConfiguration bind(InputStream stream) {
|
||||
|
||||
try {
|
||||
|
||||
JAXBContext ctx = JAXBContext.newInstance(ContainerConfiguration.class);
|
||||
|
||||
ContainerConfiguration config = (ContainerConfiguration) ctx.createUnmarshaller().unmarshal(stream);
|
||||
|
||||
return config;
|
||||
|
||||
} catch (JAXBException e) {
|
||||
|
||||
throw new RuntimeException("invalid container configuration", e);
|
||||
|
||||
}
|
||||
finally {
|
||||
|
||||
Utils.closeSafely(stream);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the handlers of the container from their XML serialisation.
|
||||
*
|
||||
* @param stream the serialisation
|
||||
* @return the handlers
|
||||
* @throws RuntimeException if the serialisation is invalid
|
||||
*/
|
||||
public ContainerHandlers bindHandlers(InputStream stream) {
|
||||
|
||||
//collects handler classes
|
||||
Set<Class<?>> classes = scanForConfigurationElements();
|
||||
|
||||
try {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package org.gcube.smartgears.configuration.container;
|
||||
|
||||
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
|
||||
boolean secure = false;
|
||||
|
||||
@XmlElement
|
||||
@NotNull
|
||||
String hostname;
|
||||
|
||||
@XmlElement
|
||||
@NotNull
|
||||
int port;
|
||||
|
||||
public String hostname() {
|
||||
return hostname;
|
||||
}
|
||||
|
||||
public ProxyAddress hostname(String hostname) {
|
||||
this.hostname = hostname;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int port() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public ProxyAddress port(int port) {
|
||||
this.port = port;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean secure() {
|
||||
return secure;
|
||||
}
|
||||
|
||||
public ProxyAddress secure(boolean secure) {
|
||||
this.secure = secure;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
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;
|
||||
|
||||
/**
|
||||
* The geographical site of the container.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
@XmlRootElement(name="site")
|
||||
public class Site {
|
||||
|
||||
@XmlElement
|
||||
@NotNull
|
||||
String country;
|
||||
|
||||
@XmlElement
|
||||
@NotNull
|
||||
String location;
|
||||
|
||||
@XmlElement
|
||||
@NotNull
|
||||
String latitude;
|
||||
|
||||
@XmlElement
|
||||
@NotNull
|
||||
String longitude;
|
||||
|
||||
/**
|
||||
* Returns the country.
|
||||
* @return the country
|
||||
*/
|
||||
public String country() {
|
||||
return country;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the country.
|
||||
* @param the country
|
||||
* @return this configuration
|
||||
*/
|
||||
public Site country(String country) {
|
||||
this.country=country;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the latitude.
|
||||
* @return the latitude
|
||||
*/
|
||||
public String latitude() {
|
||||
return latitude;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the latitude.
|
||||
* @param the latitude
|
||||
* @return this configuration
|
||||
*/
|
||||
public Site latitude(String latitude) {
|
||||
this.latitude=latitude;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the location.
|
||||
* @param the location
|
||||
* @return this location
|
||||
*/
|
||||
public Site location(String location) {
|
||||
this.location=location;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Site other = (Site) obj;
|
||||
if (country == null) {
|
||||
if (other.country != null)
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,171 @@
|
|||
package org.gcube.smartgears.context;
|
||||
|
||||
import static org.gcube.smartgears.utils.Utils.*;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A collection of uniquely named {@link Property}s.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class Properties implements Iterable<Property> {
|
||||
|
||||
private final Map<String, Property> properties = new HashMap<String, Property>();
|
||||
private Properties parent;
|
||||
|
||||
/**
|
||||
* Creates an instance that delegates to another for unknown properties.
|
||||
* @param parent the parent instance
|
||||
*/
|
||||
public Properties(Properties parent) {
|
||||
this.parent=parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance.
|
||||
*/
|
||||
public Properties() {}
|
||||
|
||||
@Override
|
||||
public Iterator<Property> iterator() {
|
||||
return properties.values().iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds one or more properties to this collection.
|
||||
*
|
||||
* @param properties the properties
|
||||
*/
|
||||
public synchronized void add(Property ... properties) {
|
||||
|
||||
notNull("properties",properties);
|
||||
|
||||
for (Property property : properties)
|
||||
this.properties.put(property.name(),property);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if this collection contains a given property.
|
||||
*
|
||||
* @param name the name of the property
|
||||
* @return <code>true</code> if this collection contains a property with the given name
|
||||
*/
|
||||
public synchronized boolean contains(String name) {
|
||||
|
||||
notNull("property name",name);
|
||||
|
||||
return this.properties.containsKey(name) ||
|
||||
parent==null?
|
||||
false:
|
||||
this.parent.contains(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a given property.
|
||||
*
|
||||
* @param name the name of the property
|
||||
*
|
||||
* @throws IllegalStateException if a property with the given name does not exist in this collection
|
||||
*/
|
||||
public void delete(String name) {
|
||||
|
||||
notNull("property name",name);
|
||||
|
||||
if (this.properties.remove(name) == null)
|
||||
if (parent==null)
|
||||
throw new IllegalStateException("unknown property " + name);
|
||||
else
|
||||
parent.delete(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a given property in this collection.
|
||||
*
|
||||
* @param name the name of the property
|
||||
* @return the property
|
||||
*
|
||||
* @throws IllegalStateException if a property with a given name does not exist in this collection
|
||||
*/
|
||||
public synchronized Property lookup(String name) {
|
||||
|
||||
notNull("property name",name);
|
||||
|
||||
Property property = this.properties.get(name);
|
||||
|
||||
if (property == null)
|
||||
if (parent==null)
|
||||
throw new IllegalStateException("unknown property " + name);
|
||||
else
|
||||
property = parent.lookup(name);
|
||||
|
||||
return property;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if this collection has no properties.
|
||||
*
|
||||
* @return <code>true</code> if this collection has no properties
|
||||
*/
|
||||
public synchronized boolean isEmpty() {
|
||||
return properties.isEmpty() || parent==null? true: parent.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final int maxLen = 100;
|
||||
return "board=" + (properties != null ? toString(properties.entrySet(), maxLen) : null);
|
||||
}
|
||||
|
||||
private String toString(Collection<?> collection, int maxLen) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("[");
|
||||
int i = 0;
|
||||
for (Iterator<?> iterator = collection.iterator(); iterator.hasNext() && i < maxLen; i++) {
|
||||
if (i > 0)
|
||||
builder.append(", ");
|
||||
builder.append(iterator.next());
|
||||
}
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((parent == null) ? 0 : parent.hashCode());
|
||||
result = prime * result + ((properties == null) ? 0 : properties.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;
|
||||
Properties other = (Properties) obj;
|
||||
if (parent == null) {
|
||||
if (other.parent != null)
|
||||
return false;
|
||||
} else if (!parent.equals(other.parent))
|
||||
return false;
|
||||
if (properties == null) {
|
||||
if (other.properties != null)
|
||||
return false;
|
||||
} else if (!properties.equals(other.properties))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
package org.gcube.smartgears.context;
|
||||
|
||||
import static org.gcube.smartgears.utils.Utils.*;
|
||||
|
||||
|
||||
/**
|
||||
* A named property with a value and a description.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class Property {
|
||||
|
||||
private final String name;
|
||||
private final String description;
|
||||
private final Object value;
|
||||
private boolean display =true;
|
||||
|
||||
/**
|
||||
* Creates an instance with a given name and value.
|
||||
* @param name the name
|
||||
* @param value the value
|
||||
*/
|
||||
public Property(String name, Object value) {
|
||||
this(name,value,null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance with a given name, value, and description.
|
||||
* @param name the name
|
||||
* @param value the value
|
||||
* @param description the description
|
||||
*/
|
||||
public Property(String name, Object value, String description) {
|
||||
|
||||
notNull("property name",name);
|
||||
notNull("property value",value);
|
||||
|
||||
this.name=name;
|
||||
this.description=description;
|
||||
this.value=value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this property.
|
||||
* @return the name
|
||||
*/
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the property is intended for display
|
||||
* @param display <code>true</code> if the property is intended for display
|
||||
*/
|
||||
public void display(boolean display) {
|
||||
this.display = display;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the property is intended for display
|
||||
* @return <code>true</code> if the property is intended for display
|
||||
*/
|
||||
public boolean isDisplay() {
|
||||
return display;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this property.
|
||||
*
|
||||
* @return the value
|
||||
*/
|
||||
public Object value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this property under a given type.
|
||||
*
|
||||
* @return the value
|
||||
*
|
||||
* @throws IllegalStateException if the value cannot be returned under the given type.
|
||||
*/
|
||||
public <S> S value(Class<S> type) {
|
||||
|
||||
if (is(type))
|
||||
return type.cast(value());
|
||||
|
||||
throw new IllegalStateException("property value "+value()+" of type "+value().getClass()+" cannot be typed as "+type.getCanonicalName());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the value of this property has a given type.
|
||||
* @param type the type
|
||||
* @return <code>true</code> if the value of this property has a given type
|
||||
*/
|
||||
public boolean is(Class<?> type) {
|
||||
return type.isInstance(value());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description of this property.
|
||||
*
|
||||
* @return the description
|
||||
*/
|
||||
public String description() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((description == null) ? 0 : description.hashCode());
|
||||
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 (description == null) {
|
||||
if (other.description != null)
|
||||
return false;
|
||||
} else if (!description.equals(other.description))
|
||||
return false;
|
||||
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 String toString() {
|
||||
return name+"="+value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package org.gcube.smartgears.context.application;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.gcube.common.events.Hub;
|
||||
import org.gcube.smartgears.configuration.application.ApplicationConfiguration;
|
||||
import org.gcube.smartgears.context.Properties;
|
||||
import org.gcube.smartgears.context.container.ContainerContext;
|
||||
import org.gcube.smartgears.lifecycle.application.ApplicationLifecycle;
|
||||
import org.gcube.smartgears.persistence.Persistence;
|
||||
|
||||
/**
|
||||
* The management context of an application.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public interface ApplicationContext {
|
||||
|
||||
|
||||
String id();
|
||||
|
||||
/**
|
||||
* Returns the name of the application.
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* Returns the configuration of the application.
|
||||
*
|
||||
* @return the configuration
|
||||
*/
|
||||
ApplicationConfiguration configuration();
|
||||
|
||||
|
||||
<T> T profile(Class<T> type);
|
||||
|
||||
/**
|
||||
* Returns the lifecycle of the application.
|
||||
*
|
||||
* @return the lifecycle
|
||||
*/
|
||||
ApplicationLifecycle lifecycle();
|
||||
|
||||
/**
|
||||
* Returns the event hub of the application
|
||||
*
|
||||
* @return the hub
|
||||
*/
|
||||
Hub events();
|
||||
|
||||
/**
|
||||
* Returns the persistence manager of the application.
|
||||
*
|
||||
* @return the manager
|
||||
*/
|
||||
Persistence persistence();
|
||||
|
||||
/**
|
||||
* Returns the servlet context of the application.
|
||||
*
|
||||
* @return the context
|
||||
*/
|
||||
ServletContext application();
|
||||
|
||||
/**
|
||||
* Returns the management context of the container.
|
||||
*
|
||||
* @return the context
|
||||
*/
|
||||
ContainerContext container();
|
||||
|
||||
/**
|
||||
* Returns the properties of the application
|
||||
*
|
||||
* @return the properties
|
||||
*/
|
||||
Properties properties();
|
||||
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
package org.gcube.smartgears.context.application;
|
||||
|
||||
import static org.gcube.smartgears.Constants.profile_property;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.gcube.common.events.Hub;
|
||||
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.Persistence;
|
||||
|
||||
/**
|
||||
* Default {@link ApplicationContext} implementation.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class DefaultApplicationContext implements ApplicationContext {
|
||||
|
||||
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 String id;
|
||||
|
||||
/**
|
||||
* Crates an intance with mandatory parameters
|
||||
* @param container the container context
|
||||
* @param sctx the servlet context
|
||||
* @param configuration the configuration
|
||||
* @param hub the event hub
|
||||
* @param lifecycle the lifecycle
|
||||
* @param properties the properties
|
||||
*/
|
||||
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.properties=properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance by copying the configuration of another.
|
||||
* @param context the other instance
|
||||
*/
|
||||
public DefaultApplicationContext(ApplicationContext context) {
|
||||
this(context.id(), context.container(),context.application(),context.configuration(),context.events(), context.lifecycle(), new Properties(context.properties()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletContext application() {
|
||||
return sctx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContainerContext container() {
|
||||
return container;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("all")
|
||||
public <T> T profile(Class<T> type) {
|
||||
|
||||
if (type==GCoreEndpoint.class)
|
||||
return (T) properties().lookup(profile_property).value(GCoreEndpoint.class);
|
||||
|
||||
throw new IllegalArgumentException("unsupported profile type: "+type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() { //little shortcut for ease of logging
|
||||
return configuration.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationConfiguration configuration() {
|
||||
return configuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationLifecycle lifecycle() {
|
||||
return lifecycle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hub events() {
|
||||
return hub;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Persistence persistence() {
|
||||
return configuration.persistence();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Properties properties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package org.gcube.smartgears.context.container;
|
||||
|
||||
import org.gcube.common.events.Hub;
|
||||
import org.gcube.smartgears.configuration.container.ContainerConfiguration;
|
||||
import org.gcube.smartgears.context.Properties;
|
||||
import org.gcube.smartgears.lifecycle.container.ContainerLifecycle;
|
||||
import org.gcube.smartgears.persistence.Persistence;
|
||||
|
||||
/**
|
||||
* The management context of the container.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public interface ContainerContext {
|
||||
|
||||
|
||||
/**
|
||||
* Returns the configuration of the container.
|
||||
* @return the configuration
|
||||
*/
|
||||
ContainerConfiguration configuration();
|
||||
|
||||
/**
|
||||
* Returns the resource profile of a given type of the container.
|
||||
* @return the profile
|
||||
*/
|
||||
<T> T profile(Class<T> type);
|
||||
|
||||
/**
|
||||
* Returns the lifecycle of the container
|
||||
* @return the lifecycle
|
||||
*/
|
||||
ContainerLifecycle lifecycle();
|
||||
|
||||
/**
|
||||
* Returns the event hub of the container
|
||||
* @return the hub
|
||||
*/
|
||||
Hub events();
|
||||
|
||||
/**
|
||||
* Returns the persistence manager of the container.
|
||||
* @return the manager
|
||||
*/
|
||||
Persistence persistence();
|
||||
|
||||
/**
|
||||
* Returns the properties of the container.
|
||||
* @return the properties
|
||||
*/
|
||||
Properties properties();
|
||||
|
||||
String id();
|
||||
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package org.gcube.smartgears.context.container;
|
||||
|
||||
import static org.gcube.smartgears.Constants.*;
|
||||
import org.gcube.common.events.Hub;
|
||||
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.Persistence;
|
||||
|
||||
/**
|
||||
* Default {@link ContainerContext} implementation.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class DefaultContainerContext implements ContainerContext {
|
||||
|
||||
private final ContainerConfiguration configuration;
|
||||
private final ContainerLifecycle lifecycle;
|
||||
private final Properties properties;
|
||||
private final Hub hub;
|
||||
private final String id;
|
||||
/**
|
||||
* Creates an instance with mandatory parameters.
|
||||
* @param configuration the configuration
|
||||
* @param hub the event hub
|
||||
* @param lifecycle the lifecycle
|
||||
* @param properties the properties
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@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() {
|
||||
return configuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContainerLifecycle lifecycle() {
|
||||
return lifecycle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hub events() {
|
||||
return hub;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Persistence persistence() {
|
||||
return configuration.persistence();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Properties properties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package org.gcube.smartgears.extensions;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
import static org.gcube.common.events.impl.Utils.*;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.gcube.smartgears.extensions.HttpExtension.Method;
|
||||
|
||||
public class ApiMethodSignature {
|
||||
|
||||
Method method;
|
||||
Set<String> requestTypes = new HashSet<String>();
|
||||
Set<String> responseTypes = new HashSet<String>();
|
||||
|
||||
public ApiMethodSignature(Method method) {
|
||||
notNull("method",method);
|
||||
this.method=method;
|
||||
}
|
||||
|
||||
public ApiMethodSignature accepts(String ... types) {
|
||||
|
||||
notNull("request types",types);
|
||||
this.requestTypes.addAll(asList(types));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ApiMethodSignature produces(String ... types) {
|
||||
|
||||
notNull("response types",types);
|
||||
this.responseTypes.addAll(asList(types));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
package org.gcube.smartgears.extensions;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.gcube.smartgears.Constants;
|
||||
import org.gcube.smartgears.configuration.application.Exclude;
|
||||
|
||||
/**
|
||||
* A resource-specifc API handled by an {@link HttpController}.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public abstract class ApiResource extends HttpExtension {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Returns a {@link ApiSignature} that declares the method and media types handled by an {@link ApiResource} for a given mapping.
|
||||
* @param mapping the mapping
|
||||
* @return the signature
|
||||
*/
|
||||
public static ApiSignature handles(String mapping) {
|
||||
return new ApiSignature(mapping);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@link ApiMethodSignature} that declares the media types handled by an {@link ApiResource} for a given method.
|
||||
* @param method the method
|
||||
* @return the signature
|
||||
*/
|
||||
public static ApiMethodSignature method(Method method) {
|
||||
return new ApiMethodSignature(method);
|
||||
}
|
||||
|
||||
private final ApiSignature signature;
|
||||
|
||||
/**
|
||||
* Creates an instance with a given signature.
|
||||
* @param signature the signature
|
||||
*/
|
||||
public ApiResource(ApiSignature signature) {
|
||||
|
||||
super("extension-resource", signature.mapping());
|
||||
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
* @return <code>true</code> if this resource supports the given method
|
||||
*/
|
||||
public boolean supports(Method method) {
|
||||
|
||||
return signature.methods().contains(method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if this resource accepts a given media type for a given method.
|
||||
* @param method the method
|
||||
* @param type the media type
|
||||
* @return <code>true</code> if this resource accepts the given media type for the given method
|
||||
*/
|
||||
public boolean accepts(Method method, String type) {
|
||||
|
||||
Set<String> requestTypes = signature.requestTypes().get(method);
|
||||
|
||||
//if signature does not specify, we assume resource can work with anything or uses a default and is in charge
|
||||
return requestTypes.isEmpty() || requestTypes.contains(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if this resource produces a given media type for a given method.
|
||||
* @param method the method
|
||||
* @param type the media type
|
||||
* @return <code>true</code> if this resource produces the given media type for the given method
|
||||
*/
|
||||
public boolean produces(Method method, String type) {
|
||||
|
||||
if (type.contains("*"))
|
||||
return true;
|
||||
|
||||
Set<String> contentTypes = signature.responseTypes().get(method);
|
||||
return contentTypes.contains(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the signature of this resource.
|
||||
* @return the signature
|
||||
*/
|
||||
public ApiSignature signature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
|
||||
//adapt to http servlet API delegating to standard implementation that throws a 405, resources ovverride in line with their signature
|
||||
|
||||
@Override
|
||||
public void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
super.doHead(req, resp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
super.doGet(req, resp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
// TODO Auto-generated method stub
|
||||
super.doPost(req, resp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
// TODO Auto-generated method stub
|
||||
super.doPut(req, resp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
// TODO Auto-generated method stub
|
||||
super.doDelete(req, resp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
// TODO Auto-generated method stub
|
||||
super.doOptions(req, resp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
// TODO Auto-generated method stub
|
||||
super.doTrace(req, resp);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package org.gcube.smartgears.extensions;
|
||||
|
||||
import static org.gcube.common.events.impl.Utils.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.gcube.smartgears.extensions.HttpExtension.Method;
|
||||
|
||||
public class ApiSignature {
|
||||
|
||||
|
||||
private final String mapping;
|
||||
private Set<Method> methods = new HashSet<Method>();
|
||||
private Map<Method,Set<String>> requestTypes = new HashMap<Method, Set<String>>();
|
||||
private Map<Method,Set<String>> responseTypes = new HashMap<Method, Set<String>>();
|
||||
|
||||
|
||||
public ApiSignature(String mapping) {
|
||||
this.mapping=mapping;
|
||||
}
|
||||
|
||||
public ApiSignature with(ApiMethodSignature signature) {
|
||||
|
||||
notNull("method signature",signature);
|
||||
|
||||
this.methods.add(signature.method);
|
||||
this.requestTypes.put(signature.method,signature.requestTypes);
|
||||
this.responseTypes.put(signature.method,signature.responseTypes);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public String mapping() {
|
||||
return mapping;
|
||||
}
|
||||
|
||||
public Set<Method> methods() {
|
||||
return methods;
|
||||
}
|
||||
|
||||
public Map<Method,Set<String>> requestTypes() {
|
||||
return requestTypes;
|
||||
}
|
||||
|
||||
public Map<Method,Set<String>> responseTypes() {
|
||||
return responseTypes;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package org.gcube.smartgears.extensions;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.Servlet;
|
||||
|
||||
import org.gcube.smartgears.configuration.application.Exclude;
|
||||
import org.gcube.smartgears.context.application.ApplicationContext;
|
||||
|
||||
/**
|
||||
* A servlet that allows remote management of the application.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public interface ApplicationExtension extends Servlet {
|
||||
|
||||
/**
|
||||
* Initialises the extensions with the context of the application.
|
||||
* @param context the application context
|
||||
* @throws Exception if the extension cannot be initialised
|
||||
*/
|
||||
void init(ApplicationContext context) throws Exception;
|
||||
|
||||
/**
|
||||
* Returns the name of this extension.
|
||||
* @return the name
|
||||
*/
|
||||
String name();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the mapping of this extension.
|
||||
* @return the mapping
|
||||
*/
|
||||
String mapping();
|
||||
|
||||
/**
|
||||
* Returns the set of request paths that should be excluded from request management.
|
||||
* @return the set of request paths that should be excluded from request management
|
||||
*/
|
||||
Set<Exclude> excludes();
|
||||
}
|
|
@ -0,0 +1,245 @@
|
|||
package org.gcube.smartgears.extensions;
|
||||
|
||||
import static org.gcube.smartgears.Constants.*;
|
||||
import static org.gcube.smartgears.handlers.application.request.RequestError.*;
|
||||
import static org.gcube.smartgears.utils.Utils.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.gcube.smartgears.configuration.application.Exclude;
|
||||
import org.gcube.smartgears.context.application.ApplicationContext;
|
||||
|
||||
/**
|
||||
* An {@link HttpExtension} that dispatches to one or more {@link ApiResource}s, handling the generic, HTTP-aspects
|
||||
* aspects of their client interactions
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class HttpController extends HttpExtension {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final Map<String, ApiResource> resources = new HashMap<String, ApiResource>();
|
||||
|
||||
|
||||
HttpController() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance with a given name and a given mapping.
|
||||
*
|
||||
* @param name the name
|
||||
* @param mapping the mapping
|
||||
*/
|
||||
public HttpController(String name, String mapping) {
|
||||
super(name, mapping);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds one ore more {@link ApiResource}s to this controller.
|
||||
*
|
||||
* @param resources the resources
|
||||
*/
|
||||
public void addResources(ApiResource... resources) {
|
||||
|
||||
notNull("API resources", resources);
|
||||
|
||||
for (ApiResource resource : resources)
|
||||
this.resources.put(resource.mapping(), resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(ApplicationContext context) throws Exception {
|
||||
super.init(context);
|
||||
for (ApiResource resource : resources.values())
|
||||
resource.init(context);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Exclude> excludes() {
|
||||
|
||||
Set<Exclude> resourceExcludes = new LinkedHashSet<Exclude>();
|
||||
|
||||
for (ApiResource resource : resources.values())
|
||||
resourceExcludes.addAll(resource.excludes());
|
||||
|
||||
return resourceExcludes;
|
||||
}
|
||||
|
||||
// final because we dispatch to http servlet method inside, resources can use init(Context)
|
||||
@Override
|
||||
public final void service(ServletRequest req, ServletResponse resp) throws ServletException, IOException {
|
||||
|
||||
HttpServletRequest request = HttpServletRequest.class.cast(req);
|
||||
HttpServletResponse response = HttpServletResponse.class.cast(resp);
|
||||
|
||||
ApiResource resource = resourceFor(request.getPathInfo());
|
||||
|
||||
checkMethod(resource, request, response);
|
||||
|
||||
checkContentTyperHeader(resource, request, response);
|
||||
checkAcceptHeader(resource, request, response);
|
||||
|
||||
setContentTypeHeader(resource, request, response);
|
||||
|
||||
dispatch(resource, request, response);
|
||||
|
||||
|
||||
}
|
||||
|
||||
// helpers
|
||||
|
||||
private void setContentTypeHeader(ApiResource resource, HttpServletRequest request, HttpServletResponse response) {
|
||||
|
||||
Method method = valueOf(request.getMethod());
|
||||
|
||||
Set<String> responseTypes = resource.signature().responseTypes().get(method);
|
||||
|
||||
if (responseTypes.isEmpty())
|
||||
//overridden by resources that e.g. create something and return more specific codes
|
||||
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
|
||||
else
|
||||
if (responseTypes.size() == 1 && !response.containsHeader(content_type))
|
||||
response.addHeader(content_type, responseTypes.iterator().next());
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
private ApiResource resourceFor(String path) {
|
||||
|
||||
if (path==null) {
|
||||
path = "/";
|
||||
}
|
||||
else
|
||||
// some tolerance for trailing slashes
|
||||
if (path.length()>1 && path.endsWith("/"))
|
||||
path = path.substring(0, path.length() - 1);
|
||||
|
||||
ApiResource resource = resources.get(path);
|
||||
|
||||
if (resource == null)
|
||||
resource_notfound_error.fire();
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void checkMethod(ApiResource resource, HttpServletRequest request, HttpServletResponse response) {
|
||||
|
||||
Method method = valueOf(request.getMethod());
|
||||
|
||||
if (!resource.supports(method)) {
|
||||
response.addHeader(allow, toSingleString(resource.signature().methods()));
|
||||
method_unsupported_error.fire("this resource does not support method " + method);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void dispatch(ApiResource resource, HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
|
||||
Method method = valueOf(request.getMethod());
|
||||
|
||||
// dispatches
|
||||
switch (method) {
|
||||
|
||||
case HEAD:
|
||||
resource.doHead(request, response);
|
||||
break;
|
||||
case GET:
|
||||
resource.doGet(request, response);
|
||||
break;
|
||||
case POST:
|
||||
resource.doPost(request, response);
|
||||
break;
|
||||
case PUT:
|
||||
resource.doPut(request, response);
|
||||
break;
|
||||
case DELETE:
|
||||
resource.doDelete(request, response);
|
||||
break;
|
||||
case OPTIONS:
|
||||
resource.doOptions(request, response);
|
||||
break;
|
||||
case TRACE:
|
||||
resource.doTrace(request, response);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkContentTyperHeader(ApiResource resource, HttpServletRequest request, HttpServletResponse response) {
|
||||
|
||||
Method method = valueOf(request.getMethod());
|
||||
|
||||
// if request specifies a media-type, we check it against signature
|
||||
// if it doesn't we let it pass and let the resource apply default or complain.
|
||||
String requestTypes = request.getHeader(content_type);
|
||||
if (requestTypes != null) {
|
||||
String type = null;
|
||||
for (String value : valuesOf(requestTypes))
|
||||
if (resource.accepts(method, value)) {
|
||||
type = value;
|
||||
break;
|
||||
}
|
||||
if (type == null)
|
||||
incoming_contenttype_unsupported_error.fire("this resource does not accept " + requestTypes);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAcceptHeader(ApiResource resource, HttpServletRequest request, HttpServletResponse response) {
|
||||
|
||||
Method method = valueOf(request.getMethod());
|
||||
|
||||
// check match on outgoing media type, if any
|
||||
String responseType = request.getHeader(accept);
|
||||
if (responseType != null) {
|
||||
String type = null;
|
||||
for (String value : valuesOf(responseType))
|
||||
if (resource.produces(method, value)) {
|
||||
type = value;
|
||||
break;
|
||||
}
|
||||
if (type == null)
|
||||
outgoing_contenttype_unsupported_error.fire("this resource cannot produce " + responseType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private String toSingleString(Collection<? extends Object> values) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
for (Object value : values)
|
||||
builder.append(value).append(",");
|
||||
|
||||
String concat = builder.toString();
|
||||
return concat.substring(0, concat.length() - 1);
|
||||
|
||||
}
|
||||
|
||||
private String[] valuesOf(String header) {
|
||||
return header.split(",");
|
||||
}
|
||||
|
||||
private Method valueOf(String method) {
|
||||
|
||||
try {
|
||||
return Method.valueOf(method);
|
||||
} catch (Exception e) {
|
||||
throw method_unsupported_error.toException("unsupported method " + method);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package org.gcube.smartgears.extensions;
|
||||
|
||||
import static org.gcube.common.events.impl.Utils.*;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
|
||||
import org.gcube.common.validator.annotations.NotEmpty;
|
||||
import org.gcube.smartgears.configuration.application.Exclude;
|
||||
import org.gcube.smartgears.context.application.ApplicationContext;
|
||||
|
||||
/**
|
||||
* An {@link ApplicationExtension} that implements the {@link HttpServlet} interface
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public abstract class HttpExtension extends HttpServlet implements ApplicationExtension {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Enumeration of HTTP methods.
|
||||
*
|
||||
*/
|
||||
public static enum Method {
|
||||
GET, PUT, POST, HEAD, DELETE, OPTIONS, TRACE
|
||||
}
|
||||
|
||||
@XmlAttribute @NotEmpty
|
||||
private String name;
|
||||
|
||||
@XmlAttribute @NotEmpty
|
||||
private String mapping;
|
||||
|
||||
private ApplicationContext context;
|
||||
|
||||
protected HttpExtension() {}
|
||||
|
||||
public HttpExtension(String name, String mapping) {
|
||||
|
||||
valid("extension name",name);
|
||||
valid("extension mapping",name);
|
||||
|
||||
name(name);
|
||||
mapping(mapping);
|
||||
}
|
||||
|
||||
//extensions use init(context) instead
|
||||
public final void init() throws javax.servlet.ServletException {
|
||||
};
|
||||
|
||||
@Override
|
||||
public void init(ApplicationContext context) throws Exception {
|
||||
this.context=context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Exclude> excludes() {
|
||||
return new HashSet<Exclude>(); //all managed by default
|
||||
}
|
||||
|
||||
protected ApplicationContext context() {
|
||||
return context;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void name(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mapping() {
|
||||
return mapping;
|
||||
}
|
||||
|
||||
public void mapping(String mapping) {
|
||||
this.mapping = mapping;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package org.gcube.smartgears.extensions;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.gcube.smartgears.handlers.application.request.RequestException;
|
||||
import org.gcube.smartgears.utils.Utils;
|
||||
|
||||
/**
|
||||
* A {@link Filter} that maps {@link RequestException}s onto error responses.
|
||||
*
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class RequestExceptionBarrier implements Filter {
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package org.gcube.smartgears.extensions.resource;
|
||||
|
||||
import static org.gcube.smartgears.Constants.application_xml;
|
||||
import static org.gcube.smartgears.extensions.HttpExtension.Method.GET;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import 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;
|
||||
|
||||
/**
|
||||
* An {@link ApiResource} of {@link RemoteResource} at {@link #mapping}.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class ConfigurationResource extends ApiResource {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final String mapping = "/configuration";
|
||||
|
||||
private static final ApiSignature signature = handles(mapping).with(method(GET).produces(application_xml));
|
||||
|
||||
ConfigurationResource() {
|
||||
super(signature);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
|
||||
ApplicationConfiguration config = BridgedApplicationConfiguration.class.cast(context().configuration()).inner();
|
||||
Resources.marshal(config,resp.getWriter());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
package org.gcube.smartgears.extensions.resource;
|
||||
|
||||
import static org.gcube.smartgears.Constants.application_xhtml;
|
||||
import static org.gcube.smartgears.Constants.frontpage_file_path;
|
||||
import static org.gcube.smartgears.extensions.HttpExtension.Method.GET;
|
||||
import static org.gcube.smartgears.handlers.application.request.RequestError.application_error;
|
||||
import static org.gcube.smartgears.provider.ProviderFactory.provider;
|
||||
import static org.gcube.smartgears.utils.Utils.closeSafely;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import 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.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* An {@link ApiResource} of {@link RemoteResource} at {@link #mapping}.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class FrontPageResource extends ApiResource {
|
||||
|
||||
// the variable replacement pattern
|
||||
private static Pattern pattern = Pattern.compile("\\$\\{(.+?)\\}");
|
||||
|
||||
// log on behalf of extension
|
||||
private static final Logger log = LoggerFactory.getLogger(RemoteResource.class);
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final String mapping = "/";
|
||||
|
||||
private static final ApiSignature signature = handles(mapping).with(method(GET).produces(application_xhtml));
|
||||
|
||||
FrontPageResource() {
|
||||
super(signature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
|
||||
InputStream page = getClass().getResourceAsStream(frontpage_file_path);
|
||||
|
||||
if (page == null) {
|
||||
log.error("invalid distribution: missing {}", frontpage_file_path);
|
||||
application_error.fire("invalid distribution: missing " + frontpage_file_path);
|
||||
}
|
||||
|
||||
Map<String, String> values = values();
|
||||
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
|
||||
String line = null;
|
||||
reader = new BufferedReader(new InputStreamReader(page));
|
||||
while ((line = reader.readLine()) != null)
|
||||
resp.getWriter().write(interpolate(line, values));
|
||||
|
||||
} catch (Exception e) {
|
||||
application_error.fire("could not read " + frontpage_file_path, e);
|
||||
} finally {
|
||||
closeSafely(reader);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Map<String, String> values() {
|
||||
|
||||
Map<String, String> values = new HashMap<String, String>();
|
||||
|
||||
values.put("profile_link", ProfileResource.mapping.substring(1,ProfileResource.mapping.length()));
|
||||
values.put("config_link", ConfigurationResource.mapping.substring(1,ConfigurationResource.mapping.length()));
|
||||
|
||||
values.put("name", context().name());
|
||||
values.put("version", context().configuration().version());
|
||||
|
||||
String infrastructure = context().container().configuration().infrastructure();
|
||||
StringBuilder voValue = new StringBuilder();
|
||||
|
||||
Collection<String> scopes = context().profile(GCoreEndpoint.class).scopes().asCollection();
|
||||
Set<String> vos = new HashSet<String>();
|
||||
|
||||
//pre-process
|
||||
for (String scope : scopes) {
|
||||
ScopeBean bean = new ScopeBean(scope);
|
||||
switch (bean.type()) {
|
||||
case INFRASTRUCTURE:
|
||||
infrastructure = bean.name();
|
||||
break;
|
||||
case VO:
|
||||
vos.add(bean.name());
|
||||
break;
|
||||
case VRE:
|
||||
vos.add(bean.enclosingScope().name());
|
||||
infrastructure=bean.enclosingScope().enclosingScope().name();
|
||||
}
|
||||
}
|
||||
|
||||
//build vo value
|
||||
int i = 0;
|
||||
int max = vos.size()-1;
|
||||
for (String vo : vos) {
|
||||
String voPrefix = i == 0 ? "" : (i==max?" and ":", ");
|
||||
voValue.append(voPrefix+"<em>" + vo + "</em>");
|
||||
i++;
|
||||
}
|
||||
|
||||
values.put("infra", infrastructure);
|
||||
values.put("vos", voValue.toString());
|
||||
|
||||
values.put("status", context().lifecycle().state().toString());
|
||||
|
||||
values.put("smartgears-version", provider().smartgearsConfiguration().version());
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
public static String interpolate(String text, Map<String, String> replacements) {
|
||||
|
||||
Matcher matcher = pattern.matcher(text);
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
while (matcher.find()) {
|
||||
String replacement = replacements.get(matcher.group(1));
|
||||
if (replacement != null) {
|
||||
matcher.appendReplacement(buffer, ""); // safer in case replacements include some of the variable
|
||||
// characters
|
||||
buffer.append(replacement);
|
||||
}
|
||||
}
|
||||
matcher.appendTail(buffer);
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
package org.gcube.smartgears.extensions.resource;
|
||||
|
||||
import static org.gcube.smartgears.Constants.application_xml;
|
||||
import static org.gcube.smartgears.extensions.HttpExtension.Method.GET;
|
||||
import static org.gcube.smartgears.extensions.HttpExtension.Method.POST;
|
||||
import static org.gcube.smartgears.handlers.application.request.RequestError.illegal_state_error;
|
||||
import static org.gcube.smartgears.handlers.application.request.RequestError.invalid_request_error;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlValue;
|
||||
|
||||
import org.gcube.common.resources.gcore.Resources;
|
||||
import org.gcube.smartgears.extensions.ApiResource;
|
||||
import org.gcube.smartgears.extensions.ApiSignature;
|
||||
import org.gcube.smartgears.lifecycle.application.ApplicationState;
|
||||
|
||||
/**
|
||||
* An {@link ApiResource} of {@link RemoteResource} at {@link #mapping}.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class LifecycleResource extends ApiResource {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final String mapping = "/lifecyle";
|
||||
|
||||
private static final ApiSignature signature = handles(mapping).with(method(GET).produces(application_xml)).with(
|
||||
method(POST).accepts(application_xml));
|
||||
|
||||
|
||||
LifecycleResource() {
|
||||
super(signature);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean supports(Method method) {
|
||||
|
||||
return method == GET || method == POST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
|
||||
try {
|
||||
Resources.marshal(new State(context().lifecycle().state()), resp.getWriter());
|
||||
}
|
||||
catch(Exception e) {
|
||||
invalid_request_error.fire("cannot parse request body",e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
|
||||
State wrapper = Resources.unmarshal(State.class, req.getReader());
|
||||
String value = wrapper.value;
|
||||
|
||||
if (value == null || value.isEmpty())
|
||||
invalid_request_error.fire("missing state in request body");
|
||||
|
||||
ApplicationState state = null;
|
||||
|
||||
try {
|
||||
state = ApplicationState.valueOf(value);
|
||||
}
|
||||
catch(Exception e) {
|
||||
invalid_request_error.fire(value+" is an unkown resource state",e);
|
||||
}
|
||||
|
||||
try {
|
||||
context().lifecycle().moveTo(state);
|
||||
}
|
||||
catch(Exception e) {
|
||||
illegal_state_error.fire("invalid state transition for this resource"+value, e);
|
||||
}
|
||||
}
|
||||
|
||||
// helper classes
|
||||
|
||||
@XmlRootElement(name="state")
|
||||
public static class State {
|
||||
|
||||
@XmlValue
|
||||
public String value;
|
||||
|
||||
State() {
|
||||
}
|
||||
|
||||
public State(ApplicationState state) {
|
||||
this.value=state.name();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package org.gcube.smartgears.extensions.resource;
|
||||
|
||||
import static org.gcube.smartgears.Constants.application_xml;
|
||||
import static org.gcube.smartgears.extensions.HttpExtension.Method.GET;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import 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;
|
||||
|
||||
/**
|
||||
* An {@link ApiResource} of {@link RemoteResource} at {@link #mapping}.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class ProfileResource extends ApiResource {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final String mapping = "/profile";
|
||||
|
||||
private static final ApiSignature signature = handles(mapping).with(method(GET).produces(application_xml));
|
||||
|
||||
ProfileResource() {
|
||||
super(signature);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
|
||||
Resources.marshal(context().profile(GCoreEndpoint.class),resp.getWriter());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package org.gcube.smartgears.extensions.resource;
|
||||
|
||||
import static org.gcube.smartgears.Constants.*;
|
||||
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
import org.gcube.smartgears.Constants;
|
||||
import org.gcube.smartgears.extensions.ApiResource;
|
||||
import org.gcube.smartgears.extensions.HttpController;
|
||||
|
||||
/**
|
||||
* An {@link HttpController} for remote management of the application.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
@XmlRootElement(name = remote_management)
|
||||
public class RemoteResource extends HttpController {
|
||||
|
||||
private static final String default_mapping = Constants.root_mapping+"/*";
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Creates an instance with its {@link ApiResource}s.
|
||||
*/
|
||||
public RemoteResource() {
|
||||
super(remote_management, default_mapping);
|
||||
addResources(new FrontPageResource(), new ConfigurationResource(), new ProfileResource(),
|
||||
new LifecycleResource());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return remote_management;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package org.gcube.smartgears.handlers;
|
||||
|
||||
/**
|
||||
* Partial implementation of {@link Handler}.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractHandler {
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName();
|
||||
}
|
||||
|
||||
//so far, largely a placeholder for future cross-handler behaviour
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package org.gcube.smartgears.handlers;
|
||||
|
||||
|
||||
/**
|
||||
* An event related to a given context.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <C> the context type
|
||||
*/
|
||||
public abstract class Event<C> {
|
||||
|
||||
private final C context;
|
||||
|
||||
/**
|
||||
* Creates an instance with the context of the container.
|
||||
* @param context the context
|
||||
*/
|
||||
public Event(C context) {
|
||||
this.context=context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the context of the container.
|
||||
* @return the context
|
||||
*/
|
||||
public C context() {
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package org.gcube.smartgears.handlers;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Handles events.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <E> the event type
|
||||
*/
|
||||
public interface Handler<E> {
|
||||
|
||||
|
||||
/**
|
||||
* Processes a given event.
|
||||
*
|
||||
* @param e the event
|
||||
*/
|
||||
void onEvent(E event);
|
||||
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
package org.gcube.smartgears.handlers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An ordered list {@link Handler}s that handle related events.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <E> the type of events
|
||||
* @param <H> the type of handlers
|
||||
*
|
||||
*
|
||||
*/
|
||||
public abstract class Pipeline<E,H extends Handler<E>> {
|
||||
|
||||
//private static Logger log = LoggerFactory.getLogger(Pipeline.class);
|
||||
|
||||
private final List<H> handlers;
|
||||
private int cursor = 0;
|
||||
|
||||
/**
|
||||
* Creates an instance with a given list of handlers.
|
||||
* <p>
|
||||
* Modification to the input list do not affect the pipeline.
|
||||
*
|
||||
* @param handlers the handlers
|
||||
*/
|
||||
public Pipeline(List<H> handlers) {
|
||||
this.handlers = new ArrayList<H>(handlers); // defensive copy
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the handlers in this pipeline.
|
||||
* <p>
|
||||
* Modification to the input list do not affect the pipeline.
|
||||
*
|
||||
* @return the handlers
|
||||
*/
|
||||
public List<H> handlers() {
|
||||
return new ArrayList<H>(handlers); // defensive copy
|
||||
}
|
||||
|
||||
/**
|
||||
* Forwards an event through the pipeline.
|
||||
*
|
||||
* @param event the event
|
||||
*/
|
||||
public void forward(E event) {
|
||||
|
||||
if (cursor >= handlers.size()) {
|
||||
reset();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
H handler = handlers.get(cursor);
|
||||
|
||||
cursor++;
|
||||
|
||||
//log.trace("forwarding {} to {}", event, handler);
|
||||
|
||||
try {
|
||||
handler.onEvent(event);
|
||||
}
|
||||
catch(RuntimeException e) {
|
||||
reset();
|
||||
throw e;
|
||||
}
|
||||
|
||||
forward(event); //make sure it's the last thing we do, or it will keep acting as recursion retracts
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
cursor=0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package org.gcube.smartgears.handlers;
|
||||
|
||||
/**
|
||||
* Profile lifetime events for container and application.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class ProfileEvents {
|
||||
|
||||
/**
|
||||
* The event that signals the publication of the profile.
|
||||
*/
|
||||
public static final String published ="published";
|
||||
|
||||
/**
|
||||
* The event that signals a change to the profile.
|
||||
*/
|
||||
public static final String changed ="changed";
|
||||
|
||||
public static final String addToContext = "addToContext";
|
||||
|
||||
public static final String removeFromContext = "removeFromContext";
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package org.gcube.smartgears.handlers.application;
|
||||
|
||||
import org.gcube.smartgears.context.application.ApplicationContext;
|
||||
import org.gcube.smartgears.handlers.Event;
|
||||
|
||||
/**
|
||||
* An {@link Event} that occurs in the lifetime of the application.
|
||||
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <T> the type of handler that handles these events
|
||||
*
|
||||
*/
|
||||
public abstract class ApplicationEvent<T extends ApplicationHandler<T>> extends Event<ApplicationContext> {
|
||||
|
||||
/**
|
||||
* Creates an instance with a given application context.
|
||||
* @param context the context
|
||||
*/
|
||||
ApplicationEvent(ApplicationContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package org.gcube.smartgears.handlers.application;
|
||||
|
||||
import org.gcube.smartgears.handlers.Handler;
|
||||
|
||||
/**
|
||||
* A {@link Handler} of {@link ApplicationEvent}s.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <T> the self type of the handler.
|
||||
*
|
||||
* @see ApplicationEvent
|
||||
*/
|
||||
public interface ApplicationHandler<T extends ApplicationHandler<T>> extends Handler<ApplicationEvent<T>> {
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package org.gcube.smartgears.handlers.application;
|
||||
|
||||
import org.gcube.smartgears.context.application.ApplicationContext;
|
||||
|
||||
/**
|
||||
* An {@link ApplicationEvent} that occurs when the application starts or stops.
|
||||
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public abstract class ApplicationLifecycleEvent extends ApplicationEvent<ApplicationLifecycleHandler> {
|
||||
|
||||
/**
|
||||
* An {@link ApplicationEvent} that occurs when the application starts.
|
||||
*/
|
||||
public static class Start extends ApplicationLifecycleEvent {
|
||||
|
||||
/**
|
||||
* Creates an instance for a given {@link ApplicationContext}.
|
||||
* @param context the context
|
||||
*/
|
||||
public Start(ApplicationContext context) {
|
||||
super(context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An {@link ApplicationEvent} that occurs when the application stops.
|
||||
*/
|
||||
public static class Stop extends ApplicationLifecycleEvent {
|
||||
|
||||
/**
|
||||
* Creates an instance for a given {@link ApplicationContext}.
|
||||
* @param context the context
|
||||
*/
|
||||
public Stop(ApplicationContext context) {
|
||||
super(context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance for a given {@link ApplicationContext}.
|
||||
* @param context the context
|
||||
*/
|
||||
ApplicationLifecycleEvent(ApplicationContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package org.gcube.smartgears.handlers.application;
|
||||
|
||||
import java.util.logging.Handler;
|
||||
|
||||
import org.gcube.smartgears.handlers.AbstractHandler;
|
||||
|
||||
/**
|
||||
* A {@link Handler} of {@link ApplicationLifecycleEvent}s.
|
||||
* <p>
|
||||
* The handler participates in a {@link ApplicationPipeline} of other handlers registered for notification of the same events.
|
||||
* After processing the event, it may or may not propagate the event to the handlers further down in the {@link ApplicationPipeline}
|
||||
* {@link ApplicationPipeline#forward(ApplicationEvent)}.
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @see ApplicationLifecycleEvent
|
||||
* @see ApplicationPipeline
|
||||
*/
|
||||
public abstract class ApplicationLifecycleHandler extends AbstractHandler implements ApplicationHandler<ApplicationLifecycleHandler> {
|
||||
|
||||
|
||||
/**
|
||||
* Invoked when the container starts a managed app.
|
||||
* @param pipeline the pipeline in which this handler is registered
|
||||
* @param e the event
|
||||
*/
|
||||
public void onStart(ApplicationLifecycleEvent.Start e) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when the container stops a managed app.
|
||||
* @param pipeline the pipeline in which this handler is registered
|
||||
* @param e the stop event
|
||||
*/
|
||||
public void onStop(ApplicationLifecycleEvent.Stop e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(ApplicationEvent<ApplicationLifecycleHandler> e) {
|
||||
|
||||
if (e instanceof ApplicationLifecycleEvent.Start)
|
||||
onStart(ApplicationLifecycleEvent.Start.class.cast(e));
|
||||
else
|
||||
if (e instanceof ApplicationLifecycleEvent.Stop)
|
||||
onStop(ApplicationLifecycleEvent.Stop.class.cast(e));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package org.gcube.smartgears.handlers.application;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.gcube.smartgears.handlers.Pipeline;
|
||||
|
||||
/**
|
||||
* A {@link Pipeline} of {@link ApplicationHandler}s.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @param <H> the type of the handlers
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class ApplicationPipeline<H extends ApplicationHandler<H>> extends Pipeline<ApplicationEvent<H>,H> {
|
||||
|
||||
/**
|
||||
* Creates an instance with a given list of handlers.
|
||||
* <p>
|
||||
* Modification to the input list do not affect the pipeline.
|
||||
*
|
||||
* @param handlers the handlers
|
||||
*/
|
||||
public ApplicationPipeline(List<H> handlers) {
|
||||
super(handlers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pipeline with the same handlers as this pipeline but in reverse order.
|
||||
* @return the reversed pipeline
|
||||
*/
|
||||
public ApplicationPipeline<H> reverse() {
|
||||
|
||||
List<H> handlers = handlers(); // it's a copy, we're not changing this pipeline
|
||||
|
||||
Collections.reverse(handlers);
|
||||
|
||||
return new ApplicationPipeline<H>(handlers);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package org.gcube.smartgears.handlers.application;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.gcube.smartgears.context.application.ApplicationContext;
|
||||
|
||||
/**
|
||||
* An {@link ApplicationEvent} that occurs when an application receives a request.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class RequestEvent extends ApplicationEvent<RequestHandler> {
|
||||
|
||||
private final String servlet;
|
||||
private final HttpServletRequest request;
|
||||
private final HttpServletResponse response;
|
||||
|
||||
|
||||
/**
|
||||
* Creates an instance with the application context, the client request, and the name of the target servlet.
|
||||
*
|
||||
* @param context the context of the application
|
||||
* @param servlet the name of the target servlet
|
||||
* @param request the client request
|
||||
*/
|
||||
public RequestEvent(String servlet,ApplicationContext context, HttpServletRequest request, HttpServletResponse response) {
|
||||
super(context);
|
||||
this.request = request;
|
||||
this.response=response;
|
||||
this.servlet = servlet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the target servlet.
|
||||
*
|
||||
* @return the name of the servlet.
|
||||
*/
|
||||
public String servlet() {
|
||||
return servlet;
|
||||
}
|
||||
|
||||
public String uri() {
|
||||
String query = request().getQueryString();
|
||||
return query==null?request().getRequestURI():request().getRequestURI()+"?"+query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the client request.
|
||||
*
|
||||
* @return the request
|
||||
*/
|
||||
public HttpServletRequest request() {
|
||||
return request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the response.
|
||||
*
|
||||
* @return the response
|
||||
*/
|
||||
public HttpServletResponse response() {
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "[req=" + request().getRemoteHost() + ",resp="
|
||||
+ response.toString().substring(0, 12) + "]";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package org.gcube.smartgears.handlers.application;
|
||||
|
||||
import java.util.logging.Handler;
|
||||
|
||||
import org.gcube.smartgears.context.application.ApplicationContext;
|
||||
import org.gcube.smartgears.handlers.AbstractHandler;
|
||||
|
||||
/**
|
||||
* A {@link Handler} of {@link RequestEvent}s and {@link ResponseEvent}s.
|
||||
* <p>
|
||||
* The handler participates in a {@link ApplicationPipeline} of other handlers registered for notification of the same events.
|
||||
* After processing the event, it may or may not propagate the event to the handlers further down in the
|
||||
* {@link ApplicationPipeline} {@link ApplicationPipeline#forward(ApplicationEvent)}.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
* @see RequestEvent
|
||||
* @see ResponseEvent
|
||||
* @see ApplicationPipeline
|
||||
*/
|
||||
public abstract class RequestHandler extends AbstractHandler implements ApplicationHandler<RequestHandler> {
|
||||
|
||||
abstract public String getName();
|
||||
|
||||
/**
|
||||
* Initialises the handler.
|
||||
*
|
||||
* @param ctx the servlet context of the managed app.
|
||||
*/
|
||||
public void start(ApplicationContext ctx) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when the container receives a request for a servlet of a managed app.
|
||||
*
|
||||
* @param pipeline the pipeline in which this handler is registered
|
||||
* @param e the event
|
||||
*/
|
||||
public void handleRequest(RequestEvent e) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a servlet of a managed app has produced a response to a request.
|
||||
*
|
||||
* @param pipeline the pipeline in which this handler is registered
|
||||
* @param e the event
|
||||
*/
|
||||
public void handleResponse(ResponseEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(ApplicationEvent<RequestHandler> e) {
|
||||
|
||||
// mind the order here, ResponseEvent<RequestEvent so must be checked first
|
||||
if (e instanceof ResponseEvent)
|
||||
handleResponse(ResponseEvent.class.cast(e));
|
||||
else if (e instanceof RequestEvent)
|
||||
handleRequest(RequestEvent.class.cast(e));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminates the handler.
|
||||
*/
|
||||
public void stop() {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package org.gcube.smartgears.handlers.application;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.gcube.smartgears.context.application.ApplicationContext;
|
||||
|
||||
/**
|
||||
* A {@link ApplicationEvent} that occurs when the application returns a response to a given request.
|
||||
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class ResponseEvent extends RequestEvent {
|
||||
|
||||
/**
|
||||
* Creates an instance with the name of the target servlet, the context of the application, the client request, and the application response.
|
||||
*
|
||||
* @param servlet the name of the servlet
|
||||
* @param context the context of the application
|
||||
* @param request the request
|
||||
* @param response the response
|
||||
*/
|
||||
public ResponseEvent(String servlet, ApplicationContext context, HttpServletRequest request, HttpServletResponse response) {
|
||||
super(servlet, context, request, response);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package org.gcube.smartgears.handlers.application.lifecycle;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletRegistration;
|
||||
|
||||
import org.gcube.common.resources.gcore.GCoreEndpoint;
|
||||
import org.gcube.common.resources.gcore.HostingNode;
|
||||
import org.gcube.smartgears.configuration.application.ApplicationConfiguration;
|
||||
import org.gcube.smartgears.configuration.container.ContainerConfiguration;
|
||||
import org.gcube.smartgears.context.application.ApplicationContext;
|
||||
|
||||
public class ProfileBuilder {
|
||||
|
||||
private static List<String> servletExcludes = Arrays.asList("default","jsp");
|
||||
|
||||
private 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 = container.proxyAddress().secure()? "https://": "http://";
|
||||
int port = container.proxyAddress().port();
|
||||
|
||||
baseAddress=String.format("%s%s:%d%s", protocol , container.proxyAddress().hostname(), port,context.application().getContextPath());
|
||||
} else {
|
||||
String protocol = configuration.secure()? "https://": "http://";
|
||||
int port = configuration.secure()?container.securePort(): 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));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,306 @@
|
|||
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.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.context.Property;
|
||||
import org.gcube.smartgears.context.application.ApplicationContext;
|
||||
import org.gcube.smartgears.handlers.application.ApplicationLifecycleEvent;
|
||||
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.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 ProfilePublisher
|
||||
*/
|
||||
@XmlRootElement(name = profile_management)
|
||||
public class ProfileManager extends ApplicationLifecycleHandler {
|
||||
|
||||
Logger log = LoggerFactory.getLogger(ProfileManager.class);
|
||||
|
||||
private ApplicationContext context;
|
||||
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
|
||||
// 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(){
|
||||
GCoreEndpoint profile = loadOrCreateProfile();
|
||||
|
||||
share(profile);
|
||||
|
||||
publisher = new ProfilePublisher(context);
|
||||
|
||||
registerObservers();
|
||||
}
|
||||
|
||||
// helpers
|
||||
private void registerObservers() {
|
||||
|
||||
context.events().subscribe(new Object() {
|
||||
|
||||
@Observes({ activation, stop, failure })
|
||||
void onChanged(ApplicationLifecycle lc) {
|
||||
|
||||
GCoreEndpoint profile = context.profile(GCoreEndpoint.class);
|
||||
|
||||
profile.profile().deploymentData().status(lc.state().remoteForm());
|
||||
|
||||
log.debug("moving app {} to {}",context.name(), lc.state().remoteForm());
|
||||
|
||||
// since we do not know the observers, they will deal with
|
||||
// failures and their consequences
|
||||
// any that comes back will be logged in this event thread
|
||||
context.events().fire(profile, changed);
|
||||
}
|
||||
|
||||
@Observes(value = published)
|
||||
void shareAfterPublish(GCoreEndpoint profile) {
|
||||
|
||||
share(profile); // publish may produce a new profile instance
|
||||
|
||||
}
|
||||
|
||||
@Observes(value = changed, kind = Kind.safe)
|
||||
void publishAfterChange(GCoreEndpoint profile) {
|
||||
|
||||
boolean firstPublication = profile.scopes().isEmpty();
|
||||
|
||||
//if we've failed before first publication do not try to publish
|
||||
//(we may well have failed there)
|
||||
try {
|
||||
|
||||
if (firstPublication) {
|
||||
if (context.lifecycle().state()!= failed)
|
||||
publishFirstTime(profile);
|
||||
}
|
||||
else{
|
||||
log.debug("update app {} profile",context.name());
|
||||
publisher.update(); // if successful, triggers share.
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Observes(value = addToContext)
|
||||
void addTo(String token) {
|
||||
try {
|
||||
log.trace("publishing application with new token");
|
||||
publisher.addTo(Collections.singleton(token));
|
||||
publisher.update();
|
||||
}catch (Exception e) {
|
||||
|
||||
log.error("cannot add token {} (see details)",token, e);
|
||||
|
||||
// since we've failed no published event is fired and profile
|
||||
// will not be stored.
|
||||
// we do it manually to ensure we leave some local trace of the
|
||||
// changed profile.
|
||||
//TODO: CHECK --- store(profile);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Observes(value = removeFromContext)
|
||||
void removeFrom(String token) {
|
||||
try {
|
||||
log.trace("unpublishing application with token");
|
||||
publisher.removeFrom(Collections.singleton(token));
|
||||
publisher.update();
|
||||
}catch (Exception e) {
|
||||
|
||||
log.error("cannot remove token {} (see details)",token, e);
|
||||
|
||||
// since we've failed no published event is fired and profile
|
||||
// will not be stored.
|
||||
// we do it manually to ensure we leave some local trace of the
|
||||
// changed profile.
|
||||
//TODO: CHECK --- store(profile);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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 }, kind = resilient)
|
||||
synchronized void restartPeriodicUpdates(ApplicationLifecycle lc) {
|
||||
|
||||
//already running
|
||||
if (periodicUpdates!=null)
|
||||
return;
|
||||
|
||||
if (lc.state()==ApplicationState.active)
|
||||
log.info("scheduling periodic updates of application {} profile", context.name());
|
||||
|
||||
else
|
||||
log.info("resuming periodic updates of application {} profile", context.name());
|
||||
|
||||
final Runnable updateTask = new Runnable() {
|
||||
public void run() {
|
||||
GCoreEndpoint profile = context.profile(GCoreEndpoint.class);
|
||||
|
||||
//if handling of event generates failures these will be reported
|
||||
//for resilience we do not fail the application
|
||||
log.trace("firing change event on application {} profile", context.name());
|
||||
context.events().fire(profile,changed);
|
||||
}
|
||||
};
|
||||
|
||||
periodicUpdates = Utils.scheduledServicePool.scheduleAtFixedRate(updateTask, Constants.application_republish_frequency_in_minutes, Constants.application_republish_frequency_in_minutes , TimeUnit.MINUTES);
|
||||
|
||||
}
|
||||
|
||||
@Observes(value = { stop, failure }, kind = resilient)
|
||||
synchronized void cancelPeriodicUpdates(ContainerLifecycle ignore) {
|
||||
|
||||
if (periodicUpdates != null){
|
||||
log.trace("stopping periodic updates of application {} profile", context.name());
|
||||
|
||||
try {
|
||||
periodicUpdates.cancel(true);
|
||||
periodicUpdates=null;
|
||||
}
|
||||
catch(Exception e) {
|
||||
log.warn("could not stop periodic updates of application {} profile", context.name(),e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return profile_management;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,214 @@
|
|||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package org.gcube.smartgears.handlers.application.request;
|
||||
|
||||
import static org.gcube.smartgears.Constants.called_method_header;
|
||||
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
import org.gcube.accounting.datamodel.UsageRecord.OperationResult;
|
||||
import org.gcube.accounting.datamodel.usagerecords.ServiceUsageRecord;
|
||||
import org.gcube.accounting.persistence.AccountingPersistence;
|
||||
import org.gcube.accounting.persistence.AccountingPersistenceFactory;
|
||||
import org.gcube.common.authorization.library.provider.AuthorizationProvider;
|
||||
import org.gcube.common.authorization.library.provider.CalledMethodProvider;
|
||||
import org.gcube.common.scope.api.ScopeProvider;
|
||||
import org.gcube.smartgears.Constants;
|
||||
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.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>();
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return Constants.request_accounting;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRequest(RequestEvent e) {
|
||||
ApplicationContext context = e.context();
|
||||
String calledMethod = e.request().getHeader(called_method_header);
|
||||
if (calledMethod==null){
|
||||
calledMethod = e.request().getRequestURI().substring(e.request().getContextPath().length());
|
||||
if (calledMethod.startsWith("/"))
|
||||
calledMethod = calledMethod.replaceFirst("/","");
|
||||
}
|
||||
CalledMethodProvider.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 {} ",
|
||||
context.configuration().name(),context.configuration().serviceClass(), CalledMethodProvider.instance.get(),
|
||||
caller, e.request().getRemoteHost(), ScopeProvider.instance.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleResponse(ResponseEvent e) {
|
||||
ApplicationContext context = e.context();
|
||||
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();
|
||||
|
||||
generateAccounting(caller,callerQualifier,callerIp==null?"UNKNOWN":callerIp , context);
|
||||
|
||||
log.info("REQUEST SERVED ON {}:{}({}) CALLED FROM {}@{} IN SCOPE {} FINISHED IN {} millis",
|
||||
context.configuration().name(),context.configuration().serviceClass(), CalledMethodProvider.instance.get(),
|
||||
caller, callerIp, ScopeProvider.instance.get(), System.currentTimeMillis()-startCallThreadLocal.get());
|
||||
startCallThreadLocal.remove();
|
||||
CalledMethodProvider.instance.reset();
|
||||
}
|
||||
|
||||
void generateAccounting(String caller, String callerQualifier, String remoteHost, ApplicationContext context){
|
||||
AccountingPersistenceFactory.setFallbackLocation(context.container().persistence().location());
|
||||
AccountingPersistence persistence = AccountingPersistenceFactory.getPersistence();
|
||||
ServiceUsageRecord serviceUsageRecord = new ServiceUsageRecord();
|
||||
try{
|
||||
|
||||
serviceUsageRecord.setConsumerId(caller);
|
||||
serviceUsageRecord.setCallerQualifier(callerQualifier);
|
||||
serviceUsageRecord.setScope(ScopeProvider.instance.get());
|
||||
serviceUsageRecord.setServiceClass(context.configuration().serviceClass());
|
||||
serviceUsageRecord.setServiceName(context.configuration().name());
|
||||
|
||||
serviceUsageRecord.setHost(context.container().configuration().hostname()+":"+context.container().configuration().port());
|
||||
serviceUsageRecord.setCalledMethod(CalledMethodProvider.instance.get());
|
||||
serviceUsageRecord.setCallerHost(remoteHost);
|
||||
serviceUsageRecord.setOperationResult(OperationResult.SUCCESS);
|
||||
serviceUsageRecord.setDuration(System.currentTimeMillis()-startCallThreadLocal.get());
|
||||
persistence.account(serviceUsageRecord);
|
||||
|
||||
}catch(Exception ex){
|
||||
log.warn("invalid record passed to accounting ",ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
package org.gcube.smartgears.handlers.application.request;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
||||
/**
|
||||
* Known error types.
|
||||
* <p>
|
||||
* Each type can throw a corresponding {@link RequestException}.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public enum RequestError {
|
||||
|
||||
/**
|
||||
* The error raised when requests are made to failed applications.
|
||||
*/
|
||||
application_failed_error(HttpServletResponse.SC_GONE,"this resource is not currently available"),
|
||||
|
||||
/**
|
||||
* The error raised when requests are made to stopped applications.
|
||||
*/
|
||||
application_unavailable_error(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "this resource is permanently available"),
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The error raised when requests are made to unknown resources.
|
||||
*/
|
||||
resource_notfound_error(HttpServletResponse.SC_NOT_FOUND, "no gCube resource at this URI"),
|
||||
|
||||
|
||||
/**
|
||||
* The error raised when requests require illegal resource state transitions.
|
||||
*/
|
||||
illegal_state_error(HttpServletResponse.SC_CONFLICT, "this resource cannot assume the required state"),
|
||||
|
||||
|
||||
/**
|
||||
* The error raised when requests are made with unsupported HTTP methods.
|
||||
*/
|
||||
method_unsupported_error(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "this resource does not support this method"),
|
||||
|
||||
/**
|
||||
* The error raised when requests are genrically invalid.
|
||||
*/
|
||||
invalid_request_error(HttpServletResponse.SC_BAD_REQUEST, "this resource cannot process this request because it is malformed"),
|
||||
|
||||
request_not_authorized_error(HttpServletResponse.SC_UNAUTHORIZED, "this resource cannot process this request because it needs authorization"),
|
||||
|
||||
internal_server_error(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "unexpected error"),
|
||||
|
||||
|
||||
/**
|
||||
* The error raised when requests carry an unsupported media type.
|
||||
*/
|
||||
incoming_contenttype_unsupported_error(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE, "this resource cannot consume this media type"),
|
||||
|
||||
|
||||
/**
|
||||
* The error raised when requests request an unsupported media type.
|
||||
*/
|
||||
outgoing_contenttype_unsupported_error(HttpServletResponse.SC_NOT_ACCEPTABLE, "this resource cannot produce this media type"),
|
||||
|
||||
|
||||
/**
|
||||
* An error raised by managed applications.
|
||||
*/
|
||||
application_error(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "this resource has incurred in a generic error");
|
||||
|
||||
|
||||
private final int code;
|
||||
private final String msg;
|
||||
|
||||
private RequestError(int code,String msg) {
|
||||
this.code=code;
|
||||
this.msg=msg;
|
||||
}
|
||||
|
||||
public int code() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String message() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public void fire() {
|
||||
throw toException();
|
||||
}
|
||||
|
||||
public void fire(String msg) {
|
||||
throw toException(msg);
|
||||
}
|
||||
|
||||
public void fire(Throwable cause) {
|
||||
throw toException(cause);
|
||||
}
|
||||
|
||||
public void fire(String msg,Throwable cause) {
|
||||
throw toException(msg,cause);
|
||||
}
|
||||
|
||||
public RequestException toException() {
|
||||
return new RequestException(this);
|
||||
}
|
||||
|
||||
public RequestException toException(String msg) {
|
||||
return new RequestException(this, msg);
|
||||
}
|
||||
|
||||
public RequestException toException(Throwable cause) {
|
||||
return new RequestException(this,cause);
|
||||
}
|
||||
|
||||
public RequestException toException(String msg,Throwable cause) {
|
||||
return new RequestException(this,cause,msg);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package org.gcube.smartgears.handlers.application.request;
|
||||
|
||||
|
||||
/**
|
||||
* Thrown for the occurrence of an error during request processing.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class RequestException extends RuntimeException {
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final RequestError error;
|
||||
|
||||
/**
|
||||
* Creates an instance with an underlying error.
|
||||
* @param error the error
|
||||
*/
|
||||
public RequestException(RequestError error) {
|
||||
this(error, error.message());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance with an underling error and a custom message.
|
||||
* @param message the message
|
||||
* @param error the error
|
||||
*/
|
||||
public RequestException(RequestError error,String message) {
|
||||
super(message);
|
||||
this.error=error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance with an underlying error and an underlying cause
|
||||
* @param error the error
|
||||
* @param cause the cause;
|
||||
*/
|
||||
public RequestException(RequestError error,Throwable cause) {
|
||||
this(error, cause, error.message());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance with an underlying error, an underlying cause, and an underlying message.
|
||||
* @param error the error
|
||||
* @param cause the cause;
|
||||
* @Param message the message;
|
||||
*/
|
||||
public RequestException(RequestError error,Throwable cause,String message) {
|
||||
super(message,cause);
|
||||
this.error=error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying error.
|
||||
* @return the error
|
||||
*/
|
||||
public RequestError error() {
|
||||
return error;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
package org.gcube.smartgears.handlers.application.request;
|
||||
|
||||
import static org.gcube.common.authorization.client.Constants.authorizationService;
|
||||
import static org.gcube.smartgears.Constants.request_validation;
|
||||
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.application_failed_error;
|
||||
import static org.gcube.smartgears.handlers.application.request.RequestError.application_unavailable_error;
|
||||
import static org.gcube.smartgears.handlers.application.request.RequestError.internal_server_error;
|
||||
import static org.gcube.smartgears.handlers.application.request.RequestError.invalid_request_error;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.xml.bind.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.PolicyUtils;
|
||||
import org.gcube.common.authorization.library.policies.Policy;
|
||||
import org.gcube.common.authorization.library.provider.AuthorizationProvider;
|
||||
import org.gcube.common.authorization.library.provider.ClientInfo;
|
||||
import org.gcube.common.authorization.library.provider.SecurityTokenProvider;
|
||||
import org.gcube.common.authorization.library.provider.ServiceIdentifier;
|
||||
import org.gcube.common.authorization.library.provider.UserInfo;
|
||||
import org.gcube.common.authorization.library.utils.Caller;
|
||||
import org.gcube.common.scope.api.ScopeProvider;
|
||||
import org.gcube.smartgears.Constants;
|
||||
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.utils.Utils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@XmlRootElement(name = Constants.request_validation)
|
||||
public class RequestValidator extends RequestHandler {
|
||||
|
||||
private static Logger log = LoggerFactory.getLogger(RequestValidator.class);
|
||||
|
||||
private ApplicationContext context;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return Constants.request_validation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRequest(RequestEvent call) {
|
||||
|
||||
log.trace("executing request validator ON REQUEST");
|
||||
|
||||
context = call.context();
|
||||
|
||||
validateAgainstLifecycle(call);
|
||||
|
||||
if (!validateToken(call)){
|
||||
String scope = call.request().getParameter(scope_header)==null? call.request().getHeader(scope_header):call.request().getParameter(scope_header);
|
||||
validateScope(scope);
|
||||
log.info("received call to {} in context {}",call.uri(),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 validateAgainstLifecycle(RequestEvent call) {
|
||||
|
||||
switch(context.lifecycle().state()) {
|
||||
|
||||
case stopped :
|
||||
application_unavailable_error.fire(); break;
|
||||
|
||||
case failed:
|
||||
application_failed_error.fire(); break;
|
||||
|
||||
default:
|
||||
//nothing to do, but avoids warnings
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void validateScope(String scope) {
|
||||
|
||||
if (scope == null) {
|
||||
log.warn("rejecting unscoped call to {}",context.name());
|
||||
invalid_request_error.fire("call is unscoped");
|
||||
}
|
||||
|
||||
if (!context.container().configuration().allowedContexts().contains(scope)) {
|
||||
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);
|
||||
}
|
||||
|
||||
ScopeProvider.instance.set(scope);
|
||||
}
|
||||
|
||||
private boolean validateToken(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 && scope==null){
|
||||
log.warn("rejecting call to {}, authorization required",context.name(),token);
|
||||
if (call.context().container().configuration().authenticationEnpoint()==null){
|
||||
if (call.request().getHeader(Constants.authorization_header)!=null){
|
||||
String basicAuthorization = call.request().getHeader(Constants.authorization_header);
|
||||
String base64Credentials = basicAuthorization.substring("Basic".length()).trim();
|
||||
String credentials = new String(DatatypeConverter.parseBase64Binary(base64Credentials));
|
||||
// credentials = username:password
|
||||
final String[] values = credentials.split(":",2);
|
||||
token = values[1];
|
||||
ClientInfo info = retreiveAndSetInfo(token, call);
|
||||
if (!(info instanceof UserInfo) || !info.getId().equals(values[0])) {
|
||||
log.warn("rejecting call to {}, username {} not valid for token {}",context.name(),values[0],token);
|
||||
RequestError.request_not_authorized_error.fire(context.name()+": username "+values[0]+" not valid for token "+token);
|
||||
}
|
||||
return true;
|
||||
}else {
|
||||
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 );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
log.trace("token is "+token);
|
||||
|
||||
if (token!=null){
|
||||
retreiveAndSetInfo(token, call);
|
||||
return true;
|
||||
}
|
||||
log.info("invalid token, checking the context");
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return request_validation;
|
||||
}
|
||||
|
||||
private ClientInfo retreiveAndSetInfo(String token, RequestEvent call){
|
||||
log.info("accessing 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");
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
AuthorizationProvider.instance.set(new Caller(authEntry.getClientInfo(), authEntry.getQualifier()));
|
||||
validateScope(authEntry.getContext());
|
||||
log.info("retrieved request authorization info {} in scope {} ", AuthorizationProvider.instance.get(), authEntry.getContext());
|
||||
SecurityTokenProvider.instance.set(token);
|
||||
return authEntry.getClientInfo();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package org.gcube.smartgears.handlers.container;
|
||||
|
||||
|
||||
|
||||
import org.gcube.smartgears.handlers.AbstractHandler;
|
||||
import org.gcube.smartgears.handlers.Handler;
|
||||
|
||||
/**
|
||||
* A {@link Handler} of {@link ContainerLifecycleEvent}s.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public abstract class ContainerHandler extends AbstractHandler implements Handler<ContainerLifecycleEvent> {
|
||||
|
||||
|
||||
/**
|
||||
* Invoked when the container starts.
|
||||
* @param e the event
|
||||
*/
|
||||
public void onStart(ContainerLifecycleEvent.Start e) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when the container stops.
|
||||
* @param e the stop event
|
||||
*/
|
||||
public void onStop(ContainerLifecycleEvent.Stop e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(ContainerLifecycleEvent e) {
|
||||
|
||||
if (e instanceof ContainerLifecycleEvent.Start)
|
||||
onStart(ContainerLifecycleEvent.Start.class.cast(e));
|
||||
else
|
||||
if (e instanceof ContainerLifecycleEvent.Stop)
|
||||
onStop(ContainerLifecycleEvent.Stop.class.cast(e));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package org.gcube.smartgears.handlers.container;
|
||||
|
||||
import org.gcube.smartgears.context.container.ContainerContext;
|
||||
import org.gcube.smartgears.handlers.Event;
|
||||
|
||||
/**
|
||||
* An {@link Event} that occurs in the lifetime of the container.
|
||||
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public abstract class ContainerLifecycleEvent extends Event<ContainerContext> {
|
||||
|
||||
/**
|
||||
* An {@link ContainerLifecycleEvent} that occurs when the container starts.
|
||||
*/
|
||||
public static class Start extends ContainerLifecycleEvent {
|
||||
|
||||
/**
|
||||
* Creates an instance for a given {@link ContainerContext}.
|
||||
* @param context the context
|
||||
*/
|
||||
public Start(ContainerContext context) {
|
||||
super(context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An {@link ContainerLifecycleEvent} that occurs when the container stops.
|
||||
*/
|
||||
public static class Stop extends ContainerLifecycleEvent {
|
||||
|
||||
/**
|
||||
* Creates an instance for a given {@link ContainerContext}.
|
||||
* @param context the context
|
||||
*/
|
||||
public Stop(ContainerContext context) {
|
||||
super(context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance with the context of the container.
|
||||
* @param context the context
|
||||
*/
|
||||
ContainerLifecycleEvent(ContainerContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package org.gcube.smartgears.handlers.container;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.gcube.smartgears.handlers.Pipeline;
|
||||
|
||||
/**
|
||||
* A {@link Pipeline} of {@link ContainerHandler}s.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class ContainerPipeline extends Pipeline<ContainerLifecycleEvent, ContainerHandler> {
|
||||
|
||||
/**
|
||||
* Creates an instance with a given list of handlers.
|
||||
* <p>
|
||||
* Modification to the input list do not affect the pipeline.
|
||||
*
|
||||
* @param handlers the handlers
|
||||
*/
|
||||
public ContainerPipeline(List<ContainerHandler> handlers) {
|
||||
super(handlers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pipeline with the same handlers as this pipeline but in reverse order.
|
||||
* @return the reversed pipeline
|
||||
*/
|
||||
public ContainerPipeline reverse() {
|
||||
|
||||
List<ContainerHandler> handlers = handlers(); // it's a copy, we're not changing this pipeline
|
||||
|
||||
Collections.reverse(handlers);
|
||||
|
||||
return new ContainerPipeline(handlers);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
*
|
||||
*/
|
||||
package org.gcube.smartgears.handlers.container.lifecycle;
|
||||
|
||||
import static org.gcube.smartgears.Constants.accounting_management;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
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;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
@Override
|
||||
public void onStop(ContainerLifecycleEvent.Stop e) {
|
||||
logger.trace("Going to flush accounting data");
|
||||
AccountingPersistenceFactory.flushAll(1000, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return accounting_management;
|
||||
}
|
||||
}
|
|
@ -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 = null;
|
||||
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];
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package org.gcube.smartgears.lifecycle;
|
||||
|
||||
import org.gcube.common.events.Hub;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class DefaultLifecycle<S extends State<S>> implements Lifecycle<S> {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(Lifecycle.class);
|
||||
|
||||
private final Hub hub;
|
||||
private final String name;
|
||||
private S currentState;
|
||||
private S previousState;
|
||||
|
||||
public DefaultLifecycle(Hub hub, String name, S startState){
|
||||
this.hub = hub;
|
||||
this.currentState = startState;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public S state() {
|
||||
return currentState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveTo(S next) {
|
||||
|
||||
if (next == currentState)
|
||||
return;
|
||||
|
||||
if (currentState.next().contains(next)){
|
||||
|
||||
this.previousState = this.currentState;
|
||||
this.currentState = next;
|
||||
|
||||
log.trace("{} transitioned from {} to {}",new Object[]{name,previousState,currentState});
|
||||
|
||||
hub.fire(this, currentState.event());
|
||||
|
||||
}
|
||||
else throw new IllegalStateException("cannot transition "+name+" from " +currentState+" to "+next);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryMoveTo(S next) {
|
||||
try {
|
||||
moveTo(next);
|
||||
return true;
|
||||
}
|
||||
catch(IllegalStateException ignore) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public S previous() {
|
||||
return this.previousState;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package org.gcube.smartgears.lifecycle;
|
||||
|
||||
/**
|
||||
* The lifecycle of an application managed as a gCube service.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public interface Lifecycle<S extends State<S>> {
|
||||
|
||||
/**
|
||||
* Returns the state from which this lifecycle transitioned to its current state.
|
||||
* @return the previous state
|
||||
*/
|
||||
S previous();
|
||||
|
||||
/**
|
||||
* Returns the current state of this lifecycle.
|
||||
* @return the current state.
|
||||
*/
|
||||
S state();
|
||||
|
||||
/**
|
||||
* Transition this lifecycle to a given state.
|
||||
* @param state the state
|
||||
*
|
||||
* @throws IllegalStateException if the transition is illegal for this lifecycle
|
||||
*/
|
||||
void moveTo(S state);
|
||||
|
||||
|
||||
/**
|
||||
* Attempts to transition this lifecycle to a given state, but does not fail if the transition is illegal for this lifecycle.
|
||||
* @param state the state
|
||||
* @return <code>true<code> if this lifecycle has transitioned to the given state
|
||||
*/
|
||||
boolean tryMoveTo(S state);
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package org.gcube.smartgears.lifecycle;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface State<S extends State<S>> {
|
||||
|
||||
/**
|
||||
* Returns the list of states to which services can transition to from this state.
|
||||
* @return the states
|
||||
*/
|
||||
public abstract List<S> next();
|
||||
|
||||
/**
|
||||
* Returns the event corresponding to this state.
|
||||
* @return the
|
||||
*/
|
||||
public String event();
|
||||
|
||||
/**
|
||||
* Returns a serialisation of this state for exchange purposes.
|
||||
* @return
|
||||
*/
|
||||
public String remoteForm();
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package org.gcube.smartgears.lifecycle.application;
|
||||
|
||||
import org.gcube.common.events.Hub;
|
||||
import org.gcube.smartgears.lifecycle.DefaultLifecycle;
|
||||
import org.gcube.smartgears.lifecycle.Lifecycle;
|
||||
|
||||
/**
|
||||
* The {@link Lifecycle} of an application.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class ApplicationLifecycle extends DefaultLifecycle<ApplicationState> {
|
||||
|
||||
/**
|
||||
* The event qualifier that correspond to the {@link ApplicationState#started} state of the service lifecycle.
|
||||
*/
|
||||
public static final String start = "start";
|
||||
|
||||
/**
|
||||
* The event qualifier that correspond to the {@link ApplicationState#active} state of the service lifecycle.
|
||||
*/
|
||||
public static final String activation = "activation";
|
||||
|
||||
/**
|
||||
* The event qualifier that correspond to the {@link ApplicationState#started#stopped} state of the service lifecycle.
|
||||
*/
|
||||
public static final String stop = "stop";
|
||||
|
||||
/**
|
||||
* The event qualifier that correspond to the {@link ApplicationState#failed} state of the service lifecycle.
|
||||
*/
|
||||
public static final String failure = "failure";
|
||||
|
||||
public ApplicationLifecycle(Hub hub, String name){
|
||||
super(hub,name,ApplicationState.started);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package org.gcube.smartgears.lifecycle.application;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
import static java.util.Collections.*;
|
||||
import static org.gcube.smartgears.lifecycle.application.ApplicationLifecycle.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.gcube.smartgears.lifecycle.State;
|
||||
|
||||
/**
|
||||
* The state and state transitions of the lifecycle of an application managed as a gCube service.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public enum ApplicationState implements State<ApplicationState> {
|
||||
|
||||
/**
|
||||
* The state of applications that are in the process of initialisation.
|
||||
*/
|
||||
started(start){
|
||||
|
||||
public List<ApplicationState> next() {
|
||||
return asList(active, failed);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String remoteForm() {
|
||||
return "STARTED";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The state of applications that have completed initialisation and can accept client requests.
|
||||
*/
|
||||
active(activation) {
|
||||
|
||||
public List<ApplicationState> next() {
|
||||
return asList(failed,stopped);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String remoteForm() {
|
||||
return "ready";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The state of applications that have can no longer accept requests, even though they may in the future.
|
||||
*/
|
||||
stopped(stop){
|
||||
|
||||
public List<ApplicationState> next() {
|
||||
return asList(failed,active);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String remoteForm() {
|
||||
return "down";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The permanent state of applications that has encountered some fatal failure and can no longer accept requests.
|
||||
*/
|
||||
failed(failure){
|
||||
|
||||
public List<ApplicationState> next() {
|
||||
return emptyList();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String remoteForm() {
|
||||
return "failed";
|
||||
}
|
||||
};
|
||||
|
||||
private final String event;
|
||||
|
||||
//used internally
|
||||
ApplicationState(String event) {
|
||||
this.event=event;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String event() {
|
||||
return event;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package org.gcube.smartgears.lifecycle.container;
|
||||
|
||||
import org.gcube.common.events.Hub;
|
||||
import org.gcube.smartgears.lifecycle.DefaultLifecycle;
|
||||
import org.gcube.smartgears.lifecycle.Lifecycle;
|
||||
|
||||
/**
|
||||
* The {@link Lifecycle} of the application container.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class ContainerLifecycle extends DefaultLifecycle<ContainerState> {
|
||||
|
||||
/**
|
||||
* The event qualifier that correspond to the {@link ContainerState#pending} state of the container lifecycle.
|
||||
*/
|
||||
public static final String pending = "pending";
|
||||
|
||||
/**
|
||||
* The event qualifier that correspond to the {@link ContainerState#started} state of the container lifecycle.
|
||||
*/
|
||||
public static final String start = "start";
|
||||
|
||||
/**
|
||||
* The event qualifier that correspond to the {@link ContainerState#partActive} state of the container lifecycle.
|
||||
*/
|
||||
public static final String activation = "activation";
|
||||
|
||||
/**
|
||||
* The event qualifier that correspond to the {@link ContainerState#active} state of the container lifecycle.
|
||||
*/
|
||||
public static final String part_activation = "part_activation";
|
||||
|
||||
/**
|
||||
* The event qualifier that correspond to the {@link ContainerState#stopped} state of the container lifecycle.
|
||||
*/
|
||||
public static final String stop = "stop";
|
||||
|
||||
/**
|
||||
* The event qualifier that correspond to the {@link ContainerState#stopped} state of the container lifecycle.
|
||||
*/
|
||||
public static final String shutdown = "shutdown";
|
||||
|
||||
/**
|
||||
* The event qualifier that correspond to the {@link ContainerState#fa} state of the container lifecycle.
|
||||
*/
|
||||
public static final String failure = "failure";
|
||||
|
||||
/**
|
||||
* Creates an instance with an event {@link Hub}.
|
||||
*
|
||||
* @param hub the event hub
|
||||
*/
|
||||
public ContainerLifecycle(Hub hub){
|
||||
super(hub,"container",ContainerState.started);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
package org.gcube.smartgears.lifecycle.container;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
import static org.gcube.smartgears.lifecycle.container.ContainerLifecycle.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.gcube.smartgears.lifecycle.State;
|
||||
|
||||
/**
|
||||
* The state and state transitions of the lifecycle of an application managed as a gCube service.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public enum ContainerState implements State<ContainerState> {
|
||||
|
||||
/**
|
||||
* The state of a container that is in the process of pre authorization.
|
||||
*/
|
||||
notAuthorized(pending){
|
||||
|
||||
public List<ContainerState> next() {
|
||||
return asList(active,failed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String remoteForm() {
|
||||
return "started";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The state of a container that is in the process of initialisation.
|
||||
*/
|
||||
started(start){
|
||||
|
||||
public List<ContainerState> next() {
|
||||
return asList(active,failed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String remoteForm() {
|
||||
return "started";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The state of a container in which not all applications are active.
|
||||
*/
|
||||
partActive(part_activation) {
|
||||
|
||||
public List<ContainerState> next() {
|
||||
return asList(active,stopped,down);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String remoteForm() {
|
||||
return "ready";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The state of a container in which all applications are active.
|
||||
*/
|
||||
active(activation) {
|
||||
|
||||
public List<ContainerState> next() {
|
||||
return asList(partActive,stopped,down);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String remoteForm() {
|
||||
return "certified";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The state of a container in which applications can no longer accept requests, even though they may in the future.
|
||||
*/
|
||||
stopped(stop){
|
||||
|
||||
public List<ContainerState> next() {
|
||||
return asList(partActive,active);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String remoteForm() {
|
||||
return "down";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The state of a container which has been explicitly shutdown.
|
||||
*/
|
||||
down(shutdown){
|
||||
|
||||
public List<ContainerState> next() {
|
||||
return asList(partActive,active);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String remoteForm() {
|
||||
return "down";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The state of a container that has not completed initialisation.
|
||||
*/
|
||||
failed(failure) {
|
||||
|
||||
public List<ContainerState> next() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String remoteForm() {
|
||||
return "failed";
|
||||
}
|
||||
};
|
||||
|
||||
private final String event;
|
||||
|
||||
//used internally
|
||||
ContainerState(String event) {
|
||||
this.event=event;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String event() {
|
||||
return event;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,363 @@
|
|||
package org.gcube.smartgears.managers;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static org.gcube.common.events.Observes.Kind.critical;
|
||||
import static org.gcube.smartgears.Constants.context_attribute;
|
||||
import static org.gcube.smartgears.Constants.profile_file_path;
|
||||
import static org.gcube.smartgears.lifecycle.application.ApplicationState.active;
|
||||
import static org.gcube.smartgears.lifecycle.application.ApplicationState.failed;
|
||||
import static org.gcube.smartgears.lifecycle.application.ApplicationState.stopped;
|
||||
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.Set;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Coordinates management of an application as a gCube resource.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class ApplicationManager {
|
||||
|
||||
private static Logger log = LoggerFactory.getLogger(ApplicationManager.class);
|
||||
|
||||
private ApplicationPipeline<ApplicationLifecycleHandler> lifecyclePipeline;
|
||||
|
||||
private ApplicationContext context;
|
||||
|
||||
/**
|
||||
* Starts application management.
|
||||
*
|
||||
* @param container
|
||||
* @param application the context of the application
|
||||
* @return the context of the application
|
||||
*/
|
||||
public ApplicationContext start(ContainerContext container, ServletContext application) {
|
||||
|
||||
try {
|
||||
|
||||
context = provider().contextFor(container, application);
|
||||
|
||||
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
|
||||
shareContextWith(application);
|
||||
|
||||
// prepare for events as early as possible
|
||||
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();
|
||||
|
||||
|
||||
log.trace("managing {} lifecycle with {}", context.name(), lifecycleHandlers);
|
||||
log.trace("managing {} requests with {}", context.name(), requestHandlers);
|
||||
log.trace("extending {} API with {}", context.name(), extensions);
|
||||
|
||||
// order is important here: first add APIs
|
||||
register(extensions);
|
||||
|
||||
// then intercept them all
|
||||
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) {
|
||||
|
||||
log.error("error starting application {} ", context.name(),e);
|
||||
|
||||
if (context != null)
|
||||
context.lifecycle().moveTo(failed);
|
||||
|
||||
throw e;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Set<String> generateTokensForApplication(ContainerContext container){
|
||||
log.info("generating token for app {}",context.configuration().name());
|
||||
Set<String> tokens = new HashSet<String>();
|
||||
AuthorizationProxy authProxy = provider().authorizationProxy();
|
||||
for (String containerToken :container.configuration().startTokens())
|
||||
tokens.add(generateApplicationToken(containerToken, authProxy));
|
||||
return tokens;
|
||||
}
|
||||
|
||||
private String generateApplicationToken(String containerToken, AuthorizationProxy authProxy){
|
||||
SecurityTokenProvider.instance.set(containerToken);
|
||||
try {
|
||||
log.info("generating token for app {} with container token {} ",context.configuration().name(), containerToken);
|
||||
return authProxy.generateServiceToken(Utils.getServiceInfo(context));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("error contacting authorization service",e);
|
||||
} finally{
|
||||
SecurityTokenProvider.instance.reset();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void saveApplicationState() {
|
||||
File file = context.configuration().persistence().file(profile_file_path);
|
||||
try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file))){
|
||||
oos.writeObject(context.id());
|
||||
}catch (Exception e) {
|
||||
log.error("error serializing application {} state", context.name());
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops application management.
|
||||
*
|
||||
*/
|
||||
public void stop() {
|
||||
|
||||
if (context == null)
|
||||
return;
|
||||
|
||||
log.info("stopping {} management", context.name());
|
||||
|
||||
try {
|
||||
|
||||
context.lifecycle().tryMoveTo(stopped);
|
||||
|
||||
context.events().fire(context, ApplicationLifecycle.stop);
|
||||
|
||||
stopLifecycleHandlers();
|
||||
|
||||
log.info("stopping application events for {}", context.name());
|
||||
|
||||
context.events().stop();
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
log.warn("cannot stop {} management (see cause)", context.name(), e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void register(List<RequestHandler> rqHandlers) {
|
||||
|
||||
ServletContext app = context.application();
|
||||
|
||||
// attach filters based on request pipeline to each servlet
|
||||
Collection<? extends ServletRegistration> servlets = app.getServletRegistrations().values();
|
||||
|
||||
for (ServletRegistration servlet : servlets) {
|
||||
|
||||
String name = servlet.getName();
|
||||
|
||||
if (name.equals("default") || name.equals("jsp")) // skip page-resolving servlets
|
||||
continue;
|
||||
|
||||
for (String mapping : servlet.getMappings()) {
|
||||
|
||||
RequestManager requestFilter = new RequestManager(context, name, rqHandlers);
|
||||
|
||||
FilterRegistration.Dynamic filter = app.addFilter(name + "-filter-"+mapping.replaceAll("/", ""), requestFilter);
|
||||
|
||||
log.trace("filter {} for requestfilter {} in contextPath {} is null ?? {} ",name ,requestFilter, mapping, (filter==null));
|
||||
|
||||
filter.addMappingForUrlPatterns(null, false, mapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void register(ApplicationExtensions extensions) {
|
||||
|
||||
ServletContext application = context.application();
|
||||
|
||||
for (ApplicationExtension extension : extensions.extensions())
|
||||
|
||||
try {
|
||||
|
||||
extension.init(context);
|
||||
|
||||
//register excludes
|
||||
context.configuration().excludes().addAll(extension.excludes());
|
||||
|
||||
String mapping = extension.mapping();
|
||||
|
||||
application.addServlet(context.configuration().name() + "-" + extension.name(), extension)
|
||||
.addMapping(mapping);
|
||||
|
||||
// adds a filter to map request exceptions onto error responses,
|
||||
// repeating for our extensions what we already do for our filters.
|
||||
// we do not interfere with error management of native application servlets
|
||||
RequestExceptionBarrier barrier = new RequestExceptionBarrier();
|
||||
FilterRegistration.Dynamic filter = application.addFilter("exception-barrier", barrier);
|
||||
filter.addMappingForUrlPatterns(null, false, mapping);
|
||||
|
||||
log.info("registered API extension {} @ {}", extension.name(), mapping);
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
throw new RuntimeException("cannot register API extension " + extension.name(), e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void start(List<ApplicationLifecycleHandler> handlers) {
|
||||
|
||||
try {
|
||||
|
||||
lifecyclePipeline = new ApplicationPipeline<ApplicationLifecycleHandler>(handlers);
|
||||
|
||||
lifecyclePipeline.forward(new ApplicationLifecycleEvent.Start(context));
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
context.lifecycle().tryMoveTo(failed);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void stopLifecycleHandlers() {
|
||||
|
||||
if (lifecyclePipeline == null)
|
||||
return;
|
||||
|
||||
// copy pipeline, flip it, and
|
||||
ApplicationPipeline<ApplicationLifecycleHandler> returnPipeline = lifecyclePipeline.reverse();
|
||||
|
||||
// start lifetime pipeline in inverse order with stop event
|
||||
returnPipeline.forward(new ApplicationLifecycleEvent.Stop(context));
|
||||
|
||||
}
|
||||
|
||||
private void registerObservers() {
|
||||
Object observer = new Object() {
|
||||
|
||||
@Observes(value = ContainerLifecycle.stop, kind = critical)
|
||||
void onStopOf(ContainerLifecycle ignore) {
|
||||
|
||||
if (!context.lifecycle().tryMoveTo(stopped))
|
||||
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);
|
||||
|
||||
// we cleanup when container stops
|
||||
context.application().addListener(new ServletContextListener() {
|
||||
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent sce) {
|
||||
log.info("initilizing context {} ",context.name());
|
||||
context.events().fire(context.application().getContextPath(), ApplicationLifecycle.activation);
|
||||
log.info("webApp {} initialized ",context.name());
|
||||
}
|
||||
|
||||
//when the container shuts down we go down
|
||||
@Override
|
||||
public void contextDestroyed(ServletContextEvent sce) {
|
||||
|
||||
try {
|
||||
|
||||
log.info("stopping {} on undeployment|shutdown",context.name());
|
||||
|
||||
stop();
|
||||
|
||||
log.info("suspending undeployment|shutdow to allow {} to stop gracefully",context.name());
|
||||
|
||||
SECONDS.sleep(3);
|
||||
|
||||
log.info("resuming undeployment|shutdow after stopping {}",context.name());
|
||||
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
log.warn(context.name()+" cannot gracefully stop on undeployment|shutdow",e);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void shareContextWith(ServletContext application) {
|
||||
application.setAttribute(context_attribute, context);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,327 @@
|
|||
package org.gcube.smartgears.managers;
|
||||
|
||||
import static org.gcube.smartgears.Constants.container_profile_file_path;
|
||||
import static org.gcube.smartgears.lifecycle.container.ContainerState.active;
|
||||
import static org.gcube.smartgears.lifecycle.container.ContainerState.down;
|
||||
import static org.gcube.smartgears.lifecycle.container.ContainerState.failed;
|
||||
import static org.gcube.smartgears.lifecycle.container.ContainerState.stopped;
|
||||
import static org.gcube.smartgears.provider.ProviderFactory.provider;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.gcube.common.authorization.client.exceptions.ObjectNotFound;
|
||||
import org.gcube.common.authorization.client.proxy.AuthorizationProxy;
|
||||
import org.gcube.common.authorization.library.AuthorizationEntry;
|
||||
import org.gcube.common.authorization.library.provider.ClientInfo;
|
||||
import org.gcube.common.authorization.library.provider.ContainerInfo;
|
||||
import org.gcube.common.events.Observes;
|
||||
import org.gcube.common.events.Observes.Kind;
|
||||
import org.gcube.smartgears.configuration.container.ContainerHandlers;
|
||||
import org.gcube.smartgears.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;
|
||||
import org.gcube.smartgears.lifecycle.application.ApplicationLifecycle;
|
||||
import org.gcube.smartgears.lifecycle.container.ContainerState;
|
||||
import org.gcube.smartgears.utils.Utils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Coordinates management of the container as a gCube resource.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class ContainerManager {
|
||||
|
||||
private static Logger log = LoggerFactory.getLogger(ContainerManager.class);
|
||||
|
||||
public static ContainerManager instance = new ContainerManager();
|
||||
|
||||
private AuthorizationProxy authProvider = provider().authorizationProxy();
|
||||
|
||||
private ContainerContext context;
|
||||
|
||||
private ContainerPipeline pipeline;
|
||||
|
||||
private ContainerManager() {}
|
||||
|
||||
/**
|
||||
* Starts container management.
|
||||
*/
|
||||
public ContainerContext start(ContainerContext context) {
|
||||
|
||||
this.context = context;
|
||||
|
||||
try {
|
||||
|
||||
// TODO Ask if is not enough that is already done in
|
||||
// Bootstrap.initialiseContainer() function;
|
||||
context.configuration().validate();
|
||||
|
||||
validateContainer(context);
|
||||
|
||||
saveContainerState();
|
||||
|
||||
ContainerHandlers handlers = provider().containerHandlers();
|
||||
|
||||
log.trace("managing container lifecycle with {}", handlers.get());
|
||||
|
||||
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) {
|
||||
|
||||
log.error("cannot manage container (see cause)",e);
|
||||
|
||||
if (context!=null)
|
||||
context.lifecycle().moveTo(failed);
|
||||
|
||||
throw e;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void saveContainerState() {
|
||||
File file = context.configuration().persistence().file(container_profile_file_path);
|
||||
try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file))){
|
||||
oos.writeObject(context.id());
|
||||
oos.writeObject(context.configuration().startTokens());
|
||||
}catch (Exception e) {
|
||||
log.error("error serializing cointainer state");
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void validateContainer(ContainerContext context) {
|
||||
List<String> tokensToRemove = new ArrayList<String>();
|
||||
Set<String> foundContexts= new HashSet<String>();
|
||||
|
||||
for (String token : context.configuration().startTokens()){
|
||||
String tokenContext = resolveTokenForAdd(foundContexts, token);
|
||||
if (tokenContext!=null){
|
||||
log.info("the container will be started in context {}",tokenContext);
|
||||
foundContexts.add(tokenContext);
|
||||
} else
|
||||
tokensToRemove.add(token);
|
||||
}
|
||||
|
||||
if (foundContexts.isEmpty()){
|
||||
log.error("no valid starting token are specified, moving the container to failed");
|
||||
throw new RuntimeException("no valid starting token are specified");
|
||||
}
|
||||
|
||||
context.configuration().startTokens().removeAll(tokensToRemove);
|
||||
context.configuration().allowedContexts(foundContexts);
|
||||
}
|
||||
|
||||
private String resolveTokenForAdd(Set<String> alreadyAddedContext, String token){
|
||||
try {
|
||||
AuthorizationEntry entry = authProvider.get(token);
|
||||
ClientInfo info = entry.getClientInfo();
|
||||
log.info("resolved authorization entry for container {}",entry);
|
||||
if (alreadyAddedContext.contains(entry.getContext())){
|
||||
log.warn("the token {} cannot be used, another token with the same context {} found ", entry.getContext());
|
||||
} else if(!entry.getContext().startsWith("/"+context.configuration().infrastructure())){
|
||||
log.warn("the token {} cannot be used, is not in the infrastructure {} of the container ", token,context.configuration().infrastructure());
|
||||
}else if (!(info instanceof ContainerInfo)){
|
||||
log.warn("the token {} cannot be used, is not for a container token ", token);
|
||||
} else if (!((ContainerInfo)info).getHost().equals(context.configuration().hostname())
|
||||
|| context.configuration().port()!=((ContainerInfo)info).getPort()){
|
||||
log.warn("the token {} cannot be used, the client id {} resolved with the token is not the same of the one specified in this container ", token, info.getId());
|
||||
} else
|
||||
return entry.getContext();
|
||||
}catch(ObjectNotFound onf){
|
||||
log.error("token {} not valid", token);
|
||||
} catch (Exception e) {
|
||||
log.error("error contacting authorization for token {}",token,e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void manage(ApplicationContext app) {
|
||||
|
||||
app.events().subscribe(this);
|
||||
|
||||
}
|
||||
|
||||
@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.
|
||||
*
|
||||
*/
|
||||
public void stop() {
|
||||
|
||||
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,
|
||||
|
||||
//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;
|
||||
|
||||
log.info("stopping container management");
|
||||
|
||||
try {
|
||||
|
||||
context.lifecycle().tryMoveTo(shutdown?down:stopped);
|
||||
|
||||
stopHandlers();
|
||||
|
||||
//no further reactions
|
||||
log.info("stopping container events");
|
||||
context.events().stop();
|
||||
Utils.scheduledServicePool.shutdownNow();
|
||||
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
|
||||
log.warn("cannot stop container management (see cause)", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//helpers
|
||||
|
||||
private void startHandlers(List<ContainerHandler> handlers) {
|
||||
|
||||
try {
|
||||
|
||||
pipeline = new ContainerPipeline(handlers);
|
||||
|
||||
pipeline.forward(new ContainerLifecycleEvent.Start(context));
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
|
||||
context.lifecycle().tryMoveTo(failed);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void stopHandlers() {
|
||||
|
||||
if (pipeline == null)
|
||||
return;
|
||||
|
||||
// copy pipeline, flip it, and
|
||||
ContainerPipeline returnPipeline = pipeline.reverse();
|
||||
|
||||
// start lifetime pipeline in inverse order with stop event
|
||||
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);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package org.gcube.smartgears.managers;
|
||||
|
||||
public class ContextEvents {
|
||||
|
||||
public static final String ADD_TOKEN_TO_CONTAINER ="AddTokenToContainer";
|
||||
|
||||
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";
|
||||
|
||||
}
|
|
@ -0,0 +1,234 @@
|
|||
package org.gcube.smartgears.managers;
|
||||
|
||||
import static org.gcube.smartgears.Constants.*;
|
||||
import static org.gcube.smartgears.handlers.application.request.RequestError.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
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.configuration.application.Exclude;
|
||||
import org.gcube.smartgears.context.application.ApplicationContext;
|
||||
import org.gcube.smartgears.context.application.DefaultApplicationContext;
|
||||
import org.gcube.smartgears.handlers.application.ApplicationPipeline;
|
||||
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.handlers.application.request.RequestError;
|
||||
import org.gcube.smartgears.handlers.application.request.RequestException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A {@link Filter} that executes a {@link ApplicationPipeline} of {@link RequestHandler}s before and a client request is delivered
|
||||
* to a given servlet and before the response produced by the servlet is returned to the client.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class RequestManager implements Filter {
|
||||
|
||||
private static Logger log = LoggerFactory.getLogger(RequestManager.class);
|
||||
|
||||
private final ApplicationContext context;
|
||||
private final String servlet;
|
||||
private final List<RequestHandler> handlers;
|
||||
|
||||
/**
|
||||
* Creates an instance with the name of the target servlet and a pipeline.
|
||||
*
|
||||
* @param servlet the name of the servlet
|
||||
* @param pipeline the pipeline
|
||||
*/
|
||||
public RequestManager(ApplicationContext context, String servletName, List<RequestHandler> handlers) {
|
||||
this.context = context;
|
||||
this.servlet = servletName;
|
||||
this.handlers = handlers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
|
||||
ServletException {
|
||||
|
||||
HttpServletRequest httprequest = (HttpServletRequest) request;
|
||||
HttpServletResponse httpresponse = (HttpServletResponse) response;
|
||||
|
||||
List<RequestHandler> filterHandlers = getPipelineWithExcluded(httprequest, handlers);
|
||||
|
||||
if (filterHandlers.isEmpty())
|
||||
|
||||
chain.doFilter(request, response);
|
||||
|
||||
else {
|
||||
|
||||
ApplicationPipeline<RequestHandler> pipeline = new ApplicationPipeline<RequestHandler>(filterHandlers);
|
||||
|
||||
// create a per-request context with temporary properties
|
||||
ApplicationContext ctx = new DefaultApplicationContext(context);
|
||||
|
||||
RequestEvent event = new RequestEvent(servlet, ctx, httprequest,httpresponse);
|
||||
|
||||
try {
|
||||
pipeline.forward(event);
|
||||
}
|
||||
catch(Throwable t) {
|
||||
handleError(httprequest,httpresponse,t);
|
||||
return;
|
||||
}
|
||||
|
||||
try{
|
||||
// dispatch to other filters for this servlet
|
||||
chain.doFilter(request, response);
|
||||
}catch(Throwable t){
|
||||
t.printStackTrace();
|
||||
handleError(httprequest,httpresponse,t);
|
||||
}
|
||||
|
||||
ResponseEvent responseEvent = new ResponseEvent(servlet, ctx, httprequest, httpresponse);
|
||||
|
||||
try {
|
||||
|
||||
pipeline.reverse().forward(responseEvent);
|
||||
}
|
||||
catch(Throwable t) {
|
||||
t.printStackTrace();
|
||||
handleError(httprequest,httpresponse,t);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<RequestHandler> getPipelineWithExcluded(
|
||||
HttpServletRequest request, List<RequestHandler> handlersToFilter) {
|
||||
|
||||
String query = request.getQueryString();
|
||||
|
||||
log.debug("servletPath is {} and pathInfo is {}",request.getServletPath(), request.getPathInfo());
|
||||
|
||||
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.debug("check wich handler should be excluded {}", path);
|
||||
|
||||
for (Exclude exclude : context.configuration().excludes()){
|
||||
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)))
|
||||
){
|
||||
//ALL handler are filtered
|
||||
if (exclude.getHandlers().isEmpty()) return Collections.emptyList();
|
||||
|
||||
List<RequestHandler> filteredHandlers = new ArrayList<>();
|
||||
for (RequestHandler rh : filteredHandlers)
|
||||
if (!exclude.getHandlers().contains(rh.getName()))
|
||||
filteredHandlers.add(rh);
|
||||
return filteredHandlers;
|
||||
}
|
||||
}
|
||||
|
||||
//in case no path is recognized in excludes it return all handlers
|
||||
return handlersToFilter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
|
||||
// propagate filter initialisation to handler
|
||||
try {
|
||||
|
||||
for (RequestHandler handler : this.handlers)
|
||||
|
||||
handler.start(context);
|
||||
|
||||
} catch (Throwable t) {
|
||||
|
||||
throw new ServletException(t);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
|
||||
for (RequestHandler handler : this.handlers)
|
||||
try {
|
||||
|
||||
handler.stop();
|
||||
|
||||
} catch (Throwable t) {
|
||||
|
||||
log.error("cannot terminate handler {} for application {} ", handler, context.name());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
||||
RequestError error = t instanceof RequestException?
|
||||
RequestException.class.cast(t).error():
|
||||
application_error;
|
||||
|
||||
if (error == application_error) {
|
||||
response.sendError(error.code(),error.message());
|
||||
}else {
|
||||
if (error == request_not_authorized_error){
|
||||
response.setHeader("WWW-Authenticate", "Basic realm=\"Smartgears\"");
|
||||
log.info("setting WWW-Authenticate to response header");
|
||||
}
|
||||
response.getWriter().write("Error ("+error.code()+") : "+error.message()+"\nStacktrace:\n");
|
||||
t.printStackTrace(response.getWriter());
|
||||
response.setStatus(error.code());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package org.gcube.smartgears.probe;
|
||||
|
||||
import javax.servlet.ServletContextListener;
|
||||
import javax.servlet.annotation.WebListener;
|
||||
|
||||
import org.gcube.smartgears.managers.ContainerManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@WebListener
|
||||
public class ContainerListener implements ServletContextListener {
|
||||
|
||||
public static Logger log = LoggerFactory.getLogger(ContainerListener.class);
|
||||
|
||||
public void contextDestroyed(javax.servlet.ServletContextEvent sce) {
|
||||
log.trace("shutting down container from probe");
|
||||
ContainerManager.instance.stop(true);
|
||||
};
|
||||
|
||||
public void contextInitialized(javax.servlet.ServletContextEvent sce) {
|
||||
log.trace("starting up probe...");
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,387 @@
|
|||
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_path;
|
||||
import static org.gcube.smartgears.Constants.container_profile_file_path;
|
||||
import static org.gcube.smartgears.Constants.default_extensions_file_path;
|
||||
import static org.gcube.smartgears.Constants.default_handlers_file_path;
|
||||
import static org.gcube.smartgears.Constants.extensions_file_path;
|
||||
import static org.gcube.smartgears.Constants.ghn_home_env;
|
||||
import static org.gcube.smartgears.Constants.ghn_home_property;
|
||||
import static org.gcube.smartgears.Constants.handlers_file_path;
|
||||
import static org.gcube.smartgears.Constants.library_configuration_file_path;
|
||||
import static org.gcube.smartgears.Constants.profile_file_path;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.util.List;
|
||||
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.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.lifecycle.application.ApplicationLifecycle;
|
||||
import org.gcube.smartgears.lifecycle.container.ContainerLifecycle;
|
||||
import org.gcube.smartgears.persistence.DefaultPersistence;
|
||||
import org.gcube.smartgears.utils.Utils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Default implementation of the {@link Provider} interface.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
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)
|
||||
|
||||
protected DefaultProvider(){};
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public ContainerContext containerContext() {
|
||||
|
||||
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);
|
||||
|
||||
File file = configuration.persistence().file(container_profile_file_path);
|
||||
|
||||
String id = null;
|
||||
List<String> tokens = null;
|
||||
if (file.exists()){
|
||||
log.info("loading persisted state for container");
|
||||
try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))){
|
||||
id = (String)ois.readObject();
|
||||
tokens = (List<String>) ois.readObject();
|
||||
}catch(Exception e){
|
||||
log.error("error loading persisted state, creating new uuid",e);
|
||||
}
|
||||
|
||||
}
|
||||
if (id==null){
|
||||
id = UUID.randomUUID().toString();
|
||||
log.info("container id created is {}",id);
|
||||
|
||||
}
|
||||
|
||||
if (tokens!=null)
|
||||
configuration.startTokens(tokens);
|
||||
|
||||
containerContext = new DefaultContainerContext(id, configuration, hub, lifecycle, new Properties());
|
||||
}
|
||||
return containerContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContainerHandlers containerHandlers() {
|
||||
|
||||
try {
|
||||
|
||||
InputStream config = getClass().getResourceAsStream(container_handlers_file_path);
|
||||
|
||||
if (config == null)
|
||||
throw new IllegalStateException("invalid distribution: cannot find " + container_handlers_file_path);
|
||||
|
||||
ContainerConfigurationBinder binder = new ContainerConfigurationBinder();
|
||||
|
||||
return binder.bindHandlers(config);
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
|
||||
throw new RuntimeException("cannot install container handlers (see cause) ", e);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationContext contextFor(ContainerContext context, ServletContext application) {
|
||||
|
||||
ApplicationConfiguration configuration = null;
|
||||
ApplicationConfiguration embedded = configurationFor(application);
|
||||
ApplicationConfiguration external = context.configuration().app(application.getContextPath());
|
||||
|
||||
|
||||
|
||||
//shouldn't happen: management shouldn't have started at all
|
||||
if (embedded==null && external==null)
|
||||
throw new AssertionError("application @ "+application.getContextPath()+" is not distributed with "
|
||||
+ configuration_file_path+" and there is no external configuration for it in "+container_configuraton_file_path);
|
||||
|
||||
//no embedded configuration
|
||||
if (embedded == null) {
|
||||
|
||||
configuration = external ;
|
||||
|
||||
log.info("loaded configuration for application "+configuration.name()+" from "+container_configuraton_file_path);
|
||||
}
|
||||
else {
|
||||
|
||||
configuration = embedded;
|
||||
|
||||
if (external == null)
|
||||
|
||||
log.info("loaded configuration for application "+configuration.name()+" from "+configuration_file_path);
|
||||
|
||||
else {
|
||||
|
||||
configuration.merge(external);
|
||||
|
||||
log.info("loaded configuration for application "+configuration.name()+" from "+configuration_file_path+" and "+container_configuraton_file_path);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// TODO we can check scopes here instead of in BridgedApplicationConfiguration constructor
|
||||
ApplicationConfiguration bridgedConfiguration = new BridgedApplicationConfiguration(context.configuration(),
|
||||
configuration);
|
||||
|
||||
Hub hub = new DefaultHub();
|
||||
|
||||
ApplicationLifecycle lifecycle = new ApplicationLifecycle(hub, configuration.name());
|
||||
|
||||
File file = bridgedConfiguration.persistence().file(profile_file_path);
|
||||
String id= null;
|
||||
if (file.exists()){
|
||||
log.info("loading persisted state for application {}", application.getContextPath());
|
||||
try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))){
|
||||
id = (String)ois.readObject();
|
||||
}catch(Exception e){
|
||||
log.error("error loading persisted state, creating new uuid",e);
|
||||
}
|
||||
}
|
||||
if (id==null)
|
||||
id = UUID.randomUUID().toString();
|
||||
|
||||
return new DefaultApplicationContext(id, context, application, bridgedConfiguration, hub, lifecycle,
|
||||
new Properties());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationHandlers handlersFor(ApplicationContext context) {
|
||||
|
||||
try {
|
||||
|
||||
InputStream config = context.application().getResourceAsStream(handlers_file_path);
|
||||
|
||||
if (config == null) {
|
||||
|
||||
log.trace("{} uses the default lifecycle as it does not include {}", context.name(), handlers_file_path);
|
||||
|
||||
// it's in a library, using
|
||||
config = getClass().getResourceAsStream(default_handlers_file_path);
|
||||
|
||||
if (config == null)
|
||||
throw new IllegalStateException("invalid distribution: cannot find " + default_handlers_file_path);
|
||||
|
||||
} else
|
||||
log.info("{} uses a custom lifecycle @ {}", context.name(), handlers_file_path);
|
||||
|
||||
ApplicationConfigurationBinder binder = new ApplicationConfigurationBinder();
|
||||
|
||||
return binder.bindHandlers(config);
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
|
||||
throw new RuntimeException("cannot install handlers for application @ " + context.name()+" (see cause) ", e);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationExtensions extensionsFor(ApplicationContext context) {
|
||||
|
||||
try {
|
||||
|
||||
InputStream config = context.application().getResourceAsStream(extensions_file_path);
|
||||
|
||||
if (config == null) {
|
||||
|
||||
log.trace("{} uses default extensions as it does not include {}", context.name(), extensions_file_path);
|
||||
|
||||
// it's in a library, using
|
||||
config = getClass().getResourceAsStream(default_extensions_file_path);
|
||||
|
||||
if (config == null)
|
||||
throw new IllegalStateException("invalid distribution: cannot find " + default_extensions_file_path);
|
||||
|
||||
} else
|
||||
log.info("{} uses custom extensions @ {}", context.name(), extensions_file_path);
|
||||
|
||||
ApplicationConfigurationBinder binder = new ApplicationConfigurationBinder();
|
||||
|
||||
return binder.bindExtensions(config);
|
||||
|
||||
} 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)
|
||||
return null;
|
||||
|
||||
ApplicationConfigurationBinder binder = new ApplicationConfigurationBinder();
|
||||
|
||||
return binder.bind(config);
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
|
||||
throw new RuntimeException("invalid configuration (see cause)", e);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private ContainerConfiguration containerConfiguration() {
|
||||
|
||||
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");
|
||||
|
||||
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");
|
||||
|
||||
File config = 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 @ {} ", config.getAbsolutePath());
|
||||
|
||||
ContainerConfigurationBinder binder = new ContainerConfigurationBinder();
|
||||
|
||||
FileInputStream stream = null;
|
||||
try {
|
||||
|
||||
stream = new FileInputStream(config);
|
||||
|
||||
}
|
||||
catch(Exception e) {
|
||||
throw new RuntimeException("unexpected exception reading container configuration file see cause)",e);
|
||||
}
|
||||
|
||||
ContainerConfiguration configuration = binder.bind(stream);
|
||||
|
||||
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 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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package org.gcube.smartgears.provider;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Provides dependencies for container and application management.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public interface Provider {
|
||||
|
||||
//container-level dependencies
|
||||
|
||||
/**
|
||||
* Returns the runtime properties.
|
||||
* @return the properties.
|
||||
*/
|
||||
SmartGearsConfiguration smartgearsConfiguration();
|
||||
|
||||
/**
|
||||
* Assembles and returns the context of the container.
|
||||
* @return the container's context
|
||||
*/
|
||||
ContainerContext containerContext();
|
||||
|
||||
/**
|
||||
* Returns the handlers associated with the container.
|
||||
* @return the handlers
|
||||
*/
|
||||
ContainerHandlers containerHandlers();
|
||||
|
||||
|
||||
/**
|
||||
* Returns an implementation of the IS publisher for the container
|
||||
* @param application the context of the container
|
||||
* @return the publisher implementation
|
||||
*/
|
||||
ScopedPublisher publisherFor(ContainerContext application);
|
||||
|
||||
//application-level dependencies
|
||||
|
||||
/**
|
||||
* Assembles and returns the context of a given application.
|
||||
* @param container the context of the container
|
||||
* @param application the servlet context of the application
|
||||
* @return
|
||||
*/
|
||||
|
||||
ApplicationContext contextFor(ContainerContext container,ServletContext application);
|
||||
|
||||
/**
|
||||
* Returns the handlers associated with a given application.
|
||||
* @param application the context of the application
|
||||
* @return the handlers
|
||||
*/
|
||||
ApplicationHandlers handlersFor(ApplicationContext application);
|
||||
|
||||
/**
|
||||
* Returns the API extensions associated with a given application.
|
||||
* @param application the context of the application
|
||||
* @return the extensions
|
||||
*/
|
||||
ApplicationExtensions extensionsFor(ApplicationContext application);
|
||||
|
||||
/**
|
||||
* Returns an implementation of the IS publisher for a given application
|
||||
* @param application the context of the application
|
||||
* @return the publisher implementation
|
||||
*/
|
||||
ScopedPublisher publisherFor(ApplicationContext application);
|
||||
|
||||
/**
|
||||
* Returns an implementation of the IS publisher for a given application
|
||||
* @param application the context of the application
|
||||
* @return the publisher implementation
|
||||
*/
|
||||
AuthorizationProxy authorizationProxy();
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package org.gcube.smartgears.provider;
|
||||
|
||||
public class ProviderFactory {
|
||||
|
||||
private static Provider provider = new DefaultProvider();
|
||||
|
||||
public static Provider provider() {
|
||||
return provider;
|
||||
}
|
||||
|
||||
public static void testProvider(Provider provider) {
|
||||
ProviderFactory.provider=provider;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package org.gcube.smartgears.utils;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class ScopeEvent {
|
||||
|
||||
private Collection<String> scopes;
|
||||
|
||||
public ScopeEvent(Collection<String> scopes) {
|
||||
super();
|
||||
this.scopes = scopes;
|
||||
}
|
||||
|
||||
public Collection<String> getScopes() {
|
||||
return scopes;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,221 @@
|
|||
package org.gcube.smartgears.utils;
|
||||
|
||||
import static org.gcube.smartgears.Constants.ghn_home_env;
|
||||
import static org.gcube.smartgears.Constants.ghn_home_property;
|
||||
import static org.gcube.smartgears.handlers.application.request.RequestError.application_error;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.Flushable;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.gcube.common.authorization.library.provider.ServiceIdentifier;
|
||||
import org.gcube.common.authorization.library.provider.ServiceInfo;
|
||||
import org.gcube.smartgears.context.application.ApplicationContext;
|
||||
import org.gcube.smartgears.handlers.application.request.RequestError;
|
||||
import org.gcube.smartgears.handlers.application.request.RequestException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Library-wide utils.
|
||||
*
|
||||
* @author Fabio Simeoni
|
||||
*
|
||||
*/
|
||||
public class Utils {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(Utils.class);
|
||||
|
||||
public static final ScheduledExecutorService scheduledServicePool = Executors.newScheduledThreadPool(10);
|
||||
|
||||
public static RuntimeException unchecked(Throwable t) {
|
||||
|
||||
return (t instanceof RuntimeException) ? RuntimeException.class.cast(t) : new RuntimeException(t.getMessage(),
|
||||
t);
|
||||
|
||||
}
|
||||
|
||||
public static String smartgearsVersion() {
|
||||
return "1.0.0"; // @TODO
|
||||
}
|
||||
|
||||
public static void rethrowUnchecked(Throwable t) throws RuntimeException {
|
||||
|
||||
throw unchecked(t);
|
||||
|
||||
}
|
||||
|
||||
public static void closeSafely(Closeable c) {
|
||||
|
||||
if (c != null) {
|
||||
try {
|
||||
|
||||
if (c instanceof Flushable)
|
||||
Flushable.class.cast(c).flush();
|
||||
|
||||
c.close();
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("could not close {} due to error {}: msg{}", c, e.getClass().getSimpleName(), e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static void valid(String name, Object[] o) throws IllegalArgumentException {
|
||||
notNull(name, o);
|
||||
|
||||
}
|
||||
|
||||
public static void notNull(Object[] o) throws IllegalArgumentException {
|
||||
notNull("argument", o);
|
||||
}
|
||||
|
||||
public static void notNull(String name, Object o) throws IllegalArgumentException {
|
||||
if (o == null)
|
||||
throw new IllegalArgumentException(name + " is null");
|
||||
}
|
||||
|
||||
public static void notEmpty(String name, String o) throws IllegalArgumentException {
|
||||
notNull(name, o);
|
||||
if (o.isEmpty())
|
||||
throw new IllegalArgumentException(name + " is empty");
|
||||
}
|
||||
|
||||
public static void notEmpty(String name, Object[] o) throws IllegalArgumentException {
|
||||
notNull(name, o);
|
||||
if (o.length == 0)
|
||||
throw new IllegalArgumentException(name + " is empty");
|
||||
}
|
||||
|
||||
public static void notEmpty(String name, Collection<?> o) throws IllegalArgumentException {
|
||||
notNull(name, o);
|
||||
if (o.isEmpty())
|
||||
throw new IllegalArgumentException(name + " is empty");
|
||||
}
|
||||
|
||||
public static void valid(String name, String o) throws IllegalArgumentException {
|
||||
notNull(name, o);
|
||||
notEmpty(name, o);
|
||||
}
|
||||
|
||||
public static String home() {
|
||||
|
||||
String home = System.getenv(ghn_home_env);
|
||||
|
||||
if (home == null)
|
||||
home = System.getProperty(ghn_home_property);
|
||||
|
||||
return home;
|
||||
|
||||
}
|
||||
|
||||
public static void handleError(HttpServletRequest request, HttpServletResponse response, Throwable t)
|
||||
throws IOException {
|
||||
|
||||
RequestError error = t instanceof RequestException ? RequestException.class.cast(t).error() : application_error;
|
||||
|
||||
if (error == application_error) {
|
||||
response.getWriter().write("Error (" + error.code() + ") : " + t.getMessage() + "\nStacktrace:\n");
|
||||
t.printStackTrace(response.getWriter());
|
||||
response.setStatus(error.code());
|
||||
|
||||
} else
|
||||
response.sendError(error.code(), t.getMessage());
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static interface ModeClause {
|
||||
|
||||
File toRead();
|
||||
|
||||
File toWrite();
|
||||
}
|
||||
|
||||
public static ModeClause fileAt(final String path) {
|
||||
|
||||
notNull("file path",path);
|
||||
|
||||
return new ModeClause() {
|
||||
|
||||
@Override
|
||||
public File toWrite() {
|
||||
return file(path,true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File toRead() {
|
||||
return file(path,false);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static File file(String path, boolean writeMode) throws IllegalArgumentException {
|
||||
|
||||
|
||||
File file = new File(path);
|
||||
|
||||
if (!writeMode)
|
||||
|
||||
if (!file.exists() || file.length() == 0 || !file.canRead()) {
|
||||
File backup = new File(file.getAbsolutePath() + ".backup");
|
||||
if (backup.exists())
|
||||
if (!backup.renameTo(file)) {
|
||||
log.warn("accessing directly backup {} as it cannot be renamed to {}", backup.getAbsolutePath(), file.getAbsolutePath());
|
||||
return backup; //bets effort:read from backup
|
||||
}
|
||||
else {
|
||||
log.info("cannot read {} but can access its backup {}", backup.getAbsolutePath(), file.getAbsolutePath());
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (file.isDirectory())
|
||||
throw new IllegalArgumentException(path + " cannot be used in write mode because it's folder");
|
||||
|
||||
//create folder structure it does not exist
|
||||
if (!file.getParentFile().exists())
|
||||
file.getParentFile().mkdirs();
|
||||
else
|
||||
if (file.exists())
|
||||
|
||||
try (
|
||||
BufferedReader reader = new BufferedReader(new FileReader(file));
|
||||
BufferedWriter writer = new BufferedWriter(new FileWriter(new File(file.getAbsolutePath() + ".backup")));
|
||||
)
|
||||
{
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null)
|
||||
writer.write(line);
|
||||
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.warn("cannot back up "+file.getAbsolutePath()+" on writing ", e);
|
||||
}
|
||||
|
||||
|
||||
return file;
|
||||
|
||||
}
|
||||
|
||||
public static ServiceInfo getServiceInfo(ApplicationContext application){
|
||||
String hostedin = String.format("%s_%d", application.container().configuration().hostname(), application.container().configuration().port());
|
||||
return
|
||||
new ServiceInfo(new ServiceIdentifier(application.configuration().serviceClass(), application.configuration().name(), hostedin));
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<handlers>
|
||||
<accounting-management />
|
||||
<profile-management />
|
||||
</handlers>
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<extensions>
|
||||
<remote-management/>
|
||||
</extensions>
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<handlers>
|
||||
<lifecycle>
|
||||
<profile-management />
|
||||
</lifecycle>
|
||||
<request>
|
||||
<request-validation />
|
||||
<request-accounting />
|
||||
</request>
|
||||
</handlers>
|
|
@ -0,0 +1,268 @@
|
|||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>${name}</title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
|
||||
background-color: rgb(227, 227, 227);
|
||||
color: rgb(18, 132, 194);
|
||||
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
font-family: 'HelveticaNeue-UltraLight', 'Helvetica Neue', 'Arial', sans-serif;
|
||||
}
|
||||
|
||||
a:link {color: rgb(69, 114, 132); text-decoration:none}
|
||||
a:hover {color: rgb(18, 132, 194);}
|
||||
|
||||
#top, #bottom {
|
||||
background-color: rgb(218, 218, 218);
|
||||
}
|
||||
|
||||
#container {
|
||||
width: 906px;
|
||||
margin-left: 60px;
|
||||
margin-top: 10px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#top {
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
#top h1, #top h2 {
|
||||
width:99%;
|
||||
font-weight: normal;
|
||||
margin: 0 0;
|
||||
}
|
||||
|
||||
#top h1 {
|
||||
font-size: 65px;
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
#top h2 {
|
||||
font-size: 25px;
|
||||
color: rgb(69, 114, 132);
|
||||
}
|
||||
|
||||
#middle {
|
||||
min-height: 300px;
|
||||
padding: 1em;
|
||||
padding-top: 40px;
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
#middle p {
|
||||
font-size: 25px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
padding-top: .2em;
|
||||
padding-bottom: .2em;
|
||||
color: rgb(69, 114, 132);
|
||||
}
|
||||
|
||||
#middle p em {
|
||||
color: rgb(18, 132, 194);
|
||||
font-family: HelveticaNeue-Light, 'Helvetica Neue', Arial, sans-serif;
|
||||
font-style: normal;
|
||||
font-variant: normal;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
#bottom {
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.half {
|
||||
width: 49%;
|
||||
}
|
||||
|
||||
.left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.toolbar, .footer {
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div id="top">
|
||||
<h1>${name}</h1>
|
||||
<h2>v.${version}</h2>
|
||||
<h2 style="text-align:right">managed by <a href="http://www.gcube-system.org">gCube</a></h2>
|
||||
</div>
|
||||
<div id="middle">
|
||||
<p>Welcome to <em>${name}</em>,</p>
|
||||
<p>a resource of the <em>${infra}</em> infrastructure shared in <em>${vos}</em> VOs.</p>
|
||||
<p style="padding-top:30px">The resource is <em>${status}</em>.</p>
|
||||
|
||||
</div>
|
||||
<div id="bottom">
|
||||
<span class="toolbar half left">
|
||||
<a href="${profile_link}">
|
||||
<img alt="profile" src="data:image/png;base64,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" />
|
||||
</a>
|
||||
<a href="${config_link}">
|
||||
<img alt="configuration" src="data:image/png;base64,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" /></a>
|
||||
</span>
|
||||
<span class="footer half right">SmartGears ${smartgears-version}</span>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1 @@
|
|||
org.gcube.smartgears.Bootstrap
|
|
@ -0,0 +1 @@
|
|||
org.gcube.smartgears.extensions.resource.RemoteResource
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue