2 * Copyright © 2016-2017 European Support Limited
3 * Modification Copyright (C) 2019 Nordix Foundation.
4 * Modification Copyright (C) 2021 Nokia.
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
19 package org.openecomp.sdc.tosca.csar;
21 import com.google.common.collect.ImmutableList;
22 import com.google.common.collect.ImmutableMap;
23 import java.io.BufferedReader;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.InputStreamReader;
27 import java.nio.charset.StandardCharsets;
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.HashMap;
31 import java.util.Iterator;
32 import java.util.List;
34 import java.util.Optional;
35 import org.apache.commons.collections.MapUtils;
36 import org.apache.commons.lang.StringUtils;
37 import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
38 import org.openecomp.sdc.common.errors.Messages;
39 import org.openecomp.sdc.logging.api.Logger;
40 import org.openecomp.sdc.logging.api.LoggerFactory;
42 abstract class AbstractOnboardingManifest implements Manifest {
44 protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractOnboardingManifest.class);
45 protected static final int MAX_ALLOWED_MANIFEST_META_ENTRIES = 4;
46 protected Map<String, String> metadata;
47 protected List<String> sources;
48 protected Map<String, List<String>> nonManoSources;
49 protected Map<String, AlgorithmDigest> sourceAndChecksumMap = new HashMap<>();
50 protected Map<String, SignatureData> sourceAndSignatureMap = new HashMap<>();
51 protected String cmsSignature;
52 protected List<String> errors;
53 protected boolean continueToProcess;
54 protected String currentLine;
55 protected Iterator<String> linesIterator;
56 protected int currentLineNumber;
58 protected AbstractOnboardingManifest() {
59 errors = new ArrayList<>();
60 sources = new ArrayList<>();
61 metadata = new HashMap<>();
62 nonManoSources = new HashMap<>();
66 public Optional<ResourceTypeEnum> getType() {
68 return Optional.empty();
70 final String firstKey = metadata.keySet().iterator().next();
71 final ManifestTokenType manifestTokenType = ManifestTokenType.parse(firstKey).orElse(null);
72 if (manifestTokenType == null) {
73 return Optional.empty();
75 if (manifestTokenType.isMetadataPnfEntry()) {
76 return Optional.of(ResourceTypeEnum.PNF);
78 return Optional.of(ResourceTypeEnum.VF);
82 public void parse(final InputStream manifestAsStream) {
84 final ImmutableList<String> lines = readAllLines(manifestAsStream);
85 continueToProcess = true;
86 currentLineNumber = 0;
87 processManifest(lines);
88 } catch (IOException e) {
89 LOGGER.error(e.getMessage(), e);
90 errors.add(Messages.MANIFEST_PARSER_INTERNAL.getErrorMessage());
95 * Process the manifest lines, reporting an error when detected.
97 * @param lines the manifest lines
99 protected void processManifest(final ImmutableList<String> lines) {
100 if (isEmptyManifest(lines)) {
103 linesIterator = lines.iterator();
104 readNextNonEmptyLine();
105 if (!getCurrentLine().isPresent()) {
106 errors.add(Messages.MANIFEST_EMPTY.getErrorMessage());
115 * Process the metadata part of the Manifest file.
117 protected abstract void processMetadata();
120 * Process the other parts from manifest different than metadata.
122 protected abstract void processBody();
125 * Read the manifest as a list of lines.
127 * @param manifestAsStream The manifest file input stream
128 * @return The manifest as a list of string
129 * @throws IOException when the input stream is null or a read problem happened.
131 protected ImmutableList<String> readAllLines(final InputStream manifestAsStream) throws IOException {
132 if (manifestAsStream == null) {
133 throw new IOException("Manifest Input Stream cannot be null.");
135 final ImmutableList.Builder<String> builder = ImmutableList.builder();
136 try (final BufferedReader bufferedReader = new BufferedReader(
137 new InputStreamReader(manifestAsStream, StandardCharsets.UTF_8.newDecoder()))) {
138 bufferedReader.lines().forEach(builder::add);
140 return builder.build();
144 * Checks if the line is a {@link ManifestTokenType#METADATA} entry.
146 * @param line The line to check
147 * @return {@code true} if the line is a 'metadata' entry, {@code false} otherwise.
149 protected boolean isMetadata(final String line) {
151 .equals(ManifestTokenType.METADATA.getToken() + ManifestTokenType.ATTRIBUTE_VALUE_SEPARATOR.getToken());
155 * Checks if the the entry is a valid metadata entry.
157 * @param metadataEntry the entry to be evaluated
158 * @return {@code true} if the entry is a valid metadata entry, {@code false} otherwise.
160 protected boolean isMetadataEntry(final String metadataEntry) {
161 final Optional<ManifestTokenType> manifestTokenType = ManifestTokenType.parse(metadataEntry);
162 return manifestTokenType.map(ManifestTokenType::isMetadataEntry).orElse(false);
166 * Checks if the manifest is empty
168 * @param lines the manifest parsed as a string lines list
169 * @return {@code true} if the manifest is empty, {@code false} otherwise.
171 protected boolean isEmptyManifest(final ImmutableList<String> lines) {
172 if (lines == null || lines.isEmpty()) {
173 errors.add(Messages.MANIFEST_EMPTY.getErrorMessage());
180 * Reports a manifest invalid line error occurred in the current line.
182 protected void reportInvalidLine() {
183 reportInvalidLine(currentLineNumber, getCurrentLine().orElse(""));
187 * Reports a manifest invalid line error.
189 * @param lineNumber the line number
190 * @param line the line
192 protected void reportInvalidLine(final int lineNumber, final String line) {
193 errors.add(Messages.MANIFEST_INVALID_LINE.formatMessage(lineNumber, line));
197 * Reports a manifest error occurred in the current line.
199 * @param message The error message
200 * @param params The message params
202 protected void reportError(final Messages message, final Object... params) {
203 reportError(currentLineNumber, getCurrentLine().orElse(""), message, params);
207 * Reports a manifest error occurred in the specified line.
209 * @param lineNumber The line number
210 * @param line The line
211 * @param message The error message
212 * @param params The message params
214 protected void reportError(final int lineNumber, final String line, final Messages message,
215 final Object... params) {
216 errors.add(Messages.MANIFEST_ERROR_WITH_LINE.formatMessage(message.formatMessage(params), lineNumber, line));
220 * Checks if the manifest is valid.
222 * @return {@code true} if the manifest is valid, {@code false} otherwise.
224 public boolean isValid() {
225 return errors.isEmpty();
229 * Reads the next non empty line in the manifest. Updates the current line and line number.
231 * @return the next non empty line. If there is no more lines, an empty value.
233 protected Optional<String> readNextNonEmptyLine() {
234 while (linesIterator.hasNext()) {
235 final String line = linesIterator.next().trim();
237 if (!line.isEmpty()) {
239 return getCurrentLine();
244 if (getCurrentLine().isPresent()) {
249 return getCurrentLine();
253 * Gets the current line.
255 * @return the current line.
257 protected Optional<String> getCurrentLine() {
258 return Optional.ofNullable(currentLine);
262 * Reads the current line entry name. The entry name and value must be separated by {@link
263 * ManifestTokenType#ATTRIBUTE_VALUE_SEPARATOR}.
265 * @return the entry value
267 protected Optional<String> readCurrentEntryName() {
268 final Optional<String> line = getCurrentLine();
269 if (line.isPresent()) {
270 return readEntryName(line.get());
273 return Optional.empty();
277 * Read a entry name. The entry name and value must be separated by {@link ManifestTokenType#ATTRIBUTE_VALUE_SEPARATOR}.
279 * @param line the entry line
280 * @return returns the entry name
282 protected Optional<String> readEntryName(final String line) {
283 if (StringUtils.isEmpty(line)) {
284 return Optional.empty();
286 if (!line.contains(ManifestTokenType.ATTRIBUTE_VALUE_SEPARATOR.getToken())) {
287 return Optional.empty();
289 final String attribute = line.substring(0, line.indexOf(ManifestTokenType.ATTRIBUTE_VALUE_SEPARATOR.getToken())).trim();
290 if (StringUtils.isEmpty(attribute)) {
291 return Optional.empty();
294 return Optional.of(attribute);
298 * Reads the current line entry value. The entry name and value must be separated by {@link
299 * ManifestTokenType#ATTRIBUTE_VALUE_SEPARATOR}.
301 * @return the entry value
303 protected Optional<String> readCurrentEntryValue() {
304 final Optional<String> line = getCurrentLine();
305 if (line.isPresent()) {
306 return readEntryValue(line.get());
309 return Optional.empty();
313 * Reads a entry value. The entry name and value must be separated by {@link ManifestTokenType#ATTRIBUTE_VALUE_SEPARATOR}.
315 * @param line the entry line
316 * @return the entry value
318 protected Optional<String> readEntryValue(final String line) {
319 if (StringUtils.isEmpty(line)) {
320 return Optional.empty();
322 if (!line.contains(ManifestTokenType.ATTRIBUTE_VALUE_SEPARATOR.getToken())) {
323 return Optional.empty();
325 final String value = line.substring(line.indexOf(ManifestTokenType.ATTRIBUTE_VALUE_SEPARATOR.getToken()) + 1).trim();
326 if (StringUtils.isEmpty(value)) {
327 return Optional.empty();
330 return Optional.of(value);
334 * Adds a entry to the metadata map. Only accepts new entries. If the entry is duplicated a manifest error is
337 * @param entry the metadata entry
338 * @param value the entry value
339 * @return {@code true} if the entry was added, {@code false} otherwise.
341 protected boolean addToMetadata(final String entry, final String value) {
342 if (metadata.containsKey(entry)) {
343 reportError(Messages.MANIFEST_METADATA_DUPLICATED_ENTRY, entry);
347 metadata.put(entry, value);
351 public List<String> getErrors() {
352 return ImmutableList.copyOf(errors);
355 public Map<String, String> getMetadata() {
357 return Collections.emptyMap();
359 return ImmutableMap.copyOf(metadata);
362 public List<String> getSources() {
364 return Collections.emptyList();
366 return ImmutableList.copyOf(sources);
369 public Map<String, List<String>> getNonManoSources() {
371 return Collections.emptyMap();
373 return ImmutableMap.copyOf(nonManoSources);
377 public boolean isSigned() {
378 return getCmsSignature().isPresent();
382 public Optional<String> getCmsSignature() {
383 return Optional.ofNullable(cmsSignature);
387 public Optional<Map<String, AlgorithmDigest>> getSourceAndChecksumMap() {
388 if (MapUtils.isEmpty(sourceAndChecksumMap)) {
389 return Optional.empty();
392 return Optional.of(ImmutableMap.copyOf(sourceAndChecksumMap));