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 static fr.cnes.doi.security.UtilsHeader.SELECTED_ROLE_PARAMETER;
28  import fr.cnes.doi.settings.Consts;
29  import fr.cnes.doi.settings.DoiSettings;
30  import fr.cnes.doi.utils.spec.Requirement;
31  import java.util.Arrays;
32  import org.apache.logging.log4j.Level;
33  import org.restlet.data.Form;
34  import org.restlet.data.MediaType;
35  import org.restlet.data.Method;
36  import org.restlet.data.Status;
37  import org.restlet.ext.wadl.DocumentationInfo;
38  import org.restlet.ext.wadl.MethodInfo;
39  import org.restlet.ext.wadl.ParameterInfo;
40  import org.restlet.ext.wadl.ParameterStyle;
41  import org.restlet.ext.wadl.RepresentationInfo;
42  import org.restlet.representation.Representation;
43  import org.restlet.representation.StringRepresentation;
44  import org.restlet.resource.Get;
45  import org.restlet.resource.Post;
46  
47  /**
48   * Resource to handle the Media.
49   *
50   * @author Jean-Christophe Malapert (jean-christophe.malapert@cnes.fr)
51   */
52  public class MediaResource extends BaseMdsResource {
53  
54      /**
55       * DOI parsed from the URL.
56       */
57      private volatile String mediaName;
58  
59      /**
60       * Init by getting the media name.
61       *
62       * @throws DoiServerException - if a problem happens
63       */
64      @Override
65      protected void doInit() throws DoiServerException {
66          super.doInit();
67          LOG.traceEntry();
68          this.mediaName = getResourcePath().replace(DoiMdsApplication.MEDIA_URI + "/", "");
69          LOG.debug(this.mediaName);
70          LOG.traceExit();
71      }
72  
73      /**
74       * Returns the media related to a DOI. This request returns list of pairs of
75       * media type and URLs associated with a given DOI when 200 status is
76       * returned (operation successful).
77       *
78       * @return the media related to a DOI
79       * @throws DoiServerException - if the response is not a success
80       * <ul>
81       * <li>{@link DATACITE_API_RESPONSE#DOI_NOT_FOUND}</li>
82       * <li>{@link API_MDS#DATACITE_PROBLEM}</li>
83       * </ul>
84       */
85      @Requirement(reqId = Requirement.DOI_SRV_090, reqName = Requirement.DOI_SRV_090_NAME)
86      @Requirement(reqId = Requirement.DOI_MONIT_020, reqName = Requirement.DOI_MONIT_020_NAME)
87      @Get
88      public Representation getMedias() throws DoiServerException {
89          LOG.traceEntry();
90          final Representation rep;
91          final String medias;
92          try {
93              setStatus(Status.SUCCESS_OK);
94              medias = this.getDoiApp().getClient().getMedia(this.mediaName);
95              rep = new StringRepresentation(medias, MediaType.TEXT_URI_LIST);
96          } catch (ClientMdsException ex) {
97              if (ex.getStatus().getCode() == Status.CLIENT_ERROR_NOT_FOUND.getCode()) {
98                  throw LOG.throwing(
99                          Level.ERROR,
100                         new DoiServerException(getApplication(), DATACITE_API_RESPONSE.DOI_NOT_FOUND,
101                                 ex)
102                 );
103             } else {
104                 throw LOG.throwing(
105                         Level.ERROR,
106                         new DoiServerException(getApplication(), API_MDS.DATACITE_PROBLEM, ex)
107                 );
108             }
109         }
110 
111         return LOG.traceExit(rep);
112     }
113 
114     /**
115      * Creates a media related to an URL for a given DOI. Will add/update media
116      * type/urls pairs to a DOI. Standard domain restrictions check will be
117      * performed. 200 status is returned when the operation is successful.
118      *
119      * @param mediaForm Form
120      * @return short explanation of status code
121      * @throws DoiServerException - if the response is not a success :
122      * <ul>
123      * <li>{@link DATACITE_API_RESPONSE#BAD_REQUEST}</li>
124      * <li>{@link API_MDS#DATACITE_PROBLEM}</li>
125      * <li>{@link API_MDS#SECURITY_USER_NO_ROLE}</li>
126      * <li>{@link API_MDS#SECURITY_USER_NOT_IN_SELECTED_ROLE}</li>
127      * <li>{@link API_MDS#SECURITY_USER_PERMISSION}</li>
128      * <li>{@link API_MDS#SECURITY_USER_CONFLICT}</li>
129      * </ul>
130      */
131     @Requirement(reqId = Requirement.DOI_SRV_080, reqName = Requirement.DOI_SRV_080_NAME)
132     @Requirement(reqId = Requirement.DOI_MONIT_020, reqName = Requirement.DOI_MONIT_020_NAME)
133     @Requirement(reqId = Requirement.DOI_INTER_070, reqName = Requirement.DOI_INTER_070_NAME)
134     @Requirement(reqId = Requirement.DOI_AUTO_020, reqName = Requirement.DOI_AUTO_020_NAME)
135     @Requirement(reqId = Requirement.DOI_AUTO_030, reqName = Requirement.DOI_AUTO_030_NAME)
136     @Post
137     public Representation createMedia(final Form mediaForm) throws DoiServerException {
138         LOG.traceEntry("Parameters\n\tmediaForm : {}", mediaForm);
139         checkInputs(this.mediaName, mediaForm);
140         final String result;
141         try {
142             setStatus(Status.SUCCESS_OK);
143             final String selectedRole = extractSelectedRoleFromRequestIfExists();
144             checkPermission(this.mediaName, selectedRole);
145             result = this.getDoiApp().getClient().createMedia(this.mediaName, mediaForm);
146         } catch (ClientMdsException ex) {
147             if (ex.getStatus().getCode() == Status.CLIENT_ERROR_BAD_REQUEST.getCode()) {
148                 throw LOG.throwing(
149                         Level.ERROR,
150                         new DoiServerException(getApplication(), DATACITE_API_RESPONSE.BAD_REQUEST,
151                                 ex)
152                 );
153             } else {
154                 throw LOG.throwing(
155                         Level.ERROR,
156                         new DoiServerException(getApplication(), API_MDS.DATACITE_PROBLEM, ex)
157                 );
158             }
159         }
160         return LOG.traceExit(new StringRepresentation(result));
161     }
162 
163     /**
164      * Checks input parameters
165      *
166      * @param doi DOI number
167      * @param mediaForm the parameters
168      * @throws DoiServerException - 400 Bad Request if DOI_PARAMETER is not set
169      */
170     @Requirement(reqId = Requirement.DOI_INTER_070, reqName = Requirement.DOI_INTER_070_NAME)
171     private void checkInputs(final String doi,
172             final Form mediaForm) throws DoiServerException {
173         LOG.traceEntry("Parameters\n\tdoi={}\n\tmediaForm={}", doi, mediaForm);
174         final StringBuilder errorMsg = new StringBuilder();
175         if (doi == null || doi.isEmpty() || !doi.startsWith(DoiSettings.getInstance().getString(Consts.INIST_DOI))) {
176             errorMsg.append(DOI_PARAMETER).append(" value is not set.");
177         } else {
178             try {
179                 ClientMDS.checkIfAllCharsAreValid(doi);
180             } catch (IllegalArgumentException ex) {
181                 errorMsg.append(DOI_PARAMETER).append(" no valid syntax.");
182             }
183         }
184         if (errorMsg.length() == 0) {
185             LOG.debug("The form is valid");
186         } else {
187             throw LOG.throwing(
188                     Level.ERROR,
189                     new DoiServerException(getApplication(), API_MDS.MEDIA_VALIDATION, errorMsg.
190                             toString())
191             );
192         }
193         LOG.traceExit();
194     }
195 
196     /**
197      * Media representation.
198      *
199      * @return Wadl description for a Media representation
200      */
201     private RepresentationInfo mediaRepresentation() {
202         final RepresentationInfo repInfo = new RepresentationInfo();
203         repInfo.setMediaType(MediaType.TEXT_PLAIN);
204         final DocumentationInfo docInfo = new DocumentationInfo();
205         docInfo.setTitle("Media representation");
206         docInfo.setTextContent("This request returns a key-value list of media "
207                 + "types/urls for a given DOI name");
208         repInfo.setDocumentation(docInfo);
209         return repInfo;
210     }
211 
212     /**
213      * Describes the GET method. The different representations are the
214      * followings:
215      * <ul>
216      * <li>{@link DATACITE_API_RESPONSE#SUCCESS}</li>
217      * <li>{@link DATACITE_API_RESPONSE#DOI_NOT_FOUND}</li>
218      * <li>{@link API_MDS#DATACITE_PROBLEM}</li>
219      * </ul>
220      *
221      * @param info Wadl description for a GET method
222      */
223     @Requirement(reqId = Requirement.DOI_DOC_010, reqName = Requirement.DOI_DOC_010_NAME)
224     @Override
225     protected final void describeGet(final MethodInfo info) {
226         info.setName(Method.GET);
227         info.setDocumentation("Get a specific media for a given DOI");
228 
229         addRequestDocToMethod(info, createQueryParamDoc(
230                 DoiMdsApplication.DOI_TEMPLATE, ParameterStyle.TEMPLATE,
231                 "DOI name", true, "xs:string")
232         );
233         addResponseDocToMethod(info, createResponseDoc(
234                 DATACITE_API_RESPONSE.SUCCESS.getStatus(),
235                 DATACITE_API_RESPONSE.SUCCESS.getShortMessage(),
236                 mediaRepresentation())
237         );
238         addResponseDocToMethod(info, createResponseDoc(
239                 DATACITE_API_RESPONSE.DOI_NOT_FOUND.getStatus(),
240                 DATACITE_API_RESPONSE.DOI_NOT_FOUND.getShortMessage(),
241                 "explainRepresentationID")
242         );
243         addResponseDocToMethod(info, createResponseDoc(
244                 API_MDS.DATACITE_PROBLEM.getStatus(),
245                 API_MDS.DATACITE_PROBLEM.getShortMessage(),
246                 "explainRepresentationID")
247         );
248     }
249 
250     /**
251      * Describes the POST method. The different representations are the
252      * followings:
253      * <ul>
254      * <li>{@link DATACITE_API_RESPONSE#SUCCESS}</li>
255      * <li>{@link API_MDS#MEDIA_VALIDATION}</li>
256      * <li>{@link API_MDS#DATACITE_PROBLEM}</li>
257      * <li>{@link API_MDS#SECURITY_USER_NO_ROLE}</li>
258      * <li>{@link API_MDS#SECURITY_USER_NOT_IN_SELECTED_ROLE}</li>
259      * <li>{@link API_MDS#SECURITY_USER_PERMISSION}</li>
260      * <li>{@link API_MDS#SECURITY_USER_CONFLICT}</li>
261      * </ul>
262      *
263      * @param info Wadl description for describing POST method
264      */
265     @Requirement(reqId = Requirement.DOI_DOC_010, reqName = Requirement.DOI_DOC_010_NAME)
266     @Override
267     protected final void describePost(final MethodInfo info) {
268         info.setName(Method.POST);
269         info.setDocumentation(
270                 "POST will add/update media type/urls pairs to a DOI. Standard domain restrictions check will be performed.");
271         final ParameterInfo param = new ParameterInfo();
272         param.setName("{mediaType}");
273         param.setStyle(ParameterStyle.PLAIN);
274         param.setRequired(false);
275         param.setType("xs:string");
276         param.setFixed("{url}");
277         param.setRepeating(true);
278         param.setDocumentation("(key/value) = (mediaType/url)");
279         final RepresentationInfo rep = new RepresentationInfo(MediaType.APPLICATION_WWW_FORM);
280         rep.getParameters().add(param);
281 
282         addRequestDocToMethod(info,
283                 Arrays.asList(createQueryParamDoc(SELECTED_ROLE_PARAMETER, ParameterStyle.HEADER,
284                         "A user can select one role when he is associated to several roles", false,
285                         "xs:string")),
286                 rep);
287         addResponseDocToMethod(info, createResponseDoc(
288                 DATACITE_API_RESPONSE.SUCCESS.getStatus(),
289                 DATACITE_API_RESPONSE.SUCCESS.getShortMessage(),
290                 "explainRepresentationID")
291         );
292         addResponseDocToMethod(info, createResponseDoc(
293                 API_MDS.MEDIA_VALIDATION.getStatus(),
294                 API_MDS.MEDIA_VALIDATION.getShortMessage(),
295                 "explainRepresentationID")
296         );
297         addResponseDocToMethod(info, createResponseDoc(
298                 API_MDS.DATACITE_PROBLEM.getStatus(),
299                 API_MDS.DATACITE_PROBLEM.getShortMessage(),
300                 "explainRepresentationID")
301         );
302         super.describePost(info);
303     }
304 }