re base code
[sdc.git] / openecomp-be / tools / snapshot-signature-plugin / src / main / java / org / openecomp / sdc / onboarding / SnapshotSignature.java
1 /*
2  * Copyright © 2018 European Support Limited
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on a "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package org.openecomp.sdc.onboarding;
18
19 import org.apache.maven.plugin.AbstractMojo;
20 import org.apache.maven.plugin.MojoExecutionException;
21 import org.apache.maven.plugin.MojoFailureException;
22 import org.apache.maven.plugins.annotations.LifecyclePhase;
23 import org.apache.maven.plugins.annotations.Mojo;
24 import org.apache.maven.plugins.annotations.Parameter;
25 import org.apache.maven.plugins.annotations.ResolutionScope;
26 import org.apache.maven.project.MavenProject;
27
28 import java.io.File;
29 import java.io.IOException;
30 import java.io.UncheckedIOException;
31 import java.nio.charset.StandardCharsets;
32 import java.nio.file.Files;
33 import java.nio.file.Path;
34 import java.nio.file.Paths;
35 import java.nio.file.StandardOpenOption;
36 import java.security.MessageDigest;
37 import java.security.NoSuchAlgorithmException;
38 import java.util.*;
39 import java.util.concurrent.ForkJoinPool;
40 import java.util.concurrent.RecursiveTask;
41 import java.util.stream.Collectors;
42
43 @Mojo(name = "generate-signature", threadSafe = true, defaultPhase = LifecyclePhase.COMPILE,
44         requiresDependencyResolution = ResolutionScope.NONE)
45 public class SnapshotSignature extends AbstractMojo {
46
47     public static final String JAVA_EXT = ".java";
48     public static final String UNICORN = "unicorn";
49     public static final String CHECKSUM = "checksum";
50     public static final String DOT = ".";
51     public static final String SHA1 = "sha1";
52     public static final String COLON = ":";
53     public static final String ANY_EXT = "*";
54     public static final String SNAPSHOT = "SNAPSHOT";
55     public static final String JAR = "jar";
56
57     @Parameter
58     private File mainSourceLocation;
59     @Parameter
60     private File mainResourceLocation;
61     @Parameter(defaultValue = "${project}")
62     private MavenProject project;
63
64     @Override
65     public void execute() throws MojoExecutionException, MojoFailureException {
66         if (!JAR.equals(project.getPackaging())) {
67             return;
68         }
69         init();
70         long resourceChecksum = getChecksum(mainResourceLocation, ANY_EXT);
71         long mainChecksum = getChecksum(mainSourceLocation, JAVA_EXT);
72         byte[] sourceChecksum = calculateChecksum(mainChecksum, resourceChecksum).getBytes();
73         generateSignature(sourceChecksum);
74     }
75
76     private void init() {
77         if (mainSourceLocation == null) {
78             mainSourceLocation = Paths.get(project.getBuild().getSourceDirectory()).toFile();
79         }
80         if (mainResourceLocation == null) {
81             mainResourceLocation = Paths.get(project.getBuild().getResources().get(0).getDirectory()).toFile();
82         }
83     }
84
85     private long getChecksum(File file, String fileType) {
86         try {
87             return readSources(file, fileType).hashCode();
88         } catch (IOException e) {
89             throw new UncheckedIOException(e);
90         }
91     }
92
93     private Map<String, List<String>> readSources(File file, String fileType) throws IOException {
94         Map<String, List<String>> source = new HashMap<>();
95         if (file.exists()) {
96             List<File> list = Files.walk(Paths.get(file.getAbsolutePath()))
97                                    .filter(JAVA_EXT.equals(fileType) ? this::isRegularJavaFile : Files::isRegularFile)
98                                    .map(Path::toFile).collect(Collectors.toList());
99             source.putAll(ForkJoinPool.commonPool()
100                                       .invoke(new FileReadTask(list.toArray(new File[0]), file.getAbsolutePath())));
101         }
102         return source;
103     }
104
105     private boolean isRegularJavaFile(Path path) {
106         File file = path.toFile();
107         return file.isFile() && file.getName().endsWith(JAVA_EXT);
108     }
109
110     private class FileReadTask extends RecursiveTask<Map<String, List<String>>> {
111
112         private Map<String, List<String>> store = new HashMap<>();
113         File[] files;
114         String pathPrefix;
115         private static final int MAX_FILES = 10;
116
117         FileReadTask(File[] files, String pathPrefix) {
118             this.files = files;
119             this.pathPrefix = pathPrefix;
120         }
121
122         private List<String> getData(File file) throws IOException {
123             List<String> coll = Files.readAllLines(file.toPath(), StandardCharsets.ISO_8859_1);
124             if (file.getAbsolutePath().contains(File.separator + "generated-sources" + File.separator)) {
125                 Iterator<String> itr = coll.iterator();
126                 while (itr.hasNext()) {
127                     String s = itr.next();
128                     if (s == null || s.trim().startsWith("/") || s.trim().startsWith("*")) {
129                         itr.remove();
130                     }
131                 }
132             }
133             return coll;
134         }
135
136
137         @Override
138         protected Map<String, List<String>> compute() {
139             if (files.length > MAX_FILES) {
140                 FileReadTask task1 = new FileReadTask(Arrays.copyOfRange(files, 0, files.length / 2), pathPrefix);
141                 FileReadTask task2 =
142                         new FileReadTask(Arrays.copyOfRange(files, files.length / 2, files.length), pathPrefix);
143                 task1.fork();
144                 task2.fork();
145                 store.putAll(task1.join());
146                 store.putAll(task2.join());
147             } else {
148                 for (File toRead : files) {
149                     try {
150                         store.put(toRead.getAbsolutePath().substring(pathPrefix.length())
151                                         .replace(File.separatorChar, '.'), getData(toRead));
152                     } catch (IOException e) {
153                         throw new UncheckedIOException(e);
154                     }
155                 }
156             }
157
158             return store;
159         }
160     }
161
162     private void generateSignature(byte[] sourceChecksum) {
163         try {
164             Paths.get(project.getBuild().getOutputDirectory()).toFile().mkdirs();
165             Files.write(Paths.get(project.getBuild().getOutputDirectory(), UNICORN + DOT + CHECKSUM), sourceChecksum,
166                     StandardOpenOption.CREATE);
167         } catch (IOException e) {
168             throw new UncheckedIOException(e);
169         }
170     }
171
172     private String calculateChecksum(long mainChecksum, long resourceChecksum) throws MojoExecutionException {
173         try {
174             return getSourceChecksum(mainChecksum + COLON + resourceChecksum, SHA1);
175         } catch (NoSuchAlgorithmException e) {
176             throw new MojoExecutionException(e.getMessage(), e);
177         }
178     }
179
180     private String getSourceChecksum(String data, String hashType) throws NoSuchAlgorithmException {
181         MessageDigest md = MessageDigest.getInstance(hashType);
182         md.update(data.getBytes());
183         byte[] hashBytes = md.digest();
184
185         StringBuilder buffer = new StringBuilder();
186         for (byte hashByte : hashBytes) {
187             buffer.append(Integer.toString((hashByte & 0xff) + 0x100, 16).substring(1));
188         }
189         return buffer.toString();
190     }
191 }