Commit 4c94a28895d40a3081e574ba9f1d22caaf2ddf2a

Authored by Paulo Graça
0 parents

PG: Initial Setup, working for API 2.0 RC1

README.md
pom.xml
... ... @@ -0,0 +1,177 @@
  1 +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  2 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-4.0.0.xsd">
  3 + <modelVersion>4.0.0</modelVersion>
  4 +
  5 + <groupId>org.um.dsi.gavea</groupId>
  6 + <artifactId>degois-orcid-client</artifactId>
  7 + <version>2.0-rc1</version>
  8 +
  9 + <name>Orcid Client</name>
  10 +
  11 + <repositories>
  12 + <repository>
  13 + <id>maven-restlet</id>
  14 + <name>Restlet repository</name>
  15 + <url>http://maven.restlet.com</url>
  16 + </repository>
  17 + </repositories>
  18 +
  19 + <properties>
  20 + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  21 + <java.version>1.8</java.version>
  22 +
  23 + <restlet-version>2.3.5</restlet-version>
  24 + <jersey.version>2.22.1</jersey.version>
  25 + <jaxb2-basics.version>0.9.5</jaxb2-basics.version>
  26 + <log4j.version>2.4</log4j.version>
  27 + <junit.version>4.12</junit.version>
  28 + </properties>
  29 +
  30 + <dependencies>
  31 + <!-- Restlet -->
  32 + <dependency>
  33 + <groupId>org.restlet.jse</groupId>
  34 + <artifactId>org.restlet</artifactId>
  35 + <version>${restlet-version}</version>
  36 + </dependency>
  37 + <dependency>
  38 + <groupId>org.restlet.jse</groupId>
  39 + <artifactId>org.restlet.ext.jackson</artifactId>
  40 + <version>${restlet-version}</version>
  41 + </dependency>
  42 + <!-- Jersey Client -->
  43 + <dependency>
  44 + <groupId>org.glassfish.jersey.core</groupId>
  45 + <artifactId>jersey-client</artifactId>
  46 + <version>${jersey.version}</version>
  47 + </dependency>
  48 + <dependency>
  49 + <groupId>org.glassfish.jersey.containers</groupId>
  50 + <artifactId>jersey-container-servlet</artifactId>
  51 + <version>${jersey.version}</version>
  52 + </dependency>
  53 + <dependency>
  54 + <groupId>org.glassfish.jersey.media</groupId>
  55 + <artifactId>jersey-media-json-jackson</artifactId>
  56 + <version>${jersey.version}</version>
  57 + </dependency>
  58 + <dependency>
  59 + <groupId>org.glassfish.jersey.media</groupId>
  60 + <artifactId>jersey-media-jaxb</artifactId>
  61 + <version>${jersey.version}</version>
  62 + </dependency>
  63 + <!-- JAXB2 Commons -->
  64 + <dependency>
  65 + <groupId>org.jvnet.jaxb2_commons</groupId>
  66 + <artifactId>jaxb2-basics-runtime</artifactId>
  67 + <version>${jaxb2-basics.version}</version>
  68 + </dependency>
  69 + <!-- Log4J -->
  70 + <dependency>
  71 + <groupId>org.apache.logging.log4j</groupId>
  72 + <artifactId>log4j-core</artifactId>
  73 + <version>${log4j.version}</version>
  74 + </dependency>
  75 + <!-- JUnit -->
  76 + <dependency>
  77 + <groupId>junit</groupId>
  78 + <artifactId>junit</artifactId>
  79 + <version>${junit.version}</version>
  80 + <scope>test</scope>
  81 + </dependency>
  82 + </dependencies>
  83 +
  84 + <build>
  85 + <plugins>
  86 + <plugin>
  87 + <groupId>org.jvnet.jaxb2.maven2</groupId>
  88 + <artifactId>maven-jaxb2-plugin</artifactId>
  89 + <version>0.13.1</version>
  90 + <executions>
  91 + <execution>
  92 + <goals>
  93 + <goal>generate</goal>
  94 + </goals>
  95 + </execution>
  96 + </executions>
  97 + <configuration>
  98 + <noFileHeader>true</noFileHeader>
  99 + <generateDirectory>${project.build.directory}/generated-sources
  100 + </generateDirectory>
  101 + <bindingDirectory>${basedir}/src/main/resources/binding
  102 + </bindingDirectory>
  103 + <schemaDirectory>${basedir}/src/main/resources/xsd
  104 + </schemaDirectory>
  105 + <schemaIncludes>
  106 + <include>common_2.0_rc1/*.xsd</include>
  107 + <include>record_2.0_rc1/*.xsd</include>
  108 + </schemaIncludes>
  109 + <episode>false</episode>
  110 + <args>
  111 + <arg>-Xsetters</arg>
  112 + <arg>-Xvalue-constructor</arg>
  113 + <arg>-XtoString</arg>
  114 + <arg>-extension</arg>
  115 + <arg>-Xnamespace-prefix</arg>
  116 + </args>
  117 + </configuration>
  118 + <dependencies>
  119 + <dependency>
  120 + <groupId>org.glassfish.jaxb</groupId>
  121 + <artifactId>jaxb-core</artifactId>
  122 + <version>2.2.11</version>
  123 + </dependency>
  124 + <dependency>
  125 + <groupId>org.jvnet.jaxb2_commons</groupId>
  126 + <artifactId>jaxb2-basics</artifactId>
  127 + <version>${jaxb2-basics.version}</version>
  128 + </dependency>
  129 + <dependency>
  130 + <groupId>org.jvnet.jaxb2_commons</groupId>
  131 + <artifactId>jaxb2-value-constructor</artifactId>
  132 + <version>3.0</version>
  133 + </dependency>
  134 + <dependency>
  135 + <groupId>org.jvnet.jaxb2_commons</groupId>
  136 + <artifactId>jaxb2-namespace-prefix</artifactId>
  137 + <version>1.1</version>
  138 + </dependency>
  139 + </dependencies>
  140 + </plugin>
  141 + <plugin>
  142 + <groupId>org.apache.maven.plugins</groupId>
  143 + <artifactId>maven-compiler-plugin</artifactId>
  144 + <version>3.3</version>
  145 + <inherited>true</inherited>
  146 + <configuration>
  147 + <source>${java.version}</source>
  148 + <target>${java.version}</target>
  149 + </configuration>
  150 + </plugin>
  151 + <plugin>
  152 + <groupId>org.apache.maven.plugins</groupId>
  153 + <artifactId>maven-surefire-plugin</artifactId>
  154 + <version>2.19.1</version>
  155 + <configuration>
  156 + <skipTests>true</skipTests>
  157 + </configuration>
  158 + </plugin>
  159 + </plugins>
  160 +<resources>
  161 + <resource>
  162 + <directory>${project.build.directory}/generated-sources</directory>
  163 + <includes>
  164 + <include>**/*.java</include>
  165 + </includes>
  166 + </resource>
  167 + <resource>
  168 + <directory>src/main/java</directory>
  169 + <includes>
  170 + <include>**/*.java</include>
  171 + </includes>
  172 + </resource>
  173 +</resources>
  174 + </build>
  175 +</project>
  176 +
  177 +
... ...
src/main/java/org/um/dsi/gavea/orcid/client/BaseOrcidOAuthClient.java
... ... @@ -0,0 +1,174 @@
  1 +package org.um.dsi.gavea.orcid.client;
  2 +
  3 +import java.io.Serializable;
  4 +
  5 +import javax.ws.rs.ProcessingException;
  6 +import javax.ws.rs.client.Client;
  7 +import javax.ws.rs.client.ClientBuilder;
  8 +import javax.ws.rs.client.Entity;
  9 +import javax.ws.rs.client.WebTarget;
  10 +import javax.ws.rs.core.MediaType;
  11 +import javax.ws.rs.core.MultivaluedMap;
  12 +import javax.ws.rs.core.Response;
  13 +
  14 +import org.apache.logging.log4j.LogManager;
  15 +import org.apache.logging.log4j.Logger;
  16 +import org.glassfish.jersey.client.ClientProperties;
  17 +import org.um.dsi.gavea.orcid.client.constants.OrcidConstants;
  18 +import org.um.dsi.gavea.orcid.client.exception.OrcidClientException;
  19 +import org.um.dsi.gavea.orcid.model.error.Error;
  20 +
  21 +public class BaseOrcidOAuthClient implements Serializable {
  22 + private static final long serialVersionUID = 8455820192048815978L;
  23 +
  24 + private static final Logger _log = LogManager.getLogger(BaseOrcidOAuthClient.class);
  25 +
  26 +
  27 + private Client client;
  28 + private WebTarget target;
  29 +
  30 + protected final String loginUri;
  31 + protected final String apiUri;
  32 + protected final String clientId;
  33 + protected final String clientSecret;
  34 + protected final String redirectUri;
  35 +
  36 +
  37 + public BaseOrcidOAuthClient(String loginUri, String apiUri, String clientId, String clientSecret, String redirectUri) {
  38 + if (loginUri == null || apiUri == null || clientId == null || clientSecret == null || redirectUri == null)
  39 + throw new IllegalArgumentException("Cannot create OrcidOAuthClient - missing init parameters");
  40 +
  41 + this.loginUri = loginUri;
  42 + this.apiUri = apiUri;
  43 + this.clientId = clientId;
  44 + this.clientSecret = clientSecret;
  45 + this.redirectUri = redirectUri;
  46 + }
  47 +
  48 + protected <T> Response post(final String authorizationCode, final MultivaluedMap<String, String> formData) throws OrcidClientException {
  49 + createConnection(OrcidConstants.ENDPOINT_OAUTH_TOKEN, null, null);
  50 +
  51 + Response response = target.request(MediaType.APPLICATION_JSON_TYPE).post(Entity.form(formData));
  52 + _log.debug("[getAccessTokens].[Response.Status] " + response.getStatus());
  53 + _log.debug("[getAccessTokens].[Response.Reason] " + response.getStatusInfo().getReasonPhrase());
  54 +
  55 + exceptionBuilder(response);
  56 +
  57 + return response;
  58 + }
  59 +
  60 + protected <T> Response get(final String endpoint, final OrcidAccessToken token, final String putCode) throws OrcidClientException {
  61 + Response response;
  62 + try {
  63 + createConnection(endpoint, token.getOrcid(), putCode);
  64 +
  65 + response = target.request(OrcidConstants.APPLICATION_ORCID_XML)
  66 + .header("Authorization", "Bearer" + token.getAccess_token()).get();
  67 + _log.debug("[get].[Response.Status] " + response.getStatus());
  68 + _log.debug("[get].[Response.Reason] " + response.getStatusInfo().getReasonPhrase());
  69 +
  70 + exceptionBuilder(response);
  71 + } catch(ProcessingException e) {
  72 + response = Response.serverError().build();
  73 + exceptionBuilder(response);
  74 + }
  75 +
  76 + return response;
  77 + }
  78 +
  79 + protected <T> Response post(final String endpoint, final OrcidAccessToken token, final Entity<T> entity) throws OrcidClientException {
  80 + Response response;
  81 + try {
  82 + createConnection(endpoint, token.getOrcid(), null);
  83 +
  84 + response = target.request(OrcidConstants.APPLICATION_ORCID_XML)
  85 + .header("Authorization", "Bearer" + token.getAccess_token()).post(entity);
  86 + _log.debug("[post].[Response.Status] " + response.getStatus());
  87 + _log.debug("[post].[Response.Reason] " + response.getStatusInfo().getReasonPhrase());
  88 +
  89 + exceptionBuilder(response);
  90 + } catch(ProcessingException e) {
  91 + response = Response.serverError().build();
  92 + exceptionBuilder(response);
  93 + }
  94 +
  95 + return response;
  96 + }
  97 +
  98 + protected <T> Response put(final String endpoint, final OrcidAccessToken token, final String putCode, final Entity<T> entity) throws OrcidClientException {
  99 + Response response;
  100 + try {
  101 + createConnection(endpoint, token.getOrcid(), putCode);
  102 +
  103 + response = target.request(OrcidConstants.APPLICATION_ORCID_XML)
  104 + .header("Authorization", "Bearer" + token.getAccess_token()).put(entity);
  105 + _log.debug("[put].[Response.Status] " + response.getStatus());
  106 + _log.debug("[put].[Response.Reason] " + response.getStatusInfo().getReasonPhrase());
  107 +
  108 + exceptionBuilder(response);
  109 + } catch(ProcessingException e) {
  110 + response = Response.serverError().build();
  111 + exceptionBuilder(response);
  112 + }
  113 +
  114 + return response;
  115 + }
  116 +
  117 + protected <T> Response delete(final String endpoint, final OrcidAccessToken token, final String putCode) throws OrcidClientException {
  118 + Response response;
  119 + try {
  120 + createConnection(endpoint, token.getOrcid(), putCode);
  121 +
  122 + response = target.request(OrcidConstants.APPLICATION_ORCID_XML)
  123 + .header("Authorization", "Bearer" + token.getAccess_token()).delete();
  124 + _log.debug("[delete].[Response.Status] " + response.getStatus());
  125 + _log.debug("[delete].[Response.Reason] " + response.getStatusInfo().getReasonPhrase());
  126 +
  127 + exceptionBuilder(response);
  128 + } catch(ProcessingException e) {
  129 + response = Response.serverError().build();
  130 + exceptionBuilder(response);
  131 + }
  132 +
  133 + return response;
  134 + }
  135 +
  136 +
  137 + private void createConnection(final String endpoint, final String orcidId, final String putCode) {
  138 + if (client != null)
  139 + client.close();
  140 +
  141 + client = ClientBuilder.newClient();
  142 + client.property(ClientProperties.CONNECT_TIMEOUT, OrcidConstants.CLIENT_CONNECT_TIMEOUT);
  143 + client.property(ClientProperties.READ_TIMEOUT, OrcidConstants.CLIENT_READ_TIMEOUT);
  144 +
  145 + target = client.target(apiUri).path(buildPath(endpoint, orcidId, putCode));
  146 + }
  147 +
  148 + private String buildPath(final String endpoint, final String orcidId, final String putCode) {
  149 + StringBuilder sb = new StringBuilder();
  150 +
  151 + if (orcidId != null && !"".equals(orcidId)) {
  152 + sb.append(OrcidConstants.API_VERSION).append("/").append(orcidId);
  153 + }
  154 +
  155 + if (endpoint != null && !"".equals(endpoint))
  156 + sb.append(endpoint);
  157 +
  158 + if (putCode != null && !"".equals(putCode)) {
  159 + sb.append("/").append(putCode);
  160 + }
  161 +
  162 + return sb.toString();
  163 + }
  164 +
  165 + protected void exceptionBuilder(Response response) throws OrcidClientException {
  166 + if(response.getStatus() > 300) {
  167 + if(response.getStatus() == 400) {
  168 + Error error = response.readEntity(Error.class);
  169 + throw new OrcidClientException(error.getResponseCode(), error.getUserMessage(), error.getErrorCode(), error.getDeveloperMessage());
  170 + } else
  171 + throw new OrcidClientException(response.getStatus(), response.getStatus() + " - " + response.getStatusInfo().getReasonPhrase());
  172 + }
  173 + }
  174 +}
... ...
src/main/java/org/um/dsi/gavea/orcid/client/OrcidAccessToken.java
... ... @@ -0,0 +1,89 @@
  1 +package org.um.dsi.gavea.orcid.client;
  2 +
  3 +import java.io.Serializable;
  4 +
  5 +public class OrcidAccessToken implements Serializable, Cloneable {
  6 + private static final long serialVersionUID = 6776130415600464081L;
  7 +
  8 +
  9 + private String name;
  10 + private String access_token;
  11 + private String token_type;
  12 + private String refresh_token;
  13 + private int expires_in;
  14 + private String scope;
  15 + private String orcid;
  16 + private String state;
  17 +
  18 +
  19 + public String getName() {
  20 + return name;
  21 + }
  22 + public void setName(String name) {
  23 + this.name = name;
  24 + }
  25 +
  26 + public String getAccess_token() {
  27 + return access_token;
  28 + }
  29 + public void setAccess_token(String access_token) {
  30 + this.access_token = access_token;
  31 + }
  32 +
  33 + public String getToken_type() {
  34 + return token_type;
  35 + }
  36 + public void setToken_type(String token_type) {
  37 + this.token_type = token_type;
  38 + }
  39 +
  40 + public String getRefresh_token() {
  41 + return refresh_token;
  42 + }
  43 + public void setRefresh_token(String refresh_token) {
  44 + this.refresh_token = refresh_token;
  45 + }
  46 +
  47 + public int getExpires_in() {
  48 + return expires_in;
  49 + }
  50 + public void setExpires_in(int expires_in) {
  51 + this.expires_in = expires_in;
  52 + }
  53 +
  54 + public String getScope() {
  55 + return scope;
  56 + }
  57 + public void setScope(String scope) {
  58 + this.scope = scope;
  59 + }
  60 +
  61 + public String getOrcid() {
  62 + return orcid;
  63 + }
  64 + public void setOrcid(String orcid) {
  65 + this.orcid = orcid;
  66 + }
  67 +
  68 + public String getState() {
  69 + return state;
  70 + }
  71 + public void setState(String state) {
  72 + this.state = state;
  73 + }
  74 +
  75 + @Override
  76 + public Object clone() throws CloneNotSupportedException {
  77 + return (OrcidAccessToken) super.clone();
  78 + }
  79 +
  80 + @Override
  81 + public String toString() {
  82 + return "OrcidAccessToken [name=" + name + ", access_token="
  83 + + access_token + ", token_type=" + token_type
  84 + + ", refresh_token=" + refresh_token + ", expires_in="
  85 + + expires_in + ", scope=" + scope + ", orcid=" + orcid
  86 + + ", state=" + state + "]";
  87 + }
  88 +
  89 +}
... ...
src/main/java/org/um/dsi/gavea/orcid/client/OrcidOAuthClient.java
... ... @@ -0,0 +1,176 @@
  1 +package org.um.dsi.gavea.orcid.client;
  2 +
  3 +import java.util.List;
  4 +
  5 +import javax.ws.rs.client.Entity;
  6 +import javax.ws.rs.core.MediaType;
  7 +import javax.ws.rs.core.MultivaluedHashMap;
  8 +import javax.ws.rs.core.MultivaluedMap;
  9 +import javax.ws.rs.core.Response;
  10 +import javax.ws.rs.core.UriBuilder;
  11 +
  12 +import org.apache.logging.log4j.LogManager;
  13 +import org.apache.logging.log4j.Logger;
  14 +import org.um.dsi.gavea.orcid.client.constants.OrcidConstants;
  15 +import org.um.dsi.gavea.orcid.client.exception.OrcidClientException;
  16 +import org.um.dsi.gavea.orcid.client.extend.ScopePathTypeExt;
  17 +import org.um.dsi.gavea.orcid.model.activities.ActivitiesSummary;
  18 +import org.um.dsi.gavea.orcid.model.common.ScopePathType;
  19 +import org.um.dsi.gavea.orcid.model.funding.Funding;
  20 +import org.um.dsi.gavea.orcid.model.work.Work;
  21 +
  22 +public class OrcidOAuthClient extends BaseOrcidOAuthClient {
  23 + private static final long serialVersionUID = -6655680509383446710L;
  24 +
  25 + private static final Logger _log = LogManager.getLogger(OrcidOAuthClient.class);
  26 +
  27 + public OrcidOAuthClient(String loginUri, String apiUri, String clientId, String clientSecret, String redirectUri) {
  28 + super(loginUri, apiUri, clientId, clientSecret, redirectUri);
  29 + }
  30 +
  31 +
  32 + public String getAuthorizeUriLogin(final List<ScopePathType> scopes, final String state, final String orcidOrEmail)
  33 + throws Exception {
  34 +
  35 + String orcidId = null;
  36 + String email = null;
  37 + if (orcidOrEmail != null && !"".equals(orcidOrEmail)) {
  38 + if (isValidOrcidId(orcidOrEmail)) {
  39 + orcidId = orcidOrEmail;
  40 + } else {
  41 + email = orcidOrEmail;
  42 + }
  43 + }
  44 +
  45 + return getAuthorizeUri(scopes, state, orcidId, null, null, email, null, true);
  46 + }
  47 +
  48 + public String getAuthorizeUriRegistration(final List<ScopePathType> scopes, final String state,
  49 + final String familyNames, final String givenNames) throws Exception {
  50 + return getAuthorizeUri(scopes, state, null, familyNames, givenNames, null, null, false);
  51 + }
  52 +
  53 + private String getAuthorizeUri(final List<ScopePathType> scopes, final String state, final String orcidId,
  54 + final String familyNames, final String givenNames, final String email, final String lang,
  55 + final boolean login) throws Exception {
  56 +
  57 + if (scopes == null)
  58 + throw new Exception("Cannot create Authorize Uri - missing scopes");
  59 +
  60 + UriBuilder builder = UriBuilder.fromUri(loginUri);
  61 + builder.path(OrcidConstants.ENDPOINT_OAUTH_AUTHORIZE);
  62 +
  63 + // Required params
  64 + builder.queryParam("client_id", clientId);
  65 + builder.queryParam("scope", ScopePathTypeExt.getScopesAsSingleString(scopes));
  66 + builder.queryParam("response_type", "code");
  67 + builder.queryParam("redirect_uri", redirectUri);
  68 +
  69 + // Optional params
  70 + if (state != null && !"".equals(state))
  71 + builder.queryParam("state", state);
  72 +
  73 + if (orcidId != null && !"".equals(orcidId))
  74 + builder.queryParam("orcid", orcidId);
  75 +
  76 + if (familyNames != null && !"".equals(familyNames))
  77 + builder.queryParam("family_names", familyNames);
  78 +
  79 + if (givenNames != null && !"".equals(givenNames))
  80 + builder.queryParam("given_names", givenNames);
  81 +
  82 + if (email != null && !"".equals(email))
  83 + builder.queryParam("email", email);
  84 +
  85 + if (lang != null && !"".equals(lang))
  86 + builder.queryParam("lang", lang);
  87 +
  88 + builder.queryParam("show_login", login);
  89 +
  90 + _log.debug("[URI] " + builder);
  91 +
  92 + return builder.toString();
  93 + }
  94 +
  95 +
  96 + public OrcidAccessToken getAccessTokens(final String authorizationCode) throws OrcidClientException {
  97 + MultivaluedMap<String, String> formData = new MultivaluedHashMap<String, String>();
  98 + formData.add("client_id", clientId);
  99 + formData.add("client_secret", clientSecret);
  100 + formData.add("grant_type", "authorization_code");
  101 + formData.add("code", authorizationCode);
  102 +
  103 + return post(authorizationCode, formData).readEntity(OrcidAccessToken.class);
  104 + }
  105 +
  106 +
  107 + /* Activities */
  108 + public ActivitiesSummary readActivitiesSummary(final OrcidAccessToken token) throws OrcidClientException {
  109 + return get(OrcidConstants.ENDPOINT_ACTIVITIES, token, null).readEntity(ActivitiesSummary.class);
  110 + }
  111 +
  112 +
  113 + /* Funding */
  114 + public Funding readFunding(final OrcidAccessToken token, final String putCode) throws OrcidClientException {
  115 + return get(OrcidConstants.ENDPOINT_FUNDING, token, putCode).readEntity(Funding.class);
  116 + }
  117 +
  118 + public String addFunding(final OrcidAccessToken token, final Funding funding)
  119 + throws OrcidClientException {
  120 + Response response = post(OrcidConstants.ENDPOINT_FUNDING, token, Entity.entity(funding, MediaType.APPLICATION_XML_TYPE));
  121 +
  122 + String location = response.getLocation().toString();
  123 + if (location != null && !"".equals(location)) {
  124 + String putCode = location.substring(location.lastIndexOf("/") + 1, location.length());
  125 + if (putCode.matches("\\d+")) {
  126 + return putCode;
  127 + }
  128 + }
  129 +
  130 + return null;
  131 + }
  132 +
  133 + public void updateFunding(final OrcidAccessToken token, final String putCode, final Funding funding)
  134 + throws OrcidClientException {
  135 + put(OrcidConstants.ENDPOINT_FUNDING, token, putCode, Entity.entity(funding, MediaType.APPLICATION_XML_TYPE));
  136 + }
  137 +
  138 + public void deleteFunding(final OrcidAccessToken token, final String putCode) throws OrcidClientException {
  139 + delete(OrcidConstants.ENDPOINT_FUNDING, token, putCode);
  140 + }
  141 +
  142 +
  143 + /* Work */
  144 + public Work readWork(final OrcidAccessToken token, final String putCode) throws OrcidClientException {
  145 + return get(OrcidConstants.ENDPOINT_WORK, token, putCode).readEntity(Work.class);
  146 + }
  147 +
  148 + public String addWork(final OrcidAccessToken token, final Work work)
  149 + throws OrcidClientException {
  150 + Response response = post(OrcidConstants.ENDPOINT_WORK, token, Entity.entity(work, MediaType.APPLICATION_XML_TYPE));
  151 +
  152 + String location = response.getLocation().toString();
  153 + if (location != null && !"".equals(location)) {
  154 + String putCode = location.substring(location.lastIndexOf("/") + 1, location.length());
  155 + if (putCode.matches("\\d+")) {
  156 + return putCode;
  157 + }
  158 + }
  159 +
  160 + return null;
  161 + }
  162 +
  163 + public void updateWork(final OrcidAccessToken token, final String putCode, final Work work)
  164 + throws OrcidClientException {
  165 + put(OrcidConstants.ENDPOINT_WORK, token, putCode, Entity.entity(work, MediaType.APPLICATION_XML_TYPE));
  166 + }
  167 +
  168 + public void deleteWork(final OrcidAccessToken token, final String putCode) throws OrcidClientException {
  169 + delete(OrcidConstants.ENDPOINT_WORK, token, putCode);
  170 + }
  171 +
  172 +
  173 + private boolean isValidOrcidId(final String value) {
  174 + return value.matches("([0-9]{4})-([0-9]{4})-([0-9]{4})-([0-9]{3})(?:[0-9X]{1})");
  175 + }
  176 +}
