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.converters;
22  
23  import com.fasterxml.jackson.databind.JsonNode;
24  import com.fasterxml.jackson.databind.node.ArrayNode;
25  import com.fasterxml.jackson.databind.node.ObjectNode;
26  import org.waarp.common.database.exception.WaarpDatabaseSqlException;
27  import org.waarp.common.json.JsonHandler;
28  import org.waarp.common.logging.SysErrLogger;
29  import org.waarp.common.role.RoleDefault.ROLE;
30  import org.waarp.common.utility.SingletonUtils;
31  import org.waarp.openr66.dao.BusinessDAO;
32  import org.waarp.openr66.dao.DAOFactory;
33  import org.waarp.openr66.dao.exception.DAOConnectionException;
34  import org.waarp.openr66.dao.exception.DAONoDataException;
35  import org.waarp.openr66.pojo.Business;
36  import org.waarp.openr66.protocol.http.restv2.errors.RestError;
37  import org.waarp.openr66.protocol.http.restv2.errors.RestErrorException;
38  import org.waarp.openr66.protocol.http.restv2.utils.XmlSerializable.Aliases;
39  import org.waarp.openr66.protocol.http.restv2.utils.XmlSerializable.Aliases.AliasEntry;
40  import org.waarp.openr66.protocol.http.restv2.utils.XmlSerializable.Businesses;
41  import org.waarp.openr66.protocol.http.restv2.utils.XmlSerializable.Roles;
42  import org.waarp.openr66.protocol.http.restv2.utils.XmlSerializable.Roles.RoleEntry;
43  import org.waarp.openr66.protocol.http.restv2.utils.XmlUtils;
44  
45  import javax.ws.rs.InternalServerErrorException;
46  import java.util.ArrayList;
47  import java.util.Iterator;
48  import java.util.List;
49  import java.util.Map;
50  
51  import static org.waarp.openr66.protocol.http.restv2.RestConstants.*;
52  import static org.waarp.openr66.protocol.http.restv2.RestConstants.HostConfigFields.*;
53  import static org.waarp.openr66.protocol.http.restv2.errors.RestErrors.*;
54  
55  /**
56   * A collection of utility methods to convert {@link Business} objects to their
57   * corresponding
58   * {@link ObjectNode} and vice-versa.
59   */
60  public final class HostConfigConverter {
61  
62    /**
63     * Makes the default constructor of this utility class inaccessible.
64     */
65    private HostConfigConverter() throws InstantiationException {
66      throw new InstantiationException(
67          getClass().getName() + " cannot be instantiated.");
68    }
69  
70    // ########################### PUBLIC METHODS ###############################
71  
72    /**
73     * Converts the given {@link Business} object into an {@link ObjectNode}.
74     *
75     * @param hostConfig the HostConfig object to convert
76     *
77     * @return the converted ObjectNode
78     */
79    public static ObjectNode businessToNode(final Business hostConfig) {
80      final ArrayNode business = getBusinessArray(hostConfig);
81      final ArrayNode roles = getRolesArray(hostConfig);
82      final ArrayNode aliasArray = getAliasArray(hostConfig);
83  
84      final ObjectNode node = JsonHandler.createObjectNode();
85      node.putArray(BUSINESS).addAll(business);
86      node.putArray(ROLES).addAll(roles);
87      node.putArray(ALIASES).addAll(aliasArray);
88      node.put(OTHERS, hostConfig.getOthers());
89  
90      return node;
91    }
92  
93    /**
94     * Converts the given {@link ObjectNode} into a {@link Business} object.
95     *
96     * @param object the ObjectNode to convert
97     *
98     * @return the corresponding HostConfig object
99     *
100    * @throws RestErrorException if the given ObjectNode does not
101    *     represent a Business object
102    */
103   public static Business nodeToNewBusiness(final ObjectNode object) {
104     Business emptyBusiness = null;
105     try {
106       emptyBusiness = new Business(serverName(), "", "<roles></roles>",
107                                    "<aliases></aliases>",
108                                    "<root><version></version></root>");
109     } catch (final WaarpDatabaseSqlException e) {
110       SysErrLogger.FAKE_LOGGER.syserr(e);
111     }
112 
113     return nodeToUpdatedBusiness(object, emptyBusiness);
114   }
115 
116   /**
117    * Returns the given {@link Business} object updated with the values defined
118    * in the {@link ObjectNode}
119    * parameter. All fields missing from the ObjectNode parameter will stay
120    * unchanged in the returned Business
121    * object.
122    *
123    * @param object the ObjectNode to convert.
124    * @param oldBusiness the Business object to update.
125    *
126    * @return the updated Business object
127    *
128    * @throws RestErrorException if the given ObjectNode does not
129    *     represent a Business object
130    */
131   public static Business nodeToUpdatedBusiness(final ObjectNode object,
132                                                final Business oldBusiness) {
133     final List<RestError> errors = new ArrayList<RestError>();
134 
135     final Iterator<Map.Entry<String, JsonNode>> fields = object.fields();
136     while (fields.hasNext()) {
137       final Map.Entry<String, JsonNode> field = fields.next();
138       final String name = field.getKey();
139       final JsonNode value = field.getValue();
140 
141       if (name.equalsIgnoreCase(BUSINESS)) {
142         if (value.isArray()) {
143           final Businesses businessList = new Businesses();
144           final Iterator<JsonNode> business = value.elements();
145           while (business.hasNext()) {
146             final JsonNode businessName = business.next();
147             if (businessName.isTextual()) {
148               businessList.business.add(businessName.asText());
149             } else {
150               errors.add(
151                   ILLEGAL_FIELD_VALUE(BUSINESS, businessName.toString()));
152             }
153           }
154           oldBusiness.setBusiness(XmlUtils.objectToXml(businessList));
155         } else {
156           errors.add(ILLEGAL_FIELD_VALUE(BUSINESS, value.toString()));
157         }
158       } else if (name.equalsIgnoreCase(ROLES)) {
159         if (value.isArray()) {
160           try {
161             final Roles roles = nodeToRoles((ArrayNode) value);
162             oldBusiness.setRoles(XmlUtils.objectToXml(roles));
163           } catch (final RestErrorException e) {
164             errors.addAll(e.errors);
165           }
166         } else {
167           errors.add(ILLEGAL_FIELD_VALUE(ROLES, value.toString()));
168         }
169       } else if (name.equalsIgnoreCase(ALIASES)) {
170         if (value.isArray()) {
171           try {
172             final Aliases aliases = nodeToAliasList((ArrayNode) value);
173             oldBusiness.setAliases(XmlUtils.objectToXml(aliases));
174           } catch (final RestErrorException e) {
175             errors.addAll(e.errors);
176           }
177         } else {
178           errors.add(ILLEGAL_FIELD_VALUE(ALIASES, value.toString()));
179         }
180       } else if (name.equalsIgnoreCase(OTHERS)) {
181         if (value.isTextual() &&
182             value.asText().matches("<root><version>.+</version></root>")) {
183           oldBusiness.setOthers(value.asText());
184 
185         } else {
186           errors.add(ILLEGAL_FIELD_VALUE(ALIASES, value.toString()));
187         }
188       } else {
189         errors.add(UNKNOWN_FIELD(name));
190       }
191     }
192 
193     if (errors.isEmpty()) {
194       return oldBusiness;
195     } else {
196       throw new RestErrorException(errors);
197     }
198   }
199 
200   /**
201    * Returns the requested host's list of roles on the server.
202    *
203    * @param hostName the desired host's name
204    *
205    * @return the host's list of roles
206    *
207    * @throws InternalServerErrorException if an unexpected error
208    *     occurred
209    */
210   public static List<ROLE> getRoles(final String hostName) {
211     ArrayNode array;
212     BusinessDAO businessDAO = null;
213     try {
214       businessDAO = DAO_FACTORY.getBusinessDAO(true);
215       final Business config = businessDAO.select(serverName());
216       array = getRolesArray(config);
217     } catch (final DAOConnectionException e) {
218       throw new InternalServerErrorException(e);
219     } catch (final DAONoDataException e) {
220       throw new InternalServerErrorException(e);
221     } finally {
222       DAOFactory.closeDAO(businessDAO);
223     }
224     final Roles roles = nodeToRoles(array);
225 
226     for (final RoleEntry role : roles.roles) {
227       if (role.hostName.equals(hostName)) {
228         return role.roleList;
229       }
230     }
231     return SingletonUtils.singletonList();
232   }
233 
234   // ########################## PRIVATE METHODS ###############################
235 
236   /**
237    * Converts and returns the list of aliases of the given {@link Business}
238    * object into an {@link ArrayNode}.
239    *
240    * @param hostConfig the server's HostConfig object
241    *
242    * @return the server's list of known aliases
243    */
244   private static ArrayNode getAliasArray(final Business hostConfig) {
245     final ArrayNode array = JsonHandler.createArrayNode();
246     if (hostConfig.getAliases() != null) {
247       try {
248         final Aliases aliases =
249             XmlUtils.xmlToObject(hostConfig.getAliases(), Aliases.class);
250 
251         for (final AliasEntry alias : aliases.aliases) {
252           final ObjectNode node = JsonHandler.createObjectNode();
253           final ArrayNode aliasIds = JsonHandler.createArrayNode();
254 
255           for (final String aliasId : alias.aliasList) {
256             aliasIds.add(aliasId);
257           }
258 
259           node.put(HOST_NAME, alias.hostName);
260           node.putArray(ALIAS_LIST).addAll(aliasIds);
261 
262           array.add(node);
263         }
264       } catch (final InternalServerErrorException ignore) { //NOSONAR
265         SysErrLogger.FAKE_LOGGER.syserr("No Alias definition");
266       }
267     }
268     return array;
269   }
270 
271   /**
272    * Converts and returns the list of roles of the given {@link Business}
273    * object into an {@link ArrayNode}.
274    *
275    * @param hostConfig the server's HostConfig object
276    *
277    * @return the server's list of known aliases
278    */
279   private static ArrayNode getRolesArray(final Business hostConfig) {
280     final ArrayNode array = JsonHandler.createArrayNode();
281     if (hostConfig.getRoles() != null) {
282       try {
283         final Roles roles =
284             XmlUtils.xmlToObject(hostConfig.getRoles(), Roles.class);
285 
286         for (final RoleEntry role : roles.roles) {
287           final ObjectNode node = JsonHandler.createObjectNode();
288           final ArrayNode roleTypes = JsonHandler.createArrayNode();
289 
290           for (final ROLE roleType : role.roleList) {
291             roleTypes.add(roleType.name());
292           }
293 
294           node.put(HOST_NAME, role.hostName);
295           node.putArray(ROLE_LIST).addAll(roleTypes);
296 
297           array.add(node);
298         }
299       } catch (final InternalServerErrorException ignore) {//NOSONAR
300         SysErrLogger.FAKE_LOGGER.syserr("No Roles definition");
301       }
302     }
303     return array;
304   }
305 
306   /**
307    * Converts and returns the list of business partners of the given {@link
308    * Business} object into an
309    * {@link ArrayNode}.
310    *
311    * @param hostConfig the server's HostConfig object
312    *
313    * @return the server's list of known aliases
314    */
315   private static ArrayNode getBusinessArray(final Business hostConfig) {
316     final ArrayNode array = JsonHandler.createArrayNode();
317     if (hostConfig.getBusiness() != null) {
318       try {
319         final Businesses business =
320             XmlUtils.xmlToObject(hostConfig.getBusiness(), Businesses.class);
321 
322         for (final String businessId : business.business) {
323           array.add(businessId);
324         }
325       } catch (final InternalServerErrorException ignore) {//NOSONAR
326         SysErrLogger.FAKE_LOGGER.syserr("No Business definition");
327       }
328     }
329     return array;
330   }
331 
332   /**
333    * Converts the given {@link ArrayNode} into an {@link Aliases} object.
334    *
335    * @param array the JSON array of aliases
336    *
337    * @return the XML serializable list of aliases
338    *
339    * @throws RestErrorException if the given ArrayNode does not
340    *     represent a list of aliases
341    */
342   private static Aliases nodeToAliasList(final ArrayNode array) {
343     final List<AliasEntry> aliases = new ArrayList<AliasEntry>();
344     final List<RestError> errors = new ArrayList<RestError>();
345 
346     final Iterator<JsonNode> elements = array.elements();
347     while (elements.hasNext()) {
348       final JsonNode element = elements.next();
349       final AliasEntry alias = new AliasEntry();
350 
351       if (!element.isObject()) {
352         errors.add(ILLEGAL_PARAMETER_VALUE(ALIASES, element.toString()));
353         continue;
354       }
355 
356       final Iterator<Map.Entry<String, JsonNode>> fields = element.fields();
357       while (fields.hasNext()) {
358         final Map.Entry<String, JsonNode> field = fields.next();
359         final String name = field.getKey();
360         final JsonNode value = field.getValue();
361 
362         if (name.equalsIgnoreCase(HOST_NAME)) {
363           if (value.isTextual()) {
364             alias.hostName = value.asText();
365           } else {
366             errors.add(ILLEGAL_PARAMETER_VALUE(HOST_NAME, value.toString()));
367           }
368         } else if (name.equalsIgnoreCase(ALIAS_LIST)) {
369           if (value.isArray()) {
370             final Iterator<JsonNode> aliasList = value.elements();
371             while (aliasList.hasNext()) {
372               final JsonNode aliasId = aliasList.next();
373               if (aliasId.isTextual()) {
374                 alias.aliasList.add(aliasId.asText());
375               } else {
376                 errors.add(
377                     ILLEGAL_PARAMETER_VALUE(ALIAS_LIST, aliasId.toString()));
378               }
379             }
380           } else {
381             errors.add(ILLEGAL_PARAMETER_VALUE(ALIAS_LIST, value.toString()));
382           }
383         } else {
384           errors.add(UNKNOWN_FIELD(name));
385         }
386       }
387 
388       if (alias.hostName.isEmpty()) {
389         errors.add(MISSING_FIELD(HOST_NAME));
390       }
391       if (alias.aliasList.isEmpty()) {
392         errors.add(MISSING_FIELD(ALIAS_LIST));
393       }
394       aliases.add(alias);
395     }
396 
397     if (errors.isEmpty()) {
398       return new Aliases(aliases);
399     } else {
400       throw new RestErrorException(errors);
401     }
402   }
403 
404   /**
405    * Converts the given {@link ArrayNode} into an {@link Roles} object.
406    *
407    * @param array the JSON list of roles
408    *
409    * @return the XML serializable list of roles
410    *
411    * @throws RestErrorException if the given ArrayNode does not
412    *     represent a list of roles
413    */
414   private static Roles nodeToRoles(final ArrayNode array) {
415     final List<RoleEntry> roles = new ArrayList<RoleEntry>();
416     final List<RestError> errors = new ArrayList<RestError>();
417 
418     final Iterator<JsonNode> elements = array.elements();
419     while (elements.hasNext()) {
420       final JsonNode element = elements.next();
421       final RoleEntry role = new RoleEntry();
422 
423       if (!element.isObject()) {
424         errors.add(ILLEGAL_PARAMETER_VALUE(ROLES, element.toString()));
425         continue;
426       }
427 
428       final Iterator<Map.Entry<String, JsonNode>> fields = element.fields();
429       while (fields.hasNext()) {
430         final Map.Entry<String, JsonNode> field = fields.next();
431         final String name = field.getKey();
432         final JsonNode value = field.getValue();
433 
434         if (name.equalsIgnoreCase(HOST_NAME)) {
435           if (value.isTextual()) {
436             role.hostName = value.asText();
437           } else {
438             errors.add(ILLEGAL_PARAMETER_VALUE(HOST_NAME, value.toString()));
439           }
440         } else if (name.equalsIgnoreCase(ROLE_LIST)) {
441           if (value.isArray()) {
442             final Iterator<JsonNode> roleTypes = value.elements();
443             while (roleTypes.hasNext()) {
444               final JsonNode roleType = roleTypes.next();
445               if (roleType.isTextual()) {
446                 try {
447                   role.roleList.add(ROLE.valueOf(roleType.asText()));
448                 } catch (final IllegalArgumentException e) {
449                   errors.add(
450                       ILLEGAL_PARAMETER_VALUE(ROLE_LIST, roleType.toString()));
451                 }
452               } else {
453                 errors.add(
454                     ILLEGAL_PARAMETER_VALUE(ROLE_LIST, roleType.toString()));
455               }
456             }
457           } else {
458             errors.add(ILLEGAL_PARAMETER_VALUE(ROLE_LIST, value.toString()));
459           }
460         } else {
461           errors.add(UNKNOWN_FIELD(name));
462         }
463       }
464 
465       if (role.hostName.isEmpty()) {
466         errors.add(MISSING_FIELD(HOST_NAME));
467       }
468       if (role.roleList.isEmpty()) {
469         errors.add(MISSING_FIELD(ROLE_LIST));
470       }
471       roles.add(role);
472     }
473 
474     if (errors.isEmpty()) {
475       return new Roles(roles);
476     } else {
477       throw new RestErrorException(errors);
478     }
479   }
480 }