Inital commit for its-phabricator

This plugin is based on its-bugzilla's
  1a9e450e102842cae6dc225fe230da2eab0b7cc5

Change-Id: I34a4b3c49385dbb3dfc050095f867262c244cea6
diff --git a/BUCK b/BUCK
index 9a3e8dc..aa5978f 100644
--- a/BUCK
+++ b/BUCK
@@ -1,18 +1,18 @@
 gerrit_plugin(
-  name = 'its-bugzilla',
+  name = 'its-phabricator',
   srcs = glob(['src/main/java/**/*.java']),
   resources = glob(['src/main/resources/**/*']),
   manifest_entries = [
-    'Gerrit-PluginName: its-bugzilla',
-    'Gerrit-Module: com.googlesource.gerrit.plugins.hooks.bz.BugzillaModule',
-    'Gerrit-InitStep: com.googlesource.gerrit.plugins.hooks.bz.InitBugzilla',
+    'Gerrit-Module: com.googlesource.gerrit.plugins.its.phabricator.PhabricatorModule',
     'Gerrit-ReloadMode: reload',
-    'Implementation-Title: Plugin its-bugzilla',
+    'Implementation-Title: Plugin its-phabricator',
     'Implementation-URL: https://www.wikimediafoundation.org',
   ],
   deps = [
     ':its-base_stripped',
-    '//plugins/its-bugzilla/lib:j2bugzilla',
+    '//lib/httpcomponents:httpcore',
+    '//lib/httpcomponents:httpclient',
+    '//lib:gson',
   ],
 )
 
@@ -48,18 +48,18 @@
   ]
 )
 