... ...
src/main/java/org/um/dsi/gavea/orcid/client/constants/OrcidConstants.java
... ... @@ -0,0 +1,27 @@
  1 +package org.um.dsi.gavea.orcid.client.constants;
  2 +
  3 +import javax.ws.rs.core.MediaType;
  4 +
  5 +public final class OrcidConstants {
  6 +
  7 + public final static String API_VERSION = "v2.0_rc1";
  8 +
  9 + public final static String ENDPOINT_OAUTH_AUTHORIZE = "/oauth/authorize";
  10 + public final static String ENDPOINT_OAUTH_TOKEN = "/oauth/token";
  11 + public final static String ENDPOINT_ACTIVITIES = "/activities";
  12 + public final static String ENDPOINT_EDUCATON = "/education";
  13 + public final static String ENDPOINT_EMPLOYMENT = "/employment";
  14 + public final static String ENDPOINT_FUNDING = "/funding";
  15 + public final static String ENDPOINT_PEERREVIEW = "/peer-review";
  16 + public final static String ENDPOINT_WORK = "/work";
  17 +
  18 + public final static String APPLICATION_VND_ORCID_XML = "application/vnd.orcid+xml";
  19 + public final static MediaType APPLICATION_VND_ORCID_XML_TYPE = new MediaType("application", "vnd.orcid+xml");
  20 + public final static String APPLICATION_ORCID_JSON = "application/orcid+json";
  21 + public final static MediaType APPLICATION_ORCID_JSON_TYPE = new MediaType("application", "orcid+json");
  22 + public final static String APPLICATION_ORCID_XML = "application/orcid+xml";
  23 + public final static MediaType APPLICATION_ORCID_XML_TYPE = new MediaType("application", "orcid+xml");
  24 +
  25 + public final static String CLIENT_READ_TIMEOUT = "2000";
  26 + public final static String CLIENT_CONNECT_TIMEOUT = "2000";
  27 +}
