2 * Copyright © 2016-2017 European Support Limited
3 * Modification Copyright (C) 2019 Nordix Foundation.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * 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.lang.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 String cmsSignature;
50 protected List<String> errors;
51 protected boolean continueToProcess;
52 protected String currentLine;
53 protected Iterator<String> linesIterator;
54 protected int currentLineNumber;
56 protected AbstractOnboardingManifest() {
57 errors = new ArrayList<>();
58 sources = new ArrayList<>();
59 metadata = new HashMap<>();
60 nonManoSources = new HashMap<>();
64 public Optional<ResourceTypeEnum> getType() {
66 return Optional.empty();
68 final String firstKey = metadata.keySet().iterator().next();
69 final ManifestTokenType manifestTokenType = ManifestTokenType.parse(firstKey).orElse(null);
70 if (manifestTokenType == null) {
71 return Optional.empty();
73 if (manifestTokenType.isMetadataPnfEntry()) {
74 return Optional.of(ResourceTypeEnum.PNF);
76 return Optional.of(ResourceTypeEnum.VF);
80 public void parse(final InputStream manifestAsStream) {
82 final ImmutableList<String> lines = readAllLines(manifestAsStream);
83 continueToProcess = true;
84 currentLineNumber = 0;
85 processManifest(lines);
86 } catch (IOException e) {
87 LOGGER.error(e.getMessage(), e);
88 errors.add(Messages.MANIFEST_PARSER_INTERNAL.getErrorMessage());
93 * Process the manifest lines, reporting an error when detected.
95 * @param lines the manifest lines
97 protected void processManifest(final ImmutableList<String> lines) {
98 if (isEmptyManifest(lines)) {
101 linesIterator = lines.iterator();
102 readNextNonEmptyLine();
103 if (!getCurrentLine().isPresent()) {
104 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(
135 new InputStreamReader(manifestAsStream, StandardCharsets.UTF_8.newDecoder()))) {
136 bufferedReader.lines().forEach(builder::add);
138 return builder.build();
142 * Checks if the line is a {@link ManifestTokenType#METADATA} entry.
144 * @param line The line to check
145 * @return {@code true} if the line is a 'metadata' entry, {@code false} otherwise.
147 protected boolean isMetadata(final String line) {
149 .equals(ManifestTokenType.METADATA.getToken() + ManifestTokenType.ATTRIBUTE_VALUE_SEPARATOR.getToken());
153 * Checks if the the entry is a valid metadata entry.
155 * @param metadataEntry the entry to be evaluated
156 * @return {@code true} if the entry is a valid metadata entry, {@code false} otherwise.
158 protected boolean isMetadataEntry(final String metadataEntry) {
159 final Optional<ManifestTokenType> manifestTokenType = ManifestTokenType.parse(metadataEntry);
160 return manifestTokenType.map(ManifestTokenType::isMetadataEntry).orElse(false);
164 * Checks if the manifest is empty
166 * @param lines the manifest parsed as a string lines list
167 * @return {@code true} if the manifest is empty, {@code false} otherwise.
169 protected boolean isEmptyManifest(final ImmutableList<String> lines) {
170 if (lines == null || lines.isEmpty()) {
171 errors.add(Messages.MANIFEST_EMPTY.getErrorMessage());
178 * Reports a manifest invalid line error occurred in the current line.
180 protected void reportInvalidLine() {
181 reportInvalidLine(currentLineNumber, getCurrentLine().orElse(""));
185 * Reports a manifest invalid line error.
187 * @param lineNumber the line number
188 * @param line the line
190 protected void reportInvalidLine(final int lineNumber, final String line) {
191 errors.add(Messages.MANIFEST_INVALID_LINE.formatMessage(lineNumber, line));
195 * Reports a manifest error occurred in the current line.
197 * @param message The error message
198 * @param params The message params
200 protected void reportError(final Messages message, final Object... params) {
201 reportError(currentLineNumber, getCurrentLine().orElse(""), message, params);
205 * Reports a manifest error occurred in the specified line.
207 * @param lineNumber The line number
208 * @param line The line
209 * @param message The error message
210 * @param params The message params
212 protected void reportError(final int lineNumber, final String line, final Messages message,
213 final Object... params) {
214 errors.add(Messages.MANIFEST_ERROR_WITH_LINE.formatMessage(message.formatMessage(params), lineNumber, line));
218 * Checks if the manifest is valid.
220 * @return {@code true} if the manifest is valid, {@code false} otherwise.
222 public boolean isValid() {
223 return errors.isEmpty();
227 * Reads the next non empty line in the manifest. Updates the current line and line number.
229 * @return the next non empty line. If there is no more lines, an empty value.
231 protected Optional<String> readNextNonEmptyLine() {
232 while (linesIterator.hasNext()) {
233 final String line = linesIterator.next().trim();
235 if (!line.isEmpty()) {
237 return getCurrentLine();
242 if (getCurrentLine().isPresent()) {
247 return getCurrentLine();
251 * Gets the current line.
253 * @return the current line.
255 protected Optional<String> getCurrentLine() {
256 return Optional.ofNullable(currentLine);
260 * Reads the current line entry name. The entry name and value must be separated by {@link
261 * ManifestTokenType#ATTRIBUTE_VALUE_SEPARATOR}.
263 * @return the entry value
265 protected Optional<String> readCurrentEntryName() {
266 final Optional<String> line = getCurrentLine();
267 if (line.isPresent()) {
268 return readEntryName(line.get());
271 return Optional.empty();
275 * Read a entry name. The entry name and value must be separated by {@link ManifestTokenType#ATTRIBUTE_VALUE_SEPARATOR}.
277 * @param line the entry line
278 * @return returns the entry name
280 protected Optional<String> readEntryName(final String line) {
281 if (StringUtils.isEmpty(line)) {
282 return Optional.empty();
284 if (!line.contains(ManifestTokenType.ATTRIBUTE_VALUE_SEPARATOR.getToken())) {
285 return Optional.empty();
287 final String attribute = line.substring(0, line.indexOf(ManifestTokenType.ATTRIBUTE_VALUE_SEPARATOR.getToken())).trim();
288 if (StringUtils.isEmpty(attribute)) {
289 return Optional.empty();
292 return Optional.of(attribute);
296 * Reads the current line entry value. The entry name and value must be separated by {@link
297 * ManifestTokenType#ATTRIBUTE_VALUE_SEPARATOR}.
299 * @return the entry value
301 protected Optional<String> readCurrentEntryValue() {
302 final Optional<String> line = getCurrentLine();
303 if (line.isPresent()) {
304 return readEntryValue(line.get());
307 return Optional.empty();
311 * Reads a entry value. The entry name and value must be separated by {@link ManifestTokenType#ATTRIBUTE_VALUE_SEPARATOR}.
313 * @param line the entry line
314 * @return the entry value
316 protected Optional<String> readEntryValue(final String line) {
317 if (StringUtils.isEmpty(line)) {
318 return Optional.empty();
320 if (!line.contains(ManifestTokenType.ATTRIBUTE_VALUE_SEPARATOR.getToken())) {
321 return Optional.empty();
323 final String value = line.substring(line.indexOf(ManifestTokenType.ATTRIBUTE_VALUE_SEPARATOR.getToken()) + 1).trim();
324 if (StringUtils.isEmpty(value)) {
325 return Optional.empty();
328 return Optional.of(value);
332 * Adds a entry to the metadata map. Only accepts new entries. If the entry is duplicated a manifest error is
335 * @param entry the metadata entry
336 * @param value the entry value
337 * @return {@code true} if the entry was added, {@code false} otherwise.
339 protected boolean addToMetadata(final String entry, final String value) {
340 if (metadata.containsKey(entry)) {
341 reportError(Messages.MANIFEST_METADATA_DUPLICATED_ENTRY, entry);
345 metadata.put(entry, value);
349 public List<String> getErrors() {
350 return ImmutableList.copyOf(errors);
353 public Map<String, String> getMetadata() {
355 return Collections.emptyMap();
357 return ImmutableMap.copyOf(metadata);
360 public List<String> getSources() {
362 return Collections.emptyList();
364 return ImmutableList.copyOf(sources);
367 public Map<String, List<String>> getNonManoSources() {
369 return Collections.emptyMap();
371 return ImmutableMap.copyOf(nonManoSources);
375 public boolean isSigned() {
376 return getCmsSignature().isPresent();
380 public Optional<String> getCmsSignature() {
381 return Optional.ofNullable(cmsSignature);
385 public Optional<Map<String, AlgorithmDigest>> getSourceAndChecksumMap() {
386 if (MapUtils.isEmpty(sourceAndChecksumMap)) {
387 return Optional.empty();
390 return Optional.of(ImmutableMap.copyOf(sourceAndChecksumMap));