Move stream-event JSON objects to a common package

This way they aren't so tied up with the ChangeHookRunner itself,
and we can start to reuse them for a gerrit query implementation.

Change-Id: If531bc193e3569f7df626c303cc8dc54be237ee0
Signed-off-by: Shawn O. Pearce <sop@google.com>
diff --git a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java
index 259b0fb..f2d0ad0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java
@@ -24,18 +24,22 @@
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.AccountState;
-import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.SitePaths;
+import com.google.gerrit.server.events.ApprovalAttribute;
+import com.google.gerrit.server.events.ChangeAbandonedEvent;
+import com.google.gerrit.server.events.ChangeEvent;
+import com.google.gerrit.server.events.ChangeMergedEvent;
+import com.google.gerrit.server.events.CommentAddedEvent;
+import com.google.gerrit.server.events.EventFactory;
+import com.google.gerrit.server.events.PatchSetCreatedEvent;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.WorkQueue;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectControl;
 import com.google.gerrit.server.project.ProjectState;
 import com.google.inject.Inject;
-import com.google.inject.Provider;
 import com.google.inject.Singleton;
-import com.google.inject.internal.Nullable;
 
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.Repository;
@@ -60,68 +64,6 @@
     /** A logger for this class. */
     private static final Logger log = LoggerFactory.getLogger(ChangeHookRunner.class);
 
