From 560f2e3ab0f78429fe22dc23ae25de6a7500cf02 Mon Sep 17 00:00:00 2001 From: Tom Date: Wed, 26 Oct 2016 22:49:56 +0200 Subject: Issue #1011: 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 * https://github.com/gitblit/gitblit/issues/1011 * https://github.com/tomaswolf/gerrit-gitblit-plugin/issues/21 --- src/main/java/com/gitblit/models/RefModel.java | 93 ++++++++++++++++++---- .../java/com/gitblit/models/RepositoryCommit.java | 49 +++++++++--- .../java/com/gitblit/wicket/SessionlessForm.java | 14 +++- .../java/com/gitblit/wicket/pages/BlamePage.java | 2 +- .../com/gitblit/wicket/pages/EditFilePage.java | 2 +- .../java/com/gitblit/wicket/pages/MetricsPage.java | 2 +- .../java/com/gitblit/wicket/pages/RawPage.java | 31 +++++--- .../com/gitblit/wicket/pages/RepositoryPage.java | 10 +-- .../java/com/gitblit/wicket/pages/SummaryPage.java | 2 +- .../java/com/gitblit/wicket/pages/TicketPage.java | 21 +++-- .../com/gitblit/wicket/panels/HistoryPanel.java | 39 +++++---- .../java/com/gitblit/wicket/panels/LogPanel.java | 33 +++++--- .../com/gitblit/wicket/panels/SearchPanel.java | 29 ++++--- 13 files changed, 230 insertions(+), 97 deletions(-) diff --git a/src/main/java/com/gitblit/models/RefModel.java b/src/main/java/com/gitblit/models/RefModel.java index d20c2dc6..86d98474 100644 --- a/src/main/java/com/gitblit/models/RefModel.java +++ b/src/main/java/com/gitblit/models/RefModel.java @@ -36,18 +36,42 @@ import com.gitblit.utils.JGitUtils; */ public class RefModel implements Serializable, Comparable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 876822269940583606L; + public final String displayName; - public final RevObject referencedObject; - public transient Ref reference; + + private final Date date; + private final String name; + private final int type; + private final String id; + private final String referencedId; + private final boolean annotated; + private final PersonIdent person; + private final String shortMessage; + private final String fullMessage; + + private transient ObjectId objectId; + private transient ObjectId referencedObjectId; + + public transient Ref reference; // Used in too many places. public RefModel(String displayName, Ref ref, RevObject refObject) { - this.displayName = displayName; this.reference = ref; - this.referencedObject = refObject; + this.displayName = displayName; + this.date = internalGetDate(refObject); + this.name = (ref != null) ? ref.getName() : displayName; + this.type = internalGetReferencedObjectType(refObject); + this.objectId = internalGetObjectId(reference); + this.id = this.objectId.getName(); + this.referencedObjectId = internalGetReferencedObjectId(refObject); + this.referencedId = this.referencedObjectId.getName(); + this.annotated = internalIsAnnotatedTag(ref, refObject); + this.person = internalGetAuthorIdent(refObject); + this.shortMessage = internalGetShortMessage(refObject); + this.fullMessage = internalGetFullMessage(refObject); } - public Date getDate() { + private Date internalGetDate(RevObject referencedObject) { Date date = new Date(0); if (referencedObject != null) { if (referencedObject instanceof RevTag) { @@ -64,14 +88,15 @@ public class RefModel implements Serializable, Comparable { return date; } + public Date getDate() { + return date; + } + public String getName() { - if (reference == null) { - return displayName; - } - return reference.getName(); + return name; } - public int getReferencedObjectType() { + private int internalGetReferencedObjectType(RevObject referencedObject) { int type = referencedObject.getType(); if (referencedObject instanceof RevTag) { type = ((RevTag) referencedObject).getObject().getType(); @@ -79,14 +104,25 @@ public class RefModel implements Serializable, Comparable { return type; } - public ObjectId getReferencedObjectId() { + public int getReferencedObjectType() { + return type; + } + + private ObjectId internalGetReferencedObjectId(RevObject referencedObject) { if (referencedObject instanceof RevTag) { return ((RevTag) referencedObject).getObject().getId(); } return referencedObject.getId(); } - public String getShortMessage() { + public ObjectId getReferencedObjectId() { + if (referencedObjectId == null) { + referencedObjectId = ObjectId.fromString(referencedId); + } + return referencedObjectId; + } + + private String internalGetShortMessage(RevObject referencedObject) { String message = ""; if (referencedObject instanceof RevTag) { message = ((RevTag) referencedObject).getShortMessage(); @@ -96,7 +132,11 @@ public class RefModel implements Serializable, Comparable { return message; } - public String getFullMessage() { + public String getShortMessage() { + return shortMessage; + } + + private String internalGetFullMessage(RevObject referencedObject) { String message = ""; if (referencedObject instanceof RevTag) { message = ((RevTag) referencedObject).getFullMessage(); @@ -106,7 +146,11 @@ public class RefModel implements Serializable, Comparable { return message; } - public PersonIdent getAuthorIdent() { + public String getFullMessage() { + return fullMessage; + } + + private PersonIdent internalGetAuthorIdent(RevObject referencedObject) { if (referencedObject instanceof RevTag) { return ((RevTag) referencedObject).getTaggerIdent(); } else if (referencedObject instanceof RevCommit) { @@ -115,17 +159,32 @@ public class RefModel implements Serializable, Comparable { return null; } - public ObjectId getObjectId() { + public PersonIdent getAuthorIdent() { + return person; + } + + private ObjectId internalGetObjectId(Ref reference) { return reference.getObjectId(); } - public boolean isAnnotatedTag() { + public ObjectId getObjectId() { + if (objectId == null) { + objectId = ObjectId.fromString(id); + } + return objectId; + } + + private boolean internalIsAnnotatedTag(Ref reference, RevObject referencedObject) { if (referencedObject instanceof RevTag) { return !getReferencedObjectId().equals(getObjectId()); } return reference.getPeeledObjectId() != null; } + public boolean isAnnotatedTag() { + return annotated; + } + @Override public int hashCode() { return getReferencedObjectId().hashCode() + getName().hashCode(); diff --git a/src/main/java/com/gitblit/models/RepositoryCommit.java b/src/main/java/com/gitblit/models/RepositoryCommit.java index 765b4898..43f314a3 100644 --- a/src/main/java/com/gitblit/models/RepositoryCommit.java +++ b/src/main/java/com/gitblit/models/RepositoryCommit.java @@ -15,6 +15,8 @@ */ package com.gitblit.models; +import java.io.IOException; +import java.io.ObjectInputStream; import java.io.Serializable; import java.text.MessageFormat; import java.util.Date; @@ -22,30 +24,36 @@ import java.util.List; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; + +import com.gitblit.wicket.GitBlitWebApp; /** - * Model class to represent a RevCommit, it's source repository, and the branch. - * This class is used by the activity page. + * Model class to represent a RevCommit, it's source repository, and the branch. This class is used by the activity page. * * @author James Moger */ public class RepositoryCommit implements Serializable, Comparable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = -2214911650485772022L; - public final String repository; + public String repository; - public final String branch; + public String branch; - private final RevCommit commit; + private final String commitId; private List refs; + private transient RevCommit commit; + public RepositoryCommit(String repository, String branch, RevCommit commit) { this.repository = repository; this.branch = branch; this.commit = commit; + this.commitId = commit.getName(); } public void setRefs(List refs) { @@ -80,7 +88,7 @@ public class RepositoryCommit implements Serializable, Comparable