... ...
src/main/java/org/um/dsi/gavea/orcid/client/exception/OrcidClientException.java
... ... @@ -0,0 +1,55 @@
  1 +package org.um.dsi.gavea.orcid.client.exception;
  2 +
  3 +import java.math.BigInteger;
  4 +
  5 +public class OrcidClientException extends Exception {
  6 + private static final long serialVersionUID = 414547340012254266L;
  7 +
  8 +
  9 + private int code;
  10 + private String message;
  11 + private BigInteger developerErrorCode;
  12 + private String developerMessage;
  13 +
  14 +
  15 + public OrcidClientException() {
  16 + }
  17 +
  18 + public OrcidClientException(int code) {
  19 + this.code = code;
  20 + this.message = "";
  21 + this.developerErrorCode = BigInteger.ZERO;
  22 + this.developerMessage = "";
  23 + }
  24 +
  25 + public OrcidClientException(int code, String message) {
  26 + this.code = code;
  27 + this.message = message;
  28 + this.developerErrorCode = BigInteger.ZERO;
  29 + this.developerMessage = "";
  30 + }
  31 +
  32 + public OrcidClientException(int code, String message, BigInteger developerErrorCode, String developerMessage) {
  33 + this.code = code;
  34 + this.message = message;
  35 + this.developerErrorCode = developerErrorCode;
  36 + this.developerMessage = developerMessage;
  37 + }
  38 +
  39 +
  40 + public int getCode() {
  41 + return code;
  42 + }
  43 +
  44 + public String getMessage() {
  45 + return message;
  46 + }
  47 +
  48 + public BigInteger getDeveloperErrorCode() {
  49 + return developerErrorCode;
  50 + }
  51 +
  52 + public String getDeveloperMessage() {
  53 + return developerMessage;
  54 + }
  55 +}
