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.ArrayNode;
24  import com.fasterxml.jackson.databind.node.ObjectNode;
25  import io.cdap.http.HttpResponder;
26  import io.netty.handler.codec.http.DefaultHttpHeaders;
27  import io.netty.handler.codec.http.HttpHeaders;
28  import io.netty.handler.codec.http.HttpMethod;
29  import io.netty.handler.codec.http.HttpRequest;
30  import org.waarp.common.json.JsonHandler;
31  import org.waarp.common.role.RoleDefault.ROLE;
32  import org.waarp.common.utility.ParametersChecker;
33  import org.waarp.openr66.dao.DAOFactory;
34  import org.waarp.openr66.dao.Filter;
35  import org.waarp.openr66.dao.HostDAO;
36  import org.waarp.openr66.dao.exception.DAOConnectionException;
37  import org.waarp.openr66.pojo.Host;
38  import org.waarp.openr66.protocol.http.restv2.converters.HostConverter;
39  import org.waarp.openr66.protocol.http.restv2.errors.RestError;
40  import org.waarp.openr66.protocol.http.restv2.errors.RestErrorException;
41  import org.waarp.openr66.protocol.http.restv2.utils.JsonUtils;
42  import org.waarp.openr66.protocol.http.restv2.utils.RestUtils;
43  
44  import javax.ws.rs.Consumes;
45  import javax.ws.rs.DefaultValue;
46  import javax.ws.rs.GET;
47  import javax.ws.rs.InternalServerErrorException;
48  import javax.ws.rs.OPTIONS;
49  import javax.ws.rs.POST;
50  import javax.ws.rs.Path;
51  import javax.ws.rs.QueryParam;
52  import java.util.ArrayList;
53  import java.util.Collections;
54  import java.util.List;
55  
56  import static io.netty.handler.codec.http.HttpResponseStatus.*;
57  import static javax.ws.rs.core.HttpHeaders.*;
58  import static javax.ws.rs.core.MediaType.*;
59  import static org.waarp.openr66.dao.database.DBHostDAO.*;
60  import static org.waarp.openr66.protocol.http.restv2.RestConstants.*;
61  import static org.waarp.openr66.protocol.http.restv2.RestConstants.GetHostsParams.*;
62  import static org.waarp.openr66.protocol.http.restv2.errors.RestErrors.*;
63  
64  /**
65   * This is the {@link AbstractRestDbHandler} handling all requests made on the
66   * host collection REST entry
67   * point.
68   */
69  @Path(HOSTS_HANDLER_URI)
70  public class HostsHandler extends AbstractRestDbHandler {
71  
72    /**
73     * The content of the 'Allow' header sent when an 'OPTIONS' request is made
74     * on the handler.
75     */
76    private static final HttpHeaders OPTIONS_HEADERS;
77  
78    static {
79      OPTIONS_HEADERS = new DefaultHttpHeaders();
80      final List<HttpMethod> allow = new ArrayList<HttpMethod>();
81      allow.add(HttpMethod.GET);
82      allow.add(HttpMethod.POST);
83      allow.add(HttpMethod.OPTIONS);
84      OPTIONS_HEADERS.add(ALLOW, allow);
85    }
86  
87    /**
88     * Initializes the handler with the given CRUD mask.
89     *
90     * @param crud the CRUD mask for this handler
91     */
92    public HostsHandler(final byte crud) {
93      super(crud);
94    }
95  
96    /**
97     * Method called to get a list of host entries from the server's database,
98     * with optional filters applied.
99     *
100    * @param request The {@link HttpRequest} made on the resource.
101    * @param responder The {@link HttpResponder} which sends the reply
102    *     to the request.
103    * @param limit_str HTTP query parameter, maximum number of entries
104    *     allowed in the response.
105    * @param offset_str HTTP query parameter, index of the first
106    *     accepted entry in the list of all valid
107    *     answers.
108    * @param order_str HTTP query parameter, the criteria used to sort
109    *     the entries and the way of ordering.
110    * @param address HTTP query parameter, filter only hosts with this
111    *     address.
112    * @param isSSL_str HTTP query parameter, filter only hosts that use
113    *     SSL, or those that don't. Leave empty
114    *     to get both.
115    * @param isActive_str HTTP query parameter, filter hosts that are
116    *     active, or those that aren't. Leave empty
117    *     to get both.
118    * @param countOrder if true is specified, it turns out to be a "count"
119    *     request only
120    */
121   @GET
122   @Consumes(APPLICATION_FORM_URLENCODED)
123   @RequiredRole(ROLE.READONLY)
124   public final void filterHosts(final HttpRequest request,
125                                 final HttpResponder responder,
126                                 @QueryParam(LIMIT) @DefaultValue("20")
127                                 final String limit_str,
128                                 @QueryParam(OFFSET) @DefaultValue("0")
129                                 final String offset_str,
130                                 @QueryParam(ORDER) @DefaultValue("ascId")
131                                 final String order_str,
132                                 @QueryParam(ADDRESS) final String address,
133                                 @QueryParam(IS_SSL) final String isSSL_str,
134                                 @QueryParam(IS_ACTIVE)
135                                 final String isActive_str,
136                                 @QueryParam(COUNT_ORDER) @DefaultValue("")
137                                 final String countOrder) {
138     checkSanity(limit_str, offset_str, order_str, address, isActive_str,
139                 isActive_str, countOrder);
140     final List<RestError> errors = new ArrayList<RestError>();
141 
142     boolean count = false;
143     try {
144       if (ParametersChecker.isNotEmpty(countOrder) &&
145           RestUtils.stringToBoolean(countOrder)) {
146         count = true;
147       }
148     } catch (final Exception ignore) {
149       // Ignore
150     }
151     int limit = 20;
152     int offset = 0;
153     HostConverter.Order order = HostConverter.Order.ascId;
154     try {
155       limit = Integer.parseInt(limit_str);
156     } catch (final NumberFormatException e) {
157       errors.add(ILLEGAL_PARAMETER_VALUE(LIMIT, limit_str));
158     }
159     try {
160       order = HostConverter.Order.valueOf(order_str);
161     } catch (final IllegalArgumentException e) {
162       errors.add(ILLEGAL_PARAMETER_VALUE(ORDER, order_str));
163     }
164     try {
165       offset = Integer.parseInt(offset_str);
166     } catch (final NumberFormatException e) {
167       errors.add(ILLEGAL_PARAMETER_VALUE(OFFSET, offset_str));
168     }
169 
170     if (limit < 0) {
171       errors.add(ILLEGAL_PARAMETER_VALUE(LIMIT, limit_str));
172     } else if (offset < 0) {
173       errors.add(ILLEGAL_PARAMETER_VALUE(OFFSET, offset_str));
174     }
175 
176     boolean isSSL = false;
177     boolean isActive = false;
178     if (ParametersChecker.isNotEmpty(isSSL_str)) {
179       try {
180         isSSL = RestUtils.stringToBoolean(isSSL_str);
181       } catch (final IllegalArgumentException e) {
182         errors.add(ILLEGAL_PARAMETER_VALUE(IS_SSL, isSSL_str));
183       }
184     }
185     if (ParametersChecker.isNotEmpty(isActive_str)) {
186       try {
187         isActive = RestUtils.stringToBoolean(isActive_str);
188       } catch (final IllegalArgumentException e) {
189         errors.add(ILLEGAL_PARAMETER_VALUE(IS_ACTIVE, isActive_str));
190       }
191     }
192 
193     if (!errors.isEmpty()) {
194       throw new RestErrorException(errors);
195     }
196 
197     final List<Filter> filters = new ArrayList<Filter>();
198     if (ParametersChecker.isNotEmpty(address)) {
199       filters.add(new Filter(ADDRESS_FIELD, "=", address));
200     }
201     if (ParametersChecker.isNotEmpty(isSSL_str)) {
202       filters.add(new Filter(IS_SSL_FIELD, "=", isSSL));
203     }
204     if (ParametersChecker.isNotEmpty(isActive_str)) {
205       filters.add(new Filter(IS_ACTIVE_FIELD, "=", isActive));
206     }
207     HostDAO hostDAO = null;
208     final ObjectNode responseObject = JsonHandler.createObjectNode();
209     if (count) {
210       long nbCount = -1;
211       try {
212         hostDAO = DAO_FACTORY.getHostDAO(false);
213         nbCount = hostDAO.count(filters);
214       } catch (final DAOConnectionException e) {
215         throw new InternalServerErrorException(e);
216       } finally {
217         DAOFactory.closeDAO(hostDAO);
218       }
219       responseObject.put("totalResults", nbCount);
220     } else {
221       List<Host> hosts;
222       try {
223         hostDAO = DAO_FACTORY.getHostDAO(false);
224         hosts = hostDAO.find(filters);
225       } catch (final DAOConnectionException e) {
226         throw new InternalServerErrorException(e);
227       } finally {
228         DAOFactory.closeDAO(hostDAO);
229       }
230 
231       final int totalResults = hosts.size();
232       Collections.sort(hosts, order.comparator);
233 
234       final ArrayNode results = JsonHandler.createArrayNode();
235       for (int i = offset; i < offset + limit && i < hosts.size(); i++) {
236         results.add(HostConverter.hostToNode(hosts.get(i)));
237       }
238 
239       responseObject.put("totalResults", totalResults);
240       responseObject.set("results", results);
241     }
242     final String responseText = JsonUtils.nodeToString(responseObject);
243     responder.sendJson(OK, responseText);
244   }
245 
246   /**
247    * Method called to add a new host authentication entry to the server
248    * database.
249    *
250    * @param request The {@link HttpRequest} made on the resource.
251    * @param responder The {@link HttpResponder} which sends the reply
252    *     to the request.
253    */
254   @POST
255   @Consumes(APPLICATION_JSON)
256   @RequiredRole(ROLE.HOST)
257   public final void addHost(final HttpRequest request,
258                             final HttpResponder responder) {
259     final ObjectNode requestObject = JsonUtils.deserializeRequest(request);
260     checkSanity(requestObject);
261     final Host host = HostConverter.nodeToNewHost(requestObject);
262 
263     HostDAO hostDAO = null;
264     try {
265       hostDAO = DAO_FACTORY.getHostDAO(false);
266 
267       if (!hostDAO.exist(host.getHostid())) {
268         throw new RestErrorException(ALREADY_EXISTING(host.getHostid()));
269       }
270 
271       hostDAO.insert(host);
272     } catch (final DAOConnectionException e) {
273       throw new InternalServerErrorException(e);
274     } finally {
275       DAOFactory.closeDAO(hostDAO);
276     }
277 
278     final ObjectNode responseObject = HostConverter.hostToNode(host);
279     final String responseText = JsonUtils.nodeToString(responseObject);
280 
281     final DefaultHttpHeaders headers = new DefaultHttpHeaders();
282     headers.add(CONTENT_TYPE, APPLICATION_JSON);
283     headers.add("host-uri", HOSTS_HANDLER_URI + host.getHostid());
284 
285     responder.sendString(CREATED, responseText, headers);
286   }
287 
288   /**
289    * Method called to get a list of all allowed HTTP methods on this entry
290    * point. The HTTP methods are sent as
291    * an array in the reply's headers.
292    *
293    * @param request The {@link HttpRequest} made on the resource.
294    * @param responder The {@link HttpResponder} which sends the reply
295    *     to the request.
296    */
297   @OPTIONS
298   @Consumes(WILDCARD)
299   @RequiredRole(ROLE.NOACCESS)
300   public final void options(final HttpRequest request,
301                             final HttpResponder responder) {
302     responder.sendStatus(OK, OPTIONS_HEADERS);
303   }
304 }