Merge remote-tracking branch 'origin/smartgears4'

master
Lucio Lelii 1 year ago
commit 725b19c168

1
.gitignore vendored

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

@ -2,17 +2,15 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
# Changelog for Common Smartgears
## [v3.1.6]
## [v4.0.0-SNAPSHOT]
- Added Linux distribution version [#22933]
## [v3.1.5] - 2022-04-20
- porting to keycloak
- Added roles to ExternalService Info on request handler verification
## [v3.1.4] - 2022-03-29
## [v3.2.0-SNAPSHOT]
- fixes issue [#23075]
- Added SecretManagerProvider thread local from authorization-utils [#22871]
- Added Linux distribution version [#22933]
## [v3.1.3] - 2022-03-21

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

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

@ -8,39 +8,55 @@
<version>1.1.0</version>
<relativePath />
</parent>
<groupId>org.gcube.core</groupId>
<artifactId>common-smartgears</artifactId>
<version>3.1.6</version>
<version>4.0.0-SNAPSHOT</version>
<name>SmartGears</name>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.gcube.distribution</groupId>
<artifactId>gcube-bom</artifactId>
<version>2.1.0</version>
<version>3.0.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<properties>
<distroDirectory>distro</distroDirectory>
<tomcat.version>7.0.42</tomcat.version>
<jersey.version>1.17.1</jersey.version>
<tomcat.version>8.0.42</tomcat.version>
<jersey.version>2.25.1</jersey.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<scm>
<connection>scm:git:https://code-repo.d4science.org/gCubeSystem/common-smartgears.git</connection>
<developerConnection>scm:git:https://code-repo.d4science.org/gCubeSystem/common-smartgears.git</developerConnection>
<developerConnection>scm:git:httpstps://code-repo.d4science.org/gCubeSystem/common-smartgears.git</developerConnection>
<url>https://code-repo.d4science.org/gCubeSystem/common-smartgears</url>
</scm>
<dependencies>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.10</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.21</version>
</dependency>
<dependency>
<groupId>io.github.classgraph</groupId>
<artifactId>classgraph</artifactId>
<version>4.8.28</version>
</dependency>
<dependency>
<groupId>org.gcube.common</groupId>
<artifactId>health-api</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<!-- gCube Jackson -->
<dependency>
<groupId>org.gcube.common</groupId>
@ -55,78 +71,59 @@
<artifactId>gcube-jackson-core</artifactId>
</dependency>
<!-- END gCube Jackson -->
<dependency>
<groupId>org.gcube.common</groupId>
<artifactId>authorization-client</artifactId>
<artifactId>common-security</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.common</groupId>
<artifactId>common-authorization</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.data.publishing</groupId>
<artifactId>document-store-lib</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.accounting</groupId>
<artifactId>accounting-lib</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.resources</groupId>
<artifactId>registry-publisher</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.resources</groupId>
<artifactId>common-gcore-resources</artifactId>
<groupId>org.ini4j</groupId>
<artifactId>ini4j</artifactId>
<version>0.5.4</version>
</dependency>
<dependency>
<groupId>org.gcube.core</groupId>
<artifactId>common-validator</artifactId>
<version>[1.0.0,2.0.0-SNAPSHOT)</version>
</dependency>
<dependency>
<groupId>org.gcube.core</groupId>
<artifactId>common-scope</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.core</groupId>
<artifactId>common-events</artifactId>
<version>[1.0.0,2.0.0-SNAPSHOT)</version>
</dependency>
<dependency>
<groupId>org.gcube.common.security</groupId>
<artifactId>gcube-secrets</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- Added to support Java 11 JDK -->
<!-- https://mvnrepository.com/artifact/io.micrometer/micrometer-core -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<scope>provided</scope>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
<version>1.9.0</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<version>1.9.0</version>
</dependency>
<!-- END Added to support Java 11 JDK -->
<!-- ***************** test ******************* -->
<dependency>
<groupId>org.jboss.shrinkwrap.resolver</groupId>
<artifactId>shrinkwrap-resolver-depchain</artifactId>
@ -134,7 +131,6 @@
<type>pom</type>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-catalina -->
<dependency>
<groupId>org.apache.tomcat</groupId>
@ -142,67 +138,49 @@
<version>${tomcat.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>${tomcat.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>${jersey.version}</version>
<version>2.25.1</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>
<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>
<!-- excludes probe package from jar -->
<plugin>
@ -223,7 +201,18 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<property>
<name>projectVersion</name>
<value>${project.version}</value>
</property>
</systemPropertyVariables>
</configuration>
</plugin>
<!-- include probe in attached war -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@ -232,7 +221,6 @@
<configuration>
<primaryArtifact>false</primaryArtifact>
<classifier>probe</classifier>
<packagingIncludes>WEB-INF/classes/org/gcube/smartgears/probe/**/*</packagingIncludes>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
@ -246,8 +234,6 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
@ -277,9 +263,6 @@
<resources>
<resource>
<directory>${distroDirectory}</directory>
<includes>
<include>smartgears-config.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
@ -287,7 +270,6 @@
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -14,6 +14,16 @@ import org.gcube.smartgears.provider.ProviderFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.binder.jvm.ClassLoaderMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics;
import io.micrometer.core.instrument.binder.system.ProcessorMetrics;
import io.micrometer.core.instrument.binder.system.UptimeMetrics;
import io.micrometer.prometheus.PrometheusConfig;
import io.micrometer.prometheus.PrometheusMeterRegistry;
/**
* Bootstraps management of all deployed applications which require it.
*
@ -76,6 +86,7 @@ public class Bootstrap implements ServletContainerInitializer {
}
// helpers
@SuppressWarnings("resource")
private void initialiseContainer() {
try {
@ -86,11 +97,24 @@ public class Bootstrap implements ServletContainerInitializer {
/* Get the ContainerContext. Look at DefaultProvider */
context = ProviderFactory.provider().containerContext();
PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
new ClassLoaderMetrics().bindTo(registry);
new JvmMemoryMetrics().bindTo(registry);
new JvmGcMetrics().bindTo(registry);
new ProcessorMetrics().bindTo(registry);
new JvmThreadMetrics().bindTo(registry);
new UptimeMetrics().bindTo(registry);
new ProcessorMetrics().bindTo(registry);
Metrics.addRegistry(registry);
/* Validate the configuration retrieved by ContainerContext
* using gcube facilities annotation based
* ( i.e org.gcube.common.validator.annotations)
*/
//context.configuration().validate();
context.configuration().validate();
} catch (RuntimeException e) {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -7,7 +7,8 @@ import org.gcube.smartgears.configuration.application.ApplicationConfiguration;
import org.gcube.smartgears.context.Properties;
import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.lifecycle.application.ApplicationLifecycle;
import org.gcube.smartgears.persistence.Persistence;
import org.gcube.smartgears.persistence.PersistenceWriter;
import org.gcube.smartgears.security.AuthorizationProvider;
/**
* The management context of an application.
@ -33,9 +34,6 @@ public interface ApplicationContext {
* @return the configuration
*/
ApplicationConfiguration configuration();
<T> T profile(Class<T> type);
/**
* Returns the lifecycle of the application.
@ -56,7 +54,7 @@ public interface ApplicationContext {
*
* @return the manager
*/
Persistence persistence();
PersistenceWriter persistence();
/**
* Returns the servlet context of the application.
@ -78,5 +76,11 @@ public interface ApplicationContext {
* @return the properties
*/
Properties properties();
/**
* Returns the authorization provider.
* @return the AuhtorizationProvider
**/
AuthorizationProvider authorizationProvider();
}

@ -1,16 +1,24 @@
package org.gcube.smartgears.context.application;
import static org.gcube.smartgears.Constants.profile_property;
import static org.gcube.smartgears.Constants.profile_file_path;
import java.io.File;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.UUID;
import javax.servlet.ServletContext;
import org.gcube.common.events.Hub;
import org.gcube.common.resources.gcore.GCoreEndpoint;
import org.gcube.smartgears.configuration.PersistenceConfiguration;
import org.gcube.smartgears.configuration.application.ApplicationConfiguration;
import org.gcube.smartgears.context.Properties;
import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.lifecycle.application.ApplicationLifecycle;
import org.gcube.smartgears.persistence.Persistence;
import org.gcube.smartgears.persistence.PersistenceWriter;
import org.gcube.smartgears.security.AuthorizationProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Default {@link ApplicationContext} implementation.
@ -20,12 +28,15 @@ import org.gcube.smartgears.persistence.Persistence;
*/
public class DefaultApplicationContext implements ApplicationContext {
private static Logger log = LoggerFactory.getLogger(DefaultApplicationContext.class);
private final ContainerContext container;
private final ServletContext sctx;
private final ApplicationConfiguration configuration;
private final ApplicationLifecycle lifecycle;
private final Properties properties;
private final Hub hub;
private final PersistenceWriter persistenceWriter;
private final String id;
/**
@ -37,14 +48,37 @@ public class DefaultApplicationContext implements ApplicationContext {
* @param lifecycle the lifecycle
* @param properties the properties
*/
public DefaultApplicationContext(String id,ContainerContext container,ServletContext sctx,ApplicationConfiguration configuration, Hub hub, ApplicationLifecycle lifecycle, Properties properties) {
public DefaultApplicationContext(ContainerContext container,ServletContext sctx,ApplicationConfiguration configuration, Hub hub, ApplicationLifecycle lifecycle, Properties properties) {
PersistenceConfiguration persistenceWriterConf = configuration.persistenceConfiguration();
try {
persistenceWriter = persistenceWriterConf.getImplementationClass().getDeclaredConstructor().newInstance();
persistenceWriter.configure(persistenceWriterConf.getWriterConfiguration());
}catch (Exception e) {
throw new RuntimeException(e);
}
File file = persistenceWriter.file(profile_file_path);
String id = null;
if (file.exists()) {
log.info("loading persisted state for application {}", sctx.getContextPath());
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) {
id = (String) ois.readObject();
} catch (Exception e) {
log.error("error loading persisted state, creating new uuid", e);
}
}
if (id == null)
id = UUID.randomUUID().toString();
this.id = id;
this.container=container;
this.sctx = sctx;
this.configuration=configuration;
this.hub=hub;
this.lifecycle = lifecycle;
this.properties=properties;
this.properties=properties;
}
/**
@ -52,9 +86,21 @@ public class DefaultApplicationContext implements ApplicationContext {
* @param context the other instance
*/
public DefaultApplicationContext(ApplicationContext context) {
this(context.id(), context.container(),context.application(),context.configuration(),context.events(), context.lifecycle(), new Properties(context.properties()));
this(context.id(), context.persistence(), context.container(),context.application(),context.configuration(),context.events(), context.lifecycle(), new Properties(context.properties()));
}
private DefaultApplicationContext(String id, PersistenceWriter writer, ContainerContext container,ServletContext sctx,ApplicationConfiguration configuration, Hub hub, ApplicationLifecycle lifecycle, Properties properties) {
this.id = id;
this.container=container;
this.sctx = sctx;
this.configuration=configuration;
this.hub=hub;
this.lifecycle = lifecycle;
this.properties=properties;
this.persistenceWriter = writer;
}
@Override
public ServletContext application() {
return sctx;
@ -65,16 +111,6 @@ public class DefaultApplicationContext implements ApplicationContext {
return container;
}
@Override
@SuppressWarnings("all")
public <T> T profile(Class<T> type) {
if (type==GCoreEndpoint.class)
return (T) properties().lookup(profile_property).value(GCoreEndpoint.class);
throw new IllegalArgumentException("unsupported profile type: "+type);
}
@Override
public String name() { //little shortcut for ease of logging
return configuration.name();
@ -96,10 +132,10 @@ public class DefaultApplicationContext implements ApplicationContext {
}
@Override
public Persistence persistence() {
return configuration.persistence();
public PersistenceWriter persistence() {
return persistenceWriter;
}
@Override
public Properties properties() {
return properties;
@ -110,4 +146,12 @@ public class DefaultApplicationContext implements ApplicationContext {
return id;
}
/**
* Returns the authorization provider.
* @return the AuhtorizationProvider
**/
public AuthorizationProvider authorizationProvider() {
return container().authorizationProvider();
}
}

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

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

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

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

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

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

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

@ -4,18 +4,14 @@ 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;
@ -23,10 +19,9 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.gcube.common.resources.gcore.GCoreEndpoint;
import org.gcube.common.scope.impl.ScopeBean;
import org.gcube.smartgears.extensions.ApiResource;
import org.gcube.smartgears.extensions.ApiSignature;
import org.gcube.smartgears.provider.ProviderFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -93,42 +88,12 @@ public class FrontPageResource extends ApiResource {
values.put("version", context().configuration().version());
String infrastructure = context().container().configuration().infrastructure();
StringBuilder voValue = new StringBuilder();
Collection<String> scopes = context().profile(GCoreEndpoint.class).scopes().asCollection();
Set<String> vos = new HashSet<String>();
//pre-process
for (String scope : scopes) {
ScopeBean bean = new ScopeBean(scope);
switch (bean.type()) {
case INFRASTRUCTURE:
infrastructure = bean.name();
break;
case VO:
vos.add(bean.name());
break;
case VRE:
vos.add(bean.enclosingScope().name());
infrastructure=bean.enclosingScope().enclosingScope().name();
}
}
//build vo value
int i = 0;
int max = vos.size()-1;
for (String vo : vos) {
String voPrefix = i == 0 ? "" : (i==max?" and ":", ");
voValue.append(voPrefix+"<em>" + vo + "</em>");
i++;
}
values.put("infra", infrastructure);
values.put("vos", voValue.toString());
values.put("status", context().lifecycle().state().toString());
values.put("smartgears-version", provider().smartgearsConfiguration().version());
values.put("smartgears-version", ProviderFactory.provider().smartgearsConfiguration().getVersion());
return values;
}

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

@ -11,8 +11,6 @@ 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;
@ -86,10 +84,8 @@ public class LifecycleResource extends ApiResource {
// helper classes
@XmlRootElement(name="state")
public static class State {
@XmlValue
public String value;
State() {

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

@ -9,8 +9,6 @@ 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;
@ -36,7 +34,8 @@ public class ProfileResource extends ApiResource {
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Resources.marshal(context().profile(GCoreEndpoint.class),resp.getWriter());
//Resources.marshal(context().profile(),resp.getWriter());
//TODO: return something to show
}
}

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

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

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

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

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

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

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

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

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

@ -1,47 +1,39 @@
package org.gcube.smartgears.handlers.application.request;
import static org.gcube.common.authorization.client.Constants.authorizationService;
import static org.gcube.smartgears.Constants.token_header;
import static org.gcube.smartgears.handlers.application.request.RequestError.application_failed_error;
import static org.gcube.smartgears.handlers.application.request.RequestError.application_unavailable_error;
import static org.gcube.smartgears.handlers.application.request.RequestError.invalid_request_error;
import java.util.Collections;
import java.util.List;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import org.gcube.common.authorization.library.PolicyUtils;
import org.gcube.common.authorization.library.policies.Policy;
import org.gcube.common.authorization.library.policies.User2ServicePolicy;
import org.gcube.common.authorization.library.policies.UserEntity;
import org.gcube.common.authorization.library.provider.AccessTokenProvider;
import org.gcube.common.authorization.library.provider.AuthorizationProvider;
import org.gcube.common.authorization.library.provider.SecurityTokenProvider;
import org.gcube.common.authorization.library.provider.ServiceIdentifier;
import org.gcube.common.scope.api.ScopeProvider;
import org.gcube.common.scope.impl.ScopeBean;
import org.gcube.common.scope.impl.ScopeBean.Type;
import java.util.Set;
import org.gcube.common.keycloak.KeycloakClient;
import org.gcube.common.keycloak.KeycloakClientException;
import org.gcube.common.keycloak.KeycloakClientFactory;
import org.gcube.common.security.ContextBean;
import org.gcube.common.security.ContextBean.Type;
import org.gcube.common.security.providers.SecretManagerProvider;
import org.gcube.common.security.secrets.GCubeSecret;
import org.gcube.common.security.secrets.Secret;
import org.gcube.common.security.secrets.UmaTokenSecret;
import org.gcube.smartgears.Constants;
import org.gcube.smartgears.configuration.Mode;
import org.gcube.smartgears.configuration.container.ContainerConfiguration;
import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.handlers.application.RequestEvent;
import org.gcube.smartgears.handlers.application.RequestHandler;
import org.gcube.smartgears.utils.Utils;
import org.gcube.smartgears.handlers.application.ResponseEvent;
import org.gcube.smartgears.security.SimpleCredentials;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@XmlRootElement(name = Constants.request_validation)
public class RequestValidator extends RequestHandler {
@XmlAttribute(required=false, name="oauth")
@Deprecated
boolean oauthCompatibility = false;
private static Logger log = LoggerFactory.getLogger(RequestValidator.class);
private ApplicationContext context;
private static final String BEARER_AUTH_PREFIX ="Bearer";
private ApplicationContext appContext;
@Override
public String getName() {
@ -53,27 +45,31 @@ public class RequestValidator extends RequestHandler {
log.trace("executing request validator ON REQUEST");
log.trace("accessToken is null? {} \nGcubeToken is null ? {} \nscope rpvideris null? {}",
AccessTokenProvider.instance.get()==null,
SecurityTokenProvider.instance.get()==null,
ScopeProvider.instance.get()==null);
context = call.context();
appContext = call.context();
SecretManagerProvider.instance.set(getSecret(call));
validateAgainstLifecycle(call);
rejectUnauthorizedCalls(call);
if (context.container().configuration().mode()!=Mode.offline) {
if (appContext.container().configuration().mode()!=Mode.offline) {
validateScopeCall();
validatePolicy(ScopeProvider.instance.get(), call);
validatePolicy(call);
}
}
@Override
public void handleResponse(ResponseEvent e) {
log.debug("resetting all the Thread local for this call.");
SecretManagerProvider.instance.reset();
}
private void validateAgainstLifecycle(RequestEvent call) {
switch(context.lifecycle().state()) {
switch(appContext.lifecycle().state()) {
case stopped :
application_unavailable_error.fire(); break;
@ -85,36 +81,36 @@ public class RequestValidator extends RequestHandler {
//nothing to do, but avoids warnings
}
}
private void validateScopeCall() {
String scope = ScopeProvider.instance.get();
String context = SecretManagerProvider.instance.get().getContext();
if (scope == null) {
log.warn("rejecting unscoped call to {}",context.name());
if (context == null) {
log.warn("rejecting unscoped call to {}",appContext.name());
invalid_request_error.fire("call is unscoped");
}
ScopeBean bean = new ScopeBean(scope);
ContextBean bean = new ContextBean(context);
ContainerConfiguration conf = context.container().configuration();
if (!conf.allowedContexts().contains(scope) &&
!(conf.authorizeChildrenContext() && bean.is(Type.VRE) && conf.allowedContexts().contains(bean.enclosingScope().toString()) ) ) {
log.warn("rejecting call to {} in invalid context {}, allowed context are {}",context.name(),scope,context.container().configuration().allowedContexts());
invalid_request_error.fire(context.name()+" cannot be called in scope "+scope);
ContainerConfiguration conf = appContext.container().configuration();
Set<String> allowedContexts =appContext.authorizationProvider().getContexts();
if (!allowedContexts.contains(context) &&
!(conf.authorizeChildrenContext() && bean.is(Type.VRE)
&& allowedContexts.contains(bean.enclosingScope().toString()) ) ) {
log.warn("rejecting call to {} in invalid context {}, allowed context are {}",appContext.name(),context,allowedContexts);
invalid_request_error.fire(appContext.name()+" cannot be called in scope "+context);
}
}
private void rejectUnauthorizedCalls(RequestEvent call){
String token = SecurityTokenProvider.instance.get();
String accessToken = AccessTokenProvider.instance.get();
if (token == null && accessToken==null){
log.warn("rejecting call to {}, authorization required",context.name(),token);
RequestError.request_not_authorized_error.fire(context.name()+": authorization required");
Secret secret = SecretManagerProvider.instance.get();
if (secret == null){
log.warn("rejecting call to {}, authorization required",appContext.name());
RequestError.request_not_authorized_error.fire(appContext.name()+": authorization required");
}
}
@ -123,45 +119,39 @@ public class RequestValidator extends RequestHandler {
return getName();
}
private void validatePolicy(String scope, RequestEvent call){
log.info("accessing policy validator in scope {} ", scope);
private void validatePolicy(RequestEvent call){
//TODO: must be re-thought
}
ServiceIdentifier serviceIdentifier = Utils.getServiceInfo(call.context()).getServiceIdentifier();
private Secret getSecret(RequestEvent call){
String token = call.request().getParameter(token_header)==null? call.request().getHeader(token_header):call.request().getParameter(token_header);
String authHeader = call.request().getHeader(Constants.authorization_header);
String previousToken = SecurityTokenProvider.instance.get();
try {
String serviceToken = context.configuration().startTokens().stream().findFirst().get();
SecurityTokenProvider.instance.set(serviceToken);
String callerId = AuthorizationProvider.instance.get().getClient().getId();
List<Policy> policies = Collections.emptyList();
try {
policies = authorizationService().getPolicies(scope);
}catch (Exception e) {
log.error("error contacting authorization services for policies");
}
log.trace("authorization header is {}",authHeader);
log.trace("token header is {}", token);
for (Policy policy: policies) {
log.debug("policy: {}", policy.getPolicyAsString() );
if (PolicyUtils.isPolicyValidForClient(policy.getServiceAccess(), serviceIdentifier )) {
boolean toReject = false;
UserEntity entity = (((User2ServicePolicy) policy).getEntity());
if (entity.getIdentifier()!=null)
toReject = entity.getIdentifier().equals(callerId);
else if (entity.getExcludes().isEmpty())
toReject = true;
else toReject = !entity.getExcludes().contains(callerId);
if (toReject) {
log.error("rejecting call to {} : {} is not allowed to contact the service ",context.name(), callerId);
RequestError.request_not_authorized_error.fire("rejecting call to "+context.name()+" for polices: "+callerId+" is not allowed to contact the service: "+serviceIdentifier.getServiceName() );
}
}
log.info("d4s-user set to {} ", call.request().getHeader("d4s-user"));
String umaToken = null;
if (authHeader!=null && !authHeader.isEmpty())
if (authHeader.startsWith(BEARER_AUTH_PREFIX))
umaToken = authHeader.substring(BEARER_AUTH_PREFIX.length()).trim();
Secret secret = null;
if (umaToken!=null) {
secret = new UmaTokenSecret(umaToken);
SimpleCredentials credentials = (SimpleCredentials)appContext.authorizationProvider().getCredentials();
KeycloakClient client = KeycloakClientFactory.newInstance();
try {
if(!client.isAccessTokenVerified(secret.getContext(), credentials.getClientID(), credentials.getSecret(), umaToken))
RequestError.request_not_authorized_error.fire("access token verification error");
}catch (KeycloakClientException e) {
RequestError.internal_server_error.fire("error contacting keycloak client", e);
}
}finally {
SecurityTokenProvider.instance.set(previousToken);
}
} else if (token!=null && !token.isEmpty())
secret = new GCubeSecret(token);
return secret;
}

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

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

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

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

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

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

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

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

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

@ -15,7 +15,6 @@ import java.io.ObjectOutputStream;
import java.util.Collection;
import java.util.List;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContext;
@ -23,25 +22,18 @@ 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.Mode;
import org.gcube.smartgears.configuration.application.ApplicationExtensions;
import org.gcube.smartgears.configuration.application.ApplicationHandlers;
import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.extensions.ApplicationExtension;
import org.gcube.smartgears.extensions.RequestExceptionBarrier;
import org.gcube.smartgears.handlers.ProfileEvents;
import org.gcube.smartgears.handlers.application.ApplicationLifecycleEvent;
import org.gcube.smartgears.handlers.application.ApplicationLifecycleHandler;
import org.gcube.smartgears.handlers.application.ApplicationPipeline;
import org.gcube.smartgears.handlers.application.RequestHandler;
import org.gcube.smartgears.lifecycle.application.ApplicationLifecycle;
import org.gcube.smartgears.lifecycle.container.ContainerLifecycle;
import org.gcube.smartgears.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -59,6 +51,8 @@ public class ApplicationManager {
private ApplicationContext context;
private List<ApplicationExtension> extensions;
/**
* Starts application management.
*
@ -74,19 +68,9 @@ public class ApplicationManager {
for (Entry<String,? extends ServletRegistration> servlet : application.getServletRegistrations().entrySet())
log.trace("servlet {} : {} {} ", application.getServletContextName(),servlet.getKey(), servlet.getValue().getMappings());
/* if (context.configuration().secure() &&
container.configuration().securePort()==null)
throw new IllegalStateException(
String.format("Application %s cannot be managed because is declared as secure without a secure connector port declared in the container", context.application().getContextPath()));
*/
context.configuration().validate();
if (context.container().configuration().mode()!=Mode.offline) {
context.configuration().startTokens(generateTokensForApplication(container).stream().collect(Collectors.toSet()));
context.configuration().validate();
}
saveApplicationState();
// make context available to application in case it is gcube-aware
@ -96,13 +80,12 @@ public class ApplicationManager {
registerObservers();
ApplicationHandlers handlers = provider().handlersFor(context);
handlers.validate();
ApplicationExtensions extensions = provider().extensionsFor(context);
/*
extensions = provider().extensionsFor(context);
extensions.validate();
*/
List<ApplicationLifecycleHandler> lifecycleHandlers = handlers.lifecycleHandlers();
List<RequestHandler> requestHandlers = handlers.requestHandlers();
@ -111,11 +94,12 @@ public class ApplicationManager {
log.trace("managing {} requests with {}", context.name(), requestHandlers);
log.trace("extending {} API with {}", context.name(), extensions);
extensions = provider().extensionsFor(context);
// order is important here: first add APIs
register(extensions);
registerExtension(extensions);
// then intercept them all
register(requestHandlers);
registerHandlers(requestHandlers);
// start lifecycle management
start(lifecycleHandlers);
@ -143,43 +127,8 @@ public class ApplicationManager {
}
private List<String> generateTokensForApplication(ContainerContext container){
log.info("generating token for app {}",context.configuration().name());
SecurityTokenProvider.instance.set(container.configuration().startTokens().get(0));
try {
AuthorizationProxy authProxy = provider().authorizationProxy();
try {
return authProxy.generateServiceToken(Utils.getServiceInfo(context), container.configuration().startTokens());
}catch (Exception e) {
log.error("error generating service token",e);
throw new RuntimeException(e);
}
} catch (Exception e) {
throw new RuntimeException("error contacting authorization service",e);
} finally{
SecurityTokenProvider.instance.reset();
}
}
private String generateApplicationToken(String containerToken, AuthorizationProxy authProxy){
SecurityTokenProvider.instance.set(containerToken);
try {
log.info("generating token for app {} with container token {} ",context.configuration().name(), containerToken);
return authProxy.generateServiceToken(Utils.getServiceInfo(context));
} catch (Exception e) {
throw new RuntimeException("error contacting authorization service",e);
} finally{
SecurityTokenProvider.instance.reset();
}
}
private void saveApplicationState() {
File file = context.configuration().persistence().file(profile_file_path);
File file = context.persistence().file(profile_file_path);
try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file))){
oos.writeObject(context.id());
}catch (Exception e) {
@ -205,7 +154,10 @@ public class ApplicationManager {
context.lifecycle().tryMoveTo(stopped);
context.events().fire(context, ApplicationLifecycle.stop);
if (extensions != null)
unregister(extensions);
stopLifecycleHandlers();
log.info("stopping application events for {}", context.name());
@ -219,7 +171,7 @@ public class ApplicationManager {
}
private void register(List<RequestHandler> rqHandlers) {
private void registerHandlers(List<RequestHandler> rqHandlers) {
ServletContext app = context.application();
@ -246,11 +198,11 @@ public class ApplicationManager {
}
}
private void register(ApplicationExtensions extensions) {
private void registerExtension(List<ApplicationExtension> extensions) {
ServletContext application = context.application();
for (ApplicationExtension extension : extensions.extensions())
for (ApplicationExtension extension : extensions)
try {
@ -282,6 +234,14 @@ public class ApplicationManager {
}
}
private void unregister(List<ApplicationExtension> extensions) {
for (ApplicationExtension extension : extensions)
extension.stop();
}
private void start(List<ApplicationLifecycleHandler> handlers) {
@ -320,28 +280,6 @@ public class ApplicationManager {
log.warn("cannot stop {} after container has stopped", context.name());
}
@Observes(value = ContextEvents.ADD_TOKEN_TO_APPLICATION, kind = critical)
void onAddToken(String containerToken) {
log.trace("event add received with token {} ",containerToken);
String appToken = generateApplicationToken(containerToken, provider().authorizationProxy());
context.configuration().startTokens().add(appToken);
log.trace("app token created : {} ", appToken);
context.events().fire(appToken, ProfileEvents.addToContext);
context.events().fire(appToken, Constants.token_registered);
saveApplicationState();
}
@Observes(value = ContextEvents.REMOVE_TOKEN_FROM_APPLICATION, kind = critical)
void onRemoveToken(String containerToken) {
log.trace("event remove received with token {} ",containerToken);
String appToken = generateApplicationToken(containerToken, provider().authorizationProxy());
context.configuration().startTokens().remove(appToken);
log.trace("app token removed : {} ", appToken);
context.events().fire(appToken, ProfileEvents.removeFromContext);
context.events().fire(appToken, Constants.token_removed);
saveApplicationState();
}
};
context.container().events().subscribe(observer);
@ -352,7 +290,8 @@ public class ApplicationManager {
@Override
public void contextInitialized(ServletContextEvent sce) {
log.info("initilizing context {} ",context.name());
context.events().fire(context.application().getContextPath(), ApplicationLifecycle.activation);
context.events().fire(context, ApplicationLifecycle.activation);
log.info("webApp {} initialized ",context.name());
}

@ -10,19 +10,12 @@ import static org.gcube.smartgears.provider.ProviderFactory.provider;
import java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.gcube.common.authorization.client.exceptions.ObjectNotFound;
import org.gcube.common.authorization.client.proxy.AuthorizationProxy;
import org.gcube.common.authorization.library.AuthorizationEntry;
import org.gcube.common.authorization.library.provider.ClientInfo;
import org.gcube.common.authorization.library.provider.ContainerInfo;
import org.gcube.common.events.Observes;
import org.gcube.common.events.Observes.Kind;
import org.gcube.smartgears.configuration.Mode;
import org.gcube.smartgears.configuration.container.ContainerHandlers;
import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.handlers.ProfileEvents;
@ -47,8 +40,6 @@ public class ContainerManager {
public static ContainerManager instance = new ContainerManager();
private AuthorizationProxy authProvider = provider().authorizationProxy();
private ContainerContext context;
private ContainerPipeline pipeline;
@ -70,18 +61,15 @@ public class ContainerManager {
saveContainerState();
ContainerHandlers handlers = provider().containerHandlers();
List<ContainerHandler> handlers = provider().containerHandlers();
log.trace("managing container lifecycle with {}", handlers.get());
log.trace("managing container lifecycle with {}", handlers);
startHandlers(handlers.get());
startHandlers(handlers);
context.lifecycle().moveTo(active);
log.trace("loading keys for starting token ...");
//loadKeyForToken(context.configuration().startTokens());
log.trace("keys loaded for starting token ...");
return context;
}
catch(RuntimeException e) {
@ -98,10 +86,9 @@ public class ContainerManager {
private void saveContainerState() {
File file = context.configuration().persistence().file(container_profile_file_path);
File file = context.persistenceWriter().file(container_profile_file_path);
try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file))){
oos.writeObject(context.id());
oos.writeObject(context.configuration().startTokens());
}catch (Exception e) {
log.error("error serializing cointainer state");
throw new RuntimeException(e);
@ -112,59 +99,24 @@ public class ContainerManager {
private void validateContainer(ContainerContext context) {
//List<String> tokensToRemove = new ArrayList<String>();
context.configuration().validate();
Set<String> foundContexts= new HashSet<String>();
Set<String> foundContexts;
try {
List<AuthorizationEntry> entries = authProvider.get(context.configuration().startTokens());
log.info("requesting auth on {} tokens returned {} entries", context.configuration().startTokens().size(),entries.size());
for (AuthorizationEntry entry : entries ) {
log.info("the container will be started in context {}",entry.getContext());
foundContexts.add(entry.getContext());
}
foundContexts = context.authorizationProvider().getContexts();
} catch (Exception e) {
log.error("error contacting auth service on container",e);
log.error("error authorizing container",e);
throw new RuntimeException("error authorizing container, moving the container to failed",e);
}
if (foundContexts.isEmpty()){
log.error("no valid starting token are specified, moving the container to failed");
throw new RuntimeException("no valid starting token are specified");
log.error("no valid contexts found, moving the container to failed");
throw new RuntimeException("no valid contexts found, moving the container to failed");
}
//context.configuration().startTokens().removeAll(tokensToRemove);
context.configuration().allowedContexts(foundContexts);
}
private String resolveTokenForAdd(Set<String> alreadyAddedContext, String token){
try {
AuthorizationEntry entry = authProvider.get(token);
ClientInfo info = entry.getClientInfo();
log.info("resolved authorization entry for container {}",entry);
if (alreadyAddedContext.contains(entry.getContext())){
log.warn("the token {} cannot be used, another token with the same context {} found ", entry.getContext());
} else if(!entry.getContext().startsWith("/"+context.configuration().infrastructure())){
log.warn("the token {} cannot be used, is not in the infrastructure {} of the container ", token,context.configuration().infrastructure());
}else if (!(info instanceof ContainerInfo)){
log.warn("the token {} cannot be used, is not for a container token ", token);
} else if (!((ContainerInfo)info).getHost().equals(context.configuration().hostname())
|| context.configuration().port()!=((ContainerInfo)info).getPort()){
log.warn("the token {} cannot be used, the client id {} resolved with the token is not the same of the one specified in this container ", token, info.getId());
} else
return entry.getContext();
}catch(ObjectNotFound onf){
log.error("token {} not valid", token);
} catch (Exception e) {
log.error("error contacting authorization for token {}",token,e);
}
return null;
}
public void manage(ApplicationContext app) {
app.events().subscribe(this);
}
@Observes(value={ApplicationLifecycle.failure,ApplicationLifecycle.stop},kind=Kind.critical)
@ -172,40 +124,16 @@ public class ContainerManager {
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.ADD_CONTEXT_TO_CONTAINER,kind=Kind.critical)
void addContext(String scope) {
log.info("adding context {} event send", context);
context.events().fire(scope, ProfileEvents.addToContext);
}
@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");
@Observes(value=ContextEvents.REMOVE_CONTEXT_FROM_CONTAINER,kind=Kind.critical)
void removeContext(String scope) {
log.info("removing context {} event send", context);
context.events().fire(scope, ProfileEvents.removeFromContext);
}
/**

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

@ -8,6 +8,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
@ -19,8 +20,8 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.gcube.common.authorization.library.exception.AuthorizationException;
import org.gcube.smartgears.configuration.application.Exclude;
import org.gcube.smartgears.configuration.application.Include;
import org.gcube.smartgears.configuration.application.GCubeExclude;
import org.gcube.smartgears.configuration.application.GCubeInclude;
import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.context.application.DefaultApplicationContext;
import org.gcube.smartgears.handlers.application.ApplicationPipeline;
@ -136,27 +137,27 @@ public class RequestManager implements Filter {
log.debug("check wich handler should be excluded {}", path);
if (!context.configuration().excludes().isEmpty()) {
for (Exclude exclude : context.configuration().excludes()){
for (GCubeExclude exclude : context.configuration().excludes()){
String excludePath= exclude.getPath();
log.trace("exclude is {}",exclude);
if (
(WILDCARD).equals(excludePath) ||
if ((WILDCARD).equals(excludePath) ||
(excludePath.endsWith(WILDCARD) && path!=null && path.startsWith(excludePath.substring(0,excludePath.length()-2))) ||
excludePath.equals(path) || (path.endsWith("/") && excludePath.equals(path.substring(0, path.length()-1)))
){
//ALL handler are filtered
if (exclude.getHandlers().isEmpty()) return Collections.emptyList();
//ALL handler are filtered except for unfiltrable
if (exclude.getHandlers().isEmpty()) return handlersToFilter.stream()
.filter(RequestHandler::isUnfiltrable).collect(Collectors.toList());
List<RequestHandler> filteredHandlers = new ArrayList<>();
for (RequestHandler rh : handlersToFilter){
if (!exclude.getHandlers().contains(rh.getName()))
for (RequestHandler rh : handlersToFilter)
if (rh.isUnfiltrable() || !exclude.getHandlers().contains(rh.getName()))
filteredHandlers.add(rh);
}
return filteredHandlers;
}
}
} else if (!context.configuration().includes().isEmpty()) {
for (Include include : context.configuration().includes()){
for (GCubeInclude include : context.configuration().includes()){
String includePath= include.getPath();
log.trace("include is {}",include);
if (
@ -168,10 +169,10 @@ public class RequestManager implements Filter {
if (include.getHandlers().isEmpty()) return handlersToFilter;
List<RequestHandler> filteredHandlers = new ArrayList<>();
for (RequestHandler rh : handlersToFilter){
if (include.getHandlers().contains(rh.getName()))
for (RequestHandler rh : handlersToFilter)
if (rh.isUnfiltrable() || include.getHandlers().contains(rh.getName()))
filteredHandlers.add(rh);
}
return filteredHandlers;
}
}
@ -213,37 +214,6 @@ public class RequestManager implements Filter {
}
// helpers
/*
private boolean shouldExcludeRequest(HttpServletRequest request) {
String query = request.getQueryString();
log.debug("servletPath is {} and pathInfo is {}",request.getServletPath(), request.getPathInfo());
if ("wsdl".equals(query) || "wsdl=1".equals(query))
return true;
String path = request.getServletPath()==null?"":request.getServletPath();
path += request.getPathInfo() ==null?"":request.getPathInfo();
log.debug("check if should exclude call with path {}", path);
for (Exclude exclude : context.configuration().excludes()){
if (!exclude.getHandlers().isEmpty()) continue;
String excludePath= exclude.getPath();
log.trace("exclude is {}",exclude);
if (
(EXCLUDE_ALL).equals(exclude) ||
(excludePath.endsWith(EXCLUDE_ALL) && path!=null && path.startsWith(excludePath.substring(0,excludePath.length()-2))) ||
excludePath.equals(path) || (path.endsWith("/") && excludePath.equals(path.substring(0, path.length()-1)))
)
return true;
}
return false;
}*/
private void handleError(HttpServletRequest request, HttpServletResponse response,Throwable t) throws IOException {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -1,6 +1,5 @@
package org.gcube.smartgears.utils;
import org.gcube.common.authorization.library.provider.CalledMethodProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -8,7 +7,7 @@ public class InnerMethodName {
public static InnerMethodName instance = new InnerMethodName();
private static Logger logger = LoggerFactory.getLogger(CalledMethodProvider.class);
private static Logger logger = LoggerFactory.getLogger(InnerMethodName.class);
// Thread local variable containing each thread's ID
private static final InheritableThreadLocal<String> threadMethod =

@ -214,7 +214,7 @@ public class Utils {
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));
new ServiceInfo(new ServiceIdentifier(application.configuration().group(), application.configuration().name(), hostedin));
}
public static Throwable getCause(Throwable e) {

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<handlers>
<accounting-management />
<profile-management />
</handlers>

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<extensions>
<remote-management/>
</extensions>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<handlers>
<lifecycle>
<profile-management />
</lifecycle>
<request>
<context-retriever />
<request-validation />
<request-accounting />
</request>
</handlers>

@ -104,7 +104,7 @@
</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>a resource of the <em>${infra}</em> infrastructure</p>
<p style="padding-top:30px">The resource is <em>${status}</em>.</p>
</div>

@ -1,4 +0,0 @@
org.gcube.smartgears.handlers.application.lifecycle.ProfileManager
org.gcube.smartgears.handlers.application.request.RequestValidator
org.gcube.smartgears.handlers.application.request.RequestAccounting
org.gcube.smartgears.handlers.application.request.RequestContextRetriever

@ -1,2 +0,0 @@
org.gcube.smartgears.handlers.container.lifecycle.ProfileManager
org.gcube.smartgears.handlers.container.lifecycle.AccountingManager

@ -1,179 +0,0 @@
package app;
import static com.sun.jersey.api.client.Client.create;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.gcube.smartgears.Constants.scope_header;
import static org.gcube.smartgears.extensions.HttpExtension.Method.DELETE;
import static org.gcube.smartgears.extensions.HttpExtension.Method.GET;
import static utils.TestUtils.context_root;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.CountDownLatch;
import org.gcube.common.authorization.library.provider.AuthorizationProvider;
import org.gcube.common.authorization.library.provider.UserInfo;
import org.gcube.common.authorization.library.utils.Caller;
import org.gcube.smartgears.extensions.HttpExtension.Method;
import utils.TestUtils;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.UniformInterfaceException;
import com.sun.jersey.api.client.WebResource.Builder;
import com.sun.jersey.api.client.filter.LoggingFilter;
import com.sun.jersey.core.header.OutBoundHeaders;
public class Request {
private String path="";
private String scope = TestUtils.scope;
private OutBoundHeaders headers = new OutBoundHeaders();
private Method method = GET;
private String body = null;
private boolean logged = false;
public static Request request() {
return new Request();
}
private Request() {
}
public Request at(String path) {
this.path=path;
return this;
}
public Request logging() {
logged=true;
return this;
}
public Request inScope(String scope) {
this.scope=scope;
return this;
}
public Request with(String body) {
this.body=body;
return this;
}
public Request with(String name, String value) {
this.headers.add(name, value);
return this;
}
public Request using(Method method) {
this.method=method;
return this;
}
public String path() {
return path;
}
public String body() {
return body;
}
public Method method() {
return method;
}
public String scope() {
return scope;
}
ClientResponse make(final int port) {
// we make a scoped call in a separate thread, with which we then synchronize for completion.
// this helps isolate the caller's thread (Main normally) from the app's thread,
// starting with the scope itself.
final CountDownLatch latch = new CountDownLatch(1);
class Box {
volatile UniformInterfaceException failure;
volatile ClientResponse response;
}
final Box box = new Box();
new Thread() {
public void run() {
AuthorizationProvider.instance.set(new Caller(new UserInfo("test", new ArrayList<String>()),"DEFAULT"));
try {
Client client = create();
if (logged)
client.addFilter(new LoggingFilter(System.err));
Builder builder = client.resource(address(path,port))
.entity(body).header(scope_header, scope);
for (Entry<String,List<Object>> header : headers.entrySet())
for (Object value : header.getValue())
builder.header(header.getKey(), value);
if (method==DELETE)
builder.delete();
else {
System.err.println("making request @ "+address(path,port));
ClientResponse response = builder.method(method.name(),ClientResponse.class);
//throws an exception if there response has error status
if (response.getStatus()>300)
throw new UniformInterfaceException(response);
box.response=response;
}
} catch (UniformInterfaceException t) {
box.failure=t;
}
latch.countDown();
};
}.start();
try {
if (!latch.await(2000, MILLISECONDS))
throw new RuntimeException("application has not responded in time");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (box.failure !=null)
throw box.failure;
else
return box.response;
}
private String address(String path, long port) {
path = (path.isEmpty() || path.startsWith("/"))?path:"/"+path;
return "http://localhost:" + port+ "/" + context_root+path;
}
}

@ -1,449 +0,0 @@
package app;
import static org.gcube.smartgears.Constants.configuration_file_path;
import static org.gcube.smartgears.Constants.extensions_file_path;
import static org.gcube.smartgears.Constants.ghn_home_property;
import static org.gcube.smartgears.Constants.handlers_file_path;
import static utils.TestUtils.context_root;
import static utils.TestUtils.context_root_path;
import static utils.TestUtils.location;
import static utils.TestUtils.servlet_name;
import java.io.File;
import org.apache.catalina.Wrapper;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Tomcat;
import org.apache.commons.io.FileUtils;
import org.apache.tomcat.util.scan.StandardJarScanner;
import org.gcube.informationsystem.publisher.ScopedPublisher;
import org.gcube.smartgears.Constants;
import org.gcube.smartgears.configuration.Mode;
import org.gcube.smartgears.configuration.application.ApplicationConfiguration;
import org.gcube.smartgears.configuration.application.ApplicationExtensions;
import org.gcube.smartgears.configuration.application.ApplicationHandlers;
import org.gcube.smartgears.configuration.application.DefaultApplicationConfiguration;
import org.gcube.smartgears.configuration.container.ContainerConfiguration;
import org.gcube.smartgears.configuration.container.Site;
import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.managers.ContainerManager;
import org.gcube.smartgears.provider.ProviderFactory;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.exporter.ZipExporter;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.jboss.shrinkwrap.impl.base.path.BasicPath;
import com.sun.jersey.api.client.ClientResponse;
import utils.TestProvider;
import utils.TestUtils;
/**
* Simulates a single-servlet application to be transformed into a gCube resource.
* <p>
* <ul>
* <li>uses a default configuration that can be customised (cf. {@link #configuration()});
* <li>can be configured with the default handlers (cf. {@link #useDefaultHandlers()}) or custom ones (
* {@link #handlers()}), including those that are not deployable through standard means, such as mocks (
* {@link #bypassHandlerDeployment()};
* <li>can be started to have a default behaviour when called, ({@link #start()}) or else a custom behaviour (
* {@link #startWith(Runnable)};
* </ul>
* can be called in a default scope ({@link #call()} or in a specific scope ({@link #callIn(String)}). Calls are blocking but always
* configured and executed in a separate thread;
*
* @author Fabio Simeoni
*
*/
public class SomeApp {
private final Tomcat tomcat = new Tomcat();
private WebArchive war = defaultWar();
private ContainerConfiguration containerConfiguration;
private ApplicationConfiguration configuration;
private ApplicationHandlers handlers = new ApplicationHandlers();
private ApplicationExtensions extensions = new ApplicationExtensions();
private TestProvider provider = new TestProvider();
private boolean deployHandlers = true;
private boolean deployExtensions = true;
private boolean deployConfiguration = true;
private boolean clean=true;
public SomeApp() {
if (ContainerManager.instance!=null)
ContainerManager.instance.stop(true);
tomcat.getConnector().setPort(0);
tomcat.setBaseDir(location);
System.setProperty(ghn_home_property,location);
containerConfiguration = defaultContainerConfiguration();
configuration = defaultConfiguration();
}
/**
* Sets a {@link TestProvider} to resolve dependencies at runtime.
*
* @param provider the provider
*/
public void set(TestProvider provider) {
this.provider = provider;
}
/**
* Runs the test with the state left by previous run.
* <p>
* Use only within a single test!
*/
public void dirtyRun() {
this.clean = false;
}
/**
* Returns the configuration of the application.
* <p>
* The initial configuration is based on defaults, but can be changed and extended.
*
* @return the configuration
*/
public ApplicationConfiguration configuration() {
return configuration;
}
/**
* Returns the configuration of the containerConfiguration.
* <p>
* The initial configuration is based on defaults, but can be changed and extended.
*
* @return the configuration
*/
public ContainerConfiguration containerConfiguration() {
return containerConfiguration;
}
/**
* Returns the handlers that manage the application, none by default.
*
* @return the handlers
*/
public ApplicationHandlers handlers() {
return handlers;
}
/**
* Avoids deployment of the configured handlers in the application's WAR.
* <p>
* The handlers will instead be directly available at runtime.
*/
public void bypassHandlerDeployment() {
provider.use(handlers());
deployHandlers = false;
}
/**
* Avoids resource configuration deployment.
*/
public void asExternal() {
configuration.context(context_root_path);
containerConfiguration.app(configuration);
bypassConfigurationDeployment();
bypassExtensionsDeployment();
bypassHandlerDeployment();
}
/**
* Avoids resource configuration deployment.
*/
public void withExternal(ApplicationConfiguration config) {
config.context(context_root_path);
containerConfiguration.app(config.context()).merge(config);
}
public void bypassConfigurationDeployment() {
deployConfiguration = false;
}
public void usePublisher(ScopedPublisher publisher) {
provider.use(publisher);
}
/**
* Uses default handlers.
*/
public void useDefaultHandlers() {
deployHandlers = false;
}
/**
* Returns the extensions of the application, none by default.
*
* @return the handlers
*/
public ApplicationExtensions extensions() {
return extensions;
}
/**
* Uses default extensions.
*/
public void useDefaultExtensions() {
deployExtensions = false;
}
/**
* Avoids deployment of the configured extensions in the application's WAR.
* <p>
* The extensions will instead be directly available at runtime.
*/
public void bypassExtensionsDeployment() {
provider.use(extensions());
deployExtensions = false;
}
/**
* Starts the application.
*
* @return the context of the application
*/
public ApplicationContext start() {
return startWith(new Runnable() {
@Override
public void run() {
System.err.println("test servlet invoked with no particular task");
}
});
}
/**
* Starts the application, injecting test logic in its single servlet
*
* @param test test logic
* @return the context of the application
*/
public ApplicationContext startWith(Runnable test) {
// install provider
ProviderFactory.testProvider(provider);
if (clean)
cleanupInstallation();
installContainerConfiguration();
if (deployConfiguration)
deployConfiguration();
if (deployHandlers)
deployHandlers();
if (deployExtensions)
deployExtensions();
try {
System.err.println("deploying with " + war.toString(true));
StandardContext ctx = (StandardContext) tomcat.addWebapp(context_root_path, warFile().getAbsolutePath());
// tells tomcat to look also for exploded directories such as this project's (finds initializer)
((StandardJarScanner) ctx.getJarScanner()).setScanAllDirectories(true);
//this starts webapp and always comes back
tomcat.start();
ApplicationContext context = provider.context;
if (context==null)
throw new RuntimeException("app failed @ startup");
Wrapper webapp = (Wrapper) tomcat.getHost().findChild(context_root_path).findChild(servlet_name);
if (webapp!=null) {
webapp.setServlet(new TestServlet(test));
context.container().configuration().port(port());
containerConfiguration = context.container().configuration();
}
return context;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Retuens <code>true</code> if the application was successfully start in the container.
*
* @return <code>true</code> if the application was successfully start in the container
*/
public boolean isActive() {
return tomcat.getHost().findChild("/" + context_root).findChild(servlet_name) != null;
}
/**
* Makes a request to the application.
*/
public String send(Request call) {
return call.make(port()).getEntity(String.class);
}
/**
* Makes a request to the application.
*/
public ClientResponse httpSend(Request call) {
return call.make(port());
}
/**
* Stops the application
*/
public void stop() {
try {
tomcat.stop();
tomcat.destroy();
} catch (Exception e) {
System.err.println("WARNING: could not clearly stop container:");
e.printStackTrace();
}
}
public File containerConfigurationFile() {
return new File(location,Constants.container_configuraton_file_path);
}
// helpers
/**
* Installs the container configuration.
*/
private void installContainerConfiguration() {
TestUtils.serialise(containerConfiguration(),containerConfigurationFile());
}
/**
* Includes the configuration in the application's WAR.
*/
private void deployConfiguration() {
String xml = TestUtils.bind(configuration());
System.err.println("deploying with configuration:\n" + xml);
war.addAsWebResource(new StringAsset(xml), new BasicPath(configuration_file_path));
}
/**
* Includes the handlers in the application's WAR.
*/
private void deployHandlers() {
String xml = TestUtils.bind(handlers());
System.err.println("deploying with handlers:\n" + xml);
war.addAsWebResource(new StringAsset(xml), new BasicPath(handlers_file_path));
}
/**
* Includes the extensions in the application's WAR.
*/
private void deployExtensions() {
String xml = TestUtils.bind(extensions());
System.err.println("deploying with extensions:\n" + xml);
war.addAsWebResource(new StringAsset(xml), new BasicPath(extensions_file_path));
}
private File warFile() {
File warFile = new File(location, "test.war");
if (warFile.exists() && !warFile.delete())
System.out.println("could not delete old deployment");; // seems safer than plain overwrite to avoid war corruption
war.as(ZipExporter.class).exportTo(warFile, true);
return warFile;
}
private WebArchive defaultWar() {
WebArchive war = ShrinkWrap.create(WebArchive.class);
war.setWebXML(new File("src/test/java/app/web.xml"));
return war;
}
private ApplicationConfiguration defaultConfiguration() {
return new DefaultApplicationConfiguration().mode(Mode.offline).serviceClass("test-class").name("test-app").version("1.0");
}
private ContainerConfiguration defaultContainerConfiguration() {
return new ContainerConfiguration().mode(Mode.offline).hostname("localhost").port(port()).infrastructure("gcube")
.site(new Site().country("it").location("rome").latitude("41.9000").longitude("12.5000"))
.property("test-prop1","foo")
.property("test-prop2","bar")
.publicationFrequency(5);
}
public int port() {
return tomcat.getConnector().getLocalPort();
}
private void cleanupInstallation() {
System.out.println("cleaning installation in location "+location);
File installation = new File(location);
if (installation.exists())
try {
FileUtils.deleteDirectory(installation);
}
catch(Exception e) {
throw new RuntimeException(e);
}
installation.mkdirs();
}
}

@ -1,30 +0,0 @@
package app;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(name = "test", urlPatterns = "/test")
public class TestServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private Runnable test;
public TestServlet(Runnable test) {
this.test=test;
}
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
test.run();
}
}

@ -1,15 +0,0 @@
<web-app>
<display-name>test-app</display-name>
<servlet>
<servlet-name>test</servlet-name>
<servlet-class>app.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>test</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>

@ -0,0 +1,20 @@
package test;
import java.io.InputStream;
import org.gcube.smartgears.configuration.container.ContainerConfiguration;
import org.gcube.smartgears.configuration.container.ContainerConfigurationBinder;
import org.junit.Test;
public class BinderTest {
@Test
public void bindConfig() throws Exception {
InputStream stream = BinderTest.class.getResourceAsStream("/container.ini");
ContainerConfiguration conf = new ContainerConfigurationBinder().load(stream);
System.out.println(conf.toString());
}
}

@ -1,37 +0,0 @@
package test;
import static junit.framework.Assert.*;
import java.io.ByteArrayInputStream;
import org.gcube.smartgears.configuration.library.SmartGearsConfiguration;
import org.gcube.smartgears.configuration.library.SmartGearsConfigurationBinder;
import org.junit.Test;
public class SmartgearsConfigurationTest {
@Test
public void configurationBinds() throws Exception {
String xml = "<smartgears version='1.0.0-SNAPSHOT'/>";
SmartGearsConfigurationBinder binder = new SmartGearsConfigurationBinder();
SmartGearsConfiguration bound = binder.bind(new ByteArrayInputStream(xml.getBytes()));
bound.validate();
String version = bound.version();
assertEquals("1.0.0-SNAPSHOT",version);
assertEquals(sampleSmartgearsConfiguration(),bound);
}
private SmartGearsConfiguration sampleSmartgearsConfiguration() {
return new SmartGearsConfiguration().version("1.0.0-SNAPSHOT");
}
}

@ -1,36 +0,0 @@
package test.application;
import static org.junit.Assert.*;
import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.handlers.application.lifecycle.ProfileManager;
import org.gcube.smartgears.lifecycle.application.ApplicationState;
import org.gcube.smartgears.lifecycle.container.ContainerState;
import org.junit.Test;
import app.SomeApp;
public class AppLifecycleTest {
@Test
public void applicationGoesDownIfContainerDoes() {
SomeApp app = new SomeApp();
app.handlers().set(new ProfileManager());
ApplicationContext actx = app.start();
assertEquals(ApplicationState.active,actx.lifecycle().state());
ContainerContext ctx = actx.container();
assertEquals(ContainerState.active,ctx.lifecycle().state());
ctx.lifecycle().moveTo(ContainerState.stopped);
assertEquals(ApplicationState.stopped,actx.lifecycle().state());
}
}

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

Loading…
Cancel
Save