... ...
src/main/java/org/um/dsi/gavea/orcid/client/extend/ScopePathTypeExt.java
... ... @@ -0,0 +1,62 @@
  1 +package org.um.dsi.gavea.orcid.client.extend;
  2 +
  3 +import java.io.Serializable;
  4 +import java.util.Arrays;
  5 +import java.util.Collection;
  6 +import java.util.HashMap;
  7 +import java.util.HashSet;
  8 +import java.util.Map;
  9 +import java.util.Set;
  10 +
  11 +import org.um.dsi.gavea.orcid.model.common.ScopePathType;
  12 +
  13 +public class ScopePathTypeExt implements Serializable {
  14 + private static final long serialVersionUID = -7497383493796080293L;
  15 +
  16 +
  17 + static Map<String, ScopePathType> mapByValue = new HashMap<String, ScopePathType>();
  18 + static {
  19 + for (ScopePathType scope : ScopePathType.values()) {
  20 + mapByValue.put(scope.value(), scope);
  21 + }
  22 + }
  23 +
  24 + public static Set<ScopePathType> getScopesFromStrings(Collection<String> scopes) {
  25 + Set<ScopePathType> newScopes = new HashSet<ScopePathType>();
  26 + for (String scopeString : scopes) {
  27 + ScopePathType scope = mapByValue.get(scopeString);
  28 + if (scope != null) {
  29 + newScopes.add(scope);
  30 + }
  31 + }
  32 + return newScopes;
  33 + }
  34 +
  35 + public static Set<ScopePathType> getScopesFromSpaceSeparatedString(String scopesString) {
  36 + if (scopesString == null) {
  37 + return null;
  38 + }
  39 + return getScopesFromStrings(Arrays.asList(scopesString.split("\\s")));
  40 + }
  41 +
  42 + public static Set<String> getScopesAsStrings(Collection<ScopePathType> scopes) {
  43 + Set<String> scopeStrings = new HashSet<String>();
  44 + for (ScopePathType scope : scopes) {
  45 + scopeStrings.add(scope.value());
  46 + }
  47 + return scopeStrings;
  48 + }
  49 +
  50 + public static String getScopesAsSingleString(Collection<ScopePathType> scopes) {
  51 + StringBuilder sb = new StringBuilder();
  52 +
  53 + for (ScopePathType scope : scopes) {
  54 + sb.append(scope.value() + " ");
  55 + }
  56 + return sb.toString().trim();
  57 + }
  58 +
  59 + public static Set<String> getScopesAsStrings(ScopePathType... scopes) {
  60 + return getScopesAsStrings(Arrays.asList(scopes));
  61 + }
  62 +}
