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.
18 package org.openecomp.sdc.tosca.csar;
20 import com.google.common.collect.ImmutableList;
21 import com.google.common.collect.ImmutableMap;
22 import java.io.BufferedReader;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.InputStreamReader;
26 import java.nio.charset.StandardCharsets;
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.HashMap;
30 import java.util.Iterator;
31 import java.util.List;
33 import java.util.Optional;
34 import org.apache.commons.collections.MapUtils;
35 import org.apache.commons.lang3.StringUtils;
36 import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
37 import org.openecomp.sdc.common.errors.Messages;
38 import org.openecomp.sdc.logging.api.Logger;
39 import org.openecomp.sdc.logging.api.LoggerFactory;
41 abstract class AbstractOnboardingManifest implements Manifest {
43 protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractOnboardingManifest.class);
44 protected static final int MAX_ALLOWED_MANIFEST_META_ENTRIES = 4;
45 protected Map<String, String> metadata;
46 protected List<String> sources;
47 protected Map<String, List<String>> nonManoSources;
48 protected Map<String, AlgorithmDigest> sourceAndChecksumMap = new HashMap<>();
49 protected Map<String, SignatureData> sourceAndSignatureMap = new HashMap<>();
50 protected String cmsSignature;
51 protected List<String> errors;
52 protected boolean continueToProcess;
53 protected String currentLine;
54 protected Iterator<String> linesIterator;
55 protected int currentLineNumber;
57 protected AbstractOnboardingManifest() {
58 errors = new ArrayList<>();
59 sources = new ArrayList<>();
60 metadata = new HashMap<>();
61 nonManoSources = new HashMap<>();
65 public Optional<ResourceTypeEnum> getType() {
67 return Optional.empty();
69 final String firstKey = metadata.keySet().iterator().next();
70 final ManifestTokenType manifestTokenType = ManifestTokenType.parse(firstKey).orElse(null);
71 if (manifestTokenType == null) {
72 return Optional.empty();
74 if (manifestTokenType.isMetadataPnfEntry()) {
75 return Optional.of(ResourceTypeEnum.PNF);
77 return Optional.of(ResourceTypeEnum.VF);
81 public void parse(final InputStream manifestAsStream) {
83 final ImmutableList<String> lines = readAllLines(manifestAsStream);
84 continueToProcess = true;
85 currentLineNumber = 0;
86 processManifest(lines);
87 } catch (IOException e) {
88 LOGGER.error(e.getMessage(), e);
89 errors.add(Messages.MANIFEST_PARSER_INTERNAL.getErrorMessage());
94 * Process the manifest lines, reporting an error when detected.
96 * @param lines the manifest lines
98 protected void processManifest(final ImmutableList<String> lines) {
99 if (isEmptyManifest(lines)) {
102 linesIterator = lines.iterator();
103 readNextNonEmptyLine();
104 if (!getCurrentLine().isPresent()) {
105 errors.add(Messages.MANIFEST_EMPTY.getErrorMessage());
113 * Process the metadata part of the Manifest file.
115 protected abstract void processMetadata();
118 * Process the other parts from manifest different than metadata.
120 protected abstract void processBody();
123 * Read the manifest as a list of lines.
125 * @param manifestAsStream The manifest file input stream
126 * @return The manifest as a list of string
127 * @throws IOException when the input stream is null or a read problem happened.
129 protected ImmutableList<String> readAllLines(final InputStream manifestAsStream) throws IOException {
130 if (manifestAsStream == null) {
131 throw new IOException("Manifest Input Stream cannot be null.");
133 final ImmutableList.Builder<String> builder = ImmutableList.builder();
134 try (final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(manifestAsStream, StandardCharsets.UTF_8.newDecoder()))) {
135 bufferedReader.lines().forEach(builder::add);
137 return builder.build();
141 * Checks if the line is a {@link ManifestTokenType#METADATA} entry.
143 * @param line The line to check
144 * @return {@code true} if the line is a 'metadata' entry, {@code false} otherwise.
146 protected boolean isMetadata(final String line) {
147 return line.trim().equals(ManifestTokenType.METADATA.getToken() + ManifestTokenType.ATTRIBUTE_VALUE_SEPARATOR.getToken());
151 * Checks if the the entry is a valid metadata entry.
153 * @param metadataEntry the entry to be evaluated
154 * @return {@code true} if the entry is a valid metadata entry, {@code false} otherwise.
156 protected boolean isMetadataEntry(final String metadataEntry) {
157 final Optional<ManifestTokenType> manifestTokenType = ManifestTokenType.parse(metadataEntry);
158 return manifestTokenType.map(ManifestTokenType::isMetadataEntry).orElse(false);
162 * Checks if the manifest is empty
164 * @param lines the manifest parsed as a string lines list
165 * @return {@code true} if the manifest is empty, {@code false} otherwise.
167 protected boolean isEmptyManifest(final ImmutableList<String> lines) {
168 if (lines == null || lines.isEmpty()) {
169 errors.add(Messages.MANIFEST_EMPTY.getErrorMessage());
176 * Reports a manifest invalid line error occurred in the current line.
178 protected void reportInvalidLine() {
179 reportInvalidLine(currentLineNumber, getCurrentLine().orElse(""));
183 * Reports a manifest invalid line error.
185 * @param lineNumber the line number
186 * @param line the line
188 protected void reportInvalidLine(final int lineNumber, final String line) {
189 errors.add(Messages.MANIFEST_INVALID_LINE.formatMessage(lineNumber, line));
193 * Reports a manifest error occurred in the current line.
195 * @param message The error message
196 * @param params The message params
198 protected void reportError(final Messages message, final Object... params) {
199 reportError(currentLineNumber, getCurrentLine().orElse(""), message, params);
203 * Reports a manifest error occurred in the specified line.
205 * @param lineNumber The line number
206 * @param line The line
207 * @param message The error message
208 * @param params The message params
210 protected void reportError(final int lineNumber, final String line, final Messages message, final Object... params) {
211 errors.add(Messages.MANIFEST_ERROR_WITH_LINE.formatMessage(message.formatMessage(params), lineNumber, line));
215 * Checks if the manifest is valid.
217 * @return {@code true} if the manifest is valid, {@code false} otherwise.
219 public boolean isValid() {
220 return errors.isEmpty();
224 * Reads the next non empty line in the manifest. Updates the current line and line number.
226 * @return the next non empty line. If there is no more lines, an empty value.
228 protected Optional<String> readNextNonEmptyLine() {
229 while (linesIterator.hasNext()) {
230 final String line = linesIterator.next().trim();
232 if (!line.isEmpty()) {
234 return getCurrentLine();
238 if (getCurrentLine().isPresent()) {
242 return getCurrentLine();
246 * Gets the current line.
248 * @return the current line.
250 protected Optional<String> getCurrentLine() {
251 return Optional.ofNullable(currentLine);
255 * Reads the current line entry name. The entry name and value must be separated by {@link ManifestTokenType#ATTRIBUTE_VALUE_SEPARATOR}.
257 * @return the entry value
259 protected Optional<String> readCurrentEntryName() {
260 final Optional<String> line = getCurrentLine();
261 if (line.isPresent()) {
262 return readEntryName(line.get());
264 return Optional.empty();
268 * Read a entry name. The entry name and value must be separated by {@link ManifestTokenType#ATTRIBUTE_VALUE_SEPARATOR}.
270 * @param line the entry line
271 * @return returns the entry name
273 protected Optional<String> readEntryName(final String line) {
274 if (StringUtils.isEmpty(line)) {
275 return Optional.empty();
277 if (!line.contains(ManifestTokenType.ATTRIBUTE_VALUE_SEPARATOR.getToken())) {
278 return Optional.empty();
280 final String attribute = line.substring(0, line.indexOf(ManifestTokenType.ATTRIBUTE_VALUE_SEPARATOR.getToken())).trim();
281 if (StringUtils.isEmpty(attribute)) {
282 return Optional.empty();
284 return Optional.of(attribute);
288 * Reads the current line entry value. The entry name and value must be separated by {@link ManifestTokenType#ATTRIBUTE_VALUE_SEPARATOR}.
290 * @return the entry value
292 protected Optional<String> readCurrentEntryValue() {
293 final Optional<String> line = getCurrentLine();
294 if (line.isPresent()) {
295 return readEntryValue(line.get());
297 return Optional.empty();
301 * Reads a entry value. The entry name and value must be separated by {@link ManifestTokenType#ATTRIBUTE_VALUE_SEPARATOR}.
303 * @param line the entry line
304 * @return the entry value
306 protected Optional<String> readEntryValue(final String line) {
307 if (StringUtils.isEmpty(line)) {
308 return Optional.empty();
310 if (!line.contains(ManifestTokenType.ATTRIBUTE_VALUE_SEPARATOR.getToken())) {
311 return Optional.empty();
313 final String value = line.substring(line.indexOf(ManifestTokenType.ATTRIBUTE_VALUE_SEPARATOR.getToken()) + 1).trim();
314 if (StringUtils.isEmpty(value)) {
315 return Optional.empty();
317 return Optional.of(value);
321 * Adds a entry to the metadata map. Only accepts new entries. If the entry is duplicated a manifest error is reported.
323 * @param entry the metadata entry
324 * @param value the entry value
325 * @return {@code true} if the entry was added, {@code false} otherwise.
327 protected boolean addToMetadata(final String entry, final String value) {
328 if (metadata.containsKey(entry)) {
329 reportError(Messages.MANIFEST_METADATA_DUPLICATED_ENTRY, entry);
332 metadata.put(entry, value);
336 public List<String> getErrors() {
337 return ImmutableList.copyOf(errors);
340 public Map<String, String> getMetadata() {
342 return Collections.emptyMap();
344 return ImmutableMap.copyOf(metadata);
347 public List<String> getSources() {
349 return Collections.emptyList();
351 return ImmutableList.copyOf(sources);
354 public Map<String, List<String>> getNonManoSources() {
356 return Collections.emptyMap();
358 return ImmutableMap.copyOf(nonManoSources);
362 public boolean isSigned() {
363 return getCmsSignature().isPresent();
367 public Optional<String> getCmsSignature() {
368 return Optional.ofNullable(cmsSignature);
372 public Optional<Map<String, AlgorithmDigest>> getSourceAndChecksumMap() {
373 if (MapUtils.isEmpty(sourceAndChecksumMap)) {
374 return Optional.empty();
376 return Optional.of(ImmutableMap.copyOf(sourceAndChecksumMap));