View Javadoc
1   /*
2    * This file is part of Waarp Project (named also Waarp or GG).
3    *
4    *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
5    *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
6    * individual contributors.
7    *
8    *  All Waarp Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   *
13   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License along with
18   * Waarp . If not, see <http://www.gnu.org/licenses/>.
19   */
20  
21  package org.waarp.openr66.protocol.http.restv2.dbhandlers;
22  
23  import com.fasterxml.jackson.databind.node.ObjectNode;
24  import io.cdap.http.HttpResponder;
25  import io.netty.handler.codec.http.DefaultHttpHeaders;
26  import io.netty.handler.codec.http.HttpHeaders;
27  import io.netty.handler.codec.http.HttpMethod;
28  import io.netty.handler.codec.http.HttpRequest;
29  import org.waarp.common.utility.WaarpStringUtils;
30  import org.waarp.openr66.dao.DAOFactory;
31  import org.waarp.openr66.dao.TransferDAO;
32  import org.waarp.openr66.dao.exception.DAOConnectionException;
33  import org.waarp.openr66.dao.exception.DAONoDataException;
34  import org.waarp.openr66.pojo.Transfer;
35  import org.waarp.openr66.pojo.Transfer.TASKSTEP;
36  import org.waarp.openr66.protocol.http.restv2.converters.TransferConverter;
37  import org.waarp.openr66.protocol.http.restv2.utils.JsonUtils;
38  import org.waarp.openr66.protocol.localhandler.ServerActions;
39  
40  import javax.ws.rs.Consumes;
41  import javax.ws.rs.DELETE;
42  import javax.ws.rs.GET;
43  import javax.ws.rs.InternalServerErrorException;
44  import javax.ws.rs.OPTIONS;
45  import javax.ws.rs.PUT;
46  import javax.ws.rs.Path;
47  import javax.ws.rs.PathParam;
48  import java.io.UnsupportedEncodingException;
49  import java.net.URLDecoder;
50  import java.util.ArrayList;
51  import java.util.List;
52  import java.util.regex.Matcher;
53  import java.util.regex.Pattern;
54  
55  import static io.netty.handler.codec.http.HttpResponseStatus.*;
56  import static javax.ws.rs.core.HttpHeaders.*;
57  import static javax.ws.rs.core.MediaType.*;
58  import static org.waarp.common.role.RoleDefault.ROLE.*;
59  import static org.waarp.openr66.pojo.UpdatedInfo.*;
60  import static org.waarp.openr66.protocol.http.restv2.RestConstants.*;
61  
62  /**
63   * This is the {@link AbstractRestDbHandler} handling all requests made on the
64   * single transfer REST entry
65   * point.
66   */
67  @Path(TRANSFER_ID_HANDLER_URI)
68  public class TransferIdHandler extends AbstractRestDbHandler {
69    /**
70     * The content of the 'Allow' header sent when an 'OPTIONS' request is made
71     * on the handler.
72     */
73    private static final HttpHeaders OPTIONS_HEADERS;
74  
75    static {
76      OPTIONS_HEADERS = new DefaultHttpHeaders();
77      final List<HttpMethod> allow = new ArrayList<HttpMethod>();
78      allow.add(HttpMethod.GET);
79      allow.add(HttpMethod.DELETE);
80      allow.add(HttpMethod.OPTIONS);
81      OPTIONS_HEADERS.add(ALLOW, allow);
82    }
83  
84    /**
85     * Initializes the handler with the given CRUD mask.
86     *
87     * @param crud the CRUD mask for this handler
88     */
89    public TransferIdHandler(final byte crud) {
90      super(crud);
91    }
92  
93    /**
94     * Method called to obtain the information on the transfer whose id was
95     * given in the request's URI. The
96     * requested transfer is sent back in JSON format, unless an unexpected
97     * error prevents it or if the request id
98     * does not exist.
99     * <p>
100    * **NOTE:** The {@code uri} parameter refers to the concatenation of the
101    * transfer's id, and the name of the
102    * host to which the transfer was requested, separated by an underscore
103    * character.</p>
104    *
105    * @param request the HttpRequest made to the resource
106    * @param responder the HttpResponder which sends the reply to the
107    *     request
108    * @param uri the transfer's unique identifier
109    */
110   @GET
111   @Consumes(WILDCARD)
112   @RequiredRole(READONLY)
113   public final void getTransfer(final HttpRequest request,
114                                 final HttpResponder responder,
115                                 @PathParam(URI_ID) final String uri)
116       throws UnsupportedEncodingException {
117     checkSanity(uri);
118     final String key = URLDecoder.decode(uri, UTF8_CHARSET.name());
119     final Pattern pattern = Pattern.compile("(-?\\d+)_(.+)");
120     final Matcher matcher = pattern.matcher(key);
121     if (!matcher.find()) {
122       responder.sendStatus(NOT_FOUND);
123       return;
124     }
125     final String id = matcher.group(1);
126     final String requested = matcher.group(2);
127 
128     TransferDAO transferDAO = null;
129     try {
130       final long transID = Long.parseLong(id);
131       transferDAO = DAO_FACTORY.getTransferDAO();
132       if (!transferDAO.exist(transID, serverName(requested), requested,
133                              serverName())) {
134         responder.sendStatus(NOT_FOUND);
135       } else {
136         final Transfer transfer =
137             transferDAO.select(transID, serverName(requested), requested,
138                                serverName());
139         final ObjectNode response = TransferConverter.transferToNode(transfer);
140         final String responseText = JsonUtils.nodeToString(response);
141         responder.sendJson(OK, responseText);
142       }
143     } catch (final NumberFormatException e) {
144       responder.sendStatus(NOT_FOUND);
145     } catch (final DAOConnectionException e) {
146       throw new InternalServerErrorException(e);
147     } catch (final DAONoDataException e) {
148       responder.sendStatus(NOT_FOUND);
149     } finally {
150       DAOFactory.closeDAO(transferDAO);
151     }
152   }
153 
154   /**
155    * Method called to restart a paused transfer.
156    * <p>
157    * **NOTE:** The {@code uri} parameter refers to the concatenation of the
158    * transfer's id, and the name of the
159    * host to which the transfer was requested, separated by an underscore
160    * character.</p>
161    *
162    * @param request the HttpRequest made to the resource
163    * @param responder the HttpResponder which sends the reply to the
164    *     request
165    * @param uri the transfer's unique identifier
166    */
167   @Path(TransferCommandsURI.RESTART_URI)
168   @PUT
169   @Consumes(WILDCARD)
170   @RequiredRole(SYSTEM)
171   public final void restartTransfer(final HttpRequest request,
172                                     final HttpResponder responder,
173                                     @PathParam(URI_ID) final String uri)
174       throws UnsupportedEncodingException {
175     checkSanity(uri);
176     final String key = URLDecoder.decode(uri, WaarpStringUtils.UTF_8);
177     final Pattern pattern = Pattern.compile("(-?\\d+)_(.+)");
178     final Matcher matcher = pattern.matcher(key);
179     if (!matcher.find()) {
180       responder.sendStatus(NOT_FOUND);
181       return;
182     }
183     final String id = matcher.group(1);
184     final String requested = matcher.group(2);
185 
186     TransferDAO transferDAO = null;
187     try {
188       final long transID = Long.parseLong(id);
189       transferDAO = DAO_FACTORY.getTransferDAO();
190       if (!transferDAO.exist(transID, serverName(requested), requested,
191                              serverName())) {
192         responder.sendStatus(NOT_FOUND);
193       } else {
194         final Transfer transfer =
195             transferDAO.select(transID, serverName(requested), requested,
196                                serverName());
197         final ServerActions actions = new ServerActions();
198         actions.newSession();
199         actions.stopTransfer(transfer);
200         transfer.setUpdatedInfo(TOSUBMIT);
201         transfer.setGlobalStep(transfer.getLastGlobalStep());
202         transferDAO.update(transfer);
203 
204         final ObjectNode response = TransferConverter.transferToNode(transfer);
205         final String responseText = JsonUtils.nodeToString(response);
206         responder.sendJson(OK, responseText);
207       }
208     } catch (final NumberFormatException e) {
209       responder.sendStatus(NOT_FOUND);
210     } catch (final DAOConnectionException e) {
211       throw new InternalServerErrorException(e);
212     } catch (final DAONoDataException e) {
213       responder.sendStatus(NOT_FOUND);
214     } finally {
215       DAOFactory.closeDAO(transferDAO);
216     }
217   }
218 
219   /**
220    * Method called to pause a staged or running transfer.
221    * <p>
222    * **NOTE:** The {@code uri} parameter refers to the concatenation of the
223    * transfer's id, and the name of the
224    * host to which the transfer was requested, separated by an underscore
225    * character.</p>
226    *
227    * @param request the HttpRequest made to the resource
228    * @param responder the HttpResponder which sends the reply to the
229    *     request
230    * @param uri the transfer's unique identifier
231    */
232   @Path(TransferCommandsURI.STOP_URI)
233   @PUT
234   @Consumes(WILDCARD)
235   @RequiredRole(SYSTEM)
236   public final void stopTransfer(final HttpRequest request,
237                                  final HttpResponder responder,
238                                  @PathParam(URI_ID) final String uri)
239       throws UnsupportedEncodingException {
240     checkSanity(uri);
241     final String key = URLDecoder.decode(uri, WaarpStringUtils.UTF8.name());
242     final Pattern pattern = Pattern.compile("(-?\\d+)_(.+)");
243     final Matcher matcher = pattern.matcher(key);
244     if (!matcher.find()) {
245       responder.sendStatus(NOT_FOUND);
246       return;
247     }
248     final String id = matcher.group(1);
249     final String requested = matcher.group(2);
250 
251     TransferDAO transferDAO = null;
252     try {
253       final long transID = Long.parseLong(id);
254       transferDAO = DAO_FACTORY.getTransferDAO();
255       if (!transferDAO.exist(transID, serverName(requested), requested,
256                              serverName())) {
257         responder.sendStatus(NOT_FOUND);
258       } else {
259         final Transfer transfer =
260             transferDAO.select(transID, serverName(requested), requested,
261                                serverName());
262         final ServerActions actions = new ServerActions();
263         actions.newSession();
264         actions.stopTransfer(transfer);
265         transferDAO.update(transfer);
266 
267         final ObjectNode response = TransferConverter.transferToNode(transfer);
268         final String responseText = JsonUtils.nodeToString(response);
269         responder.sendJson(OK, responseText);
270       }
271     } catch (final NumberFormatException e) {
272       responder.sendStatus(NOT_FOUND);
273     } catch (final DAOConnectionException e) {
274       throw new InternalServerErrorException(e);
275     } catch (final DAONoDataException e) {
276       responder.sendStatus(NOT_FOUND);
277     } finally {
278       DAOFactory.closeDAO(transferDAO);
279     }
280   }
281 
282   /**
283    * Method called to cancel a staged or running transfer.
284    * <p>
285    * **NOTE:** The {@code uri} parameter refers to the concatenation of the
286    * transfer's id, and the name of the
287    * host to which the transfer was requested, separated by an underscore
288    * character.</p>
289    *
290    * @param request the HttpRequest made to the resource
291    * @param responder the HttpResponder which sends the reply to the
292    *     request
293    * @param uri the transfer's unique identifier
294    */
295   @Path(TransferCommandsURI.CANCEL_URI)
296   @PUT
297   @Consumes(WILDCARD)
298   @RequiredRole(SYSTEM)
299   public final void cancelTransfer(final HttpRequest request,
300                                    final HttpResponder responder,
301                                    @PathParam(URI_ID) final String uri)
302       throws UnsupportedEncodingException {
303     checkSanity(uri);
304     final String key = URLDecoder.decode(uri, WaarpStringUtils.UTF8.name());
305     final Pattern pattern = Pattern.compile("(-?\\d+)_(.+)");
306     final Matcher matcher = pattern.matcher(key);
307     if (!matcher.find()) {
308       responder.sendStatus(NOT_FOUND);
309       return;
310     }
311     final String id = matcher.group(1);
312     final String requested = matcher.group(2);
313 
314     TransferDAO transferDAO = null;
315     try {
316       final long transID = Long.parseLong(id);
317       transferDAO = DAO_FACTORY.getTransferDAO();
318       if (!transferDAO.exist(transID, serverName(requested), requested,
319                              serverName())) {
320         responder.sendStatus(NOT_FOUND);
321       } else {
322         final Transfer transfer =
323             transferDAO.select(transID, serverName(requested), requested,
324                                serverName());
325         final ServerActions actions = new ServerActions();
326         actions.newSession();
327         actions.cancelTransfer(transfer);
328         transferDAO.update(transfer);
329 
330         final ObjectNode response = TransferConverter.transferToNode(transfer);
331         final String responseBody = JsonUtils.nodeToString(response);
332         responder.sendJson(OK, responseBody);
333       }
334     } catch (final NumberFormatException e) {
335       responder.sendStatus(NOT_FOUND);
336     } catch (final DAOConnectionException e) {
337       throw new InternalServerErrorException(e);
338     } catch (final DAONoDataException e) {
339       responder.sendStatus(NOT_FOUND);
340     } finally {
341       DAOFactory.closeDAO(transferDAO);
342     }
343   }
344 
345   /**
346    * Method called to delete a Transfer entry from the database.
347    * Note that if the Transfer were already started, the delete
348    * cannot be achieved and NOT_FOUND will be returned.
349    * <p>
350    * **NOTE:** The {@code uri} parameter refers to the concatenation of the
351    * transfer's id, and the name of the
352    * host to which the transfer was requested, separated by an underscore
353    * character.</p>
354    *
355    * @param request the HttpRequest made on the resource
356    * @param responder the HttpResponder which sends the reply to the
357    *     request
358    * @param uri the transfer's unique identifier
359    */
360   @DELETE
361   @Consumes(WILDCARD)
362   @RequiredRole(SYSTEM)
363   public final void deleteTransfer(final HttpRequest request,
364                                    final HttpResponder responder,
365                                    @PathParam(URI_ID) final String uri)
366       throws UnsupportedEncodingException {
367     checkSanity(uri);
368     final String key = URLDecoder.decode(uri, UTF8_CHARSET.name());
369     final Pattern pattern = Pattern.compile("(-?\\d+)_(.+)");
370     final Matcher matcher = pattern.matcher(key);
371     if (!matcher.find()) {
372       responder.sendStatus(NOT_FOUND);
373       return;
374     }
375     final String id = matcher.group(1);
376     final String requested = matcher.group(2);
377     TransferDAO transferDAO = null;
378     try {
379       final long transID = Long.parseLong(id);
380       transferDAO = DAO_FACTORY.getTransferDAO();
381       if (!transferDAO.exist(transID, serverName(requested), requested,
382                              serverName())) {
383         responder.sendStatus(NOT_FOUND);
384       } else {
385         final Transfer transfer =
386             transferDAO.select(transID, serverName(requested), requested,
387                                serverName());
388         if (transfer.getGlobalStep() == TASKSTEP.NOTASK &&
389             transfer.getLastGlobalStep() == TASKSTEP.NOTASK) {
390           responder.sendStatus(NOT_FOUND);
391         } else {
392           transferDAO.delete(transfer);
393           responder.sendStatus(NO_CONTENT);
394         }
395       }
396     } catch (final DAOConnectionException e) {
397       throw new InternalServerErrorException(e);
398     } catch (final DAONoDataException e) {
399       responder.sendStatus(NOT_FOUND);
400     } finally {
401       DAOFactory.closeDAO(transferDAO);
402     }
403   }
404 
405   /**
406    * Method called to get a list of all allowed HTTP methods on this entry
407    * point. The HTTP methods are sent as
408    * an array in the reply's headers.
409    * <p>
410    * **NOTE:** The {@code uri} parameter refers to the concatenation of the
411    * transfer's id, and the name of the
412    * host to which the transfer was requested, separated by an underscore
413    * character.</p>
414    *
415    * @param request the HttpRequest made to the resource
416    * @param responder the HttpResponder which sends the reply to the
417    *     request
418    * @param uri the transfer's unique identifier
419    */
420   @OPTIONS
421   @Consumes(WILDCARD)
422   @RequiredRole(NOACCESS)
423   public final void options(final HttpRequest request,
424                             final HttpResponder responder,
425                             @PathParam(URI_ID) final String uri) {
426     checkSanity(uri);
427     responder.sendStatus(OK, OPTIONS_HEADERS);
428   }
429 
430   @Path("{ep}")
431   @OPTIONS
432   @Consumes(WILDCARD)
433   @RequiredRole(NOACCESS)
434   public final void subOptions(final HttpRequest request,
435                                final HttpResponder responder,
436                                @PathParam(URI_ID) final String uri,
437                                @PathParam("ep") final String ep) {
438     checkSanity(uri, ep);
439     final HttpHeaders allow = new DefaultHttpHeaders();
440     final List<HttpMethod> methods = new ArrayList<HttpMethod>();
441 
442     if (ep.equals(TransferCommandsURI.RESTART_URI)) {
443       methods.add(HttpMethod.PUT);
444     } else if (ep.equals(TransferCommandsURI.STOP_URI)) {
445       methods.add(HttpMethod.PUT);
446     } else if (ep.equals(TransferCommandsURI.CANCEL_URI)) {
447       methods.add(HttpMethod.PUT);
448     } else {
449       responder.sendStatus(NOT_FOUND);
450       return;
451     }
452     methods.add(HttpMethod.OPTIONS);
453     allow.add(ALLOW, methods);
454     responder.sendStatus(OK, allow);
455   }
456 }