... ...
src/main/java/org/um/dsi/gavea/orcid/client/util/RecordUtil.java
... ... @@ -0,0 +1,25 @@
  1 +package org.um.dsi.gavea.orcid.client.util;
  2 +
  3 +import java.io.StringWriter;
  4 +
  5 +import javax.xml.bind.JAXBContext;
  6 +import javax.xml.bind.JAXBException;
  7 +import javax.xml.bind.Marshaller;
  8 +
  9 +public class RecordUtil {
  10 +
  11 + public static String convertToString(Object obj) {
  12 + try {
  13 + JAXBContext context = JAXBContext.newInstance(obj.getClass());
  14 + StringWriter writer = new StringWriter();
  15 + Marshaller marshaller = context.createMarshaller();
  16 + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
  17 + marshaller.marshal(obj, writer);
  18 +
  19 + return writer.toString();
  20 + } catch (JAXBException e) {
  21 + return ("Unable to unmarshal because: " + e);
  22 + }
  23 + }
  24 +
  25 +}
... ...
src/main/resources/binding/orcid-2.0_rc1.xjb
... ... @@ -0,0 +1,66 @@
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
  3 + xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
  4 + xmlns:namespace="http://jaxb2-commons.dev.java.net/namespace-prefix"
  5 + jaxb:version="2.1">
  6 +
  7 + <jaxb:bindings schemaLocation="../xsd/record_2.0_rc1/activities-2.0_rc1.xsd" node="/xs:schema">
  8 + <jaxb:globalBindings>
  9 + <jaxb:serializable uid="1" />
  10 + </jaxb:globalBindings>
  11 +
  12 + <jaxb:schemaBindings>
  13 + <jaxb:package name="org.um.dsi.gavea.orcid.model.activities" />
  14 + </jaxb:schemaBindings>
  15 + </jaxb:bindings>
  16 +
  17 + <jaxb:bindings schemaLocation="../xsd/common_2.0_rc1/common-2.0_rc1.xsd" node="/xs:schema">
  18 + <jaxb:schemaBindings>
  19 + <jaxb:package name="org.um.dsi.gavea.orcid.model.common" />
  20 + </jaxb:schemaBindings>
  21 +
  22 + <jaxb:bindings node="//xs:complexType[@name='client-id']/xs:sequence/xs:choice/xs:sequence/xs:element[@name='path']">
  23 + <jaxb:property name="uri-path" />
  24 + </jaxb:bindings>
  25 +
  26 + <jaxb:bindings node="//xs:complexType[@name='orcid-id']/xs:sequence/xs:choice/xs:sequence/xs:element[@name='path']">
  27 + <jaxb:property name="uri-path" />
  28 + </jaxb:bindings>
  29 + </jaxb:bindings>
  30 +
  31 + <jaxb:bindings schemaLocation="../xsd/record_2.0_rc1/education-2.0_rc1.xsd" node="/xs:schema">
  32 + <jaxb:schemaBindings>
  33 + <jaxb:package name="org.um.dsi.gavea.orcid.model.education" />
  34 + </jaxb:schemaBindings>
  35 + </jaxb:bindings>
  36 +
  37 + <jaxb:bindings schemaLocation="../xsd/record_2.0_rc1/employment-2.0_rc1.xsd" node="/xs:schema">
  38 + <jaxb:schemaBindings>
  39 + <jaxb:package name="org.um.dsi.gavea.orcid.model.employment" />
  40 + </jaxb:schemaBindings>
  41 + </jaxb:bindings>
  42 +
  43 + <jaxb:bindings schemaLocation="../xsd/record_2.0_rc1/funding-2.0_rc1.xsd" node="/xs:schema">
  44 + <jaxb:schemaBindings>
  45 + <jaxb:package name="org.um.dsi.gavea.orcid.model.funding" />
  46 + </jaxb:schemaBindings>
  47 + </jaxb:bindings>
  48 +
  49 + <jaxb:bindings schemaLocation="../xsd/record_2.0_rc1/peer-review-2.0_rc1.xsd" node="/xs:schema">
  50 + <jaxb:schemaBindings>
  51 + <jaxb:package name="org.um.dsi.gavea.orcid.model.peerreview" />
  52 + </jaxb:schemaBindings>
  53 + </jaxb:bindings>
  54 +
  55 + <jaxb:bindings schemaLocation="../xsd/record_2.0_rc1/work-2.0_rc1.xsd" node="/xs:schema">
  56 + <jaxb:schemaBindings>
  57 + <jaxb:package name="org.um.dsi.gavea.orcid.model.work" />
  58 + </jaxb:schemaBindings>
  59 + </jaxb:bindings>
  60 +
  61 + <jaxb:bindings schemaLocation="../xsd/record_2.0_rc1/error-2.0_rc1.xsd" node="/xs:schema">
  62 + <jaxb:schemaBindings>
  63 + <jaxb:package name="org.um.dsi.gavea.orcid.model.error" />
  64 + </jaxb:schemaBindings>
  65 + </jaxb:bindings>
  66 +</jaxb:bindings>
