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.utils;
22  
23  import com.fasterxml.jackson.core.JsonParseException;
24  import com.fasterxml.jackson.core.JsonParser;
25  import com.fasterxml.jackson.core.JsonProcessingException;
26  import com.fasterxml.jackson.databind.DeserializationFeature;
27  import com.fasterxml.jackson.databind.JsonMappingException;
28  import com.fasterxml.jackson.databind.JsonNode;
29  import com.fasterxml.jackson.databind.ObjectMapper;
30  import com.fasterxml.jackson.databind.node.ObjectNode;
31  import io.netty.handler.codec.http.FullHttpRequest;
32  import io.netty.handler.codec.http.HttpRequest;
33  import org.waarp.common.exception.InvalidArgumentException;
34  import org.waarp.common.utility.ParametersChecker;
35  import org.waarp.openr66.protocol.http.restv2.errors.RestErrorException;
36  
37  import javax.ws.rs.InternalServerErrorException;
38  import javax.ws.rs.NotSupportedException;
39  import java.io.IOException;
40  
41  import static javax.ws.rs.core.HttpHeaders.*;
42  import static javax.ws.rs.core.MediaType.*;
43  import static org.waarp.openr66.protocol.http.restv2.RestConstants.*;
44  import static org.waarp.openr66.protocol.http.restv2.errors.RestErrors.*;
45  
46  /**
47   * A series of utility methods for serializing and deserializing JSON.
48   */
49  public final class JsonUtils {
50  
51    /**
52     * Makes the default constructor of this utility class inaccessible.
53     */
54    private JsonUtils() throws InstantiationException {
55      throw new InstantiationException(
56          getClass().getName() + " cannot be instantiated.");
57    }
58  
59    // ######################### PUBLIC METHODS #################################
60  
61    /**
62     * Converts an ObjectNode into a String.
63     *
64     * @param object the ObjectNode to convert
65     *
66     * @return the JSON object as a String
67     *
68     * @throws InternalServerErrorException if an unexpected error
69     *     occurred
70     */
71    public static String nodeToString(final ObjectNode object) {
72      try {
73        final ObjectMapper mapper = new ObjectMapper();
74        return mapper.writeValueAsString(object);
75      } catch (final JsonProcessingException e) {
76        throw new InternalServerErrorException(e);
77      }
78    }
79  
80    /**
81     * Deserializes a request's content as an ObjectNode
82     *
83     * @param request the request to deserialize
84     *
85     * @return the deserialized JSON object
86     *
87     * @throws RestErrorException If the content is not a valid JSON
88     *     object.
89     * @throws NotSupportedException If the content type is not JSON.
90     * @throws InternalServerErrorException if an unexpected error
91     *     occurred
92     */
93    public static ObjectNode deserializeRequest(final HttpRequest request) {
94      if (!(request instanceof FullHttpRequest)) {
95        throw new RestErrorException(MISSING_BODY());
96      }
97  
98      try {
99        final String body =
100           ((FullHttpRequest) request).content().toString(UTF8_CHARSET);
101       try {
102         ParametersChecker.checkSanityString(body);
103       } catch (final InvalidArgumentException e) {
104         throw new RestErrorException(MALFORMED_JSON(0, 0,
105                                                     "The root JSON element contains invalid data"));
106       }
107       final ObjectMapper mapper = new ObjectMapper();
108       mapper.enable(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY);
109       final JsonNode node = mapper.readTree(body);
110 
111       if (node.isObject()) {
112         return (ObjectNode) node;
113       } else {
114         throw new RestErrorException(
115             MALFORMED_JSON(0, 0, "The root JSON element is not an object"));
116       }
117     } catch (final JsonParseException e) {
118       final String contentType = request.headers().get(CONTENT_TYPE);
119       if (ParametersChecker.isEmpty(contentType)) {
120         throw new NotSupportedException(APPLICATION_JSON);
121       } else {
122         throw new RestErrorException(MALFORMED_JSON(e.getLocation().getLineNr(),
123                                                     e.getLocation()
124                                                      .getColumnNr(),
125                                                     e.getOriginalMessage()));
126       }
127     } catch (final JsonMappingException e) {
128       final JsonParser parser = (JsonParser) e.getProcessor();
129       try {
130         final String field = parser.getCurrentName();
131         if (field == null) {
132           throw new RestErrorException(MISSING_BODY());
133         } else {
134           throw new RestErrorException(DUPLICATE_KEY(field));
135         }
136       } catch (final IOException ex) {
137         throw new InternalServerErrorException(e);
138       }
139     } catch (final IOException e) {
140       throw new InternalServerErrorException(e);
141     }
142   }
143 }