Skip to content

Commit c7f8a23

Browse files
tomaswolfseder
authored and
seder
committed
Do not serialize JGit commit objects
JGit commit objects are a recursive data structure; they have links to their parent commits. Serializing a JGit commit will try to recursively serialize all reachable ancestors as faras they have been loaded. If that ancestor chain is too long, a StackOverflowError is thrown during Wicket's page serialization if a page has a reference to sucha JGit commit. Fixed by making sure that pages o not contain references to JGit commits. Use the (existing) wrapper object RepositoryCommit instead. * RepositoryCommit has a transient reference to the JGit commit and reads the commit from the repository upon de-serialization. * RefModel is a similar case (JGit tags/branches may also have links to the commits they point to). Solved a bit differently by making it a pure data object by transferring the interesting data from the JGit object in the constructor. * Change DataViews instantiated with RevCommit to use RepositoryCommit instead. * Change inner anonymous DataViews to ensure they do not have a synthesized field referencing the "allRefs" map. Such a synthesized field would also get serialized, and then serialize JGit commits again. Finally, remove non-transient logger instances in Wicket classes. Those might lead to NotSerializableException. These StackOverflowErrors have been reported in several places since 2014: * https://groups.google.com/forum/#!topic/gitblit/GH1d8WSlR6Q * https://bugs.chromium.org/p/gerrit/issues/detail?id=3316 * https://groups.google.com/d/msg/repo-discuss/Kcl0JIGNiGk/0DjH4mO8hA8J * https://groups.google.com/d/msg/repo-discuss/0_P6A3fjTec/2kcpVPIUAQAJ * gitblit-org#1011 * tomaswolf/gerrit-gitblit-plugin#21 Issue: gitblit-org#1011 (cherry picked from commit 005e8e2)
1 parent 196e50c commit c7f8a23

13 files changed

+230
-97
lines changed

src/main/java/com/gitblit/models/RefModel.java

