4207: Uri Resolver upgrade: it must support new Geonetwork Manager

Task-Url: https://support.d4science.org/issues/4207

Updated classes in order to user hte new Geonetwork Manager 
Updated pom version at 1.7.0
Updated Geonetwork Manager ranges into pom


git-svn-id: http://svn.research-infrastructures.eu/public/d4science/gcube/trunk/data-transfer/uri-resolver@129029 82a268e6-3cf1-43bd-a215-b396298e98cf
feature/18038
Francesco Mangiacrapa 8 years ago
parent f134ef87da
commit 12dfd53d86

@ -42,4 +42,9 @@
<Change>[Feature #4000] URI Resolver - must support HEAD request
</Change>
</Changeset>
<Changeset component="org.gcube.data-transfer.uri-resolver.1-7-0"
date="2016-06-09">
<Change>[Feature #4207] ri Resolver upgrade: it must support new Geonetwork Manager
</Change>
</Changeset>
</ReleaseNotes>

@ -8,7 +8,7 @@
</parent>
<groupId>org.gcube.data.transfer</groupId>
<artifactId>uri-resolver</artifactId>
<version>1.6.0-SNAPSHOT</version>
<version>1.7.0-SNAPSHOT</version>
<packaging>war</packaging>
<description>The URI Resolver is an HTTP URI resolver implemented as an HTTP servlet which gives access trough HTTP to different protocols URIs. </description>
@ -64,7 +64,7 @@
<dependency>
<groupId>org.gcube.spatial.data</groupId>
<artifactId>geonetwork</artifactId>
<version>[2.0.0-SNAPSHOT, 3.0.0-SNAPSHOT)</version>
<version>[3.0.0-SNAPSHOT, 4.0.0-SNAPSHOT)</version>
<scope>compile</scope>
</dependency>
@ -97,11 +97,6 @@
<!-- END GIS RESOLVER DEPENDENCIES -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>

@ -1,61 +0,0 @@
package org.gcube.datatransfer.resolver.gis;
import org.gcube.datatransfer.resolver.gis.entity.ServerParameters;
import org.gcube.datatransfer.resolver.gis.exception.GeonetworkInstanceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author Francesco Mangiacrapa francesco.mangiacrapa@isti.cnr.it
* @Apr 29, 2013
*
*/
public class GeonetowrkAccessParameter implements GeonetworkServiceInterface{
protected static Logger logger = LoggerFactory.getLogger(GeonetowrkAccessParameter.class);
protected GeonetworkInstance geonetworkInstance;
protected String scope;
protected ServerParameters serverParam;
/**
* @param serverParam
*/
public GeonetowrkAccessParameter(String scope, ServerParameters serverParam) {
this.scope = scope;
this.serverParam = serverParam;
}
/**
* @param geonetworkInstance
* @return
*/
public GeonetworkInstance getGeonetworkInstance(boolean authenticate) throws GeonetworkInstanceException {
return instanceGeonetwork(authenticate);
}
private GeonetworkInstance instanceGeonetwork(boolean authenticate) throws GeonetworkInstanceException{
if(scope == null || scope.isEmpty())
throw new GeonetworkInstanceException("Scope is null");
if(serverParam.getUrl() == null || serverParam.getUrl().isEmpty())
throw new GeonetworkInstanceException("Geonetwork url is null or empty");
if(geonetworkInstance==null)
geonetworkInstance = new GeonetworkInstance(scope, serverParam.getUrl(), serverParam.getUser(), serverParam.getPassword(), authenticate);
return geonetworkInstance;
}
public String getScope() {
return scope;
}
}

@ -0,0 +1,135 @@
package org.gcube.datatransfer.resolver.gis;
import org.gcube.datatransfer.resolver.gis.entity.ServerParameters;
import org.gcube.datatransfer.resolver.gis.exception.GeonetworkInstanceException;
import org.gcube.spatial.data.geonetwork.LoginLevel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The Class GeonetworkAccessParameter.
*
* @author Francesco Mangiacrapa francesco.mangiacrapa@isti.cnr.it
* Jun 9, 2016
*/
public class GeonetworkAccessParameter implements GeonetworkServiceInterface{
protected static Logger logger = LoggerFactory.getLogger(GeonetworkAccessParameter.class);
protected GeonetworkInstance geonetworkInstance;
protected String scope;
protected ServerParameters serverParam;
/**
* The Enum GeonetworkLoginLevel.
*
* @author Francesco Mangiacrapa francesco.mangiacrapa@isti.cnr.it
* Jun 9, 2016
*/
public static enum GeonetworkLoginLevel {
DEFAULT,
SCOPE,
PRIVATE,
CKAN,
ADMIN
}
/**
* Instantiates a new geonetowrk access parameter.
*
* @param scope the scope
* @param serverParam the server param
*/
public GeonetworkAccessParameter(String scope, ServerParameters serverParam) {
this.scope = scope;
this.serverParam = serverParam;
}
/* (non-Javadoc)
* @see org.gcube.datatransfer.resolver.gis.GeonetworkServiceInterface#getGeonetworkInstance(boolean, org.gcube.datatransfer.resolver.gis.GeonetowrkAccessParameter.GeonetworkLoginLevel)
*/
public GeonetworkInstance getGeonetworkInstance(boolean authenticate, GeonetworkLoginLevel loginLevel) throws GeonetworkInstanceException {
return instanceGeonetwork(authenticate, loginLevel);
}
/**
* Instance geonetwork.
*
* @param authenticate the authenticate
* @param loginLevel the login level
* @return the geonetwork instance
* @throws GeonetworkInstanceException the geonetwork instance exception
*/
private GeonetworkInstance instanceGeonetwork(boolean authenticate, GeonetworkLoginLevel loginLevel) throws GeonetworkInstanceException{
if(scope == null || scope.isEmpty())
throw new GeonetworkInstanceException("Scope is null");
if(serverParam.getUrl() == null || serverParam.getUrl().isEmpty())
throw new GeonetworkInstanceException("Geonetwork url is null or empty");
LoginLevel level = toLoginLevel(loginLevel);
if(geonetworkInstance==null)
geonetworkInstance = new GeonetworkInstance(scope, authenticate, level);
return geonetworkInstance;
}
/* (non-Javadoc)
* @see org.gcube.datatransfer.resolver.gis.GeonetworkServiceInterface#getGeonetworkInstance()
*/
@Override
public GeonetworkInstance getGeonetworkInstance()
throws Exception {
if(scope == null || scope.isEmpty())
throw new GeonetworkInstanceException("Scope is null");
if(serverParam.getUrl() == null || serverParam.getUrl().isEmpty())
throw new GeonetworkInstanceException("Geonetwork url is null or empty");
if(geonetworkInstance==null)
geonetworkInstance = new GeonetworkInstance(scope, false, null);
return geonetworkInstance;
}
/* (non-Javadoc)
* @see org.gcube.datatransfer.resolver.gis.GeonetworkServiceInterface#getScope()
*/
public String getScope() {
return scope;
}
/**
* To login level.
*
* @param loginLevel the login level
* @return the login level
*/
public static final LoginLevel toLoginLevel(GeonetworkLoginLevel loginLevel){
switch (loginLevel) {
case ADMIN:
return LoginLevel.ADMIN;
case CKAN:
return LoginLevel.CKAN;
case DEFAULT:
return LoginLevel.DEFAULT;
case PRIVATE:
return LoginLevel.PRIVATE;
case SCOPE:
return LoginLevel.SCOPE;
}
return null;
}
}

@ -1,200 +1,164 @@
package org.gcube.datatransfer.resolver.gis;
import java.util.HashMap;
import java.util.Map;
import org.gcube.common.scope.api.ScopeProvider;
import org.gcube.datatransfer.resolver.gis.exception.GeonetworkInstanceException;
import org.gcube.spatial.data.geonetwork.GeoNetwork;
import org.gcube.spatial.data.geonetwork.GeoNetworkPublisher;
import org.gcube.spatial.data.geonetwork.LoginLevel;
import org.gcube.spatial.data.geonetwork.configuration.AuthorizationException;
import org.gcube.spatial.data.geonetwork.configuration.Configuration;
import org.gcube.spatial.data.geonetwork.model.faults.AuthorizationException;
import org.gcube.spatial.data.geonetwork.model.faults.MissingConfigurationException;
import org.gcube.spatial.data.geonetwork.model.faults.MissingServiceEndpointException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author Francesco Mangiacrapa francesco.mangiacrapa@isti.cnr.it
* @Oct 7, 2014
* The Class GeonetworkInstance.
*
* @author Francesco Mangiacrapa francesco.mangiacrapa@isti.cnr.it
* Jun 9, 2016
*/
public class GeonetworkInstance {
private GeoNetworkPublisher geonetworkPublisher = null;
private String geoNetworkUrl;
private String geoNetworkUser = "";
private String geoNetworkPwd = "";
private String scope = "noscope";
/**
* Instantiates a new geonetwork instance.
*/
public GeonetworkInstance(){} //FOR SERIALIZATION
private Logger logger = LoggerFactory.getLogger(GeonetworkInstance.class);
private String scope;
/**
*
* @param scope
* Instantiates a new geonetwork instance.
*
* @param scope the scope
* @throws GeonetworkInstanceException the geonetwork instance exception
*/
public GeonetworkInstance(String scope){
this.scope = scope;
public GeonetworkInstance(String scope) throws GeonetworkInstanceException{
this(scope, false, null);
}
/**
* Creates a new configuration for Gis publisher with parameter passed in input. Executes the login on geonetwork instance if authenticate param is true, no otherwise
* Use scope found in ScopeProvider
* @param authenticate the authenticate
* @param level the level
* @throws Exception the exception
*/
public GeonetworkInstance(boolean authenticate, LoginLevel level) throws Exception {
try {
createInstanceGeonetworkPublisher(authenticate, level);
} catch (Exception e) {
logger.error("Sorry, an error occurred in getting geonetwork instance",e);
throw new Exception("Sorry, an error occurred in getting geonetwork instance",e);
}
}
/**
* Creates a new configuration for Gis publisher with parameters passed in input. By default executes the login as user passed in input on geonetwork instance
* @param scope
* @param geoNetworkUrl
* @param user
* @param pwd
* @param geoserverUrl
* @param geoserverUser
* @param geoserverPwd
* @param httpSession
* @throws Exception
* Instantiates a new geonetwork instance.
* set the scope provider to input scope
* @param scope the scope
* @param authenticate the authenticate
* @param level the level
* @throws GeonetworkInstanceException the geonetwork instance exception
*/
public GeonetworkInstance(String scope, String geoNetworkUrl, String user, String pwd, boolean authenticate) throws GeonetworkInstanceException {
public GeonetworkInstance(String scope, boolean authenticate, LoginLevel level) throws GeonetworkInstanceException {
logger.trace("Instancing GeonetworkInstance with specific parameters");
this.geoNetworkUser = user;
this.geoNetworkPwd = pwd;
this.geoNetworkUrl = geoNetworkUrl;
this.scope = scope;
try {
createInstance(authenticate);
ScopeProvider.instance.set(scope);
logger.info("setting scope "+scope);
createInstanceGeonetworkPublisher(authenticate, level);
} catch (Exception e) {
String message = "Sorry, an error occurred in instancing geonetwork";
logger.warn(message, e);
throw new GeonetworkInstanceException(message);
}finally{
logger.info("resetting scope");
ScopeProvider.instance.reset();
}
}
/**
*
* @param authenticate
* @throws Exception
* Creates the instance geonetwork publisher.
*
* @param authenticate the authenticate
* @param level the level
* @throws Exception the exception
*/
private void createInstance(boolean authenticate) throws GeonetworkInstanceException {
this.geonetworkPublisher = GeoNetwork.get(new GeonetworkConfiguration());
authenticateOnGeoenetwork(authenticate);
private void createInstanceGeonetworkPublisher(boolean authenticate, LoginLevel level) throws Exception {
this.geonetworkPublisher = GeoNetwork.get();
if(authenticate && level!=null)
authenticateOnGeoenetwork(level);
}
/**
*
* @param authenticate
* @throws Exception
* Authenticate on geoenetwork.
*
* @param level the level
* @throws GeonetworkInstanceException the geonetwork instance exception
*/
public void authenticateOnGeoenetwork(boolean authenticate) throws GeonetworkInstanceException {
private void authenticateOnGeoenetwork(LoginLevel level) throws GeonetworkInstanceException {
logger.trace("authenticating.. geonetworkPublisher is null? "+(this.geonetworkPublisher==null));
if(geonetworkPublisher==null){
logger.trace("skipping authentication.. please createInstace");
return;
}
if (authenticate){
try {
try {
this.geonetworkPublisher.login(LoginLevel.DEFAULT);
} catch (AuthorizationException e) {
logger.error("authentication on geonetwork failed ",e);
this.geonetworkPublisher.login(level);
}
catch (MissingConfigurationException | MissingServiceEndpointException e) {
logger.error("MissingConfigurationException | MissingServiceEndpointException exception ", e);
throw new GeonetworkInstanceException("Geonetwork authentication failed");
}
logger.trace("authentication on geonetwork completed");
} catch (AuthorizationException e) {
logger.error("AuthorizationException ",e);
throw new GeonetworkInstanceException("Geonetwork authentication failed");
}
}
logger.trace("authentication on geonetwork completed");
/**
* Creates a new configuration for Gis publisher with parameter passed in input. Executes the login on geonetwork instance if authenticate param is true, no otherwise
*
* @param geoNetworkUrl
* @param user
* @param pwd
* @throws Exception
*/
public GeonetworkInstance(String geoNetworkUrl, String user, String pwd, boolean authenticate) throws Exception {
try {
this.geoNetworkUrl = geoNetworkUrl;
this.geoNetworkUser = user;
this.geoNetworkPwd = pwd;
createInstance(authenticate);
} catch (Exception e) {
logger.error("Sorry, an error occurred in getting geonetwork instance",e);
throw new Exception("Sorry, an error occurred in getting geonetwork instance",e);
}
}
public class GeonetworkConfiguration implements Configuration {
public Map<LoginLevel,String> pwds=new HashMap<LoginLevel, String>();
public Map<LoginLevel,String> usrs=new HashMap<LoginLevel, String>();
@Override
public String getGeoNetworkEndpoint() {
logger.trace("geoNetworkUrl is: "+geoNetworkUrl);
return geoNetworkUrl;
}
@Override
public Map<LoginLevel, String> getGeoNetworkPasswords() {
pwds.put(LoginLevel.DEFAULT, geoNetworkPwd);
logger.trace("geoNetworkPwd is: "+geoNetworkPwd);
return pwds;
}
@Override
public Map<LoginLevel, String> getGeoNetworkUsers() {
usrs.put(LoginLevel.DEFAULT, geoNetworkUser);
logger.trace("geoNetworkUser is: "+geoNetworkUser);
return usrs;
}
@Override
public int getScopeGroup() {
return 2;
}
}
/**
* Gets the geonetwork publisher.
*
* @return the geonetwork publisher
*/
public GeoNetworkPublisher getGeonetworkPublisher() {
return geonetworkPublisher;
}
public String getGeoNetworkUrl() {
return geoNetworkUrl;
}
public String getGeoNetworkUser() {
return geoNetworkUser;
}
public String getGeoNetworkPwd() {
return geoNetworkPwd;
}
/**
* Gets the scope.
*
* @return the scope
*/
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
return scope;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("GeonetworkInstance [geonetworkPublisher=");
builder.append(geonetworkPublisher);
builder.append(", geoNetworkUrl=");
builder.append(geoNetworkUrl);
builder.append(", geoNetworkUser=");
builder.append(geoNetworkUser);
builder.append(", geoNetworkPwd=");
builder.append(geoNetworkPwd);
builder.append(", scope=");
builder.append(scope);
builder.append(", logger=");
builder.append(logger);
builder.append("]");
return builder.toString();
}

@ -3,6 +3,8 @@
*/
package org.gcube.datatransfer.resolver.gis;
import org.gcube.datatransfer.resolver.gis.GeonetworkAccessParameter.GeonetworkLoginLevel;
/**
* The Interface GeonetworkServiceInterface.
*
@ -15,10 +17,21 @@ public interface GeonetworkServiceInterface {
* Gets the geonetwork instance.
*
* @param authenticate the authenticate
* @param loginLevel the login level
* @return the geonetwork instance
* @throws Exception the exception
*/
public GeonetworkInstance getGeonetworkInstance(boolean authenticate, GeonetworkLoginLevel loginLevel) throws Exception;
/**
* Gets the geonetwork instance.
*
* @return the geonetwork instance
* @throws Exception the exception
*/
public GeonetworkInstance getGeonetworkInstance(boolean authenticate) throws Exception;
public GeonetworkInstance getGeonetworkInstance() throws Exception;
/**
* Gets the scope.

@ -21,6 +21,7 @@ import org.apache.commons.io.IOUtils;
import org.gcube.datatransfer.resolver.applicationprofile.ApplicationProfileReader;
import org.gcube.datatransfer.resolver.applicationprofile.ScopeUtil;
import org.gcube.datatransfer.resolver.gis.GeoRuntimeReader.GEO_SERVICE;
import org.gcube.datatransfer.resolver.gis.GeonetworkAccessParameter.GeonetworkLoginLevel;
import org.gcube.datatransfer.resolver.gis.entity.ServerParameters;
import org.gcube.datatransfer.resolver.gis.exception.GeonetworkInstanceException;
import org.gcube.datatransfer.resolver.gis.exception.IllegalArgumentException;
@ -280,8 +281,8 @@ public class GisResolver extends HttpServlet{
protected String getLayerWmsRequest(String scope, String gisUUID, ServerParameters geonetworkParams) throws Exception{
try {
GeonetworkServiceInterface gntwAccess = new GeonetowrkAccessParameter(scope, geonetworkParams);
return MetadataConverter.getWMSOnLineResource(gntwAccess.getGeonetworkInstance(true), gisUUID);
GeonetworkServiceInterface gntwAccess = new GeonetworkAccessParameter(scope, geonetworkParams);
return MetadataConverter.getWMSOnLineResource(gntwAccess.getGeonetworkInstance(true, GeonetworkLoginLevel.DEFAULT), gisUUID);
}catch (GeonetworkInstanceException e){
logger.error("An error occurred when instancing geonetowrk gis layer with UUID "+gisUUID, e);
throw new IllegalArgumentException("Sorry, An error occurred when instancing geonetwork with UUID: "+gisUUID);

@ -1,9 +1,9 @@
/**
*
*
*/
package org.gcube.datatransfer.resolver.gis.exception;
import org.gcube.spatial.data.geonetwork.configuration.AuthorizationException;
import org.gcube.spatial.data.geonetwork.model.faults.AuthorizationException;
/**
@ -22,7 +22,7 @@ public class GeonetworkInstanceException extends Exception {
public GeonetworkInstanceException() {
super();
}
/**
* Instantiates a new geonetwork instance exception.
*

@ -26,14 +26,17 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.gcube.common.scope.api.ScopeProvider;
import org.gcube.datatransfer.resolver.gis.GeoRuntimeReader;
import org.gcube.datatransfer.resolver.gis.GeoRuntimeReader.GEO_SERVICE;
import org.gcube.datatransfer.resolver.gis.GeonetowrkAccessParameter;
import org.gcube.datatransfer.resolver.gis.GeonetworkAccessParameter;
import org.gcube.datatransfer.resolver.gis.GeonetworkInstance;
import org.gcube.datatransfer.resolver.gis.GeonetworkServiceInterface;
import org.gcube.datatransfer.resolver.gis.MetadataConverter;
import org.gcube.datatransfer.resolver.gis.entity.ServerParameters;
import org.gcube.datatransfer.resolver.gis.exception.GeonetworkInstanceException;
import org.gcube.datatransfer.resolver.gis.exception.IllegalArgumentException;
import org.gcube.spatial.data.geonetwork.configuration.Configuration;
import org.gcube.spatial.data.geonetwork.model.Account;
import org.gcube.spatial.data.geonetwork.model.Account.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -249,6 +252,17 @@ public class GeonetworkResolver extends HttpServlet{
try {
ServerParameters geonetworkParams = getGeonetworkCachedServerParameters(scope);
GeonetworkServiceInterface gntwAccess = new GeonetworkAccessParameter(scope, geonetworkParams);
GeonetworkInstance gnInstance = gntwAccess.getGeonetworkInstance();
ScopeProvider.instance.set(scope);
logger.info("set scope provider "+scope);
Configuration config = gnInstance.getGeonetworkPublisher().getConfiguration();
Account account=config.getScopeConfiguration().getAccounts().get(Type.CKAN);
logger.info("CKAN user is: "+account.getUser());
// return MetadataConverter.getWMSOnLineResource(gntwAccess.getGeonetworkInstance(true), gisUUID);
HTTPCallsUtils httpUtils = new HTTPCallsUtils();
logger.info("Parameters..");
for (Enumeration<String> e = req.getParameterNames(); e.hasMoreElements();){
@ -261,8 +275,11 @@ public class GeonetworkResolver extends HttpServlet{
// logger.debug("doPost read body request: "+readBody);
ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
boolean authorized = GNAuthentication.login(httpUtils, geonetworkParams.getUrl(), geonetworkParams.getUser(), geonetworkParams.getPassword());
logger.trace("Authorized on "+geonetworkParams +" ? "+authorized);
if(account.getUser()!=null){
boolean authorized = GNAuthentication.login(httpUtils, geonetworkParams.getUrl(), account.getUser(), account. getPassword());
logger.trace("Authorized on "+geonetworkParams +" ? "+authorized);
}else
logger.info("Skipping authentication ckan user is null");
// SPECIFIC HANDLER FOR GEONETWORK REQUEST: /srv/en/mef.export
String gnCSWlURL;
@ -322,6 +339,9 @@ public class GeonetworkResolver extends HttpServlet{
String error = "Sorry, an error occurred on resolving geonetwork request with scope "+scope+". Please, contact support!";
sendError(resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error);
return;
}finally{
ScopeProvider.instance.reset();
logger.info("scope provider reset");
}
}
@ -340,8 +360,8 @@ public class GeonetworkResolver extends HttpServlet{
logger.info("Tentative for recovering geonetwork server parameters from cache with scope: "+scope);
ServerParameters serverParam = cacheGNServerParams.get(scope);
if(serverParam==null){
logger.info("Cache having null Geonetwork server parameters, reading from IS..");
if(serverParam==null || serverParam.getPassword()!=null){
logger.info("Cache having null Geonetwork server parameters or password 'null', reading from IS..");
GeoRuntimeReader reader = new GeoRuntimeReader();
try {
serverParam = reader.retrieveGisParameters(scope, GEO_SERVICE.GEONETWORK);
@ -383,29 +403,6 @@ public class GeonetworkResolver extends HttpServlet{
logger.info("Reset of Cache Geonetwork server params perfomed!");
}
/**
* Gets the layer wms request.
*
* @param scope the scope
* @param gisUUID the gis uuid
* @param geonetworkParams the geonetwork params
* @return the layer wms request
* @throws Exception the exception
*/
protected String getLayerWmsRequest(String scope, String gisUUID, ServerParameters geonetworkParams) throws Exception{
try {
GeonetworkServiceInterface gntwAccess = new GeonetowrkAccessParameter(scope, geonetworkParams);
return MetadataConverter.getWMSOnLineResource(gntwAccess.getGeonetworkInstance(true), gisUUID);
}catch (GeonetworkInstanceException e){
logger.error("An error occurred when instancing geonetowrk gis layer with UUID "+gisUUID, e);
throw new IllegalArgumentException("Sorry, An error occurred when instancing geonetwork with UUID: "+gisUUID);
} catch (Exception e) {
logger.error("An error occurred when retrieving gis layer with UUID "+gisUUID, e);
throw new IllegalArgumentException("Sorry, An error occurred when retrieving gis layer with UUID "+gisUUID);
}
}
/**
* Send error.
*

Loading…
Cancel
Save