]> source.dussan.org Git - sonarqube.git/blob
32abede0c8942eeccf1a0b21752c150770f8f70c
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2022 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 package org.sonar.ce.task.projectanalysis.util.cache;
21
22 import com.google.common.base.Splitter;
23 import com.google.common.collect.ImmutableSet;
24 import java.io.BufferedOutputStream;
25 import java.io.File;
26 import java.io.FileNotFoundException;
27 import java.io.FileOutputStream;
28 import java.io.OutputStream;
29 import java.io.Serializable;
30 import java.util.Collections;
31 import java.util.Date;
32 import java.util.Map;
33 import javax.annotation.CheckForNull;
34 import org.sonar.api.rule.RuleKey;
35 import org.sonar.api.rules.RuleType;
36 import org.sonar.api.utils.Duration;
37 import org.sonar.api.utils.System2;
38 import org.sonar.core.issue.DefaultIssue;
39 import org.sonar.core.issue.DefaultIssueComment;
40 import org.sonar.core.issue.FieldDiffs;
41 import org.sonar.core.util.CloseableIterator;
42 import org.sonar.core.util.Protobuf;
43 import org.sonar.db.protobuf.DbIssues;
44
45 import static java.util.Optional.ofNullable;
46
47 public class ProtobufIssueDiskCache implements DiskCache<DefaultIssue> {
48   private static final String TAGS_SEPARATOR = ",";
49   private static final Splitter TAGS_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings();
50
51   private final File file;
52   private final System2 system2;
53
54   public ProtobufIssueDiskCache(File file, System2 system2) {
55     this.file = file;
56     this.system2 = system2;
57   }
58
59   @Override
60   public long fileSize() {
61     return file.length();
62   }
63
64   @Override
65   public CacheAppender<DefaultIssue> newAppender() {
66     try {
67       return new ProtoCacheAppender();
68     } catch (FileNotFoundException e) {
69       throw new IllegalStateException(e);
70     }
71   }
72
73   @Override
74   public CloseableIterator<DefaultIssue> traverse() {
75     CloseableIterator<IssueCache.Issue> protoIterator = Protobuf.readStream(file, IssueCache.Issue.parser());
76     return new CloseableIterator<DefaultIssue>() {
77       @CheckForNull
78       @Override
79       protected DefaultIssue doNext() {
80         if (protoIterator.hasNext()) {
81           return toDefaultIssue(protoIterator.next());
82         }
83         return null;
84       }
85
86       @Override
87       protected void doClose() {
88         protoIterator.close();
89       }
90     };
91   }
92
93   private static DefaultIssue toDefaultIssue(IssueCache.Issue next) {
94     DefaultIssue defaultIssue = new DefaultIssue();
95     defaultIssue.setKey(next.getKey());
96     defaultIssue.setType(RuleType.valueOf(next.getRuleType()));
97     defaultIssue.setComponentUuid(next.hasComponentUuid() ? next.getComponentUuid() : null);
98     defaultIssue.setComponentKey(next.getComponentKey());
99     defaultIssue.setModuleUuid(next.hasModuleUuid() ? next.getModuleUuid() : null);
100     defaultIssue.setModuleUuidPath(next.hasModuleUuidPath() ? next.getModuleUuidPath() : null);
101     defaultIssue.setProjectUuid(next.getProjectUuid());
102     defaultIssue.setProjectKey(next.getProjectKey());
103     defaultIssue.setRuleKey(RuleKey.parse(next.getRuleKey()));
104     defaultIssue.setLanguage(next.hasLanguage() ? next.getLanguage() : null);
105     defaultIssue.setSeverity(next.hasSeverity() ? next.getSeverity() : null);
106     defaultIssue.setManualSeverity(next.getManualSeverity());
107     defaultIssue.setMessage(next.hasMessage() ? next.getMessage() : null);
108     defaultIssue.setLine(next.hasLine() ? next.getLine() : null);
109     defaultIssue.setGap(next.hasGap() ? next.getGap() : null);
110     defaultIssue.setEffort(next.hasEffort() ? Duration.create(next.getEffort()) : null);
111     defaultIssue.setStatus(next.getStatus());
112     defaultIssue.setResolution(next.hasResolution() ? next.getResolution() : null);
113     defaultIssue.setAssigneeUuid(next.hasAssigneeUuid() ? next.getAssigneeUuid() : null);
114     defaultIssue.setChecksum(next.hasChecksum() ? next.getChecksum() : null);
115     defaultIssue.setAttributes(next.getAttributesMap());
116     defaultIssue.setAuthorLogin(next.hasAuthorLogin() ? next.getAuthorLogin() : null);
117     next.getCommentsList().forEach(c -> defaultIssue.addComment(toDefaultIssueComment(c)));
118     defaultIssue.setTags(ImmutableSet.copyOf(TAGS_SPLITTER.split(next.getTags())));
119     defaultIssue.setLocations(next.hasLocations() ? next.getLocations() : null);
120     defaultIssue.setIsFromExternalRuleEngine(next.getIsFromExternalRuleEngine());
121     defaultIssue.setCreationDate(new Date(next.getCreationDate()));
122     defaultIssue.setUpdateDate(next.hasUpdateDate() ? new Date(next.getUpdateDate()) : null);
123     defaultIssue.setCloseDate(next.hasCloseDate() ? new Date(next.getCloseDate()) : null);
124     defaultIssue.setCurrentChangeWithoutAddChange(next.hasCurrentChanges() ? toDefaultIssueChanges(next.getCurrentChanges()) : null);
125     defaultIssue.setNew(next.getIsNew());
126     defaultIssue.setIsOnReferencedBranch(next.getIsOnReferencedBranch());
127     defaultIssue.setIsOnChangedLine(next.getIsOnChangedLine());
128     defaultIssue.setCopied(next.getIsCopied());
129     defaultIssue.setBeingClosed(next.getBeingClosed());
130     defaultIssue.setOnDisabledRule(next.getOnDisabledRule());
131     defaultIssue.setChanged(next.getIsChanged());
132     defaultIssue.setSendNotifications(next.getSendNotifications());
133     defaultIssue.setSelectedAt(next.hasSelectedAt() ? next.getSelectedAt() : null);
134     defaultIssue.setQuickFixAvailable(next.getQuickFixAvailable());
135     defaultIssue.setIsNoLongerNewCodeReferenceIssue(next.getIsNoLongerNewCodeReferenceIssue());
136
137     for (IssueCache.FieldDiffs protoFieldDiffs : next.getChangesList()) {
138       defaultIssue.addChange(toDefaultIssueChanges(protoFieldDiffs));
139     }
140
141     return defaultIssue;
142   }
143
144   private static IssueCache.Issue toProto(IssueCache.Issue.Builder builder, DefaultIssue defaultIssue) {
145     builder.clear();
146     builder.setKey(defaultIssue.key());
147     builder.setRuleType(defaultIssue.type().getDbConstant());
148     ofNullable(defaultIssue.componentUuid()).ifPresent(builder::setComponentUuid);
149     builder.setComponentKey(defaultIssue.componentKey());
150     ofNullable(defaultIssue.moduleUuid()).ifPresent(builder::setModuleUuid);
151     ofNullable(defaultIssue.moduleUuidPath()).ifPresent(builder::setModuleUuidPath);
152     builder.setProjectUuid(defaultIssue.projectUuid());
153     builder.setProjectKey(defaultIssue.projectKey());
154     builder.setRuleKey(defaultIssue.ruleKey().toString());
155     ofNullable(defaultIssue.language()).ifPresent(builder::setLanguage);
156     ofNullable(defaultIssue.severity()).ifPresent(builder::setSeverity);
157     builder.setManualSeverity(defaultIssue.manualSeverity());
158     ofNullable(defaultIssue.message()).ifPresent(builder::setMessage);
159     ofNullable(defaultIssue.line()).ifPresent(builder::setLine);
160     ofNullable(defaultIssue.gap()).ifPresent(builder::setGap);
161     ofNullable(defaultIssue.effort()).map(Duration::toMinutes).ifPresent(builder::setEffort);
162     builder.setStatus(defaultIssue.status());
163     ofNullable(defaultIssue.resolution()).ifPresent(builder::setResolution);
164     ofNullable(defaultIssue.assignee()).ifPresent(builder::setAssigneeUuid);
165     ofNullable(defaultIssue.checksum()).ifPresent(builder::setChecksum);
166     ofNullable(defaultIssue.attributes()).ifPresent(builder::putAllAttributes);
167     ofNullable(defaultIssue.authorLogin()).ifPresent(builder::setAuthorLogin);
168     defaultIssue.defaultIssueComments().forEach(c -> builder.addComments(toProtoComment(c)));
169     ofNullable(defaultIssue.tags()).ifPresent(t -> builder.setTags(String.join(TAGS_SEPARATOR, t)));
170     ofNullable(defaultIssue.getLocations()).ifPresent(l -> builder.setLocations((DbIssues.Locations) l));
171     builder.setIsFromExternalRuleEngine(defaultIssue.isFromExternalRuleEngine());
172     builder.setCreationDate(defaultIssue.creationDate().getTime());
173     ofNullable(defaultIssue.updateDate()).map(Date::getTime).ifPresent(builder::setUpdateDate);
174     ofNullable(defaultIssue.closeDate()).map(Date::getTime).ifPresent(builder::setCloseDate);
175     ofNullable(defaultIssue.currentChange()).ifPresent(c -> builder.setCurrentChanges(toProtoIssueChanges(c)));
176     builder.setIsNew(defaultIssue.isNew());
177     builder.setIsOnReferencedBranch(defaultIssue.isOnReferencedBranch());
178     builder.setIsOnChangedLine(defaultIssue.isOnChangedLine());
179     builder.setIsCopied(defaultIssue.isCopied());
180     builder.setBeingClosed(defaultIssue.isBeingClosed());
181     builder.setOnDisabledRule(defaultIssue.isOnDisabledRule());
182     builder.setIsChanged(defaultIssue.isChanged());
183     builder.setSendNotifications(defaultIssue.mustSendNotifications());
184     ofNullable(defaultIssue.selectedAt()).ifPresent(builder::setSelectedAt);
185     builder.setQuickFixAvailable(defaultIssue.isQuickFixAvailable());
186     builder.setIsNoLongerNewCodeReferenceIssue(defaultIssue.isNoLongerNewCodeReferenceIssue());
187
188     for (FieldDiffs fieldDiffs : defaultIssue.changes()) {
189       builder.addChanges(toProtoIssueChanges(fieldDiffs));
190     }
191
192     return builder.build();
193   }
194
195   private static DefaultIssueComment toDefaultIssueComment(IssueCache.Comment comment) {
196     DefaultIssueComment issueComment = new DefaultIssueComment()
197       .setCreatedAt(new Date(comment.getCreatedAt()))
198       .setUpdatedAt(new Date(comment.getUpdatedAt()))
199       .setNew(comment.getIsNew())
200       .setKey(comment.getKey())
201       .setIssueKey(comment.getIssueKey())
202       .setMarkdownText(comment.getMarkdownText());
203
204     if (comment.hasUserUuid()) {
205       issueComment.setUserUuid(comment.getUserUuid());
206     }
207     return issueComment;
208   }
209
210   private static IssueCache.Comment toProtoComment(DefaultIssueComment comment) {
211     IssueCache.Comment.Builder builder = IssueCache.Comment.newBuilder()
212       .setCreatedAt(comment.createdAt().getTime())
213       .setUpdatedAt(comment.updatedAt().getTime())
214       .setIsNew(comment.isNew())
215       .setKey(comment.key())
216       .setIssueKey(comment.issueKey())
217       .setMarkdownText(comment.markdownText());
218
219     if (comment.userUuid() != null) {
220       builder.setUserUuid(comment.userUuid());
221     }
222     return builder.build();
223   }
224
225   private static FieldDiffs toDefaultIssueChanges(IssueCache.FieldDiffs fieldDiffs) {
226     FieldDiffs defaultIssueFieldDiffs = new FieldDiffs()
227       .setUserUuid(fieldDiffs.getUserUuid())
228       .setCreationDate(new Date(fieldDiffs.getCreationDate()));
229
230     if (fieldDiffs.hasIssueKey()) {
231       defaultIssueFieldDiffs.setIssueKey(fieldDiffs.getIssueKey());
232     }
233
234     for (Map.Entry<String, IssueCache.Diff> e : fieldDiffs.getDiffsMap().entrySet()) {
235       defaultIssueFieldDiffs.setDiff(e.getKey(),
236         e.getValue().hasOldValue() ? e.getValue().getOldValue() : null,
237         e.getValue().hasNewValue() ? e.getValue().getNewValue() : null);
238     }
239
240     return defaultIssueFieldDiffs;
241   }
242
243   private static IssueCache.FieldDiffs toProtoIssueChanges(FieldDiffs fieldDiffs) {
244     IssueCache.FieldDiffs.Builder builder = IssueCache.FieldDiffs.newBuilder()
245       .setCreationDate(fieldDiffs.creationDate().getTime());
246
247     if (fieldDiffs.issueKey() != null) {
248       builder.setIssueKey(fieldDiffs.issueKey());
249     }
250
251     String userUuid = fieldDiffs.userUuid();
252     if (userUuid != null) {
253       builder.setUserUuid(userUuid);
254     }
255
256     for (Map.Entry<String, FieldDiffs.Diff> e : fieldDiffs.diffs().entrySet()) {
257       IssueCache.Diff.Builder diffBuilder = IssueCache.Diff.newBuilder();
258       Serializable oldValue = e.getValue().oldValue();
259       if (oldValue != null) {
260         diffBuilder.setOldValue(oldValue.toString());
261       }
262       Serializable newValue = e.getValue().newValue();
263       if (newValue != null) {
264         diffBuilder.setNewValue(newValue.toString());
265
266       }
267       builder.putDiffs(e.getKey(), diffBuilder.build());
268     }
269
270     return builder.build();
271   }
272
273   private class ProtoCacheAppender implements CacheAppender<DefaultIssue> {
274     private final OutputStream out;
275     private final IssueCache.Issue.Builder builder;
276
277     private ProtoCacheAppender() throws FileNotFoundException {
278       this.out = new BufferedOutputStream(new FileOutputStream(file, true));
279       this.builder = IssueCache.Issue.newBuilder();
280     }
281
282     @Override
283     public CacheAppender append(DefaultIssue object) {
284       Protobuf.writeStream(Collections.singleton(toProto(builder, object)), out);
285       return this;
286     }
287
288     @Override
289     public void close() {
290       system2.close(out);
291     }
292   }
293 }