2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2019, 2021 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.onap.policy.models.sim.dmaap.rest;
23 import java.io.BufferedReader;
24 import java.io.EOFException;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.InputStreamReader;
28 import java.io.Reader;
29 import java.lang.annotation.Annotation;
30 import java.lang.reflect.Type;
31 import java.nio.charset.StandardCharsets;
32 import java.util.LinkedList;
33 import java.util.List;
34 import javax.ws.rs.Consumes;
35 import javax.ws.rs.core.MediaType;
36 import javax.ws.rs.core.MultivaluedMap;
37 import javax.ws.rs.ext.MessageBodyReader;
38 import javax.ws.rs.ext.Provider;
39 import org.apache.commons.io.IOUtils;
42 * Provider that decodes "application/cambria" messages.
45 @Consumes(CambriaMessageBodyHandler.MEDIA_TYPE_APPLICATION_CAMBRIA)
46 public class CambriaMessageBodyHandler implements MessageBodyReader<Object> {
47 public static final String MEDIA_TYPE_APPLICATION_CAMBRIA = "application/cambria";
50 * Maximum length of a message or partition.
52 private static final int MAX_LEN = 10000000;
55 * Maximum digits in a length field.
57 private static final int MAX_DIGITS = 10;
60 public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
61 return (mediaType != null && MEDIA_TYPE_APPLICATION_CAMBRIA.equals(mediaType.toString()));
65 public List<Object> readFrom(Class<Object> type, Type genericType, Annotation[] annotations, MediaType mediaType,
66 MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException {
68 try (var bufferedReader = new BufferedReader(new InputStreamReader(entityStream, StandardCharsets.UTF_8))) {
69 List<Object> messages = new LinkedList<>();
71 while ((msg = readMessage(bufferedReader)) != null) {
82 * @param reader source from which to read
83 * @return the message that was read, or {@code null} if there are no more messages
84 * @throws IOException if an error occurs
86 private String readMessage(Reader reader) throws IOException {
87 if (!skipWhitespace(reader)) {
91 int partlen = readLength(reader);
92 if (partlen > MAX_LEN) {
93 throw new IOException("invalid partition length");
96 int msglen = readLength(reader);
97 if (msglen > MAX_LEN) {
98 throw new IOException("invalid message length");
101 // skip over the partition
102 reader.skip(partlen);
104 return readString(reader, msglen);
110 * @param reader source from which to read
111 * @return {@code true} if there is another character after the whitespace,
112 * {@code false} if the end of the stream has been reached
113 * @throws IOException if an error occurs
115 private boolean skipWhitespace(Reader reader) throws IOException {
120 if ((chr = reader.read()) < 0) {
123 } while (Character.isWhitespace(chr));
125 // push the last character back onto the reader
132 * Reads a length field, which is a number followed by ".".
134 * @param reader source from which to read
135 * @return the length, or -1 if EOF has been reached
136 * @throws IOException if an error occurs
138 private int readLength(Reader reader) throws IOException {
139 var bldr = new StringBuilder(MAX_DIGITS);
142 for (var x = 0; x < MAX_DIGITS; ++x) {
143 if ((chr = reader.read()) < 0) {
144 throw new EOFException("missing '.' in 'length' field");
148 String text = bldr.toString().trim();
149 return (text.isEmpty() ? 0 : Integer.valueOf(text));
152 if (!Character.isDigit(chr)) {
153 throw new IOException("invalid character in 'length' field");
156 bldr.append((char) chr);
159 throw new IOException("too many digits in 'length' field");
165 * @param reader source from which to read
166 * @param len length of the string (i.e., number of characters to read)
167 * @return the string that was read
168 * @throws IOException if an error occurs
170 private String readString(Reader reader, int len) throws IOException {
171 var buf = new char[len];
172 IOUtils.readFully(reader, buf);
174 return new String(buf);