326eb6a517330b78bedfec6324b62d61ededef7b
[sdc.git] /
1 /*
2  * Copyright © 2016-2017 European Support Limited
3  * Modification Copyright (C) 2019 Nordix Foundation.
4  *
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
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  */
17
18 package org.openecomp.sdc.tosca.csar;
19
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;
32 import java.util.Map;
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;
40
41 abstract class AbstractOnboardingManifest implements Manifest {
42
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;
55
56     protected AbstractOnboardingManifest() {
57         errors = new ArrayList<>();
58         sources = new ArrayList<>();
59         metadata = new HashMap<>();
60         nonManoSources = new HashMap<>();
61     }
62
63     @Override
64     public Optional<ResourceTypeEnum> getType() {
65         if (!isValid()) {
66             return Optional.empty();
67         }
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();
72         }
73         if (manifestTokenType.isMetadataPnfEntry()) {
74             return Optional.of(ResourceTypeEnum.PNF);
75         }
76         return Optional.of(ResourceTypeEnum.VF);
77     }
78
79     @Override
80     public void parse(final InputStream manifestAsStream) {
81         try {
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());
89         }
90     }
91
92     /**
93      * Process the manifest lines, reporting an error when detected.
94      *
95      * @param lines the manifest lines
96      */
97     protected void processManifest(final ImmutableList<String> lines) {
98         if (isEmptyManifest(lines)) {
99             return;
100         }
101         linesIterator = lines.iterator();
102         readNextNonEmptyLine();
103         if (!getCurrentLine().isPresent()) {
104             errors.add(Messages.MANIFEST_EMPTY.getErrorMessage());
105             return;
106         }
107
108         processMetadata();
109         processBody();
110     }
111
112     /**
113      * Process the metadata part of the Manifest file.
114      */
115     protected abstract void processMetadata();
116
117     /**
118      * Process the other parts from manifest different than metadata.
119      */
120     protected abstract void processBody();
121
122     /**
123      * Read the manifest as a list of lines.
124      *
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.
128      */
129     protected ImmutableList<String> readAllLines(final InputStream manifestAsStream) throws IOException {
130         if (manifestAsStream == null) {
131             throw new IOException("Manifest Input Stream cannot be null.");
132         }
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);
137         }
138         return builder.build();
139     }
140
141     /**
142      * Checks if the line is a {@link ManifestTokenType#METADATA} entry.
143      *
144      * @param line The line to check
145      * @return {@code true} if the line is a 'metadata' entry, {@code false} otherwise.
146      */
147     protected boolean isMetadata(final String line) {
148         return line.trim()
149             .equals(ManifestTokenType.METADATA.getToken() + ManifestTokenType.ATTRIBUTE_VALUE_SEPARATOR.getToken());
150     }
151
152     /**
153      * Checks if the the entry is a valid metadata entry.
154      *
155      * @param metadataEntry the entry to be evaluated
156      * @return {@code true} if the entry is a valid metadata entry, {@code false} otherwise.
157      */
158     protected boolean isMetadataEntry(final String metadataEntry) {
159         final Optional<ManifestTokenType> manifestTokenType = ManifestTokenType.parse(metadataEntry);
160         return manifestTokenType.map(ManifestTokenType::isMetadataEntry).orElse(false);
161     }
162
163     /**
164      * Checks if the manifest is empty
165      *
166      * @param lines the manifest parsed as a string lines list
167      * @return {@code true} if the manifest is empty, {@code false} otherwise.
168      */
169     protected boolean isEmptyManifest(final ImmutableList<String> lines) {
170         if (lines == null || lines.isEmpty()) {
171             errors.add(Messages.MANIFEST_EMPTY.getErrorMessage());
172             return true;
173         }
174         return false;
175     }
176
177     /**
178      * Reports a manifest invalid line error occurred in the current line.
179      */
180     protected void reportInvalidLine() {
181         reportInvalidLine(currentLineNumber, getCurrentLine().orElse(""));
182     }
183
184     /**
185      * Reports a manifest invalid line error.
186      *
187      * @param lineNumber the line number
188      * @param line the line
189      */
190     protected void reportInvalidLine(final int lineNumber, final String line) {
191         errors.add(Messages.MANIFEST_INVALID_LINE.formatMessage(lineNumber, line));
192     }
193
194     /**
195      * Reports a manifest error occurred in the current line.
196      *
197      * @param message The error message
198      * @param params The message params
199      */
200     protected void reportError(final Messages message, final Object... params) {
201         reportError(currentLineNumber, getCurrentLine().orElse(""), message, params);
202     }
203
204     /**
205      * Reports a manifest error occurred in the specified line.
206      *
207      * @param lineNumber The line number
208      * @param line The line
209      * @param message The error message
210      * @param params The message params
211      */
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));
215     }
216
217     /**
218      * Checks if the manifest is valid.
219      *
220      * @return {@code true} if the manifest is valid, {@code false} otherwise.
221      */
222     public boolean isValid() {
223         return errors.isEmpty();
224     }
225
226     /**
227      * Reads the next non empty line in the manifest. Updates the current line and line number.
228      *
229      * @return the next non empty line. If there is no more lines, an empty value.
230      */
231     protected Optional<String> readNextNonEmptyLine() {
232         while (linesIterator.hasNext()) {
233             final String line = linesIterator.next().trim();
234             currentLineNumber++;
235             if (!line.isEmpty()) {
236                 currentLine = line;
237                 return getCurrentLine();
238             }
239             currentLine = null;
240         }
241
242         if (getCurrentLine().isPresent()) {
243             currentLineNumber++;
244             currentLine = null;
245         }
246
247         return getCurrentLine();
248     }
249
250     /**
251      * Gets the current line.
252      *
253      * @return the current line.
254      */
255     protected Optional<String> getCurrentLine() {
256         return Optional.ofNullable(currentLine);
257     }
258
259     /**
260      * Reads the current line entry name. The entry name and value must be separated by {@link
261      * ManifestTokenType#ATTRIBUTE_VALUE_SEPARATOR}.
262      *
263      * @return the entry value
264      */
265     protected Optional<String> readCurrentEntryName() {
266         final Optional<String> line = getCurrentLine();
267         if (line.isPresent()) {
268             return readEntryName(line.get());
269         }
270
271         return Optional.empty();
272     }
273
274     /**
275      * Read a entry name. The entry name and value must be separated by {@link ManifestTokenType#ATTRIBUTE_VALUE_SEPARATOR}.
276      *
277      * @param line the entry line
278      * @return returns the entry name
279      */
280     protected Optional<String> readEntryName(final String line) {
281         if (StringUtils.isEmpty(line)) {
282             return Optional.empty();
283         }
284         if (!line.contains(ManifestTokenType.ATTRIBUTE_VALUE_SEPARATOR.getToken())) {
285             return Optional.empty();
286         }
287         final String attribute = line.substring(0, line.indexOf(ManifestTokenType.ATTRIBUTE_VALUE_SEPARATOR.getToken())).trim();
288         if (StringUtils.isEmpty(attribute)) {
289             return Optional.empty();
290         }
291
292         return Optional.of(attribute);
293     }
294
295     /**
296      * Reads the current line entry value. The entry name and value must be separated by {@link
297      * ManifestTokenType#ATTRIBUTE_VALUE_SEPARATOR}.
298      *
299      * @return the entry value
300      */
301     protected Optional<String> readCurrentEntryValue() {
302         final Optional<String> line = getCurrentLine();
303         if (line.isPresent()) {
304             return readEntryValue(line.get());
305         }
306
307         return Optional.empty();
308     }
309
310     /**
311      * Reads a entry value. The entry name and value must be separated by {@link ManifestTokenType#ATTRIBUTE_VALUE_SEPARATOR}.
312      *
313      * @param line the entry line
314      * @return the entry value
315      */
316     protected Optional<String> readEntryValue(final String line) {
317         if (StringUtils.isEmpty(line)) {
318             return Optional.empty();
319         }
320         if (!line.contains(ManifestTokenType.ATTRIBUTE_VALUE_SEPARATOR.getToken())) {
321             return Optional.empty();
322         }
323         final String value = line.substring(line.indexOf(ManifestTokenType.ATTRIBUTE_VALUE_SEPARATOR.getToken()) + 1).trim();
324         if (StringUtils.isEmpty(value)) {
325             return Optional.empty();
326         }
327
328         return Optional.of(value);
329     }
330
331     /**
332      * Adds a entry to the metadata map. Only accepts new entries. If the entry is duplicated a manifest error is
333      * reported.
334      *
335      * @param entry the metadata entry
336      * @param value the entry value
337      * @return {@code true} if the entry was added, {@code false} otherwise.
338      */
339     protected boolean addToMetadata(final String entry, final String value) {
340         if (metadata.containsKey(entry)) {
341             reportError(Messages.MANIFEST_METADATA_DUPLICATED_ENTRY, entry);
342             return false;
343         }
344
345         metadata.put(entry, value);
346         return true;
347     }
348
349     public List<String> getErrors() {
350         return ImmutableList.copyOf(errors);
351     }
352
353     public Map<String, String> getMetadata() {
354         if (!isValid()) {
355             return Collections.emptyMap();
356         }
357         return ImmutableMap.copyOf(metadata);
358     }
359
360     public List<String> getSources() {
361         if (!isValid()) {
362             return Collections.emptyList();
363         }
364         return ImmutableList.copyOf(sources);
365     }
366
367     public Map<String, List<String>> getNonManoSources() {
368         if (!isValid()) {
369             return Collections.emptyMap();
370         }
371         return ImmutableMap.copyOf(nonManoSources);
372     }
373
374     @Override
375     public boolean isSigned() {
376         return getCmsSignature().isPresent();
377     }
378
379     @Override
380     public Optional<String> getCmsSignature() {
381         return Optional.ofNullable(cmsSignature);
382     }
383
384     @Override
385     public Optional<Map<String, AlgorithmDigest>> getSourceAndChecksumMap() {
386         if (MapUtils.isEmpty(sourceAndChecksumMap)) {
387             return Optional.empty();
388         }
389
390         return Optional.of(ImmutableMap.copyOf(sourceAndChecksumMap));
391     }
392 }