package org.gcube.gcat.persistence.ckan; import static org.gcube.resources.discovery.icclient.ICFactory.clientFor; import static org.gcube.resources.discovery.icclient.ICFactory.queryFor; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import javax.ws.rs.InternalServerErrorException; import javax.ws.rs.WebApplicationException; import org.gcube.com.fasterxml.jackson.databind.JsonNode; import org.gcube.com.fasterxml.jackson.databind.ObjectMapper; import org.gcube.com.fasterxml.jackson.databind.node.ArrayNode; import org.gcube.common.authorization.utils.manager.SecretManager; import org.gcube.common.encryption.encrypter.StringEncrypter; import org.gcube.common.resources.gcore.GenericResource; import org.gcube.common.resources.gcore.ServiceEndpoint; import org.gcube.common.resources.gcore.ServiceEndpoint.AccessPoint; import org.gcube.common.resources.gcore.ServiceEndpoint.Property; import org.gcube.common.scope.impl.ScopeBean; import org.gcube.resources.discovery.client.api.DiscoveryClient; import org.gcube.resources.discovery.client.queries.api.SimpleQuery; import org.gcube.resources.discovery.icclient.ICFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Luca Frosini (ISTI - CNR) */ public class CKANInstance { private static final Logger logger = LoggerFactory.getLogger(CKANInstance.class); // CKAN Instance info private final static String RUNTIME_CATALOGUE_RESOURCE_NAME = "CKanDataCatalogue"; private final static String PLATFORM_CATALOGUE_NAME = "Tomcat"; // property to retrieve the master service endpoint into the /root scope private final static String IS_MASTER_ROOT_KEY_PROPERTY = "IS_ROOT_MASTER"; // true, false.. missing means false as well private final static String API_KEY_PROPERTY = "API_KEY"; private final static String SOCIAL_POST = "SOCIAL_POST"; private final static String ALERT_USERS_ON_POST_CREATION = "ALERT_USERS_ON_POST_CREATION"; private final static String URL_RESOLVER = "URL_RESOLVER"; private final static String MODERATION_ENABLED_KEY = "MODERATION_ENABLED"; private static final Map ckanInstancePerScope; protected String ckanURL; protected String sysAdminToken; protected boolean socialPostEnabled; protected Boolean notificationToUsersEnabled; protected String uriResolverURL; protected boolean moderationEnabled; protected final String currentContext; protected final ScopeBean currentScopeBean; protected final String currentOrganizationName; protected final Set supportedOrganizations; static { ckanInstancePerScope = new HashMap(); } public static CKANInstance getInstance() { String context = SecretManager.instance.get().getContext(); CKANInstance ckanInstance = ckanInstancePerScope.get(context); if(ckanInstance == null) { ckanInstance = new CKANInstance(); ckanInstance.getConfigurationFromIS(); ckanInstancePerScope.put(context, ckanInstance); } return ckanInstance; } private CKANInstance() { currentContext = SecretManager.instance.get().getContext(); currentScopeBean = new ScopeBean(currentContext); currentOrganizationName = CKANPackage.getOrganizationName(currentScopeBean); supportedOrganizations = getSupportedOrganizationsFromIS(); } /** * Retrieve endpoints information from IS for DataCatalogue URL * @return list of endpoints for ckan data catalogue * @throws Exception */ private static List getServiceEndpoints() { SimpleQuery query = queryFor(ServiceEndpoint.class); query.addCondition("$resource/Profile/Name/text() eq '" + RUNTIME_CATALOGUE_RESOURCE_NAME + "'"); query.addCondition("$resource/Profile/Platform/Name/text() eq '" + PLATFORM_CATALOGUE_NAME + "'"); DiscoveryClient client = clientFor(ServiceEndpoint.class); List serviceEndpoints = client.submit(query); if(serviceEndpoints.size() == 0) { logger.error("There is no {} having name {} and Platform {} in this context.", ServiceEndpoint.class.getSimpleName(), RUNTIME_CATALOGUE_RESOURCE_NAME, PLATFORM_CATALOGUE_NAME); throw new InternalServerErrorException("No CKAN configuration on IS"); } return serviceEndpoints; } private void getConfigurationFromIS() { try { List serviceEndpoints = getServiceEndpoints(); ServiceEndpoint serviceEndpoint = null; if(serviceEndpoints.size() > 1) { logger.info("Too many {} having name {} in this context. Looking for the one that has the property {}", ServiceEndpoint.class.getSimpleName(), RUNTIME_CATALOGUE_RESOURCE_NAME, IS_MASTER_ROOT_KEY_PROPERTY); for(ServiceEndpoint se : serviceEndpoints) { Iterator accessPointIterator = se.profile().accessPoints().iterator(); while(accessPointIterator.hasNext()) { ServiceEndpoint.AccessPoint accessPoint = accessPointIterator.next(); // get the is master property Property entry = accessPoint.propertyMap().get(IS_MASTER_ROOT_KEY_PROPERTY); String isMaster = entry != null ? entry.value() : null; if(isMaster == null || !isMaster.equals("true")) { continue; } // set this variable serviceEndpoint = se; break; } } // if none of them was master, throw an exception if(serviceEndpoint == null) { throw new InternalServerErrorException( "Too many CKAN configuration on IS and no one with MASTER property"); } } else { serviceEndpoint = serviceEndpoints.get(0); } Iterator accessPointIterator = serviceEndpoint.profile().accessPoints().iterator(); while(accessPointIterator.hasNext()) { AccessPoint accessPoint = accessPointIterator.next(); // add this host ckanURL = accessPoint.address(); // retrieve sys admin token sysAdminToken = accessPoint.propertyMap().get(API_KEY_PROPERTY).value(); sysAdminToken = StringEncrypter.getEncrypter().decrypt(sysAdminToken); // retrieve option to check if the social post has to be made socialPostEnabled = true; // default is true if(accessPoint.propertyMap().containsKey(SOCIAL_POST)) { if(accessPoint.propertyMap().get(SOCIAL_POST).value().trim().equalsIgnoreCase("false")) { socialPostEnabled = false; } } // retrieve option for user alert notificationToUsersEnabled = false; if(accessPoint.propertyMap().containsKey(ALERT_USERS_ON_POST_CREATION)) { if(accessPoint.propertyMap().get(ALERT_USERS_ON_POST_CREATION).value().trim() .equalsIgnoreCase("true")) { notificationToUsersEnabled = true; } } // retrieve URL_RESOLVER if(accessPoint.propertyMap().containsKey(URL_RESOLVER)) { uriResolverURL = accessPoint.propertyMap().get(URL_RESOLVER).value(); } moderationEnabled = false; if(accessPoint.propertyMap().containsKey(MODERATION_ENABLED_KEY)) { if(accessPoint.propertyMap().get(MODERATION_ENABLED_KEY).value().trim() .equalsIgnoreCase("true")) { moderationEnabled = true; } } } } catch(WebApplicationException e) { throw e; } catch(Exception e) { throw new InternalServerErrorException("Error while getting configuration on IS", e); } } public static final String GENERIC_RESOURCE_SECONDARY_TYPE_FOR_ORGANIZATIONS = "ApplicationProfile"; public static final String GENERIC_RESOURCE_NAME_FOR_ORGANIZATIONS = "Supported CKAN Organizations"; public static final String GENERIC_RESOURCE_CKAN_ORGANIZATIONS = "CKANOrganizations"; protected Set getSupportedOrganizationsFromIS() { Set supportedOrganizations = new HashSet<>(); SimpleQuery query = ICFactory.queryFor(GenericResource.class); query.addCondition(String.format("$resource/Profile/SecondaryType/text() eq '%s'", GENERIC_RESOURCE_SECONDARY_TYPE_FOR_ORGANIZATIONS)); query.addCondition( String.format("$resource/Profile/Name/text() eq '%s'", GENERIC_RESOURCE_NAME_FOR_ORGANIZATIONS)); DiscoveryClient client = ICFactory.clientFor(GenericResource.class); List resources = client.submit(query); if(resources == null || resources.size() == 0) { logger.info( "{} with SecondaryType {} and Name %s not found. Item will be only be created in {} CKAN organization", GenericResource.class.getSimpleName(), GENERIC_RESOURCE_SECONDARY_TYPE_FOR_ORGANIZATIONS, GENERIC_RESOURCE_NAME_FOR_ORGANIZATIONS, currentOrganizationName); supportedOrganizations.add(currentOrganizationName); } else { try { GenericResource genericResource = resources.get(0); String body = genericResource.profile().body().getTextContent(); ObjectMapper objectMapper = new ObjectMapper(); JsonNode jsonNode = objectMapper.readTree(body); ArrayNode array = (ArrayNode) jsonNode.get(GENERIC_RESOURCE_CKAN_ORGANIZATIONS); for(int i = 0; i < array.size(); i++) { String organization = array.get(i).asText(); supportedOrganizations.add(organization); } }catch (Exception e) { supportedOrganizations.clear(); supportedOrganizations.add(currentOrganizationName); } } logger.debug("Supported CKAN Organization for current Context ({}) are {}", currentContext, supportedOrganizations); return supportedOrganizations; } public String getUriResolverURL() throws Exception { return uriResolverURL; } public String getCKANURL() { return ckanURL; } public boolean isSocialPostEnabled() throws Exception { return socialPostEnabled; } public boolean isNotificationToUsersEnabled() throws Exception { return notificationToUsersEnabled; } public boolean isModerationEnabled() { return moderationEnabled; } public String getSysAdminToken() throws Exception { return sysAdminToken; } public String getCurrentContext() { return currentContext; } public ScopeBean getCurrentScopeBean() { return currentScopeBean; } public String getCurrentOrganizationName() { return currentOrganizationName; } public Set getSupportedOrganizations() { return supportedOrganizations; } public void setModerationEnabled(boolean moderationEnabled) { this.moderationEnabled = moderationEnabled; } }