Lines changed: 76 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,42 @@
3636
*/
3737
public class RefModel implements Serializable, Comparable<RefModel> {
3838

39-
private static final long serialVersionUID = 1L;
39+
private static final long serialVersionUID = 876822269940583606L;
40+
4041
public final String displayName;
41-
public final RevObject referencedObject;
42-
public transient Ref reference;
42+
43+
private final Date date;
44+
private final String name;
45+
private final int type;
46+
private final String id;
47+
private final String referencedId;
48+
private final boolean annotated;
49+
private final PersonIdent person;
50+
private final String shortMessage;
51+
private final String fullMessage;
52+
53+
private transient ObjectId objectId;
54+
private transient ObjectId referencedObjectId;
55+
56+
public transient Ref reference; // Used in too many places.
4357

4458
public RefModel(String displayName, Ref ref, RevObject refObject) {
45-
this.displayName = displayName;
4659
this.reference = ref;
47-
this.referencedObject = refObject;
60+
this.displayName = displayName;
61+
this.date = internalGetDate(refObject);
62+
this.name = (ref != null) ? ref.getName() : displayName;
63+
this.type = internalGetReferencedObjectType(refObject);
64+
this.objectId = internalGetObjectId(reference);
65+
this.id = this.objectId.getName();
66+
this.referencedObjectId = internalGetReferencedObjectId(refObject);
67+
this.referencedId = this.referencedObjectId.getName();
68+
this.annotated = internalIsAnnotatedTag(ref, refObject);
69+
this.person = internalGetAuthorIdent(refObject);
70+
this.shortMessage = internalGetShortMessage(refObject);
71+
this.fullMessage = internalGetFullMessage(refObject);
4872
}
4973

50-
public Date getDate() {
74+
private Date internalGetDate(RevObject referencedObject) {
5175
Date date = new Date(0);
5276
if (referencedObject != null) {
5377
if (referencedObject instanceof RevTag) {
@@ -64,29 +88,41 @@ public Date getDate() {
6488
return date;
6589
}
6690

91+
public Date getDate() {
92+
return date;
93+
}
94+
6795
public String getName() {
68-
if (reference == null) {
69-
return displayName;
70-
}
71-
return reference.getName();
96+
return name;
7297
}
7398

74-
public int getReferencedObjectType() {
99+
private int internalGetReferencedObjectType(RevObject referencedObject) {
75100
int type = referencedObject.getType();
76101
if (referencedObject instanceof RevTag) {
77102
type = ((RevTag) referencedObject).getObject().getType();
78103
}
79104
return type;
80105
}
81106

82-
public ObjectId getReferencedObjectId() {
107+
public int getReferencedObjectType() {
108+
return type;
109+
}
110+
111+
private ObjectId internalGetReferencedObjectId(RevObject referencedObject) {
83112
if (referencedObject instanceof RevTag) {
84113
return ((RevTag) referencedObject).getObject().getId();
85114
}
86115
return referencedObject.getId();
87116
}
88117

89-
public String getShortMessage() {
118+
public ObjectId getReferencedObjectId() {
119+
if (referencedObjectId == null) {
120+
referencedObjectId = ObjectId.fromString(referencedId);
121+
}
122+
return referencedObjectId;
123+
}
124+
125+
private String internalGetShortMessage(RevObject referencedObject) {
90126
String message = "";
91127
if (referencedObject instanceof RevTag) {
92128
message = ((RevTag) referencedObject).getShortMessage();
@@ -96,7 +132,11 @@ public String getShortMessage() {
96132
return message;
97133
}
98134

99-
public String getFullMessage() {
135+
public String getShortMessage() {
136+
return shortMessage;
137+
}
138+
139+
private String internalGetFullMessage(RevObject referencedObject) {
100140
String message = "";
101141
if (referencedObject instanceof RevTag) {
102142
message = ((RevTag) referencedObject).getFullMessage();
@@ -106,7 +146,11 @@ public String getFullMessage() {
106146
return message;
107147
}
108148

109-
public PersonIdent getAuthorIdent() {
149+
public String getFullMessage() {
150+
return fullMessage;
151+
}
152+
153+
private PersonIdent internalGetAuthorIdent(RevObject referencedObject) {
110154
if (referencedObject instanceof RevTag) {
111155
return ((RevTag) referencedObject).getTaggerIdent();
112156
} else if (referencedObject instanceof RevCommit) {
@@ -115,17 +159,32 @@ public PersonIdent getAuthorIdent() {
115159
return null;
116160
}
117161

118-
public ObjectId getObjectId() {
162+
public PersonIdent getAuthorIdent() {
163+
return person;
164+
}
165+
166+
private ObjectId internalGetObjectId(Ref reference) {
119167
return reference.getObjectId();
120168
}
121169

122-
public boolean isAnnotatedTag() {
170+
public ObjectId getObjectId() {
171+
if (objectId == null) {
172+
objectId = ObjectId.fromString(id);
173+
}
174+
return objectId;
175+
}
176+
177+
private boolean internalIsAnnotatedTag(Ref reference, RevObject referencedObject) {
123178
if (referencedObject instanceof RevTag) {
124179
return !getReferencedObjectId().equals(getObjectId());
125180
}
126181
return reference.getPeeledObjectId() != null;
127182
}
128183

184+
public boolean isAnnotatedTag() {
185+
return annotated;
186+
}
187+
129188
@Override
130189
public int hashCode() {
131190
return getReferencedObjectId().hashCode() + getName().hashCode();

src/main/java/com/gitblit/models/RepositoryCommit.java

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,37 +15,45 @@
1515
*/
1616
package com.gitblit.models;
1717

18+
import java.io.IOException;
19+
import java.io.ObjectInputStream;
1820
import java.io.Serializable;
1921
import java.text.MessageFormat;
2022
import java.util.Date;
2123
import java.util.List;
2224

2325
import org.eclipse.jgit.lib.ObjectId;
2426
import org.eclipse.jgit.lib.PersonIdent;
27+
import org.eclipse.jgit.lib.Repository;
2528
import org.eclipse.jgit.revwalk.RevCommit;
29+
import org.eclipse.jgit.revwalk.RevWalk;
30+
31+
import com.gitblit.wicket.GitBlitWebApp;
2632

2733
/**
28-
* Model class to represent a RevCommit, it's source repository, and the branch.
29-
* This class is used by the activity page.
34+
* Model class to represent a RevCommit, it's source repository, and the branch. This class is used by the activity page.
3035
*
3136
* @author James Moger
3237
*/
3338
public class RepositoryCommit implements Serializable, Comparable<RepositoryCommit> {
3439

35-
private static final long serialVersionUID = 1L;
40+
private static final long serialVersionUID = -2214911650485772022L;
3641

37-
public final String repository;
42+
public String repository;
3843

39-
public final String branch;
44+
public String branch;
4045

41-
private final RevCommit commit;
46+
private final String commitId;
4247

4348
private List<RefModel> refs;
4449

50+
private transient RevCommit commit;
51+
4552
public RepositoryCommit(String repository, String branch, RevCommit commit) {
4653
this.repository = repository;
4754
this.branch = branch;
4855
this.commit = commit;
56+
this.commitId = commit.getName();
4957
}
5058

5159
public void setRefs(List<RefModel> refs) {
@@ -80,7 +88,7 @@ public int getParentCount() {
8088
return commit.getParentCount();
8189
}
8290

83-
public RevCommit [] getParents() {
91+
public RevCommit[] getParents() {
8492
return commit.getParents();
8593
}
8694

@@ -92,10 +100,14 @@ public PersonIdent getCommitterIdent() {
92100
return commit.getCommitterIdent();
93101
}
94102

103+
public RevCommit getCommit() {
104+
return commit;
105+
}
106+
95107
@Override
96108
public boolean equals(Object o) {
97109
if (o instanceof RepositoryCommit) {
98-
RepositoryCommit commit = (RepositoryCommit) o;
110+
final RepositoryCommit commit = (RepositoryCommit) o;
99111
return repository.equals(commit.repository) && getName().equals(commit.getName());
100112
}
101113
return false;
@@ -123,8 +135,23 @@ public RepositoryCommit clone(String withRef) {
123135

124136
@Override
125137
public String toString() {
126-
return MessageFormat.format("{0} {1} {2,date,yyyy-MM-dd HH:mm} {3} {4}",
127-
getShortName(), branch, getCommitterIdent().getWhen(), getAuthorIdent().getName(),
128-
getShortMessage());
138+
return MessageFormat.format("{0} {1} {2,date,yyyy-MM-dd HH:mm} {3} {4}", getShortName(), branch, getCommitterIdent().getWhen(),
139+
getAuthorIdent().getName(), getShortMessage());
140+
}
141+
142+
// Serialization: restore the JGit RevCommit on reading
143+
144+
private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException {
145+
// Read in fields and any hidden stuff
146+
input.defaultReadObject();
147+
// Go find the commit again.
148+
final Repository repo = GitBlitWebApp.get().repositories().getRepository(repository);
149+
if (repo == null) {
150+
throw new IOException("Cannot find repositoy " + repository);
151+
}
152+
try (RevWalk walk = new RevWalk(repo)) {
153+
commit = walk.parseCommit(repo.resolve(commitId));
154+
}
129155
}
156+
130157
}

src/main/java/com/gitblit/wicket/SessionlessForm.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public class SessionlessForm<T> extends StatelessForm<T> {
5858

5959
protected final PageParameters pageParameters;
6060

61-
private final Logger log = LoggerFactory.getLogger(SessionlessForm.class);
61+
private transient Logger logger;
6262

6363
/**
6464
* Sessionless forms must have a bookmarkable page class. A bookmarkable
@@ -118,7 +118,10 @@ protected void onComponentTagBody(final MarkupStream markupStream, final Compone
118118
if (c != null) {
119119
// this form has a field id which matches the
120120
// parameter name, skip embedding a hidden value
121-
log.warn(MessageFormat.format("Skipping page parameter \"{0}\" from sessionless form hidden fields because it collides with a form child wicket:id", key));
121+
logger().warn(
122+
MessageFormat
123+
.format("Skipping page parameter \"{0}\" from sessionless form hidden fields because it collides with a form child wicket:id",
124+
key));
122125
continue;
123126
}
124127
String value = pageParameters.getString(key);
@@ -156,4 +159,11 @@ protected String getAbsoluteUrl(Class<? extends BasePage> pageClass, PageParamet
156159
String absoluteUrl = RequestUtils.toAbsolutePath(relativeUrl);
157160
return absoluteUrl;
158161
}
162+
163+
private Logger logger() {
164+
if (logger == null) {
165+
logger = LoggerFactory.getLogger(SessionlessForm.class);
166+
}
167+
return logger;
168+
}
159169
}

src/main/java/com/gitblit/wicket/pages/BlamePage.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ public BlamePage(PageParameters params) {
108108
if (pathModel == null) {
109109
final String notFound = MessageFormat.format("Blame page failed to find {0} in {1} @ {2}",
110110
blobPath, repositoryName, objectId);
111-
logger.error(notFound);
111+
logger().error(notFound);
112112
add(new Label("annotation").setVisible(false));
113113
add(new Label("missingBlob", missingBlob(blobPath, commit)).setEscapeModelStrings(false));
114114
return;

src/main/java/com/gitblit/wicket/pages/EditFilePage.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ protected void onSubmit() {
123123
try {
124124
ObjectId docAtLoad = getRepository().resolve(commitIdAtLoad.getObject());
125125

126-
logger.trace("Commiting Edit File page: " + commitIdAtLoad.getObject());
126+
logger().trace("Commiting Edit File page: " + commitIdAtLoad.getObject());
127127

128128
DirCache index = DirCache.newInCore();
129129
DirCacheBuilder builder = index.builder();

src/main/java/com/gitblit/wicket/pages/MetricsPage.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ private void createLineChart(Charts charts, String id, List<Metric> metrics) {
9494
try {
9595
date = df.parse(metric.name);
9696
} catch (ParseException e) {
97-
logger.error("Unable to parse date: " + metric.name);
97+
logger().error("Unable to parse date: " + metric.name);
9898
return;
9999
}
100100
chart.addValue(date, (int)metric.count);

0 commit comments

Comments
 (0)