package org.gcube.datacatalogue.ckanutillibrary.server; import static org.gcube.resources.discovery.icclient.ICFactory.client; import static org.gcube.resources.discovery.icclient.ICFactory.clientFor; import static org.gcube.resources.discovery.icclient.ICFactory.queryFor; import java.io.StringReader; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.gcube.common.encryption.StringEncrypter; 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.resources.gcore.utils.XPathHelper; import org.gcube.common.scope.api.ScopeProvider; import org.gcube.datacatalogue.ckanutillibrary.server.exceptions.ApplicationProfileNotFoundException; import org.gcube.datacatalogue.ckanutillibrary.server.exceptions.NoApplicationProfileMasterException; import org.gcube.datacatalogue.ckanutillibrary.server.exceptions.NoDataCatalogueRuntimeResourceException; import org.gcube.datacatalogue.ckanutillibrary.server.exceptions.ServiceEndPointException; import org.gcube.resources.discovery.client.api.DiscoveryClient; import org.gcube.resources.discovery.client.queries.api.Query; import org.gcube.resources.discovery.client.queries.api.SimpleQuery; import org.gcube.resources.discovery.client.queries.impl.QueryBox; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Node; import org.xml.sax.InputSource; /** * Retrieve ckan running instance information in the infrastructure (for both its database and data catalogue url) * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) */ public class DataCatalogueRunningCluster { //logger private static final Logger logger = LoggerFactory.getLogger(DataCatalogueRunningCluster.class); // database of the datacatalogue info private final static String RUNTIME_DB_RESOURCE_NAME = "CKanDatabase"; private final static String PLATFORM_DB_NAME = "postgres"; private final static String APPLICATION_PROFILE_NAME = "CkanPortlet"; // data catalogue info private final static String RUNTIME_CATALOGUE_RESOURCE_NAME = "CKanDataCatalogue"; private final static String PLATFORM_CATALOGUE_NAME = "Tomcat"; // api key property private final static String API_KEY_PROPERTY = "API_KEY"; // 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 IS_MANAGE_PRODUCT_ENABLED = "IS_MANAGE_PRODUCT_ENABLED"; // true, false.. missing means false as well (for GRSF records) // url of the http uri for this scope private final static String URL_RESOLVER = "URL_RESOLVER"; // retrieved data private List datacatalogueUrls = new ArrayList(); private List hostsDB = new ArrayList(); private List portsDB = new ArrayList(); private String nameDB; private String userDB; private String passwordDB; private String portletUrl; private boolean manageProductEnabled; private String urlResolver; // this token is needed in order to assign roles to user private String sysAdminToken; public DataCatalogueRunningCluster(String scope) throws Exception{ if(scope == null || scope.isEmpty()) throw new Exception("Invalid scope!!"); // retrieve the current scope and save it (it will be reset later) String currentScope = ScopeProvider.instance.get(); logger.info("Retrieving ckan database service end point information for scope " + scope); try { // set the scope ScopeProvider.instance.set(scope); List resources = getConfigurationFromISFORDB(); if (resources.size() == 0){ throw new NoDataCatalogueRuntimeResourceException("There is no Runtime Resource having name " + RUNTIME_DB_RESOURCE_NAME +" and Platform " + PLATFORM_DB_NAME + " in this scope."); } else { try{ if(resources.size() > 1){ boolean oneWasMaster = false; logger.info("Too many Runtime Resource having name " + RUNTIME_DB_RESOURCE_NAME +" in this scope.. Looking for the one that has the property " + IS_MASTER_ROOT_KEY_PROPERTY); for (ServiceEndpoint res : resources) { Iterator accessPointIterator = res.profile().accessPoints().iterator(); while (accessPointIterator.hasNext()) { ServiceEndpoint.AccessPoint accessPoint = (ServiceEndpoint.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 oneWasMaster = true; // add this host hostsDB.add(accessPoint.address().split(":")[0]); // save the port int port = Integer.parseInt(accessPoint.address().split(":")[1]); portsDB.add(port); // save the name of the cluster (this should be unique) nameDB = accessPoint.name(); // save user and password passwordDB = StringEncrypter.getEncrypter().decrypt(accessPoint.password()); userDB = accessPoint.username(); // now break break; } } // if none of them was master, throw an exception if(!oneWasMaster) throw new NoApplicationProfileMasterException("There is no application profile with MASTER property"); }else{ logger.debug(resources.toString()); for (ServiceEndpoint res : resources) { Iterator accessPointIterator = res.profile().accessPoints().iterator(); while (accessPointIterator.hasNext()) { ServiceEndpoint.AccessPoint accessPoint = (ServiceEndpoint.AccessPoint) accessPointIterator .next(); // add this host hostsDB.add(accessPoint.address().split(":")[0]); // save the port int port = Integer.parseInt(accessPoint.address().split(":")[1]); portsDB.add(port); // save the name of the cluster (this should be unique) nameDB = accessPoint.name(); // save user and password passwordDB = StringEncrypter.getEncrypter().decrypt(accessPoint.password()); userDB = accessPoint.username(); } } } }catch(Exception e ){ throw new ServiceEndPointException(e.toString()); } } logger.debug("Retrieving ckan data catalogue service end point information and sysadmin token."); resources = getConfigurationFromISFORCatalogueUrl(); if (resources.size() == 0){ logger.error("There is no Runtime Resource having name " + RUNTIME_CATALOGUE_RESOURCE_NAME +" and Platform " + PLATFORM_CATALOGUE_NAME + " in this scope."); throw new NoDataCatalogueRuntimeResourceException(); } else { logger.debug(resources.toString()); try{ if(resources.size() > 1){ boolean oneWasMaster = false; logger.info("Too many Runtime Resource having name " + RUNTIME_CATALOGUE_RESOURCE_NAME +" in this scope.. Looking for the one that has the property " + IS_MASTER_ROOT_KEY_PROPERTY); for (ServiceEndpoint res : resources) { Iterator accessPointIterator = res.profile().accessPoints().iterator(); while (accessPointIterator.hasNext()) { ServiceEndpoint.AccessPoint accessPoint = (ServiceEndpoint.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 oneWasMaster = true; // add this host datacatalogueUrls.add(accessPoint.address()); // retrieve sys admin token sysAdminToken = accessPoint.propertyMap().get(API_KEY_PROPERTY).value(); sysAdminToken = StringEncrypter.getEncrypter().decrypt(sysAdminToken); // retrieve URL_RESOLVER if(accessPoint.propertyMap().containsKey(URL_RESOLVER)) urlResolver = accessPoint.propertyMap().get(URL_RESOLVER).value(); // break now break; } } // if none of them was master, throw an exception if(!oneWasMaster) throw new NoApplicationProfileMasterException("There is no application profile with MASTER property"); }else{ for (ServiceEndpoint res : resources) { Iterator accessPointIterator = res.profile().accessPoints().iterator(); while (accessPointIterator.hasNext()) { ServiceEndpoint.AccessPoint accessPoint = (ServiceEndpoint.AccessPoint) accessPointIterator .next(); // add this host datacatalogueUrls.add(accessPoint.address()); // retrieve sys admin token sysAdminToken = accessPoint.propertyMap().get(API_KEY_PROPERTY).value(); sysAdminToken = StringEncrypter.getEncrypter().decrypt(sysAdminToken); // get the is manage product property Property entry = accessPoint.propertyMap().get(IS_MANAGE_PRODUCT_ENABLED); String isManageProduct = entry != null ? entry.value() : null; if(isManageProduct != null && isManageProduct.equals("true")){ logger.info("Manage product is enabled in this scope"); manageProductEnabled = true; } // retrieve URL_RESOLVER if(accessPoint.propertyMap().containsKey(URL_RESOLVER)) urlResolver = accessPoint.propertyMap().get(URL_RESOLVER).value(); } } } }catch(Exception e){ throw new ServiceEndPointException("There is no service end point for such information"); } } // finally get the url in which the ckan portlet is deployed logger.debug("Looking for portlet url in " + ScopeProvider.instance.get() + " scope" ); portletUrl = getPortletUrlFromInfrastrucure(); }catch(Exception e) { logger.warn("The following error occurred: " + e.toString()); }finally{ ScopeProvider.instance.set(currentScope); } } /** * Retrieve endpoints information from IS for DB * @return list of endpoints for ckan database * @throws Exception */ private List getConfigurationFromISFORDB() throws Exception{ SimpleQuery query = queryFor(ServiceEndpoint.class); query.addCondition("$resource/Profile/Name/text() eq '"+ RUNTIME_DB_RESOURCE_NAME +"'"); query.addCondition("$resource/Profile/Platform/Name/text() eq '"+ PLATFORM_DB_NAME +"'"); DiscoveryClient client = clientFor(ServiceEndpoint.class); List toReturn = client.submit(query); return toReturn; } /** * Retrieve endpoints information from IS for DataCatalogue URL * @return list of endpoints for ckan data catalogue * @throws Exception */ private List getConfigurationFromISFORCatalogueUrl() throws Exception{ 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 toReturn = client.submit(query); return toReturn; } /** * Retrieve the url of the ckan portlet deployed into this scope * @return */ private String getPortletUrlFromInfrastrucure() { String scope = ScopeProvider.instance.get(); logger.debug("Trying to fetch applicationProfile profile from the infrastructure for " + APPLICATION_PROFILE_NAME + " scope: " + scope); try { Query q = new QueryBox("for $profile in collection('/db/Profiles/GenericResource')//Resource " + "where $profile/Profile/SecondaryType/string() eq 'ApplicationProfile' and $profile/Profile/Name/string() " + " eq '" + APPLICATION_PROFILE_NAME + "'" + "return $profile"); DiscoveryClient client = client(); List appProfile = client.submit(q); if (appProfile == null || appProfile.size() == 0) throw new ApplicationProfileNotFoundException("Your applicationProfile is not registered in the infrastructure"); else { String elem = appProfile.get(0); DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); Node node = docBuilder.parse(new InputSource(new StringReader(elem))).getDocumentElement(); XPathHelper helper = new XPathHelper(node); List currValue = null; currValue = helper.evaluate("/Resource/Profile/Body/url/text()"); if (currValue != null && currValue.size() > 0) { logger.debug("Portlet url found is " + currValue.get(0)); return currValue.get(0); } } } catch (Exception e) { logger.error("Error while trying to fetch applicationProfile profile from the infrastructure", e); } return null; } /** Retrieve the ckan portlet url * @return the portletUrl */ public String getPortletUrl() { return portletUrl; } /** * Retrieve data catalogue url */ public List getDataCatalogueUrl() { return datacatalogueUrls; } /** * Get the hosts for such resource. * @return */ public List getDatabaseHosts() { return hostsDB; } /** * Get the ports for such resource. * @return */ public List getDatabasePorts() { return portsDB; } /** * Get the database name. * @return */ public String getDataBaseName() { return nameDB; } /** * Get the database's user. * @return */ public String getDataBaseUser() { return userDB; } /** * Get the database's password. * @return */ public String getDataBasePassword() { return passwordDB; } /** * @return the sysAdminToken */ public String getSysAdminToken() { return sysAdminToken; } /** * Is manager product enabled (e.g., for GRSF records) * @return */ public boolean isManageProductEnabled() { return manageProductEnabled; } /** * Get the url of the uri resolver for this instance/scope * @return */ public String getUrlResolver() { return urlResolver; } }