View Javadoc

1   /*
2    * Copyright (C) 2017-2019 Centre National d'Etudes Spatiales (CNES).
3    *
4    * This library is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Lesser General Public
6    * License as published by the Free Software Foundation; either
7    * version 3.0 of the License, or (at your option) any later version.
8    *
9    * This library is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   * Lesser General Public License for more details.
13   *
14   * You should have received a copy of the GNU Lesser General Public
15   * License along with this library; if not, write to the Free Software
16   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17   * MA 02110-1301  USA
18   */
19  package fr.cnes.doi.resource.mds;
20  
21  import fr.cnes.doi.application.DoiMdsApplication;
22  import fr.cnes.doi.application.DoiMdsApplication.API_MDS;
23  import fr.cnes.doi.client.ClientMDS;
24  import fr.cnes.doi.client.ClientMDS.DATACITE_API_RESPONSE;
25  import fr.cnes.doi.exception.ClientMdsException;
26  import fr.cnes.doi.exception.DoiServerException;
27  import fr.cnes.doi.utils.spec.Requirement;
28  import java.io.IOException;
29  import java.util.Arrays;
30  import org.apache.logging.log4j.Level;
31  import org.datacite.schema.kernel_4.Resource;
32  import org.restlet.data.Method;
33  import org.restlet.data.Status;
34  import org.restlet.ext.jaxb.JaxbRepresentation;
35  import org.restlet.ext.wadl.MethodInfo;
36  import org.restlet.ext.wadl.ParameterStyle;
37  import org.restlet.representation.Representation;
38  import org.restlet.resource.Delete;
39  import org.restlet.resource.Get;
40  
41  /**
42   * Resources to handle a metadata.
43   *
44   * @author Jean-Christophe Malapert (jean-christophe.malapert@cnes.fr)
45   */
46  public class MetadataResource extends BaseMdsResource {
47  
48      /**
49       * Function of this resource {@value #GET_METADATA}.
50       */
51      public static final String GET_METADATA = "Get a Metadata";
52  
53      /**
54       * Function of this resource {@value #DELETE_METADATA}.
55       */
56      public static final String DELETE_METADATA = "Delete a Metadata";
57  
58      /**
59       * DOI name, which is set on the URL template.
60       */
61      private volatile String doiName;
62  
63      /**
64       * Init by getting a DOI.
65       *
66       * @throws DoiServerException - if a problem happens
67       */
68      @Override
69      protected void doInit() throws DoiServerException {
70          super.doInit();
71          LOG.traceEntry();
72          setDescription("This resource handles a metadata : retrieve, delete");
73          this.doiName = getAttributePath(DoiMdsApplication.METADATAS_URI);
74          LOG.debug("DOI name " + this.doiName);
75          LOG.traceExit();
76      }
77  
78      /**
79       * Checks input parameters
80       *
81       * @param doiName DOI number
82       * @throws DoiServerException - 400 Bad Request if DOI_PARAMETER is not set
83       */
84      @Requirement(reqId = Requirement.DOI_INTER_070, reqName = Requirement.DOI_INTER_070_NAME)
85      private void checkInputs(final String doiName) throws DoiServerException {
86          LOG.traceEntry("Parameter\n\tdoiName : {}", doiName);
87          StringBuilder errorMsg = new StringBuilder();
88          if (doiName == null || doiName.isEmpty()) {
89              errorMsg = errorMsg.append(DoiMdsApplication.DOI_TEMPLATE).append("value is not set.");
90          } else {
91              try {
92                  ClientMDS.checkIfAllCharsAreValid(doiName);
93              } catch (IllegalArgumentException ex) {
94                  errorMsg = errorMsg.append(DoiMdsApplication.DOI_TEMPLATE).
95                          append("no valid syntax.");
96              }
97          }
98          if (errorMsg.length() == 0) {
99              LOG.debug("The input is valid");
100         } else {
101             throw LOG.throwing(
102                     Level.ERROR,
103                     new DoiServerException(getApplication(), Status.CLIENT_ERROR_BAD_REQUEST,
104                             errorMsg.toString())
105             );
106         }
107         LOG.traceExit();
108     }
109 
110     /**
111      * Retuns the metadata for a given DOI. 200 status is returned when the
112      * operation is successful.
113      *
114      * @return the metadata for a given DOI as Json or XML
115      * @throws DoiServerException - if the response is not a success
116      * <ul>
117      * <li>{@link DATACITE_API_RESPONSE#BAD_REQUEST}</li>
118      * <li>{@link DATACITE_API_RESPONSE#DOI_NOT_FOUND}</li>
119      * <li>{@link API_MDS#DATACITE_PROBLEM}</li>
120      * </ul>
121      */
122     @Requirement(reqId = Requirement.DOI_SRV_060, reqName = Requirement.DOI_SRV_060_NAME)
123     @Requirement(reqId = Requirement.DOI_MONIT_020, reqName = Requirement.DOI_MONIT_020_NAME)
124     @Get("xml")
125     public Representation getMetadata() throws DoiServerException {
126         LOG.traceEntry();
127         checkInputs(doiName);
128         final Representation resource;
129         try {
130             setStatus(Status.SUCCESS_OK);
131             resource = this.getDoiApp().getClient().getMetadata(this.doiName);
132         } catch (ClientMdsException ex) {
133             if (ex.getStatus().getCode() == Status.CLIENT_ERROR_NOT_FOUND.getCode()) {
134                 throw LOG.throwing(
135                         Level.ERROR,
136                         new DoiServerException(getApplication(), DATACITE_API_RESPONSE.DOI_NOT_FOUND,
137                                 ex)
138                 );
139             } else if (ex.getStatus().getCode() == Status.CLIENT_ERROR_BAD_REQUEST.getCode()) {
140                 throw LOG.throwing(
141                         Level.ERROR,
142                         new DoiServerException(getApplication(), DATACITE_API_RESPONSE.BAD_REQUEST,
143                                 ex)
144                 );
145             } else {
146                 throw LOG.throwing(
147                         Level.ERROR,
148                         new DoiServerException(getApplication(), API_MDS.DATACITE_PROBLEM, ex)
149                 );
150             }
151         }
152         return LOG.traceExit(resource);
153     }
154 
155     /**
156      * Returns the metadata as JSON.
157      *
158      * @return JSON representation.
159      * @throws IOException - if JSON conversion problem occurs
160      */
161     @Get("json")
162     public Resource getMetadataAsJson() throws IOException {
163         final Representation rep = getMetadata();
164         final JaxbRepresentation<Resource> resourceEntity = new JaxbRepresentation<>(rep, Resource.class);
165         return resourceEntity.getObject();
166     }
167 
168     /**
169      * Deletes a representation for a given DOI. 200 status when the operation
170      * is successful.
171      *
172      * @return the deleted representation
173      * @throws DoiServerException - if the response is not a success
174      * <ul>
175      * <li>{@link API_MDS#SECURITY_USER_NO_ROLE}</li>
176      * <li>{@link API_MDS#SECURITY_USER_NOT_IN_SELECTED_ROLE}</li>
177      * <li>{@link API_MDS#SECURITY_USER_PERMISSION}</li>
178      * <li>{@link API_MDS#SECURITY_USER_CONFLICT}</li>
179      * <li>{@link DATACITE_API_RESPONSE#DOI_NOT_FOUND}</li>
180      * <li>{@link API_MDS#DATACITE_PROBLEM}</li>
181      * </ul>
182      */
183     @Requirement(reqId = Requirement.DOI_SRV_050, reqName = Requirement.DOI_SRV_050_NAME)
184     @Requirement(reqId = Requirement.DOI_MONIT_020, reqName = Requirement.DOI_MONIT_020_NAME)
185     @Requirement(reqId = Requirement.DOI_INTER_070, reqName = Requirement.DOI_INTER_070_NAME)
186     @Requirement(reqId = Requirement.DOI_AUTO_020, reqName = Requirement.DOI_AUTO_020_NAME)
187     @Requirement(reqId = Requirement.DOI_AUTO_030, reqName = Requirement.DOI_AUTO_030_NAME)
188     @Delete
189     public Representation deleteMetadata() throws DoiServerException {
190         LOG.traceEntry();
191         checkInputs(this.doiName);
192         final Representation rep;
193         try {
194             final String selectedRole = extractSelectedRoleFromRequestIfExists();
195             checkPermission(this.doiName, selectedRole);
196             setStatus(Status.SUCCESS_OK);
197             rep = this.getDoiApp().getClient().deleteMetadata(this.doiName);
198         } catch (ClientMdsException ex) {
199             if (ex.getStatus().getCode() == Status.CLIENT_ERROR_NOT_FOUND.getCode()) {
200                 throw LOG.throwing(
201                         Level.ERROR,
202                         new DoiServerException(getApplication(), DATACITE_API_RESPONSE.DOI_NOT_FOUND,
203                                 ex)
204                 );
205             } else {
206                 throw LOG.throwing(
207                         Level.ERROR,
208                         new DoiServerException(getApplication(), API_MDS.DATACITE_PROBLEM, ex)
209                 );
210             }
211         }
212         return LOG.traceExit(rep);
213     }
214 
215     /**
216      * Describes the GET method. The different representations are the
217      * followings:
218      * <ul>
219      * <li>{@link DATACITE_API_RESPONSE#SUCCESS}</li>
220      * <li>{@link DATACITE_API_RESPONSE#DOI_NOT_FOUND}</li>
221      * <li>{@link DATACITE_API_RESPONSE#DOI_INACTIVE}</li>
222      * <li>{@link API_MDS#DATACITE_PROBLEM}</li>
223      * </ul>
224      *
225      * @param info Wadl description for GET method
226      */
227     @Requirement(reqId = Requirement.DOI_DOC_010, reqName = Requirement.DOI_DOC_010_NAME)
228     @Override
229     protected final void describeGet(final MethodInfo info) {
230         info.setName(Method.GET);
231         info.setDocumentation("Get a specific metadata");
232 
233         addRequestDocToMethod(info, createQueryParamDoc(
234                 DoiMdsApplication.DOI_TEMPLATE, ParameterStyle.TEMPLATE,
235                 "DOI name", true, "xs:string")
236         );
237 
238         addResponseDocToMethod(info, createResponseDoc(
239                 DATACITE_API_RESPONSE.SUCCESS.getStatus(),
240                 DATACITE_API_RESPONSE.SUCCESS.getShortMessage(),
241                 "metadataRepresentation")
242         );
243         addResponseDocToMethod(info, createResponseDoc(
244                 DATACITE_API_RESPONSE.DOI_NOT_FOUND.getStatus(),
245                 DATACITE_API_RESPONSE.DOI_NOT_FOUND.getShortMessage(),
246                 "explainRepresentationID")
247         );
248         addResponseDocToMethod(info, createResponseDoc(
249                 DATACITE_API_RESPONSE.DOI_INACTIVE.getStatus(),
250                 DATACITE_API_RESPONSE.DOI_INACTIVE.getShortMessage(),
251                 "explainRepresentationID")
252         );
253         addResponseDocToMethod(info, createResponseDoc(
254                 API_MDS.DATACITE_PROBLEM.getStatus(),
255                 API_MDS.DATACITE_PROBLEM.getShortMessage(),
256                 "explainRepresentationID")
257         );
258     }
259 
260     /**
261      * Describes the DELETE method. The different representations are the
262      * followings:
263      * <ul>
264      * <li>{@link DATACITE_API_RESPONSE#SUCCESS}</li>
265      * <li>{@link DATACITE_API_RESPONSE#DOI_NOT_FOUND}</li>
266      * <li>{@link API_MDS#SECURITY_USER_NO_ROLE}</li>
267      * <li>{@link API_MDS#SECURITY_USER_NOT_IN_SELECTED_ROLE}</li>
268      * <li>{@link API_MDS#SECURITY_USER_PERMISSION}</li>
269      * <li>{@link API_MDS#SECURITY_USER_CONFLICT}</li>
270      * <li>{@link API_MDS#DATACITE_PROBLEM}</li>
271      * </ul>
272      *
273      * @param info Wadl description for DELETE method
274      */
275     @Requirement(reqId = Requirement.DOI_DOC_010, reqName = Requirement.DOI_DOC_010_NAME)
276     @Override
277     protected final void describeDelete(final MethodInfo info) {
278         info.setName(Method.DELETE);
279         info.setDocumentation("Delete a specific metadata");
280 
281         addRequestDocToMethod(info,
282                 Arrays.asList(
283                         createQueryParamDoc(DoiMdsApplication.DOI_TEMPLATE,
284                                 ParameterStyle.TEMPLATE, "DOI name", true, "xs:string"
285                         ),
286                         createQueryParamDoc("selectedRole", ParameterStyle.HEADER,
287                                 "A user can select one role when he is associated "
288                                 + "to several roles", false, "xs:string"
289                         )
290                 )
291         );
292         addResponseDocToMethod(info, createResponseDoc(
293                 DATACITE_API_RESPONSE.SUCCESS.getStatus(),
294                 DATACITE_API_RESPONSE.SUCCESS.getShortMessage(),
295                 "metadataRepresentation")
296         );
297         addResponseDocToMethod(info, createResponseDoc(
298                 DATACITE_API_RESPONSE.DOI_NOT_FOUND.getStatus(),
299                 DATACITE_API_RESPONSE.DOI_NOT_FOUND.getShortMessage(),
300                 "explainRepresentationID")
301         );
302         addResponseDocToMethod(info, createResponseDoc(
303                 API_MDS.DATACITE_PROBLEM.getStatus(),
304                 API_MDS.DATACITE_PROBLEM.getShortMessage(),
305                 "explainRepresentationID")
306         );
307         super.describeDelete(info);
308     }
309 }