package org.gcube.gcat.configuration; import static org.gcube.resources.discovery.icclient.ICFactory.clientFor; import static org.gcube.resources.discovery.icclient.ICFactory.queryFor; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import javax.ws.rs.InternalServerErrorException; import javax.ws.rs.WebApplicationException; import org.gcube.com.fasterxml.jackson.core.JsonProcessingException; 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.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.Profile; import org.gcube.common.resources.gcore.ServiceEndpoint.Property; import org.gcube.common.resources.gcore.ServiceEndpoint.Runtime; import org.gcube.common.resources.gcore.common.Platform; import org.gcube.common.resources.gcore.utils.Group; import org.gcube.gcat.api.configuration.CatalogueConfiguration; import org.gcube.informationsystem.publisher.RegistryPublisher; import org.gcube.informationsystem.publisher.RegistryPublisherFactory; 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.gcube.smartgears.ContextProvider; import org.gcube.smartgears.configuration.application.ApplicationConfiguration; import org.gcube.smartgears.configuration.container.ContainerConfiguration; import org.gcube.smartgears.context.application.ApplicationContext; import org.gcube.smartgears.context.container.ContainerContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class GCoreISConfigurationProxy { private static final Logger logger = LoggerFactory.getLogger(GCoreISConfigurationProxy.class); // 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 private final static String DEFAULT_ORGANIZATION_PROPERTY = "DEFAULT_ORGANIZATION"; private final static String SUPPORTED_ORGANIZATION_PROPERTY = "SUPPORTED_ORGANIZATION"; private final static String API_KEY_PROPERTY = "API_KEY"; private final static String SOCIAL_POST_PROPERTY = "SOCIAL_POST"; private final static String ALERT_USERS_ON_POST_CREATION_PROPERTY = "ALERT_USERS_ON_POST_CREATION"; private final static String MODERATION_ENABLED_KEY_PROPERTY = "MODERATION_ENABLED"; // CKAN Instance info private final static String CATEGORY = "Application"; private final static String NAME = "CKanDataCatalogue"; protected final String context; protected CatalogueConfiguration catalogueConfiguration; public GCoreISConfigurationProxy(String context) { this.context = context; } public GCoreISConfigurationProxy(String context, CatalogueConfiguration catalogueConfiguration) { this(context); this.catalogueConfiguration = catalogueConfiguration; } public CatalogueConfiguration getCatalogueConfiguration() { if (catalogueConfiguration == null) { getCatalogueConfigurationFromIS(); } return catalogueConfiguration; } protected AccessPoint getAccessPoint(Profile profile) { Group accessPoints = profile.accessPoints(); Iterator accessPointIterator = accessPoints.iterator(); AccessPoint accessPoint = accessPointIterator.next(); return accessPoint; } protected CatalogueConfiguration getCatalogueConfigurationFromIS() { try { boolean mustBeUpdated = false; catalogueConfiguration = new CatalogueConfiguration(context); ServiceEndpoint serviceEndpoint = getServiceEndpoint(); if (serviceEndpoint == null) { throw new InternalServerErrorException("No CKAN configuration on IS"); } catalogueConfiguration.setID(serviceEndpoint.id()); Profile profile = serviceEndpoint.profile(); AccessPoint accessPoint = getAccessPoint(profile); // add this host String ckanURL = accessPoint.address(); catalogueConfiguration.setCkanURL(ckanURL); Map propertyMap = accessPoint.propertyMap(); // retrieve sys admin token String sysAdminToken = propertyMap.get(API_KEY_PROPERTY).value(); sysAdminToken = StringEncrypter.getEncrypter().decrypt(sysAdminToken); catalogueConfiguration.setSysAdminToken(sysAdminToken); String organization = CatalogueConfiguration.getOrganizationName(context); if (propertyMap.containsKey(DEFAULT_ORGANIZATION_PROPERTY)) { String org = propertyMap.get(DEFAULT_ORGANIZATION_PROPERTY).value().trim(); if(org!=null && org.compareTo("")==0) { mustBeUpdated = true; }else { organization = org; } }else { mustBeUpdated = true; } catalogueConfiguration.setOrganization(organization); // retrieve option to check if the social post has to be made Boolean socialPostEnabled = true; if (propertyMap.containsKey(SOCIAL_POST_PROPERTY)) { if (propertyMap.get(SOCIAL_POST_PROPERTY).value().trim().equalsIgnoreCase("false")) { socialPostEnabled = false; } }else { mustBeUpdated = true; } catalogueConfiguration.setSocialPostEnabled(socialPostEnabled); // retrieve option for user alert boolean notificationToUsersEnabled = false; // default is false if (propertyMap.containsKey(ALERT_USERS_ON_POST_CREATION_PROPERTY)) { if (propertyMap.get(ALERT_USERS_ON_POST_CREATION_PROPERTY).value().trim() .equalsIgnoreCase("true")) { notificationToUsersEnabled = true; } }else { mustBeUpdated = true; } catalogueConfiguration.setNotificationToUsersEnabled(notificationToUsersEnabled); boolean moderationEnabled = false; // default is false if (propertyMap.containsKey(MODERATION_ENABLED_KEY_PROPERTY)) { if (propertyMap.get(MODERATION_ENABLED_KEY_PROPERTY).value().trim().equalsIgnoreCase("true")) { moderationEnabled = true; } }else { mustBeUpdated = true; } catalogueConfiguration.setModerationEnabled(moderationEnabled); Set supportedOrganizations = null; if (propertyMap.containsKey(SUPPORTED_ORGANIZATION_PROPERTY)) { String jsonArray = propertyMap.get(SUPPORTED_ORGANIZATION_PROPERTY).value(); supportedOrganizations = unmarshallSupportedOrganizations(jsonArray); removeAllGenericResources(); }else { supportedOrganizations = getSupportedOrganizationsFromGenericResource(); mustBeUpdated = true; } if (supportedOrganizations != null) { catalogueConfiguration.setSupportedOrganizations(supportedOrganizations); } if(mustBeUpdated) { // updateOnIS(serviceEndpoint); logger.warn("The ServiceEndpoint with ID {} in context {} should be updated", serviceEndpoint.id(), context); } } catch (WebApplicationException e) { throw e; } catch (Exception e) { throw new InternalServerErrorException("Error while getting configuration on IS", e); } return catalogueConfiguration; } /** * Retrieve endpoints information from IS for DataCatalogue URL * * @return list of endpoints for ckan data catalogue * @throws Exception */ private List getServiceEndpoints() { SimpleQuery query = queryFor(ServiceEndpoint.class); query.addCondition("$resource/Profile/Category/Name/text() eq '" + CATEGORY + "'"); query.addCondition("$resource/Profile/Name/text() eq '" + NAME + "'"); DiscoveryClient client = clientFor(ServiceEndpoint.class); List serviceEndpoints = client.submit(query); return serviceEndpoints; } private ServiceEndpoint getServiceEndpoint() { List serviceEndpoints = getServiceEndpoints(); if (serviceEndpoints.size() == 0) { logger.error("There is no {} having Category {} and Name {} in this context.", ServiceEndpoint.class.getSimpleName(), CATEGORY, NAME); return null; } ServiceEndpoint serviceEndpoint = null; if (serviceEndpoints.size() > 1) { logger.info("Too many {} having Category {} and Name {} in this context. Looking for the one that has the property {}", ServiceEndpoint.class.getSimpleName(), CATEGORY, 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; return serviceEndpoint; } } // if none of them was master, throw an exception if (serviceEndpoint == null) { throw new InternalServerErrorException( "Too many catalogue configuration on IS and no one with MASTER property"); } } else { serviceEndpoint = serviceEndpoints.get(0); } return serviceEndpoint; } 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"; private List getGenericResources() { 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 genericResources = client.submit(query); return genericResources; } protected String marshallSupportedOrganizations() throws JsonProcessingException { Set supportedOrganizations = catalogueConfiguration.getSupportedOrganizations(); return marshallSupportedOrganizations(supportedOrganizations); } protected String marshallSupportedOrganizations(Set supportedOrganizations) throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); ArrayNode arrayNode = objectMapper.createArrayNode(); for(String org : supportedOrganizations) { arrayNode.add(org); } return objectMapper.writeValueAsString(arrayNode); } protected Set unmarshallSupportedOrganizations(String supportedOrganizationsJsonArray){ try { ObjectMapper objectMapper = new ObjectMapper(); JsonNode jsonNode = objectMapper.readTree(supportedOrganizationsJsonArray); ArrayNode array = (ArrayNode) jsonNode.get(GENERIC_RESOURCE_CKAN_ORGANIZATIONS); Set supportedOrganizations = new HashSet<>(array.size()); for (int i = 0; i < array.size(); i++) { String o = array.get(i).asText(); supportedOrganizations.add(o); } logger.debug("Supported CKAN Organization for current Context ({}) are {}", context, supportedOrganizations); return supportedOrganizations; } catch (Exception e) { return null; } } protected void removeAllGenericResources() { List genericResources = getGenericResources(); removeAllGenericResources(genericResources); } protected void removeAllGenericResources(List genericResources) { RegistryPublisher registryPublisher = RegistryPublisherFactory.create(); for(GenericResource genericResource : genericResources) { registryPublisher.remove(genericResource); } } protected Set getSupportedOrganizationsFromGenericResource() { List genericResources = getGenericResources(); if (genericResources == null || genericResources.size() == 0) { logger.trace( "{} 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, CatalogueConfiguration.getOrganizationName(context)); return null; } GenericResource genericResource = genericResources.get(0); String supportedOrganizationsJsonArray = genericResource.profile().body().getTextContent(); Set supportedOrganizatins = unmarshallSupportedOrganizations(supportedOrganizationsJsonArray); removeAllGenericResources(genericResources); return supportedOrganizatins; } public void delete() { RegistryPublisher registryPublisher = RegistryPublisherFactory.create(); ServiceEndpoint serviceEndpoint = getServiceEndpoint(); if(serviceEndpoint!=null) { registryPublisher.remove(serviceEndpoint); } List genericResources = getGenericResources(); if(genericResources!=null) { for(GenericResource genericResource : genericResources) { registryPublisher.remove(genericResource); } } } protected Property addProperty(Group properties, String name, String value) { return addProperty(properties, name, value, false); } protected Property addProperty(Group properties, String name, String value, boolean encrypted) { Property property = new Property(); property.nameAndValue(name, value); property.encrypted(encrypted); properties.add(property); return property; } protected AccessPoint setAccessPointProperties(AccessPoint accessPoint, boolean update) throws JsonProcessingException { accessPoint.description(String.format("Access Point %s by gcat %s", update ? "updated" : "created", getGcatVersion().toString())); accessPoint.address(catalogueConfiguration.getCkanURL()); // TODO Change ASAP //accessPoint.name("gcat Configuration"); accessPoint.name("CKan Data Catalogue"); Group properties = accessPoint.properties(); addProperty(properties, DEFAULT_ORGANIZATION_PROPERTY, catalogueConfiguration.getOrganization()); addProperty(properties, API_KEY_PROPERTY, catalogueConfiguration.getSysAdminToken(), true); addProperty(properties, SOCIAL_POST_PROPERTY, Boolean.toString(catalogueConfiguration.isSocialPostEnabled())); addProperty(properties, ALERT_USERS_ON_POST_CREATION_PROPERTY, Boolean.toString(catalogueConfiguration.isNotificationToUsersEnabled())); addProperty(properties, MODERATION_ENABLED_KEY_PROPERTY, Boolean.toString(catalogueConfiguration.isModerationEnabled())); addProperty(properties, SUPPORTED_ORGANIZATION_PROPERTY, marshallSupportedOrganizations()); return accessPoint; } private final static String PLATFORM_NAME = "Tomcat"; protected Version getGcatVersion() { ApplicationContext applicationContext = ContextProvider.get(); ApplicationConfiguration applicationConfiguration = applicationContext.configuration(); Version version = new Version(applicationConfiguration.version()); return version; } /** * Set the version of gcat so that in future implementation * we can understand if the configuration must be updated. * @param platform * @return */ protected Platform setVersion(Platform platform) { Version version = getGcatVersion(); platform.version((short) version.getMajor()); platform.minorVersion((short) version.getMinor()); platform.revisionVersion((short) version.getRevision()); platform.buildVersion((short) 0); return platform; } protected Platform setPlatformProperty(Platform platform) { /* * * Tomcat * * The version of gcat * 2 * 2 * 0 * 0 * */ // TODO change to gcat ASAP // platform.name("gcat"); platform.name(PLATFORM_NAME); platform = setVersion(platform); return platform; } private String getRunningOn(ContainerConfiguration containerConfiguration) { return String.format("%s:%s", containerConfiguration.hostname(), containerConfiguration.port()); } protected Runtime setRuntimeProperties(Runtime runtime) { ApplicationContext applicationContext = ContextProvider.get(); ContainerContext containerContext = applicationContext.container(); ContainerConfiguration containerConfiguration = containerContext.configuration(); String runningOn = getRunningOn(containerConfiguration); runtime.hostedOn(runningOn); runtime.ghnId(containerContext.id()); runtime.status(applicationContext.configuration().mode().toString()); return runtime; } protected Profile setProfileProperties(Profile profile, boolean update) { /* * * Application * CKanDataCatalogue * gCat Configuration created/updated by the service via REST */ profile.category(CATEGORY); profile.name(NAME); profile.description(String.format("gCat configuration %s by the service via REST", update ? "updated" : "created")); return profile; } protected ServiceEndpoint createServiceEndpoint(ServiceEndpoint serviceEndpoint) throws Exception { boolean update = serviceEndpoint != null; if(update) { if(catalogueConfiguration.getSysAdminToken()==null) { Profile profile = serviceEndpoint.profile(); AccessPoint accessPoint = getAccessPoint(profile); // add this host String ckanURL = accessPoint.address(); catalogueConfiguration.setCkanURL(ckanURL); Map propertyMap = accessPoint.propertyMap(); // retrieve sys admin token String sysAdminToken = propertyMap.get(API_KEY_PROPERTY).value(); sysAdminToken = StringEncrypter.getEncrypter().decrypt(sysAdminToken); catalogueConfiguration.setSysAdminToken(sysAdminToken); } } serviceEndpoint = new ServiceEndpoint(); serviceEndpoint.setId(catalogueConfiguration.getID()); Profile profile = serviceEndpoint.newProfile(); profile = setProfileProperties(profile, update); Platform platform = profile.newPlatform(); setPlatformProperty(platform); Runtime runtime = profile.newRuntime(); runtime = setRuntimeProperties(runtime); Group accessPoints = profile.accessPoints(); AccessPoint accessPoint = accessPoints.add(); setAccessPointProperties(accessPoint, update); return serviceEndpoint; } public CatalogueConfiguration createOnIS(ServiceEndpoint serviceEndpoint) throws Exception { RegistryPublisher registryPublisher = RegistryPublisherFactory.create(); String id = catalogueConfiguration.getID(); if(id==null || id.compareTo("")==0) { id = UUID.randomUUID().toString(); catalogueConfiguration.setID(id); } serviceEndpoint = createServiceEndpoint(serviceEndpoint); registryPublisher.create(serviceEndpoint); return catalogueConfiguration; } public CatalogueConfiguration updateOnIS(ServiceEndpoint serviceEndpoint) throws Exception { RegistryPublisher registryPublisher = RegistryPublisherFactory.create(); String id = serviceEndpoint.id(); if(catalogueConfiguration.getID().compareTo(id)!=0) { catalogueConfiguration.setID(id); } serviceEndpoint = createServiceEndpoint(serviceEndpoint); registryPublisher.update(serviceEndpoint); return catalogueConfiguration; } public CatalogueConfiguration createOrUpdateOnIS() throws Exception { ServiceEndpoint serviceEndpoint = getServiceEndpoint(); if(serviceEndpoint!=null) { // It's an update updateOnIS(serviceEndpoint); }else { // It's a create createOnIS(serviceEndpoint); } return catalogueConfiguration; } }