-TEST_UTIL_SRC = glob(['src/test/java/com/googlesource/gerrit/plugins/hooks/testutil/**/*.java'])
+TEST_UTIL_SRC = glob(['src/test/java/com/googlesource/gerrit/plugins/its/testutil/**/*.java'])
 
 java_library(
-  name = 'its-bugzilla_tests-utils',
+  name = 'its-phabricator_tests-utils',
   srcs = TEST_UTIL_SRC,
   deps = [
     '//lib:guava',
-    '//lib/easymock:easymock',
+    '//plugins/its-phabricator/lib:easymock',
     '//lib/log:impl_log4j',
     '//lib/log:log4j',
     '//lib:junit',
-    '//lib/powermock:powermock-api-easymock',
+    '//plugins/its-phabricator/lib:powermock-api-easymock',
     '//lib/powermock:powermock-api-support',
     '//lib/powermock:powermock-core',
     '//lib/powermock:powermock-module-junit4',
@@ -68,18 +68,18 @@
 )
 
 java_test(
-  name = 'its-bugzilla_tests',
+  name = 'its-phabricator_tests',
   srcs = glob(
     ['src/test/java/**/*.java'],
     excludes = TEST_UTIL_SRC
   ),
-  labels = ['its-bugzilla'],
-  source_under_test = [':its-bugzilla__plugin'],
+  labels = ['its-phabricator'],
+  source_under_test = [':its-phabricator__plugin'],
   deps = [
-    ':its-bugzilla__plugin',
-    ':its-bugzilla_tests-utils',
+    ':its-phabricator__plugin',
+    ':its-phabricator_tests-utils',
     '//gerrit-plugin-api:lib',
-    '//lib/easymock:easymock',
+    '//plugins/its-phabricator/lib:easymock',
     '//lib:guava',
     '//lib/guice:guice',
     '//lib/jgit:jgit',
@@ -87,9 +87,13 @@
     '//lib/log:api',
     '//lib/log:impl_log4j',
     '//lib/log:log4j',
-    '//lib/powermock:powermock-api-easymock',
+    '//plugins/its-phabricator/lib:powermock-api-easymock',
     '//lib/powermock:powermock-api-support',
     '//lib/powermock:powermock-core',
     '//lib/powermock:powermock-module-junit4',
+    '//lib/powermock:powermock-module-junit4-common',
+    '//lib/powermock:powermock-reflect',
+    '//lib/httpcomponents:httpclient',
+    '//lib:gson',
   ],
 )
diff --git a/lib/BUCK b/lib/BUCK
index b889b94..ea00ba3 100644
--- a/lib/BUCK
+++ b/lib/BUCK
@@ -1,43 +1,38 @@
 include_defs('//lib/maven.defs')
 
+# Upstream is at 3.2, which does not work with PowerMock 1.5's expectNew.
+# So we force EasyMock 3.1.
 maven_jar(
-  name = 'j2bugzilla',
-  id = 'com.j2bugzilla:j2bugzilla:2.2',
-  sha1 = 'abd2c9290e6e4ec3222c89a99c79d9c5bbc79300',
-  license = 'Apache2.0',
-  deps = [':xmlrpc-client'],
+  name = 'easymock',
+  id = 'org.easymock:easymock:3.1',
+  sha1 = '3e127311a86fc2e8f550ef8ee4abe094bbcf7e7e',
+  license = 'DO_NOT_DISTRIBUTE',
+  deps = [
+    '//lib/easymock:cglib-2_2',
+    ':objenesis',
+  ],
 )
 
+# Duplicate upstream's objenesis, which would not be
+# visible otherwise.
 maven_jar(
-  name = 'xmlrpc-client',
-  id = 'org.apache.xmlrpc:xmlrpc-client:3.1.3',
-  sha1 = 'e486ad917028b52265610206fb5a1e2b5914b94b',
-  license = 'Apache2.0',
-  deps = [':xmlrpc-common'],
-  visibility = [],
+  name = 'objenesis',
+  id = 'org.objenesis:objenesis:1.2',
+  sha1 = 'bfcb0539a071a4c5a30690388903ac48c0667f2a',
+  license = 'DO_NOT_DISTRIBUTE',
+  visibility = ['//lib/powermock:powermock-reflect'],
+  attach_source = False,
 )
 
+# Upstream's powermock-api-easymock would pull in upstream's
+# EasyMock 3.2, so we hard-wire dependency to the plugin's EasyMock.
 maven_jar(
-  name = 'xmlrpc-common',
-  id = 'org.apache.xmlrpc:xmlrpc-common:3.1.3',
-  sha1 = '415daf1f1473a947452588906dc9f5b3575fb44d',
-  license = 'Apache2.0',
-  deps = [':ws-commons-util'],
-  visibility = [],
-)
-
-maven_jar(
-  name = 'ws-commons-util',
-  id = 'org.apache.ws.commons.util:ws-commons-util:1.0.2',
-  sha1 = '3f478e6def772c19d1053f61198fa1f6a6119238',
-  license = 'Apache2.0',
-  deps = [':xml-apis'],
-  visibility = [],
-)
-
-maven_jar(
-  name = 'xml-apis',
-  id = 'xml-apis:xml-apis:1.0.b2',
-  sha1 = '3136ca936f64c9d68529f048c2618bd356bf85c9',
-  license = 'Apache2.0',
+  name = 'powermock-api-easymock',
+  id = 'org.powermock:powermock-api-easymock:1.5',
+  sha1 = 'a485b570b9debb46b53459a8e866a40343b2cfe2',
+  license = 'DO_NOT_DISTRIBUTE',
+  deps = [
+    '//lib/powermock:powermock-api-support',
+    ':easymock',
+  ],
 )
diff --git a/pom.xml b/pom.xml
index 50f3652..aa9c2b9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,20 +19,18 @@
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
 
-  <groupId>com.googlesource.gerrit.plugins.bugzilla</groupId>
-  <artifactId>its-bugzilla</artifactId>
+  <groupId>com.googlesource.gerrit.plugins.phabricator</groupId>
+  <artifactId>its-phabricator</artifactId>
   <version>2.11-SNAPSHOT</version>
 
-  <name>Gerrit - Bugzilla support</name>
+  <name>Gerrit - Phabricator support</name>
 
   <properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <Gerrit-ApiType>plugin</Gerrit-ApiType>
     <Gerrit-ReloadMode>reload</Gerrit-ReloadMode>
-    <Gerrit-PluginName>its-bugzilla</Gerrit-PluginName>
-    <Gerrit-InitStep>com.googlesource.gerrit.plugins.hooks.bz.InitBugzilla</Gerrit-InitStep>
-    <Gerrit-Module>com.googlesource.gerrit.plugins.hooks.bz.BugzillaModule</Gerrit-Module>
-    <easymockVersion>3.0</easymockVersion>
+    <Gerrit-Module>com.googlesource.gerrit.plugins.its.phabricator.PhabricatorModule</Gerrit-Module>
+    <easymockVersion>3.1</easymockVersion>
     <powermockVersion>1.5</powermockVersion>
     <slf4jVersion>1.6.2</slf4jVersion>
   </properties>
@@ -101,9 +99,14 @@
       <version>${project.version}</version>
     </dependency>
     <dependency>
-      <groupId>com.j2bugzilla</groupId>
-      <artifactId>j2bugzilla</artifactId>
-      <version>2.2</version>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpclient</artifactId>
+      <version>4.3.4</version>
+    </dependency>
+    <dependency>
+      <groupId>com.google.code.gson</groupId>
+      <artifactId>gson</artifactId>
+      <version>2.1</version>
     </dependency>
     <dependency>
       <groupId>org.slf4j</groupId>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/bz/BugzillaClient.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/bz/BugzillaClient.java
deleted file mode 100644
index 770ed96..0000000
--- a/src/main/java/com/googlesource/gerrit/plugins/hooks/bz/BugzillaClient.java
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright (C) 2013 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.googlesource.gerrit.plugins.hooks.bz;
-
-import java.util.HashMap;
-import java.util.Set;
-
-import com.googlesource.gerrit.plugins.hooks.its.InvalidTransitionException;
-import com.j2bugzilla.base.Bug;
-import com.j2bugzilla.base.BugzillaConnector;
-import com.j2bugzilla.base.BugzillaException;
-import com.j2bugzilla.base.ConnectionException;
-import com.j2bugzilla.rpc.BugzillaVersion;
-import com.j2bugzilla.rpc.CommentBug;
-import com.j2bugzilla.rpc.GetBug;
-import com.j2bugzilla.rpc.GetLegalValues;
-import com.j2bugzilla.rpc.GetLegalValues.Fields;
-import com.j2bugzilla.rpc.LogIn;
-import com.j2bugzilla.rpc.LogOut;
-import com.j2bugzilla.rpc.UpdateBug;
-
-public class BugzillaClient {
-
-  private final BugzillaConnector connector;
-  private final String xmlRpcUrl;
-  private static HashMap<Fields, Set<String>> legalFieldValues = new HashMap<Fields, Set<String>>();
-
-  public BugzillaClient(final String baseUrl) throws ConnectionException {
-    this(baseUrl, "/xmlrpc.cgi");
-  }
-
-  public BugzillaClient(final String baseUrl, final String rpcPath) throws ConnectionException {
-    xmlRpcUrl = baseUrl + rpcPath;
-    connector = new BugzillaConnector();
-    connector.connectTo(xmlRpcUrl);
-  }
-
-  public String login(final String username, final String password) throws BugzillaException {
-    LogIn logIn = new LogIn(username, password);
-    connector.executeMethod(logIn);
-    return "username="+username+", userid="+logIn.getUserID();
-  }
-
-  public void logout() throws BugzillaException {
-    connector.executeMethod(new LogOut());
-  }
-
-  public Bug getBug(int bugId) throws BugzillaException {
-    GetBug getBugId = new GetBug(bugId);
-    connector.executeMethod(getBugId);
-    return getBugId.getBug();
-  }
-
-  public Bug getBug(String bugId) throws BugzillaException {
-    return getBug(Integer.parseInt(bugId));
-  }
-
-  public void addComment(String bugId, String comment) throws BugzillaException {
-    CommentBug bugComment = new CommentBug(getBug(bugId), comment);
-    connector.executeMethod(bugComment);
-  }
-
-  private void performSimpleActionChainable(final Bug bug, final String actionName,
-      final String actionValue) throws BugzillaException,
-      InvalidTransitionException {
-    if ("status".equals(actionName)) {
-      assertLegalValue(Fields.STATUS, actionValue);
-      bug.setStatus(actionValue);
-    } else if ("resolution".equals(actionName)) {
-      assertLegalValue(Fields.RESOLUTION, actionValue);
-      bug.setResolution(actionValue);
-    } else {
-      throw new InvalidTransitionException("Simple action " + actionName
-        + " is not known");
-    }
-  }
-
-  private void performChainedAction(final Bug bug, final String actionName,
-      final String actionValue) throws BugzillaException,
-      InvalidTransitionException {
-    String[] actionNames = actionName.split("/");
-    String[] actionValues = actionValue.split("/");
-    if (actionNames.length != actionValues.length) {
-      throw new InvalidTransitionException("Number of chained actions does not"
-        + " match number of action values");
-    }
-
-    int i;
-    for (i=0; i<actionNames.length; i++) {
-        performSimpleActionChainable(bug, actionNames[i], actionValues[i]);
-    }
-  }
-
-  public void performAction(final String bugId, final String actionNameParam,
-      final String actionValueParam) throws BugzillaException,
-      InvalidTransitionException {
-    Bug bug = getBug(bugId);
-
-    String actionName = actionNameParam;
-    if (actionName.startsWith("set-")) {
-      actionName = actionName.substring(4);
-    }
-    actionName = actionName.replaceAll("-and-", "/");
-
-    String actionValue = actionValueParam;
-    actionValue = actionValue.replaceAll(" ", "/");
-
-    if ("status".equals(actionName) || "resolution".equals(actionName)) {
-      performSimpleActionChainable(bug, actionName, actionValue);
-    } else if ("status/resolution".equals(actionName)) {
-      performChainedAction(bug, actionName, actionValue);
-    } else {
-      throw new InvalidTransitionException("Action " + actionNameParam
-          + " is not known");
-    }
-    connector.executeMethod(new UpdateBug(bug));
-  }
-
-  private void assertLegalValue(Fields field, String actionValue)
-      throws BugzillaException, InvalidTransitionException {
-    if (!getLegalValues(field).contains(actionValue)) {
-      throw new InvalidTransitionException( "The value '" + actionValue
-        + "' is not an allowed value for bugzilla's " + field.getInternalName()
-        + " field");
-    }
-  }
-
-  public String getServerVersion() throws BugzillaException {
-    BugzillaVersion versionCheck = new BugzillaVersion();
-    connector.executeMethod(versionCheck);
-    return versionCheck.getVersion();
-  }
-
-  public String getXmlRpcUrl() {
-    return xmlRpcUrl;
-  }
-
-  private Set<String> getLegalValues(Fields field) throws BugzillaException {
-    if (legalFieldValues.get(field) == null) {
-      GetLegalValues getValues = new GetLegalValues(field);
-      connector.executeMethod(getValues);
-      legalFieldValues.put(field, getValues.getLegalValues());
-    }
-    return legalFieldValues.get(field);
-  }
-}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/bz/BugzillaItsFacade.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/bz/BugzillaItsFacade.java
deleted file mode 100644
index d4957cf..0000000
--- a/src/main/java/com/googlesource/gerrit/plugins/hooks/bz/BugzillaItsFacade.java
+++ /dev/null
@@ -1,239 +0,0 @@
-// Copyright (C) 2013 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.googlesource.gerrit.plugins.hooks.bz;
-
-import java.io.IOException;
-import java.net.URL;
-import java.util.concurrent.Callable;
-
-import org.eclipse.jgit.lib.Config;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.gerrit.extensions.annotations.PluginName;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.inject.Inject;
-
-import com.googlesource.gerrit.plugins.hooks.its.ItsFacade;
-import com.j2bugzilla.base.BugzillaException;
-import com.j2bugzilla.base.ConnectionException;
-
-public class BugzillaItsFacade implements ItsFacade {
-  private static final String GERRIT_CONFIG_USERNAME = "username";
-  private static final String GERRIT_CONFIG_PASSWORD = "password";
-  private static final String GERRIT_CONFIG_URL = "url";
-
-  private static final int MAX_ATTEMPTS = 3;
-
-  private Logger log = LoggerFactory.getLogger(BugzillaItsFacade.class);
-
-  private final String pluginName;
-  private Config gerritConfig;
-
-  private BugzillaClient client;
-
-  @Inject
-  public BugzillaItsFacade(@PluginName String pluginName,
-      @GerritServerConfig Config cfg) {
-    this.pluginName = pluginName;
-    try {
-      this.gerritConfig = cfg;
-      log.info("Connected to Bugzilla at " + client().getXmlRpcUrl()
-          + ", reported version is " + client().getServerVersion());
-    } catch (Exception ex) {
-      log.warn("Bugzilla is currently not available", ex);
-    }
-  }
-
-  @Override
-  public String healthCheck(final Check check) throws IOException {
-      return execute(new Callable<String>(){
-        @Override
-        public String call() throws Exception {
-          if (check.equals(Check.ACCESS))
-            return healthCheckAccess();
-          else
-            return healthCheckSysinfo();
-        }});
-  }
-
-  @Override
-  public void addComment(final String bugId, final String comment) throws IOException {
-
-    execute(new Callable<String>(){
-      @Override
-      public String call() throws Exception {
-        log.debug("Adding comment " + comment + " to bug " + bugId);
-        client().addComment(bugId, comment);
-        log.debug("Added comment " + comment + " to bug " + bugId);
-        return bugId;
-      }});
-  }
-
-  @Override
-  public void addRelatedLink(final String issueKey, final URL relatedUrl, String description)
-      throws IOException {
-    addComment(issueKey, "Related URL: " + createLinkForWebui(relatedUrl.toExternalForm(), description));
-  }
-
-  @Override
-  public void performAction(final String bugId, final String actionString)
-      throws IOException {
-
-    execute(new Callable<String>(){
-      @Override
-      public String call() throws Exception {
-        String actionName = actionString.substring(0, actionString.indexOf(" "));
-        String actionValue = actionString.substring(actionString.indexOf(" ") + 1);
-        doPerformAction(bugId, actionName, actionValue);
-        return bugId;
-      }});
-  }
-
-  private void doPerformAction(final String bugId, final String fieldName, final String fieldValue)
-      throws BugzillaException, IOException {
-    client().performAction(bugId, fieldName.toLowerCase(), fieldValue);
-  }
-
-  @Override
-  public boolean exists(final String bugId) throws IOException {
-    return execute(new Callable<Boolean>(){
-      @Override
-      public Boolean call() throws Exception {
-        return client().getBug(bugId) != null;
-      }});
-  }
-
-  public void logout() {
-    this.logout(false);
-  }
-
-  public void logout(boolean quiet) {
-    try {
-      client().logout();
-    }
-    catch (Exception ex) {
-      if (!quiet) log.error("I was unable to logout", ex);
-    }
-  }
-
-  public Object login() {
-    return login(false);
-  }
-
-  public Object login(boolean quiet) {
-    try {
-      String token = client.login(getUsername(), getPassword());
-      log.info("Connected to " + getUrl() + " as " + token);
-      return token;
-    }
-    catch (Exception ex) {
-      if (!quiet) {
-        log.error("I was unable to login", ex);
-      }
-
-      return null;
-    }
-  }
-
-  private BugzillaClient client() throws IOException {
-
-    if (client == null) {
-      try {
-        log.debug("Connecting to bugzilla at URL " + getUrl());
-        client = new BugzillaClient(getUrl());
-        log.debug("Autenthicating as user " + getUsername());
-      } catch (Exception ex) {
-        log.info("Unable to connect to " + getUrl() + " as "
-            + getUsername());
-        throw new IOException(ex);
-      }
-
-      login();
-    }
-
-    return client;
-  }
-
-  private <P> P execute(Callable<P> function) throws IOException {
-
-    int attempt = 0;
-    while(true) {
-      try {
-        return function.call();
-      } catch (Exception ex) {
-        if (isRecoverable(ex) && ++attempt < MAX_ATTEMPTS) {
-          log.debug("Call failed - retrying, attempt {} of {}", attempt, MAX_ATTEMPTS);
-          logout(true);
-          login(true);
-          continue;
-        }
-
-        if (ex instanceof IOException)
-          throw ((IOException)ex);
-        else
-          throw new IOException(ex);
-      }
-    }
-  }
-
-  private boolean isRecoverable(Exception ex) {
-    return false;
-  }
-
-  private String getPassword() {
-    final String pass =
-        gerritConfig.getString(pluginName, null,
-            GERRIT_CONFIG_PASSWORD);
-    return pass;
-  }
-
-  private String getUsername() {
-    final String user =
-        gerritConfig.getString(pluginName, null,
-            GERRIT_CONFIG_USERNAME);
-    return user;
-  }
-
-  private String getUrl() {
-    final String url =
-        gerritConfig.getString(pluginName, null, GERRIT_CONFIG_URL);
-    return url;
-  }
-
-  @Override
-  public String createLinkForWebui(String url, String text) {
-    String ret = url;
-    if (text != null && ! text.equals(url)) {
-        ret += " (" + text + ")";
-    }
-    return ret;
-  }
-
-  private String healthCheckAccess() throws BugzillaException, ConnectionException {
-    BugzillaClient client = new BugzillaClient(getUrl());
-    client.login(getUsername(), getPassword());
-    client.logout();
-    final String result = "{\"status\"=\"ok\",\"username\"=\""+getUsername()+"\"}";
-    log.debug("Healtheck on access result: {}", result);
-    return result;
-  }
-
-  private String healthCheckSysinfo() throws BugzillaException, IOException {
-    final String result = "{\"status\"=\"ok\",\"system\"=\"Bugzilla\",\"version\"=\""+client().getServerVersion()+"\",\"url\"=\""+getUrl()+"\"}";
-    log.debug("Healtheck on sysinfo result: {}", result);
-    return result;
-  }
-}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/bz/InitBugzilla.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/bz/InitBugzilla.java
deleted file mode 100644
index e9175da..0000000
--- a/src/main/java/com/googlesource/gerrit/plugins/hooks/bz/InitBugzilla.java
+++ /dev/null
@@ -1,137 +0,0 @@
-// Copyright (C) 2013 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.googlesource.gerrit.plugins.hooks.bz;
-
-import com.google.gerrit.extensions.annotations.PluginName;
-import com.google.gerrit.pgm.init.api.AllProjectsConfig;
-import com.google.gerrit.pgm.init.api.AllProjectsNameOnInitProvider;
-import com.google.gerrit.pgm.init.api.InitFlags;
-import com.google.gerrit.pgm.init.api.Section;
-import com.google.gerrit.pgm.init.api.ConsoleUI;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-
-import com.googlesource.gerrit.plugins.hooks.its.InitIts;
-import com.googlesource.gerrit.plugins.hooks.validation.ItsAssociationPolicy;
-import com.j2bugzilla.base.BugzillaException;
-import com.j2bugzilla.base.ConnectionException;
-
-import org.eclipse.jgit.errors.ConfigInvalidException;
-
-import java.io.IOException;
-import java.util.Arrays;
-
-/** Initialize the GitRepositoryManager configuration section. */
-@Singleton
-class InitBugzilla extends InitIts {
-  private final String pluginName;
-  private final Section.Factory sections;
-  private final InitFlags flags;
-  private Section bugzilla;
-  private Section bugzillaComment;
-  private String bugzillaUrl;
-  private String bugzillaUsername;
-  private String bugzillaPassword;
-
-  @Inject
-  InitBugzilla(@PluginName String pluginName, ConsoleUI ui,
-      Section.Factory sections, AllProjectsConfig allProjectsConfig,
-      AllProjectsNameOnInitProvider allProjects, InitFlags flags) {
-    super(pluginName, "Bugzilla", ui, allProjectsConfig, allProjects);
-    this.pluginName = pluginName;
-    this.sections = sections;
-    this.flags = flags;
-  }
-
-  @Override
-  public void run() throws IOException, ConfigInvalidException {
-    super.run();
-
-    ui.message("\n");
-    ui.header("Bugzilla connectivity");
-
-    if (!pluginName.equalsIgnoreCase("bugzilla")
-        && !flags.cfg.getSections().contains(pluginName)
-        && flags.cfg.getSections().contains("bugzilla")) {
-      ui.message("A Bugzilla configuration for the 'hooks-bugzilla' plugin was found.\n");
-      if (ui.yesno(true, "Copy it for the '%s' plugin?", pluginName)) {
-        for (String n : flags.cfg.getNames("bugzilla")) {
-          flags.cfg.setStringList(pluginName, null, n,
-              Arrays.asList(flags.cfg.getStringList("bugzilla", null, n)));
-        }
-        for (String n : flags.cfg.getNames(COMMENT_LINK_SECTION, "bugzilla")) {
-          flags.cfg.setStringList(COMMENT_LINK_SECTION, pluginName, n,
-              Arrays.asList(flags.cfg.getStringList(COMMENT_LINK_SECTION, "bugzilla", n)));
-        }
-
-        if (ui.yesno(false, "Remove configuration for 'hooks-bugzilla' plugin?")) {
-          flags.cfg.unsetSection("bugzilla", null);
-          flags.cfg.unsetSection(COMMENT_LINK_SECTION, "bugzilla");
-        }
-      } else {
-        init();
-      }
-    } else {
-      init();
-    }
-  }
-
-  private void init() {
-    this.bugzilla = sections.get(pluginName, null);
-    this.bugzillaComment = sections.get(COMMENT_LINK_SECTION, pluginName);
-
-
-    do {
-      enterBugzillaConnectivity();
-    } while (bugzillaUrl != null
-        && (isConnectivityRequested(bugzillaUrl) && !isBugzillaConnectSuccessful()));
-
-    if (bugzillaUrl == null) {
-      return;
-    }
-
-    ui.header("Bugzilla issue-tracking association");
-    bugzillaComment.string("Bugzilla bug number regex", "match", "\\([Bb][Uu][Gg][ ]*[1-9][0-9]*\\)");
-    bugzillaComment.set("html",
-        String.format("<a href=\"%s/show_bug.cgi?id=$1\">$1</a>", bugzillaUrl));
-    bugzillaComment.select("Bug number enforced in commit message", "association",
-        ItsAssociationPolicy.SUGGESTED);
-  }
-
-  public void enterBugzillaConnectivity() {
-    bugzillaUrl = bugzilla.string("Bugzilla URL (empty to skip)", "url", null);
-    if (bugzillaUrl != null) {
-      bugzillaUsername = bugzilla.string("Bugzilla username", "username", "");
-      bugzillaPassword = bugzilla.password("username", "password");
-    }
-  }
-
-  private boolean isBugzillaConnectSuccessful() {
-    ui.message("Checking Bugzilla connectivity ... ");
-    try {
-      BugzillaClient bugzillaClient = new BugzillaClient(bugzillaUrl);
-      bugzillaClient.login(bugzillaUsername, bugzillaPassword);
-      bugzillaClient.logout();
-      ui.message("[OK]\n");
-      return true;
-    } catch (BugzillaException e) {
-      ui.message("*FAILED* (%s)\n", e.toString());
-      return false;
-    } catch (ConnectionException e) {
-      ui.message("*FAILED* (%s)\n", e.toString());
-      return false;
-    }
-  }
-}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/PhabricatorItsFacade.java b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/PhabricatorItsFacade.java
new file mode 100644
index 0000000..999513b
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/PhabricatorItsFacade.java
@@ -0,0 +1,119 @@
+// Copyright (C) 2013 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.googlesource.gerrit.plugins.its.phabricator;
+
+import java.io.IOException;
+import java.net.URL;
+
+import org.eclipse.jgit.lib.Config;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.inject.Inject;
+
+import com.googlesource.gerrit.plugins.hooks.its.ItsFacade;
+import com.googlesource.gerrit.plugins.its.phabricator.conduit.Conduit;
+import com.googlesource.gerrit.plugins.its.phabricator.conduit.ConduitErrorException;
+import com.googlesource.gerrit.plugins.its.phabricator.conduit.ConduitException;
+
+public class PhabricatorItsFacade implements ItsFacade {
+  private static final Logger log = LoggerFactory.getLogger(PhabricatorItsFacade.class);
+
+  private static final String GERRIT_CONFIG_URL = "url";
+  private static final String GERRIT_CONFIG_USERNAME = "username";
+  private static final String GERRIT_CONFIG_CERTIFICATE = "certificate";
+
+  private final Conduit conduit;
+
+  @Inject
+  public PhabricatorItsFacade(@PluginName String pluginName,
+      @GerritServerConfig Config cfg) {
+    final String url = cfg.getString(pluginName, null, GERRIT_CONFIG_URL);
+    final String username = cfg.getString(pluginName, null,
+            GERRIT_CONFIG_USERNAME);
+    final String certificate = cfg.getString(pluginName, null,
+            GERRIT_CONFIG_CERTIFICATE);
+
+    this.conduit = new Conduit(url, username, certificate);
+  }
+
+  @Override
+  public void addComment(final String bugId, final String comment)
+      throws IOException {
+    int task_id = Integer.parseInt(bugId);
+    try {
+      conduit.maniphestUpdate(task_id, comment);
+    } catch (ConduitException e) {
+      throw new IOException("Could not update message for task " + task_id, e);
+    }
+    log.debug("Added comment " + comment + " to bug " + task_id);
+  }
+
+  @Override
+  public void addRelatedLink(final String issueKey, final URL relatedUrl,
+      String description) throws IOException {
+    addComment(issueKey, "Related URL: " + createLinkForWebui(
+        relatedUrl.toExternalForm(), description));
+  }
+
+  @Override
+  public boolean exists(final String bugId) throws IOException {
+    Boolean ret = false;
+    int task_id = Integer.parseInt(bugId);
+    try {
+      try {
+        conduit.maniphestInfo(task_id);
+        ret = true;
+      } catch (ConduitErrorException e) {
+        // An ERR_BAD_TASK just means that the task does not exist.
+        // So the default value of ret would be ok
+        if (! ("ERR_BAD_TASK".equals(e.getErrorCode()))) {
+          // So we had an exception that is /not/ ERR_BAD_TASK.
+          // We have to relay that to the caller.
+          throw e;
+        }
+      }
+    } catch (ConduitException e) {
+      throw new IOException("Could not check existence of task " + task_id, e);
+    }
+    return ret;
+  }
+
+  @Override
+  public void performAction(final String bugId, final String actionString) {
+    // No custom actions at this point.
+    //
+    // Note that you can use hashtag names in comments to associate a task
+    // with a new project.
+  }
+
+  @Override
+  public String healthCheck(final Check check) throws IOException {
+    // This method is not used, so there is no need to implement it.
+    return "unknown";
+  }
+
+  @Override
+  public String createLinkForWebui(String url, String text) {
+    String ret = "[[" + url;
+    if (text != null && !text.isEmpty() && !text.equals(url)) {
+      ret += "|" + text;
+    }
+    ret += "]]";
+    return ret;
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/bz/BugzillaModule.java b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/PhabricatorModule.java
similarity index 79%
rename from src/main/java/com/googlesource/gerrit/plugins/hooks/bz/BugzillaModule.java
rename to src/main/java/com/googlesource/gerrit/plugins/its/phabricator/PhabricatorModule.java
index 60b5181..421fe21 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/hooks/bz/BugzillaModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/PhabricatorModule.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.googlesource.gerrit.plugins.hooks.bz;
+package com.googlesource.gerrit.plugins.its.phabricator;
 
 import org.eclipse.jgit.lib.Config;
 import org.slf4j.Logger;
@@ -27,16 +27,16 @@
 import com.googlesource.gerrit.plugins.hooks.ItsHookModule;
 import com.googlesource.gerrit.plugins.hooks.its.ItsFacade;
 
-public class BugzillaModule extends AbstractModule {
+public class PhabricatorModule extends AbstractModule {
 
-  private static final Logger log = LoggerFactory.getLogger(BugzillaModule.class);
+  private static final Logger log = LoggerFactory.getLogger(PhabricatorModule.class);
 
   private final String pluginName;
   private final Config gerritConfig;
   private final PluginConfigFactory pluginCfgFactory;
 
   @Inject
-  public BugzillaModule(@PluginName final String pluginName,
+  public PhabricatorModule(@PluginName final String pluginName,
       @GerritServerConfig final Config config,
       PluginConfigFactory pluginCfgFactory) {
     this.pluginName = pluginName;
@@ -47,8 +47,8 @@
   @Override
   protected void configure() {
     if (gerritConfig.getString(pluginName, null, "url") != null) {
-      log.info("Bugzilla is configured as ITS");
-      bind(ItsFacade.class).toInstance(new BugzillaItsFacade(pluginName, gerritConfig));
+      log.info("Phabricator is configured as ITS");
+      bind(ItsFacade.class).toInstance(new PhabricatorItsFacade(pluginName, gerritConfig));
 
       install(new ItsHookModule(pluginName, pluginCfgFactory));
     }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/Conduit.java b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/Conduit.java
new file mode 100644
index 0000000..fe42b5b
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/Conduit.java
@@ -0,0 +1,185 @@
+//Copyright (C) 2014 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.googlesource.gerrit.plugins.its.phabricator.conduit;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+import com.googlesource.gerrit.plugins.its.phabricator.conduit.results.ConduitConnect;
+import com.googlesource.gerrit.plugins.its.phabricator.conduit.results.ConduitPing;
+import com.googlesource.gerrit.plugins.its.phabricator.conduit.results.ManiphestInfo;
+import com.googlesource.gerrit.plugins.its.phabricator.conduit.results.ManiphestUpdate;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.bind.DatatypeConverter;
+
+
+/**
+ * Bindings for Phabricator's Conduit API
+ * <p/>
+ * This class is not thread-safe.
+ */
+public class Conduit {
+
+  private static final Logger log = LoggerFactory.getLogger(Conduit.class);
+
+  public static final int CONDUIT_VERSION = 6;
+
+  private final ConduitConnection conduitConnection;
+  private final Gson gson;
+
+  private String username;
+  private String certificate;
+
+  private String sessionKey;
+
+  public Conduit(final String baseUrl) {
+    this(baseUrl, null, null);
+  }
+
+  public Conduit(final String baseUrl, final String username, final String certificate) {
+    this.conduitConnection = new ConduitConnection(baseUrl);
+    this.username = username;
+    this.certificate = certificate;
+    this.gson = new Gson();
+    resetSession();
+  }
+
+  private void resetSession() {
+    sessionKey = null;
+  }
+
+  public void setUsername(String username) {
+    this.username = username;
+    resetSession();
+  }
+
+  public void setCertificate(String certificate) {
+    this.certificate = certificate;
+    resetSession();
+  }
+
+  /**
+   * Adds session parameters to a Map of parameters
+   * <p/>
+   * If there is no active session, a new one is opened
+   * <p/>
+   * This method overrides the params' __conduit__ value.
+   *
+   * @param params The Map to add session paramaters to
+   */
+  private void fillInSession(Map<String, Object> params) throws ConduitException {
+    if (sessionKey == null) {
+      log.debug("Trying to start new session");
+      conduitConnect();
+    }
+    Map<String, Object> conduitParams = new HashMap<String, Object>();
+    conduitParams.put("sessionKey",sessionKey);
+    params.put("__conduit__", conduitParams);
+  }
+
+  /**
+   * Runs the API's 'conduit.ping' method
+   */
+  public ConduitPing conduitPing() throws ConduitException {
+    JsonElement callResult = conduitConnection.call("conduit.ping");
+    JsonObject callResultWrapper = new JsonObject();
+    callResultWrapper.add("hostname", callResult);
+    ConduitPing result = gson.fromJson(callResultWrapper, ConduitPing.class);
+    return result;
+  }
+
+  /**
+   * Runs the API's 'conduit.connect' method
+   */
+  public ConduitConnect conduitConnect() throws ConduitException {
+    Map<String, Object> params = new HashMap<String, Object>();
+    params.put("client", "its-phabricator");
+    params.put("clientVersion", CONDUIT_VERSION);
+    params.put("user", username);
+
+    // According to phabricator/src/applications/conduit/method/ConduitConnectConduitAPIMethod.php,
+    // the authToken needs to be an integer that is within 15 minutes of the
+    // server's current timestamp.
+    long authToken = System.currentTimeMillis() / 1000;
+    params.put("authToken", authToken);
+
+    // According to phabricator/src/applications/conduit/method/ConduitConnectConduitAPIMethod.php,
+    // The signature is the SHA1 of the concatenation of the authToken (as
+    // string) and the certificate (The long sequence of digits and lowercase
+    // hat get written into ~/.arcrc after "arc install-certificate").
+    String authSignatureInput = Long.toString(authToken) + certificate;
+
+    MessageDigest sha1;
+    try {
+      sha1 = MessageDigest.getInstance("SHA-1");
+    } catch (NoSuchAlgorithmException e) {
+      throw new ConduitException("Failed to compute authSignature, as no "
+          + "SHA-1 algorithm implementation was found", e);
+    }
+    byte[] authSignatureRaw;
+    try {
+      authSignatureRaw = sha1.digest(authSignatureInput.getBytes("UTF-8"));
+    } catch (UnsupportedEncodingException e) {
+      throw new ConduitException("Failed to convert authSignature input to "
+          + "UTF-8 String", e);
+    }
+    String authSignatureUC = DatatypeConverter.printHexBinary(authSignatureRaw);
+    String authSignature = authSignatureUC.toLowerCase();
+    params.put("authSignature", authSignature);
+
+    JsonElement callResult = conduitConnection.call("conduit.connect", params);
+
+    ConduitConnect result = gson.fromJson(callResult, ConduitConnect.class);
+    sessionKey = result.getSessionKey();
+    return result;
+  }
+
+  /**
+   * Runs the API's 'maniphest.Info' method
+   */
+  public ManiphestInfo maniphestInfo(int taskId) throws ConduitException {
+    Map<String, Object> params = new HashMap<String, Object>();
+    fillInSession(params);
+    params.put("task_id", taskId);
+
+    JsonElement callResult = conduitConnection.call("maniphest.info", params);
+    ManiphestInfo result = gson.fromJson(callResult, ManiphestInfo.class);
+    return result;
+  }
+
+  /**
+   * Runs the API's 'maniphest.update' method
+   */
+  public ManiphestUpdate maniphestUpdate(int taskId, String comment) throws ConduitException {
+    Map<String, Object> params = new HashMap<String, Object>();
+    fillInSession(params);
+    params.put("id", taskId);
+    params.put("comments", comment);
+
+    JsonElement callResult = conduitConnection.call("maniphest.update", params);
+    ManiphestUpdate result = gson.fromJson(callResult, ManiphestUpdate.class);
+    return result;
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/ConduitConnection.java b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/ConduitConnection.java
new file mode 100644
index 0000000..72d343c
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/ConduitConnection.java
@@ -0,0 +1,140 @@
+//Copyright (C) 2014 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.googlesource.gerrit.plugins.its.phabricator.conduit;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+
+import com.googlesource.gerrit.plugins.its.phabricator.conduit.results.CallCapsule;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * Abstracts the connection to Conduit API
+ */
+class ConduitConnection {
+  private static final Logger log = LoggerFactory.getLogger(Conduit.class);
+
+  private final String apiUrlBase;
+  private final Gson gson;
+
+  private CloseableHttpClient client;
+
+  ConduitConnection(final String baseUrl) {
+    apiUrlBase = baseUrl.replaceAll("/+$", "") + "/api/";
+    gson = new Gson();
+    client = null;
+  }
+
+  /**
+   * Gives a cached HttpClient
+   * <p/>
+   * If  no cached HttpClient exists, a new one is spawned.
+   *
+   * @return the cached CloseableHttpClient
+   */
+  private CloseableHttpClient getClient() {
+    if (client == null) {
+      log.trace("Creating new client connection");
+      client = HttpClients.createDefault();
+    }
+    return client;
+  }
+
+  /**
+   * Call the given Conduit method without parameters
+   *
+   * @param method The name of the method that should get called
+   * @return The call's result, if there has been no error
+   * @throws Exception
+   */
+  JsonElement call(String method) throws ConduitException {
+    return call(method, new HashMap<String, Object>());
+  }
+
+  /**
+   * Calls a conduit method with some parameters
+   *
+   * @param method The name of the method that should get called
+   * @param params A map of parameters to pass to the call
+   * @return The call's result, if there has been no error
+   * @throws Exception
+   */
+  JsonElement call(String method, Map<String, Object> params) throws ConduitException {
+    String methodUrl = apiUrlBase + method;
+
+    HttpPost httppost = new HttpPost(methodUrl);
+
+
+    String json = gson.toJson(params);
+
+    log.trace("Calling phabricator method " + method
+        + " with the parameters " + json );
+    try {
+      httppost.setEntity(new StringEntity("params=" + json));
+    } catch (UnsupportedEncodingException e) {
+      throw new ConduitException("Cannot encode parameters", e);
+    }
+
+    CloseableHttpResponse response;
+    try {
+      response = getClient().execute(httppost);
+    } catch (IOException e) {
+      throw new ConduitException("Could not execute Phabricator API call", e);
+    }
+    try {
+      log.trace("Phabricator HTTP response status: " + response.getStatusLine());
+      HttpEntity entity = response.getEntity();
+      String entityString;
+      try {
+        entityString = EntityUtils.toString(entity);
+      } catch (IOException e) {
+        throw new ConduitException("Could not read the API response", e);
+      }
+
+      log.trace("Phabricator response " + entityString);
+      CallCapsule callCapsule = gson.fromJson(entityString, CallCapsule.class);
+      log.trace("callCapsule.result: " + callCapsule.getResult());
+      log.trace("callCapsule.error_code: " + callCapsule.getErrorCode());
+      log.trace("callCapsule.error_info: " + callCapsule.getErrorInfo());
+      if (callCapsule.getErrorCode() != null
+          || callCapsule.getErrorInfo() != null) {
+        throw new ConduitErrorException(method, callCapsule.getErrorCode(),
+            callCapsule.getErrorInfo());
+      }
+      return callCapsule.getResult();
+    } finally {
+      try {
+        response.close();
+      } catch (IOException e) {
+        throw new ConduitException("Could not close API response", e);
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/ConduitErrorException.java b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/ConduitErrorException.java
new file mode 100644
index 0000000..c684297
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/ConduitErrorException.java
@@ -0,0 +1,44 @@
+//Copyright (C) 2014 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.googlesource.gerrit.plugins.its.phabricator.conduit;
+
+/**
+ * Exception thrown if Conduit response has error_code or error_info set
+ */
+public class ConduitErrorException extends ConduitException {
+
+  private static final long serialVersionUID = 1L;
+
+  private final String errorCode;
+  private final String errorInfo;
+
+  ConduitErrorException(String method, String errorCode, String errorInfo) {
+    super("Method '" + method + "' gave: " +errorCode +
+        ((errorInfo != null && !errorInfo.isEmpty())
+          ? (", " + errorInfo)
+          : ""));
+    this.errorCode = errorCode;
+    this.errorInfo = errorInfo;
+  }
+
+  public String getErrorCode() {
+    return errorCode;
+  }
+
+  public String getErrorInfo() {
+    return errorInfo;
+  }
+
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/ConduitException.java b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/ConduitException.java
new file mode 100644
index 0000000..edde2fd
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/ConduitException.java
@@ -0,0 +1,35 @@
+//Copyright (C) 2014 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.googlesource.gerrit.plugins.its.phabricator.conduit;
+
+/**
+ * Generic wrapper for Exceptions caused by processing for Conduit
+ */
+public class ConduitException extends Exception {
+
+  private static final long serialVersionUID = 1L;
+
+  public ConduitException() {
+    super();
+  }
+
+  public ConduitException(String message) {
+    super(message);
+  }
+
+  public ConduitException(String message, Throwable e) {
+    super(message, e);
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/results/CallCapsule.java b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/results/CallCapsule.java
new file mode 100644
index 0000000..dcf0484
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/results/CallCapsule.java
@@ -0,0 +1,46 @@
+//Copyright (C) 2014 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.googlesource.gerrit.plugins.its.phabricator.conduit.results;
+
+import com.google.gson.JsonElement;
+
+/**
+ * Models the generic wrapper for API calls
+ * <p/>
+ * JSON looks like:
+ * <pre>
+ * {
+ *   "result": SOME JSON OBJECT,
+ *   "error_code":null,
+ *   "error_info":null
+ * }
+ * </pre>
+ */
+public class CallCapsule {
+  private JsonElement result;
+  private String error_code;
+  private String error_info;
+
+  public JsonElement getResult() {
+    return result;
+  }
+
+  public String getErrorCode() {
+    return error_code;
+  }
+
+  public String getErrorInfo() {
+    return error_info;
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/results/ConduitConnect.java b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/results/ConduitConnect.java
new file mode 100644
index 0000000..52919f5
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/results/ConduitConnect.java
@@ -0,0 +1,46 @@
+//Copyright (C) 2014 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.googlesource.gerrit.plugins.its.phabricator.conduit.results;
+
+/**
+ * Models the result for a call to conduit.connect
+ * <p/>
+ * JSON looks like:
+ * <pre>
+ * {
+ *   "connectionID":4,
+ *   "sessionKey":"5jhpmsb3xgm1eupp7snzym7mtebndd7v4vv4ub6n",
+ *   "userPHID":"PHID-USER-h4n52fq2kt2v3a2qjyqh"
+ * }
+ * </pre>
+ * @author christian
+ *
+ */
+public class ConduitConnect {
+  private int connectionID;
+  private String sessionKey;
+  private String userPHID;
+
+  public int getConnectionId() {
+    return connectionID;
+  }
+
+  public String getSessionKey() {
+    return sessionKey;
+  }
+
+  public String getUserPhId() {
+    return userPHID;
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/results/ConduitPing.java b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/results/ConduitPing.java
new file mode 100644
index 0000000..5d409b3
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/results/ConduitPing.java
@@ -0,0 +1,28 @@
+//Copyright (C) 2014 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.googlesource.gerrit.plugins.its.phabricator.conduit.results;
+
+/**
+ * Models the result for a call to conduit.ping
+ * <p/>
+ * JSON is just the hostname of the instance. We wrap it in a proper object
+ * to make it a nicer Java citizen.
+ */
+public class ConduitPing {
+  private String hostname;
+
+  public String getHostname() {
+    return hostname;
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/results/ManiphestInfo.java b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/results/ManiphestInfo.java
new file mode 100644
index 0000000..472853e
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/results/ManiphestInfo.java
@@ -0,0 +1,51 @@
+//Copyright (C) 2014 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.googlesource.gerrit.plugins.its.phabricator.conduit.results;
+
+/**
+* Models the result for a call to maniphest.info
+* <p/>
+* JSON looks like:
+* <pre>
+* {
+*   "id":"48",
+*   "phid":"PHID-TASK-pemd324eosnymq3tdkyo",
+*   "authorPHID":"PHID-USER-na3one2sht11aone",
+*   "ownerPHID":null,
+*   "ccPHIDs":[
+*     "PHID-USER-h4n62fq2kt2v3a2qjyqh"
+*   ],
+*   "status":"open",
+*   "statusName":"Open",
+*   "isClosed":false,
+*   "priority": "Needs Triage",
+*   "priorityColor":"violet",
+*   "title":"QChris test task",
+*   "description":"",
+*   "projectPHIDs":[],
+*   "uri":"https://phab-01.wmflabs.org/T47",
+*   "auxiliary":{
+*     "std:maniphest:security_topic":"default",
+*     "isdc:sprint:storypoints":null
+*   },
+*   "objectName":"T47",
+*   "dateCreated":"1413484594",
+*   "dateModified":1413549869,
+*   "dependsOnTaskPHIDs":[]
+* }
+* </pre>
+*/
+public class ManiphestInfo extends Task {
+
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/results/ManiphestUpdate.java b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/results/ManiphestUpdate.java
new file mode 100644
index 0000000..055e8db
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/results/ManiphestUpdate.java
@@ -0,0 +1,51 @@
+//Copyright (C) 2014 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.googlesource.gerrit.plugins.its.phabricator.conduit.results;
+
+/**
+ * Models the result for a call to maniphest.update
+ * <p/>
+ * JSON looks like:
+ * <pre>
+ * {
+ *   "id":"48",
+ *   "phid":"PHID-TASK-pemd324eosnymq3tdkyo",
+ *   "authorPHID":"PHID-USER-na3one2sht11aone",
+ *   "ownerPHID":null,
+ *   "ccPHIDs":[
+ *     "PHID-USER-h4n62fq2kt2v3a2qjyqh"
+ *   ],
+ *   "status":"open",
+ *   "statusName":"Open",
+ *   "isClosed":false,
+ *   "priority": "Needs Triage",
+ *   "priorityColor":"violet",
+ *   "title":"QChris test task",
+ *   "description":"",
+ *   "projectPHIDs":[],
+ *   "uri":"https://phab-01.wmflabs.org/T47",
+ *   "auxiliary":{
+ *     "std:maniphest:security_topic":"default",
+ *     "isdc:sprint:storypoints":null
+ *   },
+ *   "objectName":"T47",
+ *   "dateCreated":"1413484594",
+ *   "dateModified":1413549869,
+ *   "dependsOnTaskPHIDs":[]
+ * }
+ * </pre>
+ */
+public class ManiphestUpdate extends Task {
+
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/results/Task.java b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/results/Task.java
new file mode 100644
index 0000000..f6e72b6
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/results/Task.java
@@ -0,0 +1,147 @@
+//Copyright (C) 2014 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.googlesource.gerrit.plugins.its.phabricator.conduit.results;
+
+import com.google.gson.JsonElement;
+
+/**
+ * Models the result for API methods returning Task information
+ * <p/>
+ * JSON looks like:
+ * <pre>
+ * {
+ *   "id":"48",
+ *   "phid":"PHID-TASK-pemd324eosnymq3tdkyo",
+ *   "authorPHID":"PHID-USER-na3one2sht11aone",
+ *   "ownerPHID":null,
+ *   "ccPHIDs":[
+ *     "PHID-USER-h4n62fq2kt2v3a2qjyqh"
+ *   ],
+ *   "status":"open",
+ *   "statusName":"Open",
+ *   "isClosed":false,
+ *   "priority": "Needs Triage",
+ *   "priorityColor":"violet",
+ *   "title":"QChris test task",
+ *   "description":"",
+ *   "projectPHIDs":[],
+ *   "uri":"https://phab-01.wmflabs.org/T47",
+ *   "auxiliary":{
+ *     "std:maniphest:security_topic":"default",
+ *     "isdc:sprint:storypoints":null
+ *   },
+ *   "objectName":"T47",
+ *   "dateCreated":"1413484594",
+ *   "dateModified":1413549869,
+ *   "dependsOnTaskPHIDs":[]
+ * }
+ * </pre>
+ */
+public class Task {
+  private int id;
+  private String phid;
+  private String authorPHID;
+  private String ownerPHID;
+  private JsonElement ccPHIDs;
+  private String status;
+  private String statusName;
+  private Boolean isClosed;
+  private String priority;
+  private String priorityColor;
+  private String title;
+  private String description;
+  private JsonElement projectPHIDs;
+  private String uri;
+  private JsonElement auxiliary;
+  private String objectName;
+  private String dateCreated;
+  private String dateModified;
+  private JsonElement dependsOnTaskPHIDs;
+
+  public int getId() {
+    return id;
+  }
+
+  public String getPhid() {
+    return phid;
+  }
+
+  public String getAuthorPHID() {
+    return authorPHID;
+  }
+
+  public String getOwnerPHID() {
+    return ownerPHID;
+  }
+
+  public JsonElement getCcPHIDs() {
+    return ccPHIDs;
+  }
+
+  public String getStatus() {
+    return status;
+  }
+
+  public String getStatusName() {
+    return statusName;
+  }
+
+  public Boolean getIsClosed() {
+    return isClosed;
+  }
+
+  public String getPriority() {
+    return priority;
+  }
+
+  public String getPriorityColor() {
+    return priorityColor;
+  }
+
+  public String getTitle() {
+    return title;
+  }
+
+  public String getDescription() {
+    return description;
+  }
+
+  public JsonElement getProjectPHIDs() {
+    return projectPHIDs;
+  }
+
+  public String getUri() {
+    return uri;
+  }
+
+  public JsonElement getAuxiliary() {
+    return auxiliary;
+  }
+
+  public String getObjectName() {
+    return objectName;
+  }
+
+  public String getDateCreated() {
+    return dateCreated;
+  }
+
+  public String getDateModified() {
+    return dateModified;
+  }
+
+  public JsonElement getDependsOnTaskPHIDs() {
+    return dependsOnTaskPHIDs;
+  }
+}
diff --git a/src/main/resources/Documentation/about.md b/src/main/resources/Documentation/about.md
index aa749b7..8e578c7 100644
--- a/src/main/resources/Documentation/about.md
+++ b/src/main/resources/Documentation/about.md
@@ -1,5 +1,5 @@
 `@PLUGIN@` is an [`its-base`][its-base] based plugin that (based on
-events in Gerrit) allows to take actions in Bugzilla. For example, it
-can add comments to bugs, or change status of bugs.
+events in Gerrit) allows to take actions in Phabricator. For example, it
+can add comments to bugs.
 
-[its-base]: https://gerrit-review.googlesource.com/#/admin/projects/plugins/its-base
\ No newline at end of file
+[its-base]: https://gerrit-review.googlesource.com/#/admin/projects/plugins/its-base
diff --git a/src/main/resources/Documentation/config-connectivity.md b/src/main/resources/Documentation/config-connectivity.md
index 1443f3b..a354c44 100644
--- a/src/main/resources/Documentation/config-connectivity.md
+++ b/src/main/resources/Documentation/config-connectivity.md
@@ -1,63 +1,36 @@
-Bugzilla connectivity
-=====================
+Phabricator connectivity
+========================
 
-In order for Gerrit to connect to Bugzilla,
-
-1. [make sure that your Bugzilla instance has the XML-RPC interface
-   enabled][rpc-enabled], and
-2. [provide url, user and password to @PLUGIN@][gerrit-configuration].
+In order for Gerrit to connect to Bugzilla, provide url, user, and certificate
+to @PLUGIN@.
 
 
-[rpc-enabled]: #rpc-enabled
-<a name="rpc-enabled">Checking XML-RPC availability</a>
--------------------------------------------------------
-
-Assuming the Bugzilla instance you want to connect to is at
-`http://my.bugzilla.instance.example.org/`, open
-
-```
-http://my.bugzilla.instance.example.org/xmlrpc.cgi
-```
-
-in your browser. If you get an empty page without errors, the XML-RPC
-interface is enabled. You can continue by [providing the needed Gerrit
-configuration][gerrit-configuration].
-
-If you get an error page saying
-
-```
-The XML-RPC Interface feature is not available in this Bugzilla.
-```
-
-the XML-RPC interface needs to be enabled. To do so, log in to the
-server that's running your Bugzilla instance, go to Bugzilla's
-directory, run
-
-```
-./checksetup.pl --check-modules
-```
-
-and install the missing modules. Then re-check the XML-RPC interface
-availability as above.
-
-[gerrit-configuration]: #gerrit-configuration
-<a name="gerrit-configuration">Gerrit configuration</a>
--------------------------------------------------------
-
-In order for @PLUGIN@ to connect to the XML-RPC service of your
-Bugzilla instance, url (without trailing "/xmlrpc.cgi") and
-credentials are required in your site's `etc/gerrit.config` or
-`etc/secure.config` under the `@PLUGIN@` section.
+In order for @PLUGIN@ to connect to your Phabricator instance, url (without
+trailing “/api”, “/conduit” or some such), user, and certificate are required in
+your site's `etc/gerrit.config` or `etc/secure.config` under the `@PLUGIN@`
+section.
 
 Example:
 
 ```
 [@PLUGIN@]
-  url=http://my.bugzilla.instance.example.org
+  url=http://my.phabricator.instance.example.org
   username=USERNAME_TO_CONNECT_TO_BUGZILLA
-  password=PASSWORD_FOR_ABOVE_USERNAME
+  certificate=CERTIFICATE_FOR_ABOVE_USERNAME
 ```
 
+Note that the certificate is not the user's password. It is … well … the users
+certificate … which is a 255 character long sequence of lowercase letters and
+digits. To get the certificate, run
+
+```
+arc install-certificate http://my.phabricator.instance.example.org
+```
+
+And follow the instructions on the screen. Once the procedure of logging in to
+the Phabricator instance in your browser and copying tokens around is complete,
+you'll find the certificate in `~/.arcrc`.
+
 [Back to @PLUGIN@ documentation index][index]
 
 [index]: index.html
diff --git a/src/main/resources/Documentation/config-rulebase-plugin-actions.md b/src/main/resources/Documentation/config-rulebase-plugin-actions.md
deleted file mode 100644
index b3e64be..0000000
--- a/src/main/resources/Documentation/config-rulebase-plugin-actions.md
+++ /dev/null
@@ -1,73 +0,0 @@
-@PLUGIN@-specific actions
-=========================
-
-In addition to the [basic actions][basic-actions], @PLUGIN@ also
-provides:
-
-[`set-resolution`][action-set-resolution]
-: sets the resolution of the issue
-
-[`set-status`][action-set-status]
-: sets the status of the issue
-
-[`set-status-and-resolution`][action-set-status-and-resolution]
-: sets both the status and resolution of the issue
-
-[basic-actions]: config-rulebase-common.html#actions
-
-[action-set-resolution]: #action-set-resolution
-### <a name="action-set-resolution">Action: set-resolution</a>
-
-The `set-resolution` action sets the issue's resolution. The first
-parameter is the resolution to set. So for example
-
-```
-  action = set-resolution WORKSFORME
-```
-
-sets the issue's status to `WORKSFORME`.
-
-If you want to set the status and the resolution, use the
-`set-status-and-resolution` action, so you can set both status and
-resolution in one go.
-
-
-
-[action-set-status]: #action-set-status
-### <a name="action-set-status">Action: set-status</a>
-
-The `set-status` action sets the issue's status. The first parameter
-is the status to set. So for example
-
-```
-  action = set-status CONFIRMED
-```
-
-sets the issue's status to `CONFIRMED`.
-
-If you want to set the status to a value that also requires a
-resolution, use the `set-status-and-resolution` action, so you can set
-both status and resolution in one go.
-
-
-
-[action-set-status-and-resolution]: #action-set-status-and-resolution
-### <a name="action-set-status-and-resolution">Action: set-status-and-resolution</a>
-
-The `set-status-and-resolution` action sets both the issue's status
-and it's resolution in one go. The first parameter denotes the status
-to set, the second parameter denotes the resolution to set.
-
-So for example
-
-```
-  action = set-status-and-resolution RESOLVED FIXED
-```
-
-sets the issue's status to `RESOLVED` and it's resolution to `FIXED`.
-
-
-
-[Back to @PLUGIN@ documentation index][index]
-
-[index]: index.html
\ No newline at end of file
diff --git a/src/test/java/com/googlesource/gerrit/plugins/hooks/bz/BugzillaItsFacadeTest.java b/src/test/java/com/googlesource/gerrit/plugins/hooks/bz/BugzillaItsFacadeTest.java
deleted file mode 100644
index 5c43601..0000000
--- a/src/test/java/com/googlesource/gerrit/plugins/hooks/bz/BugzillaItsFacadeTest.java
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright (C) 2013 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.googlesource.gerrit.plugins.hooks.bz;
-
-import static org.easymock.EasyMock.expect;
-
-import org.eclipse.jgit.lib.Config;
-
-import com.google.gerrit.extensions.annotations.PluginName;
-import com.google.gerrit.server.config.FactoryModule;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-import com.googlesource.gerrit.plugins.hooks.testutil.LoggingMockingTestCase;
-
-public class BugzillaItsFacadeTest extends LoggingMockingTestCase {
-  private Injector injector;
-  private Config serverConfig;
-
-  public void testCreateLinkForWebUiPlain() {
-    mockUnconnectableBugzilla();
-
-    replayMocks();
-
-    BugzillaItsFacade itsFacade = createBugzillaItsFacade();
-    String actual = itsFacade.createLinkForWebui("Test-Url", "Test-Text");
-
-    assertNotNull("Created link is null", actual);
-    assertTrue("Created link does not contain url",
-        actual.contains("Test-Url"));
-    assertTrue("Created link does not contain text",
-        actual.contains("Test-Text"));
-
-    assertUnconnectableBugzilla();
-  }
-
-  public void testCreateLinkForWebUiUrlEqualsText() {
-    mockUnconnectableBugzilla();
-
-    replayMocks();
-
-    BugzillaItsFacade itsFacade = createBugzillaItsFacade();
-    String actual = itsFacade.createLinkForWebui("Test-Url", "Test-Url");
-
-    assertNotNull("Created link is null", actual);
-    assertEquals("Created link does not match", "Test-Url", actual);
-
-    assertUnconnectableBugzilla();
-  }
-
-  public void testCreateLinkForWebUiUrlEqualsNull() {
-    mockUnconnectableBugzilla();
-
-    replayMocks();
-
-    BugzillaItsFacade itsFacade = createBugzillaItsFacade();
-    String actual = itsFacade.createLinkForWebui("Test-Url", null);
-
-    assertNotNull("Created link is null", actual);
-    assertEquals("Created link does not match", "Test-Url", actual);
-
-    assertUnconnectableBugzilla();
-  }
-
-  private BugzillaItsFacade createBugzillaItsFacade() {
-    return injector.getInstance(BugzillaItsFacade.class);
-  }
-
-  private void mockUnconnectableBugzilla() {
-    expect(serverConfig.getString("its-bugzilla",  null, "url"))
-    .andReturn("<no-url>").anyTimes();
-    expect(serverConfig.getString("its-bugzilla",  null, "username"))
-    .andReturn("none").anyTimes();
-    expect(serverConfig.getString("its-bugzilla",  null, "password"))
-    .andReturn("none").anyTimes();
-  }
-
-  private void assertUnconnectableBugzilla() {
-    assertLogMessageContains("Connecting to bugzilla");
-    assertLogMessageContains("Unable to connect");
-    assertLogMessageContains("Bugzilla is currently not available");
-  }
-
-  @Override
-  public void setUp() throws Exception {
-    super.setUp();
-
-    injector = Guice.createInjector(new TestModule());
-  }
-
-  private class TestModule extends FactoryModule {
-    @Override
-    protected void configure() {
-      serverConfig = createMock(Config.class);
-      bind(Config.class).annotatedWith(GerritServerConfig.class)
-          .toInstance(serverConfig);
-      bind(String.class).annotatedWith(PluginName.class)
-          .toInstance("its-bugzilla");
-    }
-  }
-}
\ No newline at end of file
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/phabricator/PhabricatorItsFacadeTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/phabricator/PhabricatorItsFacadeTest.java
new file mode 100644
index 0000000..9b22039
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/phabricator/PhabricatorItsFacadeTest.java
@@ -0,0 +1,105 @@
+// Copyright (C) 2014 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.googlesource.gerrit.plugins.its.phabricator;
+
+import static org.easymock.EasyMock.expect;
+
+import org.eclipse.jgit.lib.Config;
+
+import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.server.config.FactoryModule;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.googlesource.gerrit.plugins.its.testutil.LoggingMockingTestCase;
+
+public class PhabricatorItsFacadeTest extends LoggingMockingTestCase {
+  private Injector injector;
+  private Config serverConfig;
+
+  public void testCreateLinkForWebUiDifferentUrlAndText() {
+    mockUnconnectablePhabricator();
+
+    replayMocks();
+
+    PhabricatorItsFacade itsFacade = createPhabricatorItsFacade();
+    String actual = itsFacade.createLinkForWebui("Test-Url", "Test-Text");
+
+    assertEquals("[[Test-Url|Test-Text]]", actual);
+  }
+
+  public void testCreateLinkForWebUiSameUrlAndText() {
+    mockUnconnectablePhabricator();
+
+    replayMocks();
+
+    PhabricatorItsFacade itsFacade = createPhabricatorItsFacade();
+    String actual = itsFacade.createLinkForWebui("Test-Url", "Test-Url");
+
+    assertEquals("[[Test-Url]]", actual);
+  }
+
+  public void testCreateLinkForWebUiNullText() {
+    mockUnconnectablePhabricator();
+
+    replayMocks();
+
+    PhabricatorItsFacade itsFacade = createPhabricatorItsFacade();
+    String actual = itsFacade.createLinkForWebui("Test-Url", null);
+
+    assertEquals("[[Test-Url]]", actual);
+  }
+
+  public void testCreateLinkForWebUiEmptyText() {
+    mockUnconnectablePhabricator();
+
+    replayMocks();
+
+    PhabricatorItsFacade itsFacade = createPhabricatorItsFacade();
+    String actual = itsFacade.createLinkForWebui("Test-Url", "");
+
+    assertEquals("[[Test-Url]]", actual);
+  }
+
+  private PhabricatorItsFacade createPhabricatorItsFacade() {
+    return injector.getInstance(PhabricatorItsFacade.class);
+  }
+
+  private void mockUnconnectablePhabricator() {
+    expect(serverConfig.getString("its-phabricator",  null, "url"))
+    .andReturn("<no-url>").anyTimes();
+    expect(serverConfig.getString("its-phabricator",  null, "username"))
+    .andReturn("none").anyTimes();
+    expect(serverConfig.getString("its-phabricator",  null, "certificate"))
+    .andReturn("none").anyTimes();
+  }
+
+  @Override
+  public void setUp() throws Exception {
+    super.setUp();
+
+    injector = Guice.createInjector(new TestModule());
+  }
+
+  private class TestModule extends FactoryModule {
+    @Override
+    protected void configure() {
+      serverConfig = createMock(Config.class);
+      bind(Config.class).annotatedWith(GerritServerConfig.class)
+          .toInstance(serverConfig);
+      bind(String.class).annotatedWith(PluginName.class)
+          .toInstance("its-phabricator");
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/ConduitTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/ConduitTest.java
new file mode 100644
index 0000000..769d7fb
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/phabricator/conduit/ConduitTest.java
@@ -0,0 +1,402 @@
+//Copyright (C) 2014 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.googlesource.gerrit.plugins.its.phabricator.conduit;
+
+import static org.easymock.EasyMock.capture;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.resetToStrict;
+import static org.powermock.api.easymock.PowerMock.expectNew;
+
+import org.easymock.Capture;
+import org.junit.runner.RunWith;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import java.util.Map;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+import com.googlesource.gerrit.plugins.its.phabricator.conduit.results.ConduitConnect;
+import com.googlesource.gerrit.plugins.its.phabricator.conduit.results.ConduitPing;
+import com.googlesource.gerrit.plugins.its.phabricator.conduit.results.ManiphestInfo;
+import com.googlesource.gerrit.plugins.its.phabricator.conduit.results.ManiphestUpdate;
+import com.googlesource.gerrit.plugins.its.testutil.LoggingMockingTestCase;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(Conduit.class)
+public class ConduitTest extends LoggingMockingTestCase {
+  private final static String URL = "urlFoo";
+  private final static String USERNAME = "usernameFoo";
+  private final static String CERTIFICATE = "certificateFoo";
+
+  private ConduitConnection connection;
+
+  public void testConduitPingPass() throws Exception {
+    mockConnection();
+
+    expect(connection.call("conduit.ping"))
+      .andReturn(new JsonPrimitive("foo"))
+      .once();
+
+    replayMocks();
+
+    Conduit conduit = new Conduit(URL);
+
+    ConduitPing actual = conduit.conduitPing();
+
+    assertEquals("Hostname does not match", "foo", actual.getHostname());
+  }
+
+  public void testConduitPingConnectionFail() throws Exception {
+    mockConnection();
+
+    ConduitException conduitException = new ConduitException();
+
+    expect(connection.call("conduit.ping"))
+      .andThrow(conduitException)
+      .once();
+
+    replayMocks();
+
+    Conduit conduit = new Conduit(URL);
+
+    try {
+      conduit.conduitPing();
+      fail("no exception got thrown");
+    } catch (ConduitException e) {
+      assertSame(conduitException, e);
+    }
+  }
+
+  public void testConduitConnectPass() throws Exception {
+    mockConnection();
+
+    JsonObject ret = new JsonObject();
+    ret.add("sessionKey", new JsonPrimitive("KeyFoo"));
+
+    Capture<Map<String, Object>> paramsCapture = new Capture<Map<String, Object>>();
+
+    expect(connection.call(eq("conduit.connect"), capture(paramsCapture)))
+      .andReturn(ret)
+      .once();
+
+    replayMocks();
+
+    Conduit conduit = new Conduit(URL, USERNAME, CERTIFICATE);
+
+    ConduitConnect conduitConnect = conduit.conduitConnect();
+
+    Map<String, Object> params = paramsCapture.getValue();
+    assertEquals("Usernames do not match", USERNAME, params.get("user"));
+
+    assertEquals("Session keys don't match", "KeyFoo",
+        conduitConnect.getSessionKey());
+  }
+
+  public void testConduitConnectConnectionFail() throws Exception {
+    mockConnection();
+
+    ConduitException conduitException = new ConduitException();
+
+    Capture<Map<String, Object>> paramsCapture = new Capture<Map<String, Object>>();
+
+    expect(connection.call(eq("conduit.connect"), capture(paramsCapture)))
+      .andThrow(conduitException)
+      .once();
+
+    replayMocks();
+
+    Conduit conduit = new Conduit(URL, USERNAME, CERTIFICATE);
+
+    try {
+      conduit.conduitConnect();
+      fail("no exception got thrown");
+    } catch (ConduitException e) {
+      assertSame(conduitException, e);
+    }
+
+    Map<String, Object> params = paramsCapture.getValue();
+    assertEquals("Usernames do not match", USERNAME, params.get("user"));
+  }
+
+  public void testManiphestInfoPass() throws Exception {
+    mockConnection();
+
+    resetToStrict(connection);
+
+    JsonObject retConnect = new JsonObject();
+    retConnect.add("sessionKey", new JsonPrimitive("KeyFoo"));
+
+    Capture<Map<String, Object>> paramsCaptureConnect = new Capture<Map<String, Object>>();
+
+    expect(connection.call(eq("conduit.connect"), capture(paramsCaptureConnect)))
+      .andReturn(retConnect)
+      .once();
+
+    JsonObject retRelevant = new JsonObject();
+    retRelevant.add("id", new JsonPrimitive(42));
+
+    Capture<Map<String, Object>> paramsCaptureRelevant = new Capture<Map<String, Object>>();
+
+    expect(connection.call(eq("maniphest.info"), capture(paramsCaptureRelevant)))
+    .andReturn(retRelevant)
+    .once();
+
+    replayMocks();
+
+    Conduit conduit = new Conduit(URL, USERNAME, CERTIFICATE);
+
+    ManiphestInfo maniphestInfo = conduit.maniphestInfo(42);
+
+    Map<String, Object> paramsConnect = paramsCaptureConnect.getValue();
+    assertEquals("Usernames do not match", USERNAME, paramsConnect.get("user"));
+
+    Map<String, Object> paramsRelevant = paramsCaptureRelevant.getValue();
+    assertEquals("Task id is not set", 42, paramsRelevant.get("task_id"));
+
+    assertEquals("ManiphestInfo's id does not match", 42, maniphestInfo.getId());
+
+    assertLogMessageContains("Trying to start new session");
+  }
+
+  public void testManiphestInfoFailConnect() throws Exception {
+    mockConnection();
+
+    ConduitException conduitException = new ConduitException();
+
+    Capture<Map<String, Object>> paramsCapture = new Capture<Map<String, Object>>();
+
+    expect(connection.call(eq("conduit.connect"), capture(paramsCapture)))
+      .andThrow(conduitException)
+      .once();
+
+    replayMocks();
+
+    Conduit conduit = new Conduit(URL, USERNAME, CERTIFICATE);
+
+    try {
+      conduit.maniphestInfo(42);
+      fail("no exception got thrown");
+    } catch (ConduitException e) {
+      assertSame(conduitException, e);
+    }
+
+    Map<String, Object> params = paramsCapture.getValue();
+    assertEquals("Usernames do not match", USERNAME, params.get("user"));
+
+    assertLogMessageContains("Trying to start new session");
+  }
+
+  public void testManiphestInfoFailRelevant() throws Exception {
+    mockConnection();
+
+    resetToStrict(connection);
+
+    JsonObject retConnect = new JsonObject();
+    retConnect.add("sessionKey", new JsonPrimitive("KeyFoo"));
+
+    Capture<Map<String, Object>> paramsCaptureConnect = new Capture<Map<String, Object>>();
+
+    expect(connection.call(eq("conduit.connect"), capture(paramsCaptureConnect)))
+      .andReturn(retConnect)
+      .once();
+
+    ConduitException conduitException = new ConduitException();
+
+    Capture<Map<String, Object>> paramsCaptureRelevant = new Capture<Map<String, Object>>();
+
+    expect(connection.call(eq("maniphest.info"), capture(paramsCaptureRelevant)))
+      .andThrow(conduitException)
+      .once();
+
+    replayMocks();
+
+    Conduit conduit = new Conduit(URL, USERNAME, CERTIFICATE);
+
+    try {
+      conduit.maniphestInfo(42);
+      fail("no exception got thrown");
+    } catch (ConduitException e) {
+      assertSame(conduitException, e);
+    }
+
+    Map<String, Object> paramsConnect = paramsCaptureConnect.getValue();
+    assertEquals("Usernames do not match", USERNAME, paramsConnect.get("user"));
+
+    Map<String, Object> paramsRelevant = paramsCaptureRelevant.getValue();
+    assertEquals("Task id is not set", 42, paramsRelevant.get("task_id"));
+
+    assertLogMessageContains("Trying to start new session");
+  }
+
+  public void testManiphestUpdatePass() throws Exception {
+    mockConnection();
+
+    resetToStrict(connection);
+
+    JsonObject retConnect = new JsonObject();
+    retConnect.add("sessionKey", new JsonPrimitive("KeyFoo"));
+
+    Capture<Map<String, Object>> paramsCaptureConnect = new Capture<Map<String, Object>>();
+
+    expect(connection.call(eq("conduit.connect"), capture(paramsCaptureConnect)))
+      .andReturn(retConnect)
+      .once();
+
+    JsonObject retRelevant = new JsonObject();
+    retRelevant.add("id", new JsonPrimitive(42));
+
+    Capture<Map<String, Object>> paramsCaptureRelevant = new Capture<Map<String, Object>>();
+
+    expect(connection.call(eq("maniphest.update"), capture(paramsCaptureRelevant)))
+    .andReturn(retRelevant)
+    .once();
+
+    replayMocks();
+
+    Conduit conduit = new Conduit(URL, USERNAME, CERTIFICATE);
+
+    ManiphestUpdate maniphestUpdate = conduit.maniphestUpdate(42, "foo");
+
+    Map<String, Object> paramsConnect = paramsCaptureConnect.getValue();
+    assertEquals("Usernames do not match", USERNAME, paramsConnect.get("user"));
+
+    Map<String, Object> paramsRelevant = paramsCaptureRelevant.getValue();
+    assertEquals("Task id is not set", 42, paramsRelevant.get("id"));
+
+    assertEquals("ManiphestInfo's id does not match", 42, maniphestUpdate.getId());
+
+    assertLogMessageContains("Trying to start new session");
+  }
+
+  public void testManiphestUpdateFailConnect() throws Exception {
+    mockConnection();
+
+    ConduitException conduitException = new ConduitException();
+
+    Capture<Map<String, Object>> paramsCapture = new Capture<Map<String, Object>>();
+
+    expect(connection.call(eq("conduit.connect"), capture(paramsCapture)))
+      .andThrow(conduitException)
+      .once();
+
+    replayMocks();
+
+    Conduit conduit = new Conduit(URL, USERNAME, CERTIFICATE);
+
+    try {
+      conduit.maniphestUpdate(42, "foo");
+      fail("no exception got thrown");
+    } catch (ConduitException e) {
+      assertSame(conduitException, e);
+    }
+
+    Map<String, Object> params = paramsCapture.getValue();
+    assertEquals("Usernames do not match", USERNAME, params.get("user"));
+
+    assertLogMessageContains("Trying to start new session");
+  }
+
+  public void testManiphestUpdateFailRelevant() throws Exception {
+    mockConnection();
+
+    resetToStrict(connection);
+
+    JsonObject retConnect = new JsonObject();
+    retConnect.add("sessionKey", new JsonPrimitive("KeyFoo"));
+
+    Capture<Map<String, Object>> paramsCaptureConnect = new Capture<Map<String, Object>>();
+
+    expect(connection.call(eq("conduit.connect"), capture(paramsCaptureConnect)))
+      .andReturn(retConnect)
+      .once();
+
+    ConduitException conduitException = new ConduitException();
+
+    Capture<Map<String, Object>> paramsCaptureRelevant = new Capture<Map<String, Object>>();
+
+    expect(connection.call(eq("maniphest.update"), capture(paramsCaptureRelevant)))
+      .andThrow(conduitException)
+      .once();
+
+    replayMocks();
+
+    Conduit conduit = new Conduit(URL, USERNAME, CERTIFICATE);
+
+    try {
+      conduit.maniphestUpdate(42, "foo");
+      fail("no exception got thrown");
+    } catch (ConduitException e) {
+      assertSame(conduitException, e);
+    }
+
+    Map<String, Object> paramsConnect = paramsCaptureConnect.getValue();
+    assertEquals("Usernames do not match", USERNAME, paramsConnect.get("user"));
+
+    Map<String, Object> paramsRelevant = paramsCaptureRelevant.getValue();
+    assertEquals("Task id is not set", 42, paramsRelevant.get("id"));
+
+    assertLogMessageContains("Trying to start new session");
+  }
+
+  public void testConnectionReuse() throws Exception {
+    mockConnection();
+
+    resetToStrict(connection);
+
+    JsonObject retConnect = new JsonObject();
+    retConnect.add("sessionKey", new JsonPrimitive("KeyFoo"));
+
+    Capture<Map<String, Object>> paramsCaptureConnect = new Capture<Map<String, Object>>();
+
+    expect(connection.call(eq("conduit.connect"), capture(paramsCaptureConnect)))
+      .andReturn(retConnect)
+      .once();
+
+    JsonObject retRelevant = new JsonObject();
+    retRelevant.add("id", new JsonPrimitive(42));
+
+    Capture<Map<String, Object>> paramsCaptureRelevant = new Capture<Map<String, Object>>();
+
+    expect(connection.call(eq("maniphest.info"), capture(paramsCaptureRelevant)))
+    .andReturn(retRelevant)
+    .once();
+
+    replayMocks();
+
+    Conduit conduit = new Conduit(URL, USERNAME, CERTIFICATE);
+
+    ConduitConnect conduitConnect = conduit.conduitConnect();
+    ManiphestInfo maniphestInfo = conduit.maniphestInfo(42);
+
+    Map<String, Object> paramsConnect = paramsCaptureConnect.getValue();
+    assertEquals("Usernames do not match", USERNAME, paramsConnect.get("user"));
+
+    Map<String, Object> paramsRelevant = paramsCaptureRelevant.getValue();
+    assertEquals("Task id is not set", 42, paramsRelevant.get("task_id"));
+
+    assertEquals("Session keys don't match", "KeyFoo", conduitConnect.getSessionKey());
+
+    assertEquals("ManiphestInfo's id does not match", 42, maniphestInfo.getId());
+  }
+
+  private void mockConnection() throws Exception {
+    connection = createMock(ConduitConnection.class);;
+    expectNew(ConduitConnection.class, URL)
+      .andReturn(connection)
+      .once();
+  }
+}
\ No newline at end of file
diff --git a/src/test/java/com/googlesource/gerrit/plugins/hooks/testutil/LoggingMockingTestCase.java b/src/test/java/com/googlesource/gerrit/plugins/its/testutil/LoggingMockingTestCase.java
similarity index 94%
rename from src/test/java/com/googlesource/gerrit/plugins/hooks/testutil/LoggingMockingTestCase.java
rename to src/test/java/com/googlesource/gerrit/plugins/its/testutil/LoggingMockingTestCase.java
index 4bc25b8..a2d4d00 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/hooks/testutil/LoggingMockingTestCase.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/testutil/LoggingMockingTestCase.java
@@ -12,11 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.googlesource.gerrit.plugins.hooks.testutil;
+package com.googlesource.gerrit.plugins.its.testutil;
 
 import com.google.common.collect.Lists;
-import com.googlesource.gerrit.plugins.hooks.testutil.MockingTestCase;
-import com.googlesource.gerrit.plugins.hooks.testutil.log.LogUtil;
+import com.googlesource.gerrit.plugins.its.testutil.MockingTestCase;
+import com.googlesource.gerrit.plugins.its.testutil.log.LogUtil;
 
 import org.apache.log4j.spi.LoggingEvent;
 import org.junit.After;
diff --git a/src/test/java/com/googlesource/gerrit/plugins/hooks/testutil/MockingTestCase.java b/src/test/java/com/googlesource/gerrit/plugins/its/testutil/MockingTestCase.java
similarity index 98%
rename from src/test/java/com/googlesource/gerrit/plugins/hooks/testutil/MockingTestCase.java
rename to src/test/java/com/googlesource/gerrit/plugins/its/testutil/MockingTestCase.java
index 819b138..060269b 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/hooks/testutil/MockingTestCase.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/testutil/MockingTestCase.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.googlesource.gerrit.plugins.hooks.testutil;
+package com.googlesource.gerrit.plugins.its.testutil;
 
 import junit.framework.TestCase;
 
diff --git a/src/test/java/com/googlesource/gerrit/plugins/hooks/testutil/log/CollectionAppender.java b/src/test/java/com/googlesource/gerrit/plugins/its/testutil/log/CollectionAppender.java
similarity index 95%
rename from src/test/java/com/googlesource/gerrit/plugins/hooks/testutil/log/CollectionAppender.java
rename to src/test/java/com/googlesource/gerrit/plugins/its/testutil/log/CollectionAppender.java
index f63c7c3..8f21bde 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/hooks/testutil/log/CollectionAppender.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/testutil/log/CollectionAppender.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.googlesource.gerrit.plugins.hooks.testutil.log;
+package com.googlesource.gerrit.plugins.its.testutil.log;
 
 import com.google.common.collect.Lists;
 
diff --git a/src/test/java/com/googlesource/gerrit/plugins/hooks/testutil/log/LogUtil.java b/src/test/java/com/googlesource/gerrit/plugins/its/testutil/log/LogUtil.java
similarity index 88%
rename from src/test/java/com/googlesource/gerrit/plugins/hooks/testutil/log/LogUtil.java
rename to src/test/java/com/googlesource/gerrit/plugins/its/testutil/log/LogUtil.java
index 8ea559c..4fee52f 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/hooks/testutil/log/LogUtil.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/testutil/log/LogUtil.java
@@ -12,13 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.googlesource.gerrit.plugins.hooks.testutil.log;
+package com.googlesource.gerrit.plugins.its.testutil.log;
 
 import org.apache.log4j.LogManager;
 import org.apache.log4j.Logger;
 import org.apache.log4j.spi.LoggingEvent;
 
-import com.googlesource.gerrit.plugins.hooks.testutil.log.CollectionAppender;
+import com.googlesource.gerrit.plugins.its.testutil.log.CollectionAppender;
 
 
 import java.util.Collection;