package org.gbif.ipt.service.registry.impl; import org.gbif.api.model.common.DOI; import org.gbif.dwc.terms.DwcTerm; import org.gbif.ipt.action.BaseAction; import org.gbif.ipt.config.AppConfig; import org.gbif.ipt.config.ConfigWarnings; import org.gbif.ipt.config.DataDir; import org.gbif.ipt.model.Extension; import org.gbif.ipt.model.Ipt; import org.gbif.ipt.model.Organisation; import org.gbif.ipt.model.Resource; import org.gbif.ipt.model.VersionHistory; import org.gbif.ipt.model.Vocabulary; import org.gbif.ipt.model.voc.PublicationStatus; import org.gbif.ipt.service.BaseManager; import org.gbif.ipt.service.RegistryException; import org.gbif.ipt.service.RegistryException.TYPE; import org.gbif.ipt.service.admin.RegistrationManager; import org.gbif.ipt.service.manage.ResourceManager; import org.gbif.ipt.service.registry.RegistryManager; import org.gbif.ipt.struts2.SimpleTextProvider; import org.gbif.ipt.utils.RegistryEntryHandler; import org.gbif.ipt.validation.AgentValidator; import org.gbif.metadata.eml.Agent; import org.gbif.metadata.eml.Eml; import org.gbif.metadata.eml.EmlFactory; import org.gbif.utils.HttpUtil; import org.gbif.utils.HttpUtil.Response; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; import java.net.ConnectException; import java.net.URISyntaxException; import java.net.UnknownHostException; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.UUID; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonSyntaxException; import com.google.gson.reflect.TypeToken; import com.google.inject.Inject; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.SystemUtils; import org.apache.http.NameValuePair; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.message.BasicNameValuePair; import org.xml.sax.SAXException; public class RegistryManagerImpl extends BaseManager implements RegistryManager { private static class RegistryServices { public String serviceURLs = null; public String serviceTypes = null; } private final RegistryEntryHandler newRegistryEntryHandler = new RegistryEntryHandler(); private static final String SERVICE_TYPE_EML = "EML"; private static final String SERVICE_TYPE_OCCURRENCE = "DWC-ARCHIVE-OCCURRENCE"; private static final String SERVICE_TYPE_CHECKLIST = "DWC-ARCHIVE-CHECKLIST"; private static final String SERVICE_TYPE_SAMPLING_EVENT = "DWC-ARCHIVE-SAMPLING-EVENT"; private static final String SERVICE_TYPE_RSS = "RSS"; private static final String CONTACT_TYPE_TECHNICAL = "technical"; private static final String CONTACT_TYPE_ADMINISTRATIVE = "administrative"; private HttpUtil http; private SAXParser saxParser; private Gson gson; private ConfigWarnings warnings; private ResourceManager resourceManager; // create instance of BaseAction - allows class to retrieve i18n terms via getText() private BaseAction baseAction; @Inject public RegistryManagerImpl(AppConfig cfg, DataDir dataDir, HttpUtil httpUtil, SAXParserFactory saxFactory, ConfigWarnings warnings, SimpleTextProvider textProvider, RegistrationManager registrationManager, ResourceManager resourceManager) throws ParserConfigurationException, SAXException { super(cfg, dataDir); this.saxParser = saxFactory.newSAXParser(); this.http = httpUtil; this.gson = new GsonBuilder().setDateFormat("yyyy-MM-dd").create(); this.warnings = warnings; this.resourceManager = resourceManager; baseAction = new BaseAction(textProvider, cfg, registrationManager); } private List<NameValuePair> buildRegistryParameters(Resource resource) { List<NameValuePair> data = new ArrayList<NameValuePair>(); Eml eml = resource.getEml(); // the DOI assigned/registered to the last published public version (not the DOI reserved) DOI doi = resource.getAssignedDoi(); if (doi != null) { data.add(new BasicNameValuePair("doi", doi.toString())); log.debug("Including registry param doi=" + doi.toString()); } // otherwise try using the DOI citation identifier of the last published public version, see issue #1276 else { DOI existingDoi = getLastPublishedVersionExistingDoi(resource); if (existingDoi != null) { data.add(new BasicNameValuePair("doi", existingDoi.toString())); log.debug("Including registry param doi=" + existingDoi.toString()); } } data.add(new BasicNameValuePair("name", resource.getTitle() != null ? StringUtils.trimToEmpty(resource.getTitle()) : StringUtils.trimToEmpty(resource.getShortname()))); data.add(new BasicNameValuePair("description", Joiner.on(SystemUtils.LINE_SEPARATOR).join(eml.getDescription()))); data.add(new BasicNameValuePair("homepageURL", StringUtils.trimToEmpty(eml.getDistributionUrl()))); data.add(new BasicNameValuePair("logoURL", StringUtils.trimToEmpty(eml.getLogoUrl()))); // Eml contact agent: Agent primaryContact = getPrimaryContact(resource.getEml()); // if primaryContact is null, use resource creator as primary contact. if (primaryContact == null) { primaryContact = new Agent(); primaryContact.setEmail(resource.getCreator().getEmail()); primaryContact.setFirstName(resource.getCreator().getFirstname()); primaryContact.setLastName(resource.getCreator().getLastname()); primaryContact.setRole(null); } String primaryContactType = primaryContact.getRole() == null ? CONTACT_TYPE_TECHNICAL : CONTACT_TYPE_ADMINISTRATIVE; // Change the role to null like was before. primaryContact.setRole(null); data.add(new BasicNameValuePair("primaryContactType", primaryContactType)); data.add(new BasicNameValuePair("primaryContactEmail", StringUtils.trimToEmpty(primaryContact.getEmail()))); data.add(new BasicNameValuePair("primaryContactName", StringUtils.trimToNull(StringUtils.trimToEmpty(primaryContact.getFullName())))); data.add(new BasicNameValuePair("primaryContactAddress", StringUtils.trimToEmpty(primaryContact.getAddress().toFormattedString()))); data.add(new BasicNameValuePair("primaryContactPhone", StringUtils.trimToEmpty(primaryContact.getPhone()))); // see if we have a published dwca or if its only metadata RegistryServices services = buildServiceTypeParams(resource); data.add(new BasicNameValuePair("serviceTypes", services.serviceTypes)); data.add(new BasicNameValuePair("serviceURLs", services.serviceURLs)); return data; } /** * Builds service type parameters used in push or post to Registry. There can only be 4 different types of Services * that the IPT registers: EML, DWC-ARCHIVE-OCCURRENCE, DWC-ARCHIVE-CHECKLIST, DWC-ARCHIVE-SAMPLING-EVENT - that's it. * * @param resource published resource * * @return RegistryServices object, with urls and types strings */ private RegistryServices buildServiceTypeParams(Resource resource) { RegistryServices rs = new RegistryServices(); // the EML service is mandatory, so add the type and URL rs.serviceTypes = SERVICE_TYPE_EML; rs.serviceURLs = cfg.getResourceEmlUrl(resource.getShortname()); // check are there any other services: DWC-ARCHIVE-OCCURRENCE, DWC-ARCHIVE-CHECKLIST, or DWC-ARCHIVE-SAMPLING-EVENT if (resource.hasPublishedData() && resource.getCoreTypeTerm() != null) { if (DwcTerm.Occurrence == resource.getCoreTypeTerm()) { log.debug("Registering EML & DwC-A Occurrence Service"); rs.serviceURLs += "|" + cfg.getResourceArchiveUrl(resource.getShortname()); rs.serviceTypes += "|" + SERVICE_TYPE_OCCURRENCE; } else if (DwcTerm.Taxon == resource.getCoreTypeTerm()) { log.debug("Registering EML & DwC-A Checklist Service"); rs.serviceURLs += "|" + cfg.getResourceArchiveUrl(resource.getShortname()); rs.serviceTypes += "|" + SERVICE_TYPE_CHECKLIST; } else if (DwcTerm.Event == resource.getCoreTypeTerm()) { log.debug("Registering EML & DwC-A Sampling Event Service"); rs.serviceURLs += "|" + cfg.getResourceArchiveUrl(resource.getShortname()); rs.serviceTypes += "|" + SERVICE_TYPE_SAMPLING_EVENT; } else { log.warn("Unknown core resource type " + resource.getCoreTypeTerm()); log.debug("Registering EML service only"); } } else { log.debug("Resource has no published data, therefore only the EML Service will be registered"); } return rs; } /* * (non-Javadoc) * @see org.gbif.ipt.service.registry.RegistryManager#deregister(org.gbif.ipt.model.Resource) */ public void deregister(Resource resource) throws RegistryException { try { if (resource.getOrganisation() != null) { Response resp = http.delete(getDeleteResourceUri(resource.getKey().toString()), orgCredentials(resource.getOrganisation())); if (HttpUtil.success(resp)) { log.info("The resource has been deleted. Resource key: " + resource.getKey().toString()); } else { log.error("Deregister resource response received=" + resp.getStatusCode() + ": " + resp.content); throw new RegistryException(TYPE.BAD_RESPONSE, "Empty registry response"); } } else { throw new RegistryException(TYPE.NOT_AUTHORISED, "Credentials should be specified"); } } catch (IOException e) { throw new RegistryException(TYPE.IO_ERROR, e); } catch (Exception e) { String msg = "Bad registry response: " + e.getMessage(); log.error(msg, e); throw new RegistryException(RegistryException.TYPE.BAD_RESPONSE, msg); } } /** * Returns the delete resource URL. */ private String getDeleteResourceUri(String resourceKey) { return String.format("%s%s%s", cfg.getRegistryUrl(), "/registry/ipt/resource/", resourceKey); } /* * (non-Javadoc) * @see org.gbif.ipt.service.registry.RegistryManager#getExtensions() */ public List<Extension> getExtensions() throws RegistryException { Map<String, List<Extension>> jSONExtensions = gson .fromJson(requestHttpGetFromRegistry(getExtensionsURL(true)).content, new TypeToken<Map<String, List<Extension>>>() { }.getType()); return (jSONExtensions.get("extensions") == null) ? new ArrayList<Extension>() : jSONExtensions.get("extensions"); } /** * Returns the Extensions url. */ private String getExtensionsURL(boolean json) { return String.format("%s%s%s", cfg.getRegistryUrl(), "/registry/extensions", json ? ".json" : "/"); } /** * Returns the IPT Resource url. */ private String getIptResourceUri() { return String.format("%s%s", cfg.getRegistryUrl(), "/registry/ipt/resource"); } /** * Returns the IPT update Resource url. */ private String getIptUpdateResourceUri(String resourceKey) { return String.format("%s%s%s", cfg.getRegistryUrl(), "/registry/ipt/resource/", resourceKey); } /** * Returns the IPT update url used in GBIF Registry. */ private String getIptUpdateUri(String iptKey) { return String.format("%s%s%s", cfg.getRegistryUrl(), "/registry/ipt/update/", iptKey); } /** * Returns the IPT url. */ private String getIptUri() { return String.format("%s%s", cfg.getRegistryUrl(), "/registry/ipt/register"); } /** * Returns the login URL. */ private String getLoginURL(String organisationKey) { return String.format("%s%s%s%s", cfg.getRegistryUrl(), "/registry/organisation/", organisationKey, "?op=login"); } /** * Returns the URI that will return a list of Resources associated to an Organization in JSON. */ private String getOrganisationsResourcesUri(final String organisationKey) { return String.format("%s%s%s", cfg.getRegistryUrl(), "/registry/resource.json?organisationKey=", organisationKey); } /** * Returns the URI that will return a single Organization in JSON. */ private String getOrganisationUri(final String organisationKey) { return String.format("%s%s%s", cfg.getRegistryUrl(), "/registry/organisation/", organisationKey + ".json"); } /* * (non-Javadoc) * @see org.gbif.ipt.service.registry.RegistryManager#getOrganisations() */ public List<Organisation> getOrganisations() { List<Map<String, String>> organisationsTemp = new ArrayList<Map<String, String>>(); try { organisationsTemp = gson .fromJson(requestHttpGetFromRegistry(getOrganisationsURL(true)).content, new TypeToken<List<Map<String, String>>>() { }.getType()); } catch (RegistryException e) { // log as specific error message as possible about why the Registry error occurred String msg = RegistryException.logRegistryException(e.getType(), baseAction); // add startup error message about Registry error warnings.addStartupError(msg); log.error(msg); // add startup error message that explains the consequence of the Registry error msg = baseAction.getText("admin.organisations.couldnt.load", new String[] {cfg.getRegistryUrl()}); warnings.addStartupError(msg); log.error(msg); } // populate Organisation list List<Organisation> organisations = new ArrayList<Organisation>(); int invalid = 0; for (Map<String, String> org : organisationsTemp) { if (org.isEmpty() || StringUtils.isBlank(org.get("key")) || StringUtils.isBlank(org.get("name"))) { invalid++; } else { Organisation o = new Organisation(); o.setName(org.get("name")); try { o.setKey(org.get("key")); organisations.add(o); } catch (IllegalArgumentException e) { // this is not a uuid... invalid++; } } if (invalid > 0) { log.debug("Skipped " + invalid + " invalid organisation JSON objects"); } } return organisations; } /* * (non-Javadoc) * @see org.gbif.ipt.service.registry.RegistryManager#getOrganisation() */ public Organisation getRegisteredOrganisation(String key) { Organisation organisation = null; if (!Strings.isNullOrEmpty(key)) { try { organisation = gson.fromJson(requestHttpGetFromRegistry(getOrganisationUri(key)).content, new TypeToken<Organisation>() { }.getType()); } catch (RegistryException e) { // log as specific error message as possible about why the Registry error occurred String msg = RegistryException.logRegistryException(e.getType(), baseAction); // add startup error message about Registry error warnings.addStartupError(msg); log.error(msg); // add startup error message that explains the consequence of the Registry error msg = baseAction.getText("admin.organisation.couldnt.load", new String[] {key, cfg.getRegistryUrl()}); warnings.addStartupError(msg); log.error(msg); } catch (JsonSyntaxException e) { // add startup error message that explains the consequence of the error String msg = baseAction.getText("admin.organisation.couldnt.load", new String[] {key, cfg.getRegistryUrl()}); warnings.addStartupError(msg); log.error(msg); } } return organisation; } /** * Returns the Organisations url */ private String getOrganisationsURL(boolean json) { return String.format("%s%s%s", cfg.getRegistryUrl(), "/registry/organisation", json ? ".json" : "/"); } /** * Returns the primary contact agent depending on the following rules: * 1. Resource Contact. * 2. If (1) is incomplete (missing email or last name) use Resource Creator. * 3. if (2) is incomplete, use Metadata Provider. * 4. if (3) is incomplete return null. */ private Agent getPrimaryContact(Eml eml) { List<Agent> agents = Lists.newArrayList(); for (Agent contact: eml.getContacts()) { contact.setRole("PointOfContact"); agents.add(contact); } for (Agent creator: eml.getCreators()) { creator.setRole("Originator"); agents.add(creator); } for (Agent metadataProvider: eml.getMetadataProviders()) { metadataProvider.setRole("MetadataProvider"); agents.add(metadataProvider); } for (Agent agent: agents) { if (AgentValidator.hasCompleteContactInfo(agent)) { return agent; } } return null; } /** * Returns the ATOM url */ private String getRssFeedURL() { return String.format("%s/rss.do", cfg.getBaseUrl()); } protected InputStream getStream(String source) { return new ByteArrayInputStream(source.getBytes()); } /* * (non-Javadoc) * @see org.gbif.ipt.service.registry.RegistryManager#getVocabularies() */ public List<Vocabulary> getVocabularies() throws RegistryException { Map<String, List<Vocabulary>> jSONVocabularies = gson .fromJson(requestHttpGetFromRegistry(getVocabulariesURL(true)).content, new TypeToken<Map<String, List<Vocabulary>>>() { }.getType()); return (jSONVocabularies.get("thesauri") == null) ? new ArrayList<Vocabulary>() : jSONVocabularies.get("thesauri"); } /* * (non-Javadoc) * @see org.gbif.ipt.service.registry.RegistryManager#getOrganisationsResources */ public List<Resource> getOrganisationsResources(String organisationKey) throws RegistryException { List<Map<String, String>> resourcesTemp; try { resourcesTemp = gson.fromJson(requestHttpGetFromRegistry(getOrganisationsResourcesUri(organisationKey)).content, new TypeToken<List<Map<String, String>>>() { }.getType()); } catch (JsonSyntaxException e) { // throw new RegistryException if a non-parsable response was encountered throw new RegistryException(TYPE.BAD_RESPONSE, "Unexpected, non-parsable response format encountered."); } catch (RegistryException e) { // just rethrow if a RegistryException was encountered throw e; } // populate Resources list List<Resource> resources = new ArrayList<Resource>(); int invalid = 0; for (Map<String, String> res : resourcesTemp) { if (res.isEmpty() || StringUtils.isBlank(res.get("key")) || StringUtils.isBlank(res.get("name"))) { invalid++; } else { Resource r = new Resource(); r.setShortname(res.get("name")); r.setTitle(res.get("name")); String key = res.get("key"); // key must be UUID - convert from String to UUID try { UUID uuid = UUID.fromString(key); r.setKey(uuid); } catch (IllegalArgumentException e) { invalid++; } resources.add(r); } if (invalid > 0) { log.debug("Skipped " + invalid + " invalid dataset JSON objects"); } } return resources; } /** * Executes an HTTP Get Request against the GBIF Registry. If the content is not null, the Response is returned. * Otherwise, if the content was null, or an exception occurred, it throws the appropriate type of RegistryException. * * @param url Get request URL * @return Response if the content was not null, or a RegistryException * @throws RegistryException (with RegistryException.type) if the content was null or an exception occurred */ private Response requestHttpGetFromRegistry(String url) throws RegistryException { try { Response resp = http.get(url); if (resp != null && resp.content != null) { return resp; } else { throw new RegistryException(TYPE.BAD_RESPONSE, "Response content is null"); } } catch (ClassCastException e) { throw new RegistryException(TYPE.BAD_RESPONSE, e); } catch (ConnectException e) { // normally happens when a timeout appears - probably a firewall or proxy problem. throw new RegistryException(TYPE.PROXY, e); } catch (UnknownHostException e) { try { // if server cannot connect to Google - probably the Internet connection is not active. http.get("http://www.google.com"); } catch (Exception e1) { throw new RegistryException(TYPE.NO_INTERNET, e1); } // if server can connect to Google - probably the GBIF Registry page is down. throw new RegistryException(TYPE.SITE_DOWN, e); } catch (IOException e) { throw new RegistryException(TYPE.IO_ERROR, e); } catch (URISyntaxException e) { throw new RegistryException(TYPE.BAD_REQUEST, "Please check the request URL: " + ((url != null) ? url : "empty URL used!")); } } /** * Returns the Extensions url. */ private String getVocabulariesURL(boolean json) { return String.format("%s%s%s", cfg.getRegistryUrl(), "/registry/thesauri", json ? ".json" : "/"); } /** * Populate credentials for Organisation ws request. * * @param org Organisation * @return credentials */ private UsernamePasswordCredentials orgCredentials(Organisation org) { return new UsernamePasswordCredentials(org.getKey().toString(), org.getPassword()); } public UUID register(Resource resource, Organisation org, Ipt ipt) throws RegistryException { log.debug("Registering resource..."); if (!resource.isPublished()) { log.warn("Cannot register, resource not published yet"); return null; } // populate params for ws call to register resource List<NameValuePair> data = buildRegistryParameters(resource); // add additional ipt and organisation parameters data.add(new BasicNameValuePair("organisationKey", StringUtils.trimToEmpty(org.getKey().toString()))); data.add(new BasicNameValuePair("iptKey", StringUtils.trimToEmpty(ipt.getKey().toString()))); Response resp; try { resp = http.post(getIptResourceUri(), null, null, orgCredentials(org), new UrlEncodedFormEntity(data, Charset.forName("UTF-8"))); } catch (URISyntaxException e) { throw new RegistryException(TYPE.BAD_REQUEST, "Register resource failed: request URI invalid", e); } catch (IOException e) { throw new RegistryException(TYPE.IO_ERROR, "Register resource failed: I/O exception occurred", e); } if (HttpUtil.success(resp)) { log.info("Register resource was successful!"); } else { TYPE type = getRegistryExceptionType(resp.getStatusCode()); throw new RegistryException(type, "Register resource failed: " + resp.getStatusLine()); } // parse GBIF UDDI key String key; try { saxParser.parse(getStream(resp.content), newRegistryEntryHandler); key = newRegistryEntryHandler.key; if (StringUtils.trimToNull(key) == null) { key = newRegistryEntryHandler.resourceKey; } } catch (SAXException e) { throw new RegistryException(TYPE.BAD_RESPONSE, "Response received from resource registration couldn't be parsed", e); } catch (IOException e) { throw new RegistryException(TYPE.IO_ERROR, "Response received from resource registration couldn't be parsed", e); } // ensure non-empty key was found if (StringUtils.trimToNull(key) == null) { throw new RegistryException(TYPE.BAD_RESPONSE, "Response received from resource registration missing key!"); } // ensure valid UUID was found UUID uuidKey; try { uuidKey = UUID.fromString(key); } catch (IllegalArgumentException e) { throw new RegistryException(TYPE.BAD_RESPONSE, "Response received from resource registration has invalid key"); } log.info("A new resource has been registered with GBIF. [Key=" + key + "]"); resource.setKey(uuidKey); resource.setOrganisation(org); return uuidKey; } public String registerIPT(Ipt ipt, Organisation org) throws RegistryException { log.info("Registering IPT instance..."); // populate params for ws call to register IPT String orgKey = org.getKey().toString(); List<NameValuePair> data = buildIPTParameters(ipt, orgKey); // add IPT password used for updating the IPT data.add(new BasicNameValuePair("wsPassword", StringUtils.trimToEmpty(ipt.getWsPassword()))); // IPT instance Response resp; try { resp = http .post(getIptUri(), null, null, orgCredentials(org), new UrlEncodedFormEntity(data, Charset.forName("UTF-8"))); } catch (URISyntaxException e) { throw new RegistryException(TYPE.BAD_REQUEST, "Register IPT failed: request URI invalid", e); } catch (IOException e) { throw new RegistryException(TYPE.IO_ERROR, "Register IPT failed: I/O exception occurred", e); } if (HttpUtil.success(resp)) { log.info("Register IPT was successful!"); } else { TYPE type = getRegistryExceptionType(resp.getStatusCode()); throw new RegistryException(type, "Register IPT failed: " + resp.getStatusLine()); } // parse GBIF UUID key from response String key; try { saxParser.parse(getStream(resp.content), newRegistryEntryHandler); key = newRegistryEntryHandler.key; } catch (SAXException e) { throw new RegistryException(TYPE.BAD_RESPONSE, "Response received from IPT registration couldn't be parsed", e); } catch (IOException e) { throw new RegistryException(TYPE.IO_ERROR, "Response received from IPT registration couldn't be parsed", e); } // ensure non-empty key was found if (StringUtils.trimToNull(key) == null) { throw new RegistryException(TYPE.BAD_RESPONSE, "Response received from IPT registration missing key!"); } // ensure valid UUID was found UUID uuidKey; try { uuidKey = UUID.fromString(key); } catch (IllegalArgumentException e) { throw new RegistryException(TYPE.BAD_RESPONSE, "Response received from IPT registration has invalid key"); } log.info("A new ipt has been registered with GBIF. [Key=" + uuidKey.toString() + "]"); ipt.setKey(uuidKey.toString()); return key; } /** * Populate credentials for IPT ws request. * * @param ipt IPT * @return credentials */ private UsernamePasswordCredentials iptCredentials(Ipt ipt) { return new UsernamePasswordCredentials(ipt.getKey().toString(), ipt.getWsPassword()); } /** * Populate a list of name value pairs used in the common ws requests for IPT registrations and updates. * * @param ipt IPT * @param organisationKey Organisation key string * @return list of name value pairs, or an empty list if the IPT or organisation key were null */ private List<NameValuePair> buildIPTParameters(Ipt ipt, String organisationKey) { List<NameValuePair> data = new ArrayList<NameValuePair>(); if (ipt != null && organisationKey != null) { // main data.add(new BasicNameValuePair("organisationKey", StringUtils.trimToEmpty(organisationKey))); data.add(new BasicNameValuePair("name", StringUtils.trimToEmpty(ipt.getName()))); data.add(new BasicNameValuePair("description", StringUtils.trimToEmpty(ipt.getDescription()))); // primary contact data.add(new BasicNameValuePair("primaryContactType", StringUtils.trimToEmpty(ipt.getPrimaryContactType()))); data.add(new BasicNameValuePair("primaryContactName", StringUtils.trimToEmpty(ipt.getPrimaryContactName()))); data.add(new BasicNameValuePair("primaryContactEmail", StringUtils.trimToEmpty(ipt.getPrimaryContactEmail()))); // service/endpoint data.add(new BasicNameValuePair("serviceTypes", SERVICE_TYPE_RSS)); data.add(new BasicNameValuePair("serviceURLs", getRssFeedURL())); } else { log.debug("One or both of IPT and Organisation key were null. Params needed for ws will be empty"); } return data; } public void updateIpt(Ipt ipt) throws RegistryException { log.info("Update IPT registration..."); // populate params for ws call to update IPT String orgKey = (ipt != null && ipt.getOrganisationKey() != null) ? ipt.getOrganisationKey().toString() : null; List<NameValuePair> data = buildIPTParameters(ipt, orgKey); Response resp; try { resp = http.post(getIptUpdateUri(ipt.getKey().toString()), null, null, iptCredentials(ipt), new UrlEncodedFormEntity(data, Charset.forName("UTF-8"))); } catch (URISyntaxException e) { throw new RegistryException(TYPE.BAD_REQUEST, "Update IPT registration failed: request URI invalid", e); } catch (IOException e) { throw new RegistryException(TYPE.IO_ERROR, "Update IPT registration failed: I/O exception occurred", e); } if (HttpUtil.success(resp)) { log.info("Update IPT registration was successful!"); } else { // to continue updating registered resources, IPT update must have been successful TYPE type = getRegistryExceptionType(resp.getStatusCode()); throw new RegistryException(type, "Update IPT registration failed: " + resp.getStatusLine()); } List<Resource> resources = resourceManager.list(PublicationStatus.REGISTERED); if (!resources.isEmpty()) { log.info("Next, update " + resources.size() + " resource registrations..."); for (Resource resource : resources) { try { updateResource(resource, ipt.getKey().toString()); } catch (IllegalArgumentException e) { log.error(e.getMessage()); } } log.info("Resource registrations updated successfully!"); } } public void updateResource(Resource resource, String iptKey) throws RegistryException, IllegalArgumentException { if (!resource.isRegistered() || resource.getKey() == null) { throw new IllegalArgumentException( "Update resource registration failed: resource [shortname=" + resource.getShortname() + "] is not registered"); } log.info("Update resource registration... [key=" + resource.getKey().toString() + "]"); // populate params for ws call to update registered resource List<NameValuePair> data = buildRegistryParameters(resource); // ensure IPT serves relationship always gets created/updated data.add(new BasicNameValuePair("iptKey", StringUtils.trimToEmpty(iptKey))); Response resp; try { resp = http.post(getIptUpdateResourceUri(resource.getKey().toString()), null, null, orgCredentials(resource.getOrganisation()), new UrlEncodedFormEntity(data, Charset.forName("UTF-8"))); } catch (URISyntaxException e) { throw new RegistryException(TYPE.BAD_REQUEST, "Update resource registration failed: request URI invalid", e); } catch (IOException e) { throw new RegistryException(TYPE.IO_ERROR, "Update resource registration failed: I/O exception occurred", e); } if (HttpUtil.success(resp)) { log.info("Update resource registration was successful! [key=" + resource.getKey().toString() + "]"); // to avoid repetition, alert user here that update was successful baseAction.addActionMessage( baseAction.getText("manage.overview.resource.update.registration", new String[] {resource.getTitle()})); } else { TYPE type = getRegistryExceptionType(resp.getStatusCode()); throw new RegistryException(type, "Update resource registration failed [shortname=" + resource.getShortname() + ", key=" + resource.getKey().toString() + "]: " + resp.getStatusLine()); } } /** * Determine the type of RegistryException based on the error response code. In this context, error response codes * include all response codes higher than 300. * * @param code response code from GBIF Registry web service response * * @return RegistryException type based on response code */ @VisibleForTesting protected TYPE getRegistryExceptionType(int code) { Preconditions.checkArgument(code > 300); // never called on successful codes include OK (200) and CREATED (201) TYPE type; switch (code) { case 400: type = TYPE.BAD_REQUEST; break; case 401: type = TYPE.NOT_AUTHORISED; break; default: type = TYPE.BAD_RESPONSE; } return type; } public boolean validateOrganisation(String organisationKey, String password) { try { Response resp = http.get(getLoginURL(organisationKey), null, new UsernamePasswordCredentials(organisationKey, password)); return HttpUtil.success(resp); } catch (Exception e) { log.warn( "The organisation could not be validated using key (" + organisationKey + ") and password (" + password + ")", e); } return false; } /** * @return DOI citation identifier of last published version or null if no DOI citation identifier was assigned */ @VisibleForTesting protected DOI getLastPublishedVersionExistingDoi(Resource resource) { VersionHistory lastPublishedVersion = resource.getLastPublishedVersion(); if (lastPublishedVersion != null) { BigDecimal version = new BigDecimal(lastPublishedVersion.getVersion()); File emlFile = cfg.getDataDir().resourceEmlFile(resource.getShortname(), version); if (emlFile.exists()) { try { log.debug("Loading EML from file: " + emlFile.getAbsolutePath()); InputStream in = new FileInputStream(emlFile); Eml eml = EmlFactory.build(in); if (eml.getCitation() != null && !Strings.isNullOrEmpty(eml.getCitation().getIdentifier())) { String identifier = StringUtils.trimToNull(eml.getCitation().getIdentifier()); if (DOI.isParsable(identifier)) { return new DOI(identifier); } } } catch (Exception e) { log.error("Failed to check last published version citation identifier: " + e.getMessage(), e); } } } return null; } }