Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crossref as a DOI provider (experimental) #10806

Merged
merged 12 commits into from
Sep 17, 2024
3 changes: 3 additions & 0 deletions doc/release-notes/8581-add-crossref-pid-provider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Added CrossRef DOI Pid Provider

See Installation Configuration document for JVM Settings to enable CrossRef as a Pid Provider
53 changes: 53 additions & 0 deletions doc/sphinx-guides/source/installation/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,15 @@ this provider.
- :ref:`dataverse.pid.ezid.username`
- :ref:`dataverse.pid.ezid.password`

**JVM Options for CrossRef:**
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation, and some of the code, is for 'legacy' single pid configuration but I think we could/should only support handling the new multipid way of doing config. Although I think you've added it, I think it's reasonable to not support the legacy style at all for cross ref (since the idea of legacy was to support people who had already deployed with the old single provider jvm options and no one could have used CrossRef before now (except the developer's group).

To change the docs - see the DataCite section in the guide and add the info there (probably right after DataCite/before Handle.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will modify the doc


- :ref:`dataverse.pid.crossref.url`
- :ref:`dataverse.pid.crossref.rest-api-url`
- :ref:`dataverse.pid.crossref.username`
- :ref:`dataverse.pid.crossref.password`
- :ref:`dataverse.pid.crossref.depositor`
- :ref:`dataverse.pid.crossref.depositor-email`

**Database Settings:**

- :ref:`:DoiProvider <:DoiProvider>`
Expand Down Expand Up @@ -2841,6 +2850,50 @@ should delete the old JVM option and the wrapped password alias, then recreate
as shown for :ref:`dataverse.pid.datacite.password` but with the EZID alias
name.

.. _dataverse.pid.crossref.url:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are about legacy support which is no longer implemented - they can all be removed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed


dataverse.pid.crossref.url
++++++++++++++++++++++++++

CrossRef url used to post metadata.

.. _dataverse.pid.crossref.rest-api-url:

dataverse.pid.crossref.rest-api-url
+++++++++++++++++++++++++++++++++++

CrossRef API url to retrieve metadata information

.. _dataverse.pid.crossref.username:

dataverse.pid.crossref.username
+++++++++++++++++++++++++++++++

CrossRef uses `HTTP Basic authentication <https://en.wikipedia.org/wiki/Basic_access_authentication>`_
for their APIs.
- Used in conjunction with :ref:`dataverse.pid.crossref.url` and :ref:`dataverse.pid.crossref.password`.

.. _dataverse.pid.crossref.password:

dataverse.pid.crossref.password
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you edit this to move it to the multiple provide section, please add a note like the one on DataCite suggesting that more secure options should be used (versus having the password as a plain text jvm-option)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR was made before multiPid was supported/before docs were improved with that, etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will modify the doc

+++++++++++++++++++++++++++++++

- Used in conjunction with :ref:`dataverse.pid.crossref.url` and :ref:`dataverse.pid.crossref.username`.

.. _dataverse.pid.crossref.depositor:

dataverse.pid.crossref.depositor
++++++++++++++++++++++++++++++++

The entity, such as a person or organization, that deposited the Dataset in the repository

.. _dataverse.pid.crossref.depositor-email:

dataverse.pid.crossref.depositor-email
++++++++++++++++++++++++++++++++++++++

Contact email to the indicated Depositor

.. _dataverse.timerServer:

dataverse.timerServer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.logging.Level;
Expand All @@ -30,6 +29,7 @@
import edu.harvard.iq.dataverse.DvObjectServiceBean;
import edu.harvard.iq.dataverse.GlobalId;
import edu.harvard.iq.dataverse.pidproviders.doi.UnmanagedDOIProvider;
import edu.harvard.iq.dataverse.pidproviders.doi.crossref.CrossRefDOIProviderFactory;
import edu.harvard.iq.dataverse.pidproviders.doi.datacite.DataCiteDOIProvider;
import edu.harvard.iq.dataverse.pidproviders.doi.ezid.EZIdDOIProvider;
import edu.harvard.iq.dataverse.pidproviders.doi.fake.FakeDOIProvider;
Expand Down Expand Up @@ -188,6 +188,9 @@ private void loadProviders() {
legacy = new FakeDOIProvider("legacy", "legacy", authority, shoulder, identifierGenerationStyle,
dataFilePidFormat, "", "");
break;
case "CrossRef":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should drop this. If you want to support legacy still, this section should be doing like the DataCite legacy section above to read the legacy-style options and feed them into the provider directly (creating a new CrossRefDOIProvider rather than calling a factory method). Again - probably better to drop it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will remove

legacy = new CrossRefDOIProviderFactory().createPidProvider(JvmSettings.PID_DEFAULT_PROVIDER.lookup());
break;
}
break;
case "hdl":
Expand Down Expand Up @@ -247,4 +250,4 @@ public PidProvider getDefaultPidGenerator() {
return PidUtil.getPidProvider(protocol, authority, shoulder);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package edu.harvard.iq.dataverse.pidproviders.doi.crossref;

import edu.harvard.iq.dataverse.DvObject;
import edu.harvard.iq.dataverse.GlobalId;
import edu.harvard.iq.dataverse.pidproviders.doi.AbstractDOIProvider;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

public class CrossRefDOIPidProvider extends AbstractDOIProvider {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other DOI providers drop the Pid part of the name (or use DOI instead of Pid since it is the type of Pid they support), so this would be more consistent as CrossRefDOIProvider.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renaming

private static final Logger logger = Logger.getLogger(CrossRefDOIPidProvider.class.getCanonicalName());

public static final String TYPE = "crossref";

CrossRefDOIRegisterService crossRefDOIRegisterService;

public CrossRefDOIPidProvider(String id, String label, String providerAuthority, String providerShoulder, String identifierGenerationStyle, String datafilePidFormat, String managedList, String excludedList,
String url, String apiUrl, String username, String password, String depositor, String depositorEmail) {
super(id, label, providerAuthority, providerShoulder, identifierGenerationStyle, datafilePidFormat,
managedList, excludedList);

crossRefDOIRegisterService = new CrossRefDOIRegisterService(url, apiUrl, username, password, depositor, depositorEmail);
}

@Override
public boolean alreadyRegistered(GlobalId pid, boolean noProviderDefault) throws Exception {
logger.info("CrossRef alreadyRegistered");
if (pid == null || pid.asString().isEmpty()) {
logger.fine("No identifier sent.");
return false;
}
boolean alreadyExists;
String identifier = pid.asString();
try {
alreadyExists = crossRefDOIRegisterService.testDOIExists(identifier);
} catch (Exception e) {
logger.log(Level.WARNING, "alreadyExists failed");
return false;
}
return alreadyExists;
}

@Override
public boolean registerWhenPublished() {
return true;
}

@Override
public List<String> getProviderInformation() {
return List.of("CrossRef", "https://status.crossref.org/");
}

@Override
public String createIdentifier(DvObject dvObject) throws Throwable {
logger.info("CrossRef createIdentifier");
if (dvObject.getIdentifier() == null || dvObject.getIdentifier().isEmpty()) {
dvObject = generatePid(dvObject);
}
String identifier = getIdentifier(dvObject);
try {
String retString = crossRefDOIRegisterService.reserveIdentifier(identifier, dvObject);
logger.log(Level.FINE, "CrossRef create DOI identifier retString : " + retString);
return retString;
} catch (Exception e) {
logger.log(Level.WARNING, "CrossRef Identifier not created: create failed", e);
throw e;
}
}

@Override
public Map<String, String> getIdentifierMetadata(DvObject dvObject) {
logger.info("CrossRef getIdentifierMetadata");
String identifier = getIdentifier(dvObject);
Map<String, String> metadata = new HashMap<>();
try {
metadata = crossRefDOIRegisterService.getMetadata(identifier);
} catch (Exception e) {
logger.log(Level.WARNING, "getIdentifierMetadata failed", e);
}
return metadata;
}

@Override
public String modifyIdentifierTargetURL(DvObject dvObject) throws Exception {
logger.info("CrossRef modifyIdentifier");
String identifier = getIdentifier(dvObject);
try {
crossRefDOIRegisterService.modifyIdentifier(identifier, dvObject);
} catch (Exception e) {
logger.log(Level.WARNING, "modifyMetadata failed", e);
throw e;
}
return identifier;
}

@Override
public void deleteIdentifier(DvObject dvo) throws Exception {
logger.info("CrossRef deleteIdentifier");
}

@Override
public boolean publicizeIdentifier(DvObject dvObject) {
logger.info("CrossRef updateIdentifierStatus");
if (dvObject.getIdentifier() == null || dvObject.getIdentifier().isEmpty()) {
dvObject = generatePid(dvObject);
}
String identifier = getIdentifier(dvObject);

try {
crossRefDOIRegisterService.reserveIdentifier(identifier, dvObject);
return true;
} catch (Exception e) {
logger.log(Level.WARNING, "modifyMetadata failed: " + e.getMessage(), e);
return false;
}
}

@Override
public String getProviderType() {
return TYPE;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package edu.harvard.iq.dataverse.pidproviders.doi.crossref;

import com.google.auto.service.AutoService;
import edu.harvard.iq.dataverse.pidproviders.PidProvider;
import edu.harvard.iq.dataverse.pidproviders.PidProviderFactory;
import edu.harvard.iq.dataverse.settings.JvmSettings;
import edu.harvard.iq.dataverse.util.SystemConfig;

@AutoService(PidProviderFactory.class)
public class CrossRefDOIProviderFactory implements PidProviderFactory {

@Override
public PidProvider createPidProvider(String providerId) {
String providerType = JvmSettings.PID_PROVIDER_TYPE.lookup(providerId);
if (!providerType.equals(CrossRefDOIPidProvider.TYPE)) {
// Being asked to create a non-CrossRef provider
return null;
}
String providerLabel = JvmSettings.PID_PROVIDER_LABEL.lookup(providerId);
String providerAuthority = JvmSettings.PID_PROVIDER_AUTHORITY.lookup(providerId);
String providerShoulder = JvmSettings.PID_PROVIDER_SHOULDER.lookupOptional(providerId).orElse("");
String identifierGenerationStyle = JvmSettings.PID_PROVIDER_IDENTIFIER_GENERATION_STYLE
.lookupOptional(providerId).orElse("randomString");
String datafilePidFormat = JvmSettings.PID_PROVIDER_DATAFILE_PID_FORMAT.lookupOptional(providerId)
.orElse(SystemConfig.DataFilePIDFormat.DEPENDENT.toString());
String managedList = JvmSettings.PID_PROVIDER_MANAGED_LIST.lookupOptional(providerId).orElse("");
String excludedList = JvmSettings.PID_PROVIDER_EXCLUDED_LIST.lookupOptional(providerId).orElse("");

String baseUrl = JvmSettings.CROSSREF_URL.lookup(providerId);
String apiUrl = JvmSettings.CROSSREF_REST_API_URL.lookup(providerId);
String username = JvmSettings.CROSSREF_USERNAME.lookup(providerId);
String password = JvmSettings.CROSSREF_PASSWORD.lookup(providerId);
String depositor = JvmSettings.CROSSREF_DEPOSITOR.lookup(providerId);
String depositorEmail = JvmSettings.CROSSREF_DEPOSITOR_EMAIL.lookup(providerId);

return new CrossRefDOIPidProvider(providerId, providerLabel, providerAuthority, providerShoulder, identifierGenerationStyle,
datafilePidFormat, managedList, excludedList, baseUrl, apiUrl, username, password, depositor, depositorEmail);
}

public String getType() {
return CrossRefDOIPidProvider.TYPE;
}
}
Loading
Loading