0 67 \ No newline at end of file
... ...
src/main/resources/xsd/common_2.0_rc1/common-2.0_rc1.xsd
... ... @@ -0,0 +1,1594 @@
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
  3 + xmlns:sch="http://purl.oclc.org/dsdl/schematron" elementFormDefault="qualified"
  4 + targetNamespace="http://www.orcid.org/ns/common" xmlns:common="http://www.orcid.org/ns/common">
  5 + <xs:annotation>
  6 + <xs:documentation>
  7 + =============================================================================
  8 +
  9 + ORCID (R) Open Source
  10 + http://orcid.org
  11 +
  12 + Copyright (c) 2012-2014 ORCID,
  13 + Inc.
  14 + Licensed under an MIT-Style License (MIT)
  15 + http://orcid.org/open-source-license
  16 +
  17 + This copyright and license
  18 + information (including a link to the full
  19 + license)
  20 + shall be included in
  21 + its entirety in all copies or substantial portion of
  22 + the software.
  23 +
  24 + =============================================================================
  25 + The schema describes the message format used for ORCID API requests
  26 + and responses.
  27 + The top level element is orcid-message.
  28 + </xs:documentation>
  29 + <xs:appinfo>
  30 + <sch:title>Schematron validation</sch:title>
  31 + <sch:ns prefix="orcid" uri="http://www.orcid.org/ns/orcid" />
  32 + </xs:appinfo>
  33 + </xs:annotation>
  34 +
  35 + <xs:element name="last-modified-date">
  36 + <xs:complexType>
  37 + <xs:annotation>
  38 + <xs:documentation>The date time when the element was last modified.
  39 + </xs:documentation>
  40 + </xs:annotation>
  41 + <xs:simpleContent>
  42 + <xs:extension base="xs:dateTime" />
  43 + </xs:simpleContent>
  44 + </xs:complexType>
  45 + </xs:element>
  46 +
  47 + <xs:element name="created-date">
  48 + <xs:complexType>
  49 + <xs:annotation>
  50 + <xs:documentation>The date time when element was created.
  51 + </xs:documentation>
  52 + </xs:annotation>
  53 + <xs:simpleContent>
  54 + <xs:extension base="xs:dateTime" />
  55 + </xs:simpleContent>
  56 + </xs:complexType>
  57 + </xs:element>
  58 +
  59 + <xs:element name="read-date">
  60 + <xs:complexType>
  61 + <xs:annotation>
  62 + <xs:documentation>The date time when element was first read by the user.
  63 + </xs:documentation>
  64 + </xs:annotation>
  65 + <xs:simpleContent>
  66 + <xs:extension base="xs:dateTime" />
  67 + </xs:simpleContent>
  68 + </xs:complexType>
  69 + </xs:element>
  70 +
  71 + <xs:element name="archived-date">
  72 + <xs:complexType>
  73 + <xs:annotation>
  74 + <xs:documentation>The date time when element was archived.
  75 + </xs:documentation>
  76 + </xs:annotation>
  77 + <xs:simpleContent>
  78 + <xs:extension base="xs:dateTime" />
  79 + </xs:simpleContent>
  80 + </xs:complexType>
  81 + </xs:element>
  82 +
  83 + <xs:element name="sent-date">
  84 + <xs:complexType>
  85 + <xs:annotation>
  86 + <xs:documentation>The date time when element was sent to the user.
  87 + </xs:documentation>
  88 + </xs:annotation>
  89 + <xs:simpleContent>
  90 + <xs:extension base="xs:dateTime" />
  91 + </xs:simpleContent>
  92 + </xs:complexType>
  93 + </xs:element>
  94 +
  95 + <xs:element name="source" type="common:source-type" />
  96 +
  97 + <xs:complexType name="source-type">
  98 + <xs:annotation>
  99 + <xs:documentation>The client application (Member organization's
  100 + system) or user that created the item. XSD VERSION 1.2 UPDATE: the
  101 + identifier for the source may be either an ORCID iD (representing
  102 + individuals and legacy client applications) or a Client ID
  103 + (representing all newer client applications)
  104 + </xs:documentation>
  105 + </xs:annotation>
  106 + <xs:sequence>
  107 + <xs:choice minOccurs="0">
  108 + <xs:element ref="common:source-orcid">
  109 + <xs:annotation>
  110 + <xs:documentation>The ORCID identifier for the individual user (or
  111 + legacy client application) that created the item. For identifiers
  112 + of
  113 + individual users, it may be used to look up ORCID record
  114 + details via
  115 + the API.
  116 + </xs:documentation>
  117 + </xs:annotation>
  118 + </xs:element>
  119 + <xs:element name="source-client-id" type="common:client-id">
  120 + <xs:annotation>
  121 + <xs:documentation>The client ID for the client application (Member
  122 + organization system) that created the item.
  123 + </xs:documentation>
  124 + </xs:annotation>
  125 + </xs:element>
  126 + </xs:choice>
  127 + <xs:element name="source-name" type="common:source-name"
  128 + minOccurs="0" />
  129 + </xs:sequence>
  130 + </xs:complexType>
  131 +
  132 + <xs:complexType name="source-name" mixed="true">
  133 + <xs:annotation>
  134 + <xs:documentation>The human-readable name of the client application
  135 + (Member organization system) or individual user that created the
  136 + item. Value for the same person/organization could change over time.
  137 + source-orcid or source-client-id fields are more appropriate for
  138 + disambiguated matching.
  139 + </xs:documentation>
  140 + </xs:annotation>
  141 + </xs:complexType>
  142 +
  143 + <xs:complexType name="organization">
  144 + <xs:annotation>
  145 + <xs:documentation>A reference to an organization. When available, an
  146 + organization will be tied to a disambiguated organization which
  147 + uniquely identifies the organization.
  148 + </xs:documentation>
  149 + </xs:annotation>
  150 +
  151 + <xs:sequence>
  152 + <xs:element name="name" type="common:non-empty-string" />
  153 + <xs:element name="address" type="common:organization-address" />
  154 + <xs:element name="disambiguated-organization" type="common:disambiguated-organization"
  155 + minOccurs="0" maxOccurs="1" />
  156 + </xs:sequence>
  157 + </xs:complexType>
  158 +
  159 + <xs:complexType name="organization-address">
  160 + <xs:annotation>
  161 + <xs:documentation>Container for organization location information
  162 + </xs:documentation>
  163 + </xs:annotation>
  164 + <xs:sequence>
  165 + <xs:element minOccurs="1" name="city" type="common:non-empty-string">
  166 + <xs:annotation>
  167 + <xs:documentation>City</xs:documentation>
  168 + </xs:annotation>
  169 + </xs:element>
  170 + <xs:element minOccurs="0" name="region" type="common:non-empty-string">
  171 + <xs:annotation>
  172 + <xs:documentation>Region within a country</xs:documentation>
  173 + </xs:annotation>
  174 + </xs:element&