-    public static abstract class ChangeEvent {
-    }
-
-    public static class ApprovalAttribute {
-        public String type;
-        public String description;
-        public String value;
-    }
-
-    public static class AuthorAttribute {
-        public String name;
-        public String email;
-    }
-
-    public static class ChangeAttribute {
-        public String project;
-        public String branch;
-        public String id;
-        public String number;
-        public String subject;
-        public AuthorAttribute owner;
-        public String url;
-    }
-
-    public static class PatchSetAttribute {
-        public String number;
-        public String revision;
-        public String ref;
-        public AuthorAttribute uploader;
-    }
-
-    public static class CommentAddedEvent extends ChangeEvent {
-        public final String type = "comment-added";
-        public ChangeAttribute change;
-        public PatchSetAttribute patchSet;
-        public AuthorAttribute author;
-        public ApprovalAttribute[] approvals;
-        public String comment;
-    }
-
-    public static class ChangeMergedEvent extends ChangeEvent {
-        public final String type = "change-merged";
-        public ChangeAttribute change;
-        public PatchSetAttribute patchSet;
-        public AuthorAttribute submitter;
-    }
-
-    public static class ChangeAbandonedEvent extends ChangeEvent {
-        public final String type = "change-abandoned";
-        public ChangeAttribute change;
-        public PatchSetAttribute patchSet;
-        public AuthorAttribute abandoner;
-        public String reason;
-    }
-
-    public static class PatchSetCreatedEvent extends ChangeEvent {
-        public final String type = "patchset-created";
-        public ChangeAttribute change;
-        public PatchSetAttribute patchSet;
-        public AuthorAttribute uploader;
-    }
-
     private static class ChangeListenerHolder {
         final ChangeListener listener;
         final IdentifiedUser user;
@@ -160,8 +102,7 @@
 
     private final ApprovalTypes approvalTypes;
 
-    private final Provider<String> urlProvider;
-
+    private final EventFactory eventFactory;
 
     /**
      * Create a new ChangeHookRunner.
@@ -179,13 +120,13 @@
       final ProjectCache projectCache,
       final AccountCache accountCache,
       final ApprovalTypes approvalTypes,
-      @CanonicalWebUrl @Nullable final Provider<String> cwu) {
+      final EventFactory eventFactory) {
         this.repoManager = repoManager;
         this.hookQueue = queue.createQueue(1, "hook");
         this.projectCache = projectCache;
         this.accountCache = accountCache;
         this.approvalTypes = approvalTypes;
-        this.urlProvider = cwu;
+        this.eventFactory = eventFactory;
 
         final File hooksPath = sitePath.resolve(getValue(config, "hooks", "path", sitePath.hooks_dir.getAbsolutePath()));
 
@@ -241,9 +182,9 @@
         final PatchSetCreatedEvent event = new PatchSetCreatedEvent();
         final AccountState uploader = accountCache.get(patchSet.getUploader());
 
-        event.change = getChangeAttribute(change);
-        event.patchSet = getPatchSetAttribute(patchSet);
-        event.uploader = getAccountAttribute(uploader.getAccount());
+        event.change = eventFactory.asChangeAttribute(change);
+        event.patchSet = eventFactory.asPatchSetAttribute(patchSet);
+        event.uploader = eventFactory.asAccountAttribute(uploader.getAccount());
         fireEvent(change, event);
 
         final List<String> args = new ArrayList<String>();
@@ -279,9 +220,9 @@
     public void doCommentAddedHook(final Change change, final Account account, final PatchSet patchSet, final String comment, final Map<ApprovalCategory.Id, ApprovalCategoryValue.Id> approvals) {
         final CommentAddedEvent event = new CommentAddedEvent();
 
-        event.change = getChangeAttribute(change);
-        event.author =  getAccountAttribute(account);
-        event.patchSet = getPatchSetAttribute(patchSet);
+        event.change = eventFactory.asChangeAttribute(change);
+        event.author =  eventFactory.asAccountAttribute(account);
+        event.patchSet = eventFactory.asPatchSetAttribute(patchSet);
         event.comment = comment;
 
         if (approvals.size() > 0) {
@@ -329,9 +270,9 @@
     public void doChangeMergedHook(final Change change, final Account account, final PatchSet patchSet) {
         final ChangeMergedEvent event = new ChangeMergedEvent();
 
-        event.change = getChangeAttribute(change);
-        event.submitter =  getAccountAttribute(account);
-        event.patchSet = getPatchSetAttribute(patchSet);
+        event.change = eventFactory.asChangeAttribute(change);
+        event.submitter = eventFactory.asAccountAttribute(account);
+        event.patchSet = eventFactory.asPatchSetAttribute(patchSet);
         fireEvent(change, event);
 
         final List<String> args = new ArrayList<String>();
@@ -363,8 +304,8 @@
     public void doChangeAbandonedHook(final Change change, final Account account, final String reason) {
         final ChangeAbandonedEvent event = new ChangeAbandonedEvent();
 
-        event.change = getChangeAttribute(change);
-        event.abandoner = getAccountAttribute(account);
+        event.change = eventFactory.asChangeAttribute(change);
+        event.abandoner = eventFactory.asAccountAttribute(account);
         event.reason = reason;
         fireEvent(change, event);
 
@@ -404,22 +345,6 @@
         return pc.controlFor(change).isVisible();
     }
 
-    /** Get a link to the change; null if the server doesn't know its own address. */
-    private String getChangeUrl(final Change change) {
-        if (change != null && getGerritUrl() != null) {
-            final StringBuilder r = new StringBuilder();
-            r.append(getGerritUrl());
-            r.append(change.getChangeId());
-            return r.toString();
-        }
-        return null;
-    }
-
-    private String getGerritUrl() {
-        return urlProvider.get();
-    }
-
-
     /**
      * Create an ApprovalAttribute for the given approval suitable for serialization to JSON.
      * @param approval
@@ -436,54 +361,6 @@
     }
 
     /**
-     * Create an AuthorAttribute for the given account suitable for serialization to JSON.
-     *
-     * @param account
-     * @return object suitable for serialization to JSON
-     */
-    private AuthorAttribute getAccountAttribute(final Account account) {
-        AuthorAttribute author = new AuthorAttribute();
-        author.name = account.getFullName();
-        author.email = account.getPreferredEmail();
-        return author;
-    }
-
-    /**
-     * Create a ChangeAttribute for the given change suitable for serialization to JSON.
-     *
-     * @param change
-     * @return object suitable for serialization to JSON
-     */
-    private ChangeAttribute getChangeAttribute(final Change change) {
-        ChangeAttribute a = new ChangeAttribute();
-        a.project = change.getProject().get();
-        a.branch = change.getDest().getShortName();
-        a.id = change.getKey().get();
-        a.number = change.getId().toString();
-        a.subject = change.getSubject();
-        final AccountState owner = accountCache.get(change.getOwner());
-        a.owner = getAccountAttribute(owner.getAccount());
-        a.url = getChangeUrl(change);
-        return a;
-    }
-
-    /**
-     * Create an PatchSetAttribute for the given patchset suitable for serialization to JSON.
-     *
-     * @param patchSet
-     * @return object suitable for serialization to JSON
-     */
-    private PatchSetAttribute getPatchSetAttribute(final PatchSet patchSet) {
-        PatchSetAttribute p = new PatchSetAttribute();
-        p.revision = patchSet.getRevision().get();
-        p.number = Integer.toString(patchSet.getPatchSetId());
-        p.ref = patchSet.getRefName();
-        final AccountState uploader = accountCache.get(patchSet.getUploader());
-        p.uploader = getAccountAttribute(uploader.getAccount());
-        return p;
-    }
-
-    /**
      * Get the display name for the given account.
      *
      * @param account Account to get name for.
diff --git a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeListener.java b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeListener.java
index 7e8e853..65a9857 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeListener.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeListener.java
@@ -14,7 +14,8 @@
 
 package com.google.gerrit.common;
 
-import com.google.gerrit.common.ChangeHookRunner.ChangeEvent;
+import com.google.gerrit.server.events.ChangeEvent;
+
 
 public interface ChangeListener {
     public void onChangeEvent(ChangeEvent event);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
index 5f2c6f9..697f045 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -37,6 +37,7 @@
 import com.google.gerrit.server.account.Realm;
 import com.google.gerrit.server.auth.ldap.LdapModule;
 import com.google.gerrit.server.cache.CachePool;
+import com.google.gerrit.server.events.EventFactory;
 import com.google.gerrit.server.git.ChangeMergeQueue;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.LocalDiskRepositoryManager;
@@ -64,8 +65,8 @@
 import com.google.gerrit.server.util.IdGenerator;
 import com.google.gerrit.server.workflow.FunctionState;
 import com.google.inject.Inject;
-
 import com.google.inject.TypeLiteral;
+
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.PersonIdent;
 
@@ -125,6 +126,7 @@
     bind(FileTypeRegistry.class).to(MimeUtilFileTypeRegistry.class);
     bind(WorkQueue.class);
     bind(ToolsCatalog.class);
+    bind(EventFactory.class);
 
     bind(ReplicationQueue.class).to(PushReplication.class).in(SINGLETON);
     factory(PushAllProjectsOp.Factory.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/AccountAttribute.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/AccountAttribute.java
new file mode 100644
index 0000000..2ad7ffe
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/AccountAttribute.java
@@ -0,0 +1,20 @@
+// Copyright (C) 2010 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.events;
+
+public class AccountAttribute {
+    public String name;
+    public String email;
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/ApprovalAttribute.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/ApprovalAttribute.java
new file mode 100644
index 0000000..c0f6b01
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/ApprovalAttribute.java
@@ -0,0 +1,21 @@
+// Copyright (C) 2010 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.events;
+
+public class ApprovalAttribute {
+    public String type;
+    public String description;
+    public String value;
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeAbandonedEvent.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeAbandonedEvent.java
new file mode 100644
index 0000000..baaf30c
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeAbandonedEvent.java
@@ -0,0 +1,23 @@
+// Copyright (C) 2010 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.events;
+
+public class ChangeAbandonedEvent extends ChangeEvent {
+    public final String type = "change-abandoned";
+    public ChangeAttribute change;
+    public PatchSetAttribute patchSet;
+    public AccountAttribute abandoner;
+    public String reason;
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeAttribute.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeAttribute.java
new file mode 100644
index 0000000..3c0ea35
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeAttribute.java
@@ -0,0 +1,25 @@
+// Copyright (C) 2010 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.events;
+
+public class ChangeAttribute {
+    public String project;
+    public String branch;
+    public String id;
+    public String number;
+    public String subject;
+    public AccountAttribute owner;
+    public String url;
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeEvent.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeEvent.java
new file mode 100644
index 0000000..904a0a0
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeEvent.java
@@ -0,0 +1,18 @@
+// Copyright (C) 2010 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.events;
+
+public abstract class ChangeEvent {
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeMergedEvent.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeMergedEvent.java
new file mode 100644
index 0000000..0d5fc31
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeMergedEvent.java
@@ -0,0 +1,22 @@
+// Copyright (C) 2010 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.events;
+
+public class ChangeMergedEvent extends ChangeEvent {
+    public final String type = "change-merged";
+    public ChangeAttribute change;
+    public PatchSetAttribute patchSet;
+    public AccountAttribute submitter;
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/CommentAddedEvent.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/CommentAddedEvent.java
new file mode 100644
index 0000000..f00caaf
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/CommentAddedEvent.java
@@ -0,0 +1,24 @@
+// Copyright (C) 2010 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.events;
+
+public class CommentAddedEvent extends ChangeEvent {
+    public final String type = "comment-added";
+    public ChangeAttribute change;
+    public PatchSetAttribute patchSet;
+    public AccountAttribute author;
+    public ApprovalAttribute[] approvals;
+    public String comment;
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
new file mode 100644
index 0000000..5d14ebd
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
@@ -0,0 +1,103 @@
+// Copyright (C) 2010 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.events;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.config.CanonicalWebUrl;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import com.google.inject.internal.Nullable;
+
+@Singleton
+public class EventFactory {
+  private final AccountCache accountCache;
+  private final Provider<String> urlProvider;
+
+  @Inject
+  EventFactory(AccountCache accountCache,
+      @CanonicalWebUrl @Nullable Provider<String> urlProvider) {
+    this.accountCache = accountCache;
+    this.urlProvider = urlProvider;
+  }
+
+  /**
+   * Create a ChangeAttribute for the given change suitable for serialization to
+   * JSON.
+   *
+   * @param change
+   * @return object suitable for serialization to JSON
+   */
+  public ChangeAttribute asChangeAttribute(final Change change) {
+    ChangeAttribute a = new ChangeAttribute();
+    a.project = change.getProject().get();
+    a.branch = change.getDest().getShortName();
+    a.id = change.getKey().get();
+    a.number = change.getId().toString();
+    a.subject = change.getSubject();
+    a.url = getChangeUrl(change);
+
+    final AccountState owner = accountCache.get(change.getOwner());
+    a.owner = asAccountAttribute(owner.getAccount());
+    return a;
+  }
+
+  /**
+   * Create a PatchSetAttribute for the given patchset suitable for
+   * serialization to JSON.
+   *
+   * @param patchSet
+   * @return object suitable for serialization to JSON
+   */
+  public PatchSetAttribute asPatchSetAttribute(final PatchSet patchSet) {
+    PatchSetAttribute p = new PatchSetAttribute();
+    p.revision = patchSet.getRevision().get();
+    p.number = Integer.toString(patchSet.getPatchSetId());
+    p.ref = patchSet.getRefName();
+
+    final AccountState uploader = accountCache.get(patchSet.getUploader());
+    p.uploader = asAccountAttribute(uploader.getAccount());
+    return p;
+  }
+
+  /**
+   * Create an AuthorAttribute for the given account suitable for serialization
+   * to JSON.
+   *
+   * @param account
+   * @return object suitable for serialization to JSON
+   */
+  public AccountAttribute asAccountAttribute(final Account account) {
+    AccountAttribute who = new AccountAttribute();
+    who.name = account.getFullName();
+    who.email = account.getPreferredEmail();
+    return who;
+  }
+
+  /** Get a link to the change; null if the server doesn't know its own address. */
+  private String getChangeUrl(final Change change) {
+    if (change != null && urlProvider.get() != null) {
+      final StringBuilder r = new StringBuilder();
+      r.append(urlProvider.get());
+      r.append(change.getChangeId());
+      return r.toString();
+    }
+    return null;
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/PatchSetAttribute.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/PatchSetAttribute.java
new file mode 100644
index 0000000..acb8a3a
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/PatchSetAttribute.java
@@ -0,0 +1,22 @@
+// Copyright (C) 2010 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.events;
+
+public class PatchSetAttribute {
+    public String number;
+    public String revision;
+    public String ref;
+    public AccountAttribute uploader;
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/PatchSetCreatedEvent.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/PatchSetCreatedEvent.java
new file mode 100644
index 0000000..15e3978
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/PatchSetCreatedEvent.java
@@ -0,0 +1,22 @@
+// Copyright (C) 2010 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.events;
+
+public class PatchSetCreatedEvent extends ChangeEvent {
+    public final String type = "patchset-created";
+    public ChangeAttribute change;
+    public PatchSetAttribute patchSet;
+    public AccountAttribute uploader;
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/StreamEvents.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/StreamEvents.java
index 6e6e600..e9e6015 100755
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/StreamEvents.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/StreamEvents.java
@@ -16,8 +16,8 @@
 
 import com.google.gerrit.common.ChangeHookRunner;
 import com.google.gerrit.common.ChangeListener;
-import com.google.gerrit.common.ChangeHookRunner.ChangeEvent;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.events.ChangeEvent;
 import com.google.gerrit.server.git.WorkQueue;
 import com.google.gerrit.server.git.WorkQueue.CancelableRunnable;
 import com.google.gerrit.sshd.BaseCommand;