2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2019, 2021 AT&T Intellectual Property. All rights reserved.
6 * Modifications Copyright (C) 2023 Nordix Foundation.
7 * ================================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ============LICENSE_END=========================================================
22 package org.onap.policy.models.sim.dmaap.rest;
24 import jakarta.ws.rs.Consumes;
25 import jakarta.ws.rs.core.MediaType;
26 import jakarta.ws.rs.core.MultivaluedMap;
27 import jakarta.ws.rs.ext.MessageBodyReader;
28 import jakarta.ws.rs.ext.Provider;
29 import java.io.BufferedReader;
30 import java.io.EOFException;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.io.InputStreamReader;
34 import java.io.Reader;
35 import java.lang.annotation.Annotation;
36 import java.lang.reflect.Type;
37 import java.nio.charset.StandardCharsets;
38 import java.util.LinkedList;
39 import java.util.List;
40 import org.apache.commons.io.IOUtils;
43 * Provider that decodes "application/cambria" messages.
46 @Consumes(CambriaMessageBodyHandler.MEDIA_TYPE_APPLICATION_CAMBRIA)
47 public class CambriaMessageBodyHandler implements MessageBodyReader<Object> {
48 public static final String MEDIA_TYPE_APPLICATION_CAMBRIA = "application/cambria";
51 * Maximum length of a message or partition.
53 private static final int MAX_LEN = 10000000;
56 * Maximum digits in a length field.
58 private static final int MAX_DIGITS = 10;
61 public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
62 return (mediaType != null && MEDIA_TYPE_APPLICATION_CAMBRIA.equals(mediaType.toString()));
66 public List<Object> readFrom(Class<Object> type, Type genericType, Annotation[] annotations, MediaType mediaType,
67 MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException {
69 try (var bufferedReader = new BufferedReader(new InputStreamReader(entityStream, StandardCharsets.UTF_8))) {
70 List<Object> messages = new LinkedList<>();
72 while ((msg = readMessage(bufferedReader)) != null) {
83 * @param reader source from which to read
84 * @return the message that was read, or {@code null} if there are no more messages
85 * @throws IOException if an error occurs
87 private String readMessage(Reader reader) throws IOException {
88 if (!skipWhitespace(reader)) {
92 int partlen = readLength(reader);
93 if (partlen > MAX_LEN) {
94 throw new IOException("invalid partition length");
97 int msglen = readLength(reader);
98 if (msglen > MAX_LEN) {
99 throw new IOException("invalid message length");
102 // skip over the partition
103 reader.skip(partlen);
105 return readString(reader, msglen);
111 * @param reader source from which to read
112 * @return {@code true} if there is another character after the whitespace,
113 * {@code false} if the end of the stream has been reached
114 * @throws IOException if an error occurs
116 private boolean skipWhitespace(Reader reader) throws IOException {
121 if ((chr = reader.read()) < 0) {
124 } while (Character.isWhitespace(chr));
126 // push the last character back onto the reader
133 * Reads a length field, which is a number followed by ".".
135 * @param reader source from which to read
136 * @return the length, or -1 if EOF has been reached
137 * @throws IOException if an error occurs
139 private int readLength(Reader reader) throws IOException {
140 var bldr = new StringBuilder(MAX_DIGITS);
143 for (var x = 0; x < MAX_DIGITS; ++x) {
144 if ((chr = reader.read()) < 0) {
145 throw new EOFException("missing '.' in 'length' field");
149 String text = bldr.toString().trim();
150 return (text.isEmpty() ? 0 : Integer.parseInt(text));
153 if (!Character.isDigit(chr)) {
154 throw new IOException("invalid character in 'length' field");
157 bldr.append((char) chr);
160 throw new IOException("too many digits in 'length' field");
166 * @param reader source from which to read
167 * @param len length of the string (i.e., number of characters to read)
168 * @return the string that was read
169 * @throws IOException if an error occurs
171 private String readString(Reader reader, int len) throws IOException {
172 var buf = new char[len];
173 IOUtils.readFully(reader, buf);
175 return new String(buf);