IBM Rational Team Concert integration for Gerrit.

Implementation of the Gerrit ITS-Framework (hooks-its) 
for the support of IBM Rational Team Concert (RTC).

Provides the implementation for the RTC API for
the main hooks-its functions:

> Configuration wizard during the Gerrit init steps
> Commit validation based on Issue-ID regex in Git comment
> Ability to trigger RTC workflow actions based on Gerrit review
> Bi-directional association of Git commits to RTC Issues
> Feedback on RTC of the Gerrit review actions with hyperlinks
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..beef00d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+.classpath
+.project
+.settings
+target
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..11069ed
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+                              Apache License
+                        Version 2.0, January 2004
+                     http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+   "License" shall mean the terms and conditions for use, reproduction,
+   and distribution as defined by Sections 1 through 9 of this document.
+
+   "Licensor" shall mean the copyright owner or entity authorized by
+   the copyright owner that is granting the License.
+
+   "Legal Entity" shall mean the union of the acting entity and all
+   other entities that control, are controlled by, or are under common
+   control with that entity. For the purposes of this definition,
+   "control" means (i) the power, direct or indirect, to cause the
+   direction or management of such entity, whether by contract or
+   otherwise, or (ii) ownership of fifty percent (50%) or more of the
+   outstanding shares, or (iii) beneficial ownership of such entity.
+
+   "You" (or "Your") shall mean an individual or Legal Entity
+   exercising permissions granted by this License.
+
+   "Source" form shall mean the preferred form for making modifications,
+   including but not limited to software source code, documentation
+   source, and configuration files.
+
+   "Object" form shall mean any form resulting from mechanical
+   transformation or translation of a Source form, including but
+   not limited to compiled object code, generated documentation,
+   and conversions to other media types.
+
+   "Work" shall mean the work of authorship, whether in Source or
+   Object form, made available under the License, as indicated by a
+   copyright notice that is included in or attached to the work
+   (an example is provided in the Appendix below).
+
+   "Derivative Works" shall mean any work, whether in Source or Object
+   form, that is based on (or derived from) the Work and for which the
+   editorial revisions, annotations, elaborations, or other modifications
+   represent, as a whole, an original work of authorship. For the purposes
+   of this License, Derivative Works shall not include works that remain
+   separable from, or merely link (or bind by name) to the interfaces of,
+   the Work and Derivative Works thereof.
+
+   "Contribution" shall mean any work of authorship, including
+   the original version of the Work and any modifications or additions
+   to that Work or Derivative Works thereof, that is intentionally
+   submitted to Licensor for inclusion in the Work by the copyright owner
+   or by an individual or Legal Entity authorized to submit on behalf of
+   the copyright owner. For the purposes of this definition, "submitted"
+   means any form of electronic, verbal, or written communication sent
+   to the Licensor or its representatives, including but not limited to
+   communication on electronic mailing lists, source code control systems,
+   and issue tracking systems that are managed by, or on behalf of, the
+   Licensor for the purpose of discussing and improving the Work, but
+   excluding communication that is conspicuously marked or otherwise
+   designated in writing by the copyright owner as "Not a Contribution."
+
+   "Contributor" shall mean Licensor and any individual or Legal Entity
+   on behalf of whom a Contribution has been received by Licensor and
+   subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   copyright license to reproduce, prepare Derivative Works of,
+   publicly display, publicly perform, sublicense, and distribute the
+   Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   (except as stated in this section) patent license to make, have made,
+   use, offer to sell, sell, import, and otherwise transfer the Work,
+   where such license applies only to those patent claims licensable
+   by such Contributor that are necessarily infringed by their
+   Contribution(s) alone or by combination of their Contribution(s)
+   with the Work to which such Contribution(s) was submitted. If You
+   institute patent litigation against any entity (including a
+   cross-claim or counterclaim in a lawsuit) alleging that the Work
+   or a Contribution incorporated within the Work constitutes direct
+   or contributory patent infringement, then any patent licenses
+   granted to You under this License for that Work shall terminate
+   as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+   Work or Derivative Works thereof in any medium, with or without
+   modifications, and in Source or Object form, provided that You
+   meet the following conditions:
+
+   (a) You must give any other recipients of the Work or
+       Derivative Works a copy of this License; and
+
+   (b) You must cause any modified files to carry prominent notices
+       stating that You changed the files; and
+
+   (c) You must retain, in the Source form of any Derivative Works
+       that You distribute, all copyright, patent, trademark, and
+       attribution notices from the Source form of the Work,
+       excluding those notices that do not pertain to any part of
+       the Derivative Works; and
+
+   (d) If the Work includes a "NOTICE" text file as part of its
+       distribution, then any Derivative Works that You distribute must
+       include a readable copy of the attribution notices contained
+       within such NOTICE file, excluding those notices that do not
+       pertain to any part of the Derivative Works, in at least one
+       of the following places: within a NOTICE text file distributed
+       as part of the Derivative Works; within the Source form or
+       documentation, if provided along with the Derivative Works; or,
+       within a display generated by the Derivative Works, if and
+       wherever such third-party notices normally appear. The contents
+       of the NOTICE file are for informational purposes only and
+       do not modify the License. You may add Your own attribution
+       notices within Derivative Works that You distribute, alongside
+       or as an addendum to the NOTICE text from the Work, provided
+       that such additional attribution notices cannot be construed
+       as modifying the License.
+
+   You may add Your own copyright statement to Your modifications and
+   may provide additional or different license terms and conditions
+   for use, reproduction, or distribution of Your modifications, or
+   for any such Derivative Works as a whole, provided Your use,
+   reproduction, and distribution of the Work otherwise complies with
+   the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+   any Contribution intentionally submitted for inclusion in the Work
+   by You to the Licensor shall be under the terms and conditions of
+   this License, without any additional terms or conditions.
+   Notwithstanding the above, nothing herein shall supersede or modify
+   the terms of any separate license agreement you may have executed
+   with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+   names, trademarks, service marks, or product names of the Licensor,
+   except as required for reasonable and customary use in describing the
+   origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+   agreed to in writing, Licensor provides the Work (and each
+   Contributor provides its Contributions) on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+   implied, including, without limitation, any warranties or conditions
+   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+   PARTICULAR PURPOSE. You are solely responsible for determining the
+   appropriateness of using or redistributing the Work and assume any
+   risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+   whether in tort (including negligence), contract, or otherwise,
+   unless required by applicable law (such as deliberate and grossly
+   negligent acts) or agreed to in writing, shall any Contributor be
+   liable to You for damages, including any direct, indirect, special,
+   incidental, or consequential damages of any character arising as a
+   result of this License or out of the use or inability to use the
+   Work (including but not limited to damages for loss of goodwill,
+   work stoppage, computer failure or malfunction, or any and all
+   other commercial damages or losses), even if such Contributor
+   has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+   the Work or Derivative Works thereof, You may choose to offer,
+   and charge a fee for, acceptance of support, warranty, indemnity,
+   or other liability obligations and/or rights consistent with this
+   License. However, in accepting such obligations, You may act only
+   on Your own behalf and on Your sole responsibility, not on behalf
+   of any other Contributor, and only if You agree to indemnify,
+   defend, and hold each Contributor harmless for any liability
+   incurred by, or claims asserted against, such Contributor by reason
+   of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+   To apply the Apache License to your work, attach the following
+   boilerplate notice, with the fields enclosed by brackets "[]"
+   replaced with your own identifying information. (Don't include
+   the brackets!)  The text should be enclosed in the appropriate
+   comment syntax for the file format. We also recommend that a
+   file or class name and description of purpose be included on the
+   same "printed page" as the copyright notice for easier
+   identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+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.
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..e863808
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,372 @@
+<?xml version="1.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.
+-->
+<project
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+  xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>com.googlesource.gerrit.plugins.its</groupId>
+    <artifactId>hooks-its-parent</artifactId>
+    <version>2.6-SNAPSHOT</version>
+  </parent>
+  <artifactId>hooks-rtc</artifactId>
+  <name>Gerrit Code Review - IBM Rational Team Concert support</name>
+  <url>http://maven.apache.org</url>
+  <properties>
+    <Gerrit-ApiType>plugin</Gerrit-ApiType>
+    <Gerrit-ApiVersion>${project.version}</Gerrit-ApiVersion>
+    <Gerrit-ReloadMode>reload</Gerrit-ReloadMode>
+    <Gerrit-InitStep>com.googlesource.gerrit.plugins.hooks.rtc.InitRTC</Gerrit-InitStep>
+    <Gerrit-Module>com.googlesource.gerrit.plugins.hooks.rtc.RTCModule</Gerrit-Module>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+    <licenses>
+    <license>
+      <name>Apache License, 2.0</name>
+      <comments>
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
+      </comments>
+    </license>
+  </licenses>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>axistools-maven-plugin</artifactId>
+        <version>1.3</version>
+        <dependencies>
+          <dependency>
+            <groupId>axis</groupId>
+            <artifactId>axis</artifactId>
+            <version>1.3</version>
+          </dependency>
+        </dependencies>
+        <configuration>
+          <wsdlFiles>
+            <wsdlFile>jirasoapservice-v2.wsdl</wsdlFile>
+          </wsdlFiles>
+          <packageSpace>com.atlassian.jira.rpc.soap.client</packageSpace>
+        </configuration>
+        <executions>
+          <execution>
+            <id>wsdl2java-generation</id>
+            <phase>generate-sources</phase>
+            <goals>
+              <goal>wsdl2java</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <version>2.4</version>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-shade-plugin</artifactId>
+        <configuration>
+          <promoteTransitiveDependencies>true</promoteTransitiveDependencies>
+          <artifactSet>
+            <excludes>
+              <exclude>com.google.gerrit:*</exclude>
+              <exclude>org.slf4j:*</exclude>
+              <exclude>com.google.guava:*</exclude>
+              <exclude>org.eclipse.jgit:*</exclude>
+            </excludes>
+          </artifactSet>
+          <transformers>
+            <transformer
+              implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+              <manifestEntries>
+                <Gerrit-Module>com.gerritforge.jira.client.ItsModule</Gerrit-Module>
+                <Implementation-Vendor>GerritForge LLP</Implementation-Vendor>
+                <Implementation-URL>http://www.gerritforge.com</Implementation-URL>
+                <Implementation-Title>Plugin ${project.artifactId}</Implementation-Title>
+                <Implementation-Version>${project.version}</Implementation-Version>
+                <Gerrit-ApiType>${Gerrit-ApiType}</Gerrit-ApiType>
+                <Gerrit-ApiVersion>${Gerrit-ApiVersion}</Gerrit-ApiVersion>
+                <Gerrit-ReloadMode>${Gerrit-ReloadMode}</Gerrit-ReloadMode>
+                <Gerrit-InitStep>${Gerrit-InitStep}</Gerrit-InitStep>
+                <Gerrit-Module>${Gerrit-Module}</Gerrit-Module>
+              </manifestEntries>
+            </transformer>
+          </transformers>
+        </configuration>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>shade</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>com.google.code.gson</groupId>
+      <artifactId>gson</artifactId>
+      <version>2.1</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpclient</artifactId>
+      <version>4.2.4</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>commons-logging</groupId>
+      <artifactId>commons-logging</artifactId>
+      <version>1.0.4</version>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.3</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+      <version>1.2.17</version>
+    </dependency>
+    <dependency>
+      <groupId>commons-io</groupId>
+      <artifactId>commons-io</artifactId>
+      <version>2.1</version>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>hooks-its</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.11</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-all</artifactId>
+      <version>1.9.5</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/InitRTC.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/InitRTC.java
new file mode 100644
index 0000000..23be84b
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/InitRTC.java
@@ -0,0 +1,102 @@
+// 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.rtc;
+
+import java.io.IOException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gerrit.pgm.init.InitStep;
+import com.google.gerrit.pgm.init.Section;
+import com.google.gerrit.pgm.init.Section.Factory;
+import com.google.gerrit.pgm.util.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.rtc.network.RTCClient;
+import com.googlesource.gerrit.plugins.hooks.validation.ItsAssociationPolicy;
+
+/** Initialize the GitRepositoryManager configuration section. */
+@Singleton
+class InitRTC extends InitIts implements InitStep {
+  private static final Logger log = LoggerFactory.getLogger(InitRTC.class);
+  private static final String COMMENT_LINK_SECTION = "commentLink";
+  private final ConsoleUI ui;
+  private Section rtc;
+  private Section rtcComment;
+  private Factory sections;
+  private String rtcUrl;
+  private String rtcUsername;
+  private String rtcPassword;
+
+
+  @Inject
+  InitRTC(final ConsoleUI ui,  final Section.Factory sections) {
+    this.ui = ui;
+    this.sections = sections;
+  }
+
+  public void run() {
+    this.rtc = sections.get(RTCItsFacade.ITS_NAME_RTC, null);
+    this.rtcComment =
+        sections.get(COMMENT_LINK_SECTION, RTCItsFacade.ITS_NAME_RTC);
+
+    ui.message("\n");
+    ui.header("IBM Rational Team Concert connectivity");
+
+    boolean sslVerify = true;
+    do {
+      rtcUrl = enterRTCConnectivity();
+      if(rtcUrl != null) {
+        sslVerify = enterSSLVerify(rtc);
+      }
+    } while (rtcUrl != null
+        && (isConnectivityRequested(ui, rtcUrl) && !isRTCConnectSuccessful(rtcUrl, sslVerify)));
+
+    if (rtcUrl == null) {
+      return;
+    }
+
+    ui.header("Rational Team Concert issue-tracking association");
+    rtcComment.string("RTC Issue-Id regex", "match", "RTC#([0-9]+)");
+    rtcComment.set("html",
+        String.format("<a href=\"%s/browse/$1\">$1</a>", rtcUrl));
+    
+    rtcComment.select("RTC Issue-Id enforced in commit message", "association",
+        ItsAssociationPolicy.OPTIONAL);
+  }
+
+  public String enterRTCConnectivity() {
+    String url = rtc.string("RTC CCM URL (empty to skip)", "url", null);
+    if (url != null) {
+      rtcUsername = rtc.string("RTC username", "username", "");
+      rtcPassword = rtc.password("username", "password");
+    }
+    return url;
+  }
+
+  private boolean isRTCConnectSuccessful(String rtcUrl, boolean sslVerify) {
+    ui.message("Checking IBM Rational Team Concert connectivity ... ");
+    try {
+      RTCClient rtcClient = new RTCClient(rtcUrl, sslVerify, null);
+      rtcClient.sessionApi().login(rtcUsername, rtcPassword);
+      ui.message("[OK]\n");
+      return true;
+    } catch (IOException e) {
+      ui.message("*FAILED* (%s)\n", e.getLocalizedMessage());
+      return false;
+    }
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/RTCItsFacade.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/RTCItsFacade.java
new file mode 100644
index 0000000..54f1b96
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/RTCItsFacade.java
@@ -0,0 +1,148 @@
+// 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.rtc;
+
+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.server.config.GerritServerConfig;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.googlesource.gerrit.plugins.hooks.its.ItsFacade;
+import com.googlesource.gerrit.plugins.hooks.rtc.network.RTCClient;
+import com.googlesource.gerrit.plugins.hooks.rtc.workitems.RtcComment;
+import com.googlesource.gerrit.plugins.hooks.rtc.workitems.RtcRelatedLink;
+import com.googlesource.gerrit.plugins.hooks.rtc.workitems.RtcWorkItem;
+
+public class RTCItsFacade implements ItsFacade {
+
+  public static final String ITS_NAME_RTC = "rtc";
+
+  public static final String GERRIT_CONFIG_RTC_USERNAME = "username";
+  public static final String GERRIT_CONFIG_RTC_PASSWORD = "password";
+  public static final String GERRIT_CONFIG_CCM_URL = "url";
+  public static final String GERRIT_CONFIG_SSL_VERIFY = "sslVerify";
+
+  private Logger log = LoggerFactory.getLogger(RTCItsFacade.class);
+
+  Config gerritConfig;
+
+  private RTCClient client;
+
+  private Injector injector;
+
+  @Inject
+  public RTCItsFacade(@GerritServerConfig Config gerritConfig, Injector injector) {
+    try {
+      this.injector = injector;
+      this.gerritConfig = gerritConfig;
+      client().ping();
+      log.info("Connected to RTC at " + getRtcUrl() + " as admin user "
+          + getRtcUser());
+    } catch (Exception ex) {
+      log.warn("RTC is currently not available", ex);
+    }
+  }
+
+  @Override
+  public String name() {
+    return "RTC";
+  }
+
+  @Override
+  public void addComment(String itemId, String comment) throws IOException {
+    long workItem = Long.parseLong(itemId);
+    log.debug("Adding comment " + comment + " to workItem " + workItem);
+    RtcComment rtcComment =
+        client().workItemsApi().addComment(workItem, comment);
+    log.debug("Comment created: " + rtcComment);
+  }
+
+  @Override
+  public void addRelatedLink(String itemId, URL relatedUrl, String description)
+      throws IOException {
+    long workItem = Long.parseLong(itemId);
+    log.debug("Adding related link " + relatedUrl + " to workItem " + workItem
+        + " with description " + description);
+    RtcRelatedLink relatedLink =
+        client().workItemsApi().addRelated(workItem, relatedUrl, description);
+    log.debug("Related link " + relatedLink + " to workItem#" + workItem
+        + " CREATED");
+  }
+
+  @Override
+  public void performAction(String itemId, String actionName)
+      throws IOException {
+    long workItem = Long.parseLong(itemId);
+    log.debug("Executing action " + actionName + " on workItem " + workItem);
+    RtcWorkItem wip = client().workItemsApi().getWorkItem(workItem);
+    log.debug(" - loaded workitem " + wip);
+    wip = client().workItemsApi().performAction(wip, actionName);
+    log.debug("New item state: : " + wip);
+  }
+
+  @Override
+  public String healthCheck(Check check) throws IOException {
+    client.ping();
+    return "{\"status\"=\"ok\",\"system\"=\"RTC\",}";
+  }
+
+  private RTCClient client() throws IOException {
+
+    if (client == null) {
+      client = injector.getInstance(RTCClient.class);
+      client.setLoginCredentials(getRtcUser(), getRtcPassword());
+
+      log.debug("RTC Client pointing to " + getRtcUrl() + " as " + getRtcUser());
+    }
+
+    return client;
+  }
+
+  private String getRtcPassword() {
+    return gerritConfig.getString(ITS_NAME_RTC, null,
+        GERRIT_CONFIG_RTC_PASSWORD);
+  }
+
+  private String getRtcUser() {
+    return gerritConfig.getString(ITS_NAME_RTC, null,
+        GERRIT_CONFIG_RTC_USERNAME);
+  }
+
+  private String getRtcUrl() {
+    return gerritConfig.getString(ITS_NAME_RTC, null, GERRIT_CONFIG_CCM_URL);
+  }
+
+  private boolean getSslVerify() {
+    return gerritConfig.getBoolean(ITS_NAME_RTC, null,
+        GERRIT_CONFIG_SSL_VERIFY, true);
+  }
+
+  @Override
+  public String createLinkForWebui(String url, String text) {
+    return "<a href=\"" + url + "\">" + text + "</a>";
+  }
+
+  @Override
+  public boolean exists(String itemId) throws IOException {
+    long workItem = Long.parseLong(itemId);
+    RtcWorkItem item = client().workItemsApi().getWorkItem(workItem);
+    return item != null;
+  }
+
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/RTCModule.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/RTCModule.java
new file mode 100644
index 0000000..b5d85d7
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/RTCModule.java
@@ -0,0 +1,82 @@
+// 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.rtc;
+
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+
+import org.eclipse.jgit.lib.Config;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gerrit.common.ChangeListener;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.git.validators.CommitValidationListener;
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import com.googlesource.gerrit.plugins.hooks.its.ItsFacade;
+import com.googlesource.gerrit.plugins.hooks.its.ItsName;
+import com.googlesource.gerrit.plugins.hooks.rtc.filters.RTCAddComment;
+import com.googlesource.gerrit.plugins.hooks.rtc.filters.RTCAddRelatedLinkToChangeId;
+import com.googlesource.gerrit.plugins.hooks.rtc.filters.RTCAddRelatedLinkToGitWeb;
+import com.googlesource.gerrit.plugins.hooks.rtc.filters.RTCChangeState;
+import com.googlesource.gerrit.plugins.hooks.validation.ItsValidateComment;
+
+public class RTCModule extends AbstractModule {
+
+  private static final Logger LOG = LoggerFactory.getLogger(RTCModule.class);
+  private static final int THREAD_POOL_EXECUTORS = 10;
+
+  private final Config gerritConfig;
+
+  @Inject
+  public RTCModule(@GerritServerConfig final Config config) {
+    this.gerritConfig = config;
+  }
+
+  @Override
+  protected void configure() {
+    if (isConfigPresent(RTCItsFacade.ITS_NAME_RTC)) {
+      LOG.info("RTC is configured as ITS");
+      bind(ItsFacade.class).to(RTCItsFacade.class);
+
+      DynamicSet.bind(binder(), CommitValidationListener.class).to(
+          ItsValidateComment.class);
+
+      bind(ExecutorService.class).toInstance(
+          new ScheduledThreadPoolExecutor(THREAD_POOL_EXECUTORS));
+      bind(String.class).annotatedWith(ItsName.class).toInstance(
+          RTCItsFacade.ITS_NAME_RTC);
+
+      DynamicSet.bind(binder(), ChangeListener.class).to(
+          RTCAddRelatedLinkToChangeId.class);
+      DynamicSet.bind(binder(), ChangeListener.class).to(RTCAddComment.class);
+      DynamicSet.bind(binder(), ChangeListener.class).to(RTCChangeState.class);
+      DynamicSet.bind(binder(), ChangeListener.class).to(
+          RTCAddRelatedLinkToGitWeb.class);
+    }
+  }
+
+  private boolean isConfigPresent(String sectionName) {
+    Set<String> names = gerritConfig.getSections();
+    for (String name : names) {
+      if (name.equals(sectionName)) {
+        return true;
+      }
+    }
+    return false;
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/api/AbstractDeserializer.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/api/AbstractDeserializer.java
new file mode 100644
index 0000000..8b04a16
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/api/AbstractDeserializer.java
@@ -0,0 +1,56 @@
+// 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.rtc.api;
+
+import java.util.Calendar;
+
+import javax.xml.bind.DatatypeConverter;
+
+import com.google.gson.JsonObject;
+
+public class AbstractDeserializer {
+  
+  protected final String extractRdfResourceUrl(JsonObject json, String memberName) {
+    JsonObject rdf = json.getAsJsonObject(memberName);
+    return rdf.get("rdf:resource").getAsString();
+  }
+
+  protected final Calendar extractDateTime(JsonObject root, final String memberName) {
+    String txt = extractString(root, memberName);
+    return DatatypeConverter.parseDateTime(txt); 
+  }
+
+
+  protected final String extractString(JsonObject root, final String memberName) {
+    return root.get(memberName).getAsString();
+  }
+
+  protected final Long extractLong(JsonObject root, final String memberName) {
+    return root.get(memberName).getAsLong();
+  }
+
+  protected final String extractIdenFromRdfResource(JsonObject root, final String memberName) {
+    final String rdf = extractRdfResourceUrl(root, memberName);
+    if (rdf != null) {
+      return rdf.substring(rdf.lastIndexOf('/')+1);
+    }
+    else 
+      return null;
+  }
+
+  protected String extractRdf(JsonObject root) {
+    return extractString(root, "rdf:resource");
+  }
+
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/api/ResourceInvalidException.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/api/ResourceInvalidException.java
new file mode 100644
index 0000000..5816206
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/api/ResourceInvalidException.java
@@ -0,0 +1,25 @@
+// 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.rtc.api;
+
+import java.io.IOException;
+
+public class ResourceInvalidException extends IOException {
+
+  private static final long serialVersionUID = -2905132705439138798L;
+
+  public ResourceInvalidException(String name) {
+    super("Resource name "+name+" is not valid!");
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/api/ResourceModifiedException.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/api/ResourceModifiedException.java
new file mode 100644
index 0000000..65da3ce
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/api/ResourceModifiedException.java
@@ -0,0 +1,26 @@
+// 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.rtc.api;
+
+import java.io.IOException;
+import java.net.URI;
+
+public class ResourceModifiedException extends IOException {
+
+  private static final long serialVersionUID = -2905132705439138798L;
+
+  public ResourceModifiedException(URI uri) {
+    super("Resource modified at "+uri.toString()+ " - etag is not matching");
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/api/ResourceNotFoundException.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/api/ResourceNotFoundException.java
new file mode 100644
index 0000000..faa3d67
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/api/ResourceNotFoundException.java
@@ -0,0 +1,26 @@
+// 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.rtc.api;
+
+import java.io.IOException;
+import java.net.URI;
+
+public class ResourceNotFoundException extends IOException {
+
+  private static final long serialVersionUID = -2905132705439138798L;
+
+  public ResourceNotFoundException(URI uri) {
+    super("Resource not found at "+uri.toString());
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/api/RtcEntity.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/api/RtcEntity.java
new file mode 100644
index 0000000..684c7e0
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/api/RtcEntity.java
@@ -0,0 +1,43 @@
+// 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.rtc.api;
+
+
+public class RtcEntity extends RtcObject {
+
+  String id;
+  String title;
+
+  protected RtcEntity(String rdf) {
+    super(rdf);
+  }
+  
+  public RtcEntity(String id, String title) {
+    this(null, id, title);
+  }
+
+  public RtcEntity(String rdf, String id, String title) {
+    super(rdf);
+    this.id = id;
+    this.title = title;
+  }
+
+  public String getId() {
+    return id;
+  }
+
+  public String getTitle() {
+    return title;
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/api/RtcEntityDeserializer.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/api/RtcEntityDeserializer.java
new file mode 100644
index 0000000..d22f886
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/api/RtcEntityDeserializer.java
@@ -0,0 +1,39 @@
+// 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.rtc.api;
+
+import java.lang.reflect.Type;
+
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.googlesource.gerrit.plugins.hooks.rtc.api.AbstractDeserializer;
+
+public class RtcEntityDeserializer extends AbstractDeserializer implements JsonDeserializer<RtcEntity> {
+
+  @Override
+  public RtcEntity deserialize(JsonElement json, Type typeOfT,
+      JsonDeserializationContext context) throws JsonParseException {
+    
+    JsonObject root = json.getAsJsonObject();
+
+    RtcEntity result = new RtcEntity(extractRdf(root));
+    result.id = extractString(root, "dc:identifier");
+    result.title = extractString(root, "dc:title");
+    
+    return result;
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/api/RtcObject.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/api/RtcObject.java
new file mode 100644
index 0000000..1b37760
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/api/RtcObject.java
@@ -0,0 +1,48 @@
+// 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.rtc.api;
+
+import com.google.gson.Gson;
+import com.googlesource.gerrit.plugins.hooks.rtc.network.Transport;
+
+public class RtcObject {
+  private String rdf;
+  private String etag;
+  
+  public RtcObject() {
+    this(null, null);
+  }
+  
+  protected RtcObject(String rdf) {
+    this(rdf, Transport.etag.get());
+  }
+
+  protected RtcObject(String rdf, String etag) {
+    super();
+    this.rdf = rdf;
+    this.etag = etag;
+  }
+
+  public String getRdf() {
+    return rdf;
+  }
+  
+  public String getEtag() {
+    return etag;
+  }
+  
+  public String toString() {
+    return new Gson().toJson(this);
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/filters/ChangeListenerAsyncDecorator.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/filters/ChangeListenerAsyncDecorator.java
new file mode 100644
index 0000000..43a8ef5
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/filters/ChangeListenerAsyncDecorator.java
@@ -0,0 +1,72 @@
+// 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.rtc.filters;
+
+import java.util.ArrayList;
+import java.util.Queue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gerrit.common.ChangeListener;
+import com.google.gerrit.server.events.ChangeEvent;
+
+public class ChangeListenerAsyncDecorator<T extends ChangeListener> implements
+    ChangeListener {
+  private static final int MAX_PENDING_EVENTS = 1024;
+  private static final int MAX_BATCH_SIZE = 64;
+  private static final Logger log = LoggerFactory
+      .getLogger(ChangeListenerAsyncDecorator.class);
+  private T innerListener;
+  private final LinkedBlockingQueue<ChangeEvent> queue =
+      new LinkedBlockingQueue<ChangeEvent>(MAX_PENDING_EVENTS);
+  private ExecutorService executor;
+
+  public class ChangeRunner implements Runnable {
+    @Override
+    public void run() {
+      ArrayList<ChangeEvent> failedEvents = new ArrayList<ChangeEvent>();
+      for (int i = 0; !queue.isEmpty() && i < MAX_BATCH_SIZE; i++) {
+        ChangeEvent event = queue.remove();
+        try {
+          innerListener.onChangeEvent(event);
+        } catch (Throwable e) {
+          log.error("Execution of event " + event.getClass().getName() + "/"
+              + event.toString()
+              + " FAILED\nEvent requeued for later execution", event);
+          failedEvents.add(event);
+        }
+      }
+      
+      queue.addAll(failedEvents);
+    }
+  }
+
+  public ChangeListenerAsyncDecorator(T innerListener, ExecutorService executor) {
+    this.innerListener = innerListener;
+    this.executor = executor;
+  }
+
+  @Override
+  public void onChangeEvent(ChangeEvent event) {
+    queue.add(event);
+    executor.submit(new ChangeRunner());
+  }
+
+  public Queue<ChangeEvent> getQueue() {
+    return queue;
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/filters/RTCAddComment.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/filters/RTCAddComment.java
new file mode 100644
index 0000000..8e2d92d
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/filters/RTCAddComment.java
@@ -0,0 +1,32 @@
+// 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.rtc.filters;
+
+import java.util.concurrent.ExecutorService;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.googlesource.gerrit.plugins.hooks.workflow.GerritHookFilterAddComment;
+
+@Singleton
+public class RTCAddComment extends
+    ChangeListenerAsyncDecorator<GerritHookFilterAddComment> {
+
+  @Inject
+  public RTCAddComment(GerritHookFilterAddComment innerListener,
+      ExecutorService executor) {
+    super(innerListener, executor);
+  }
+
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/filters/RTCAddRelatedLinkToChangeId.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/filters/RTCAddRelatedLinkToChangeId.java
new file mode 100644
index 0000000..cfaad15
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/filters/RTCAddRelatedLinkToChangeId.java
@@ -0,0 +1,32 @@
+// 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.rtc.filters;
+
+import java.util.concurrent.ExecutorService;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.googlesource.gerrit.plugins.hooks.workflow.GerritHookFilterAddRelatedLinkToChangeId;
+
+@Singleton
+public class RTCAddRelatedLinkToChangeId extends
+    ChangeListenerAsyncDecorator<GerritHookFilterAddRelatedLinkToChangeId> {
+
+  @Inject
+  public RTCAddRelatedLinkToChangeId(
+      GerritHookFilterAddRelatedLinkToChangeId innerListener,
+      ExecutorService executor) {
+    super(innerListener, executor);
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/filters/RTCAddRelatedLinkToGitWeb.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/filters/RTCAddRelatedLinkToGitWeb.java
new file mode 100644
index 0000000..6f94e43
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/filters/RTCAddRelatedLinkToGitWeb.java
@@ -0,0 +1,32 @@
+// 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.rtc.filters;
+
+import java.util.concurrent.ExecutorService;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.googlesource.gerrit.plugins.hooks.workflow.GerritHookFilterAddRelatedLinkToGitWeb;
+
+@Singleton
+public class RTCAddRelatedLinkToGitWeb extends
+    ChangeListenerAsyncDecorator<GerritHookFilterAddRelatedLinkToGitWeb> {
+
+  @Inject
+  public RTCAddRelatedLinkToGitWeb(
+      GerritHookFilterAddRelatedLinkToGitWeb innerListener,
+      ExecutorService executor) {
+    super(innerListener, executor);
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/filters/RTCChangeState.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/filters/RTCChangeState.java
new file mode 100644
index 0000000..63a7f15
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/filters/RTCChangeState.java
@@ -0,0 +1,31 @@
+// 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.rtc.filters;
+
+import java.util.concurrent.ExecutorService;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.googlesource.gerrit.plugins.hooks.workflow.GerritHookFilterChangeState;
+
+@Singleton
+public class RTCChangeState extends
+    ChangeListenerAsyncDecorator<GerritHookFilterChangeState> {
+
+  @Inject
+  public RTCChangeState(GerritHookFilterChangeState innerListener,
+      ExecutorService executor) {
+    super(innerListener, executor);
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/network/AuthenticationException.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/network/AuthenticationException.java
new file mode 100644
index 0000000..99361c9
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/network/AuthenticationException.java
@@ -0,0 +1,24 @@
+// 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.rtc.network;
+
+import java.io.IOException;
+
+public class AuthenticationException extends IOException {
+  private static final long serialVersionUID = -4713503914827418174L;
+  
+  public AuthenticationException(String reason) {
+    super(reason);
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/network/CachableResourcesFactory.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/network/CachableResourcesFactory.java
new file mode 100644
index 0000000..4aba41d
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/network/CachableResourcesFactory.java
@@ -0,0 +1,72 @@
+// 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.rtc.network;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+
+public class CachableResourcesFactory {
+
+  public final Map<Class<?>, InternalFactory<?>> factories;
+  private Transport transport;
+ 
+  public CachableResourcesFactory(Transport transport) {
+    this.transport = transport;
+    this.factories = new ConcurrentHashMap<Class<?>, InternalFactory<?>>();
+  }
+  
+  @SuppressWarnings("unchecked")
+  public <T> T get(String url, Class<T> clazz) throws IOException {
+    return (T) getFactory(clazz).get(url);
+  }
+
+  @SuppressWarnings("unchecked")
+  private <T> InternalFactory<?> getFactory(Class<T> clazz) {
+    InternalFactory<?> factory = factories.get(clazz);
+    if (factory == null) {
+      factory = new InternalFactory(transport, clazz);
+      factories.put(clazz, factory);
+    }
+    return factory;
+  }
+
+  private static class InternalFactory<T> {
+
+    private final Transport transport;
+    private final Map<String, T> cache;
+    private Class<T> clazz;
+
+    private InternalFactory(Transport transport, Class<T> clazz) {
+      this.transport = transport;
+      this.clazz = clazz;
+      this.cache = new ConcurrentHashMap<String, T>();
+    }
+    
+    private T get(final String url) throws IOException {
+      T result = cache.get(url);
+      if (result == null) {
+        result = streamResourceFrom(url, clazz);
+        cache.put(url, result);
+      }
+      
+      return result;
+    }
+    
+    private T streamResourceFrom(final String url, final Class<T> clazz) throws IOException {
+      return transport.get(url+".json", clazz);
+    }
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/network/HttpPatch.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/network/HttpPatch.java
new file mode 100644
index 0000000..03b6ec5
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/network/HttpPatch.java
@@ -0,0 +1,38 @@
+// 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.rtc.network;
+
+import java.net.URI;
+
+import org.apache.http.client.methods.HttpPost;
+
+public class HttpPatch extends HttpPost {
+
+  public HttpPatch() {
+    super();
+  }
+
+  public HttpPatch(String uri) {
+    super(uri);
+  }
+
+  public HttpPatch(URI uri) {
+    super(uri);
+  }
+
+  @Override 
+  public String getMethod() { 
+    return "PATCH"; 
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/network/InvalidContentTypeException.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/network/InvalidContentTypeException.java
new file mode 100644
index 0000000..67bc7cd
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/network/InvalidContentTypeException.java
@@ -0,0 +1,24 @@
+// 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.rtc.network;
+
+import java.io.IOException;
+
+public class InvalidContentTypeException extends IOException {
+  private static final long serialVersionUID = -5010337914983644879L;
+  
+  public InvalidContentTypeException(String reason) {
+    super(reason);
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/network/RTCClient.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/network/RTCClient.java
new file mode 100644
index 0000000..0c57f60
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/network/RTCClient.java
@@ -0,0 +1,230 @@
+// 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.rtc.network;
+
+import java.io.IOException;
+import java.security.SecureRandom;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.HttpResponse;
+import org.apache.http.conn.scheme.PlainSocketFactory;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.impl.client.BasicCookieStore;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.client.DefaultRedirectHandler;
+import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HttpContext;
+import org.eclipse.jgit.lib.Config;
+
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.inject.Inject;
+import com.googlesource.gerrit.plugins.hooks.rtc.RTCItsFacade;
+import com.googlesource.gerrit.plugins.hooks.rtc.api.RtcEntity;
+import com.googlesource.gerrit.plugins.hooks.rtc.api.RtcEntityDeserializer;
+import com.googlesource.gerrit.plugins.hooks.rtc.session.SessionApi;
+import com.googlesource.gerrit.plugins.hooks.rtc.session.SessionApiImpl;
+import com.googlesource.gerrit.plugins.hooks.rtc.workitems.RtcComment;
+import com.googlesource.gerrit.plugins.hooks.rtc.workitems.RtcCommentDeserializer;
+import com.googlesource.gerrit.plugins.hooks.rtc.workitems.RtcRelatedLink;
+import com.googlesource.gerrit.plugins.hooks.rtc.workitems.RtcRelatedLinkDeserializer;
+import com.googlesource.gerrit.plugins.hooks.rtc.workitems.RtcWorkItem;
+import com.googlesource.gerrit.plugins.hooks.rtc.workitems.RtcWorkItemDeserializer;
+import com.googlesource.gerrit.plugins.hooks.rtc.workitems.RtcWorkflowAction;
+import com.googlesource.gerrit.plugins.hooks.rtc.workitems.RtcWorkflowActionDeserializer;
+import com.googlesource.gerrit.plugins.hooks.rtc.workitems.WorkItemsApi;
+import com.googlesource.gerrit.plugins.hooks.rtc.workitems.WorkItemsApiImpl;
+
+public class RTCClient {
+
+  private static Log LOG = LogFactory.getLog(RTCClient.class);
+
+  private String baseUrl;
+  private DefaultHttpClient httpclient;
+  private BasicCookieStore cookieStore;
+  private Gson gson;
+
+  private Transport transport;
+  private CachableResourcesFactory factory;
+
+  private SessionApi sessionApi;
+  private WorkItemsApi workItemsApi;
+
+  private boolean loggedIn;
+
+  private String rtcUser;
+
+  private String rtcPassword;
+
+  @Inject
+  public RTCClient(@GerritServerConfig Config config, RTCHttpParams httpParams)
+      throws IOException {
+    this(config.getString(RTCItsFacade.ITS_NAME_RTC, null, "url"), config
+        .getBoolean(RTCItsFacade.ITS_NAME_RTC, null, "sslVerify", true),
+        httpParams);
+  }
+
+  public RTCClient(String url, boolean sslVerify, HttpParams httpParams)
+      throws IOException {
+    super();
+
+    this.baseUrl = (url.endsWith("/") ? url.substring(url.length() - 1) : url);
+    if (httpParams == null) {
+      httpParams = new BasicHttpParams();
+    }
+    SchemeRegistry schemeRegistry = new SchemeRegistry();
+    schemeRegistry.register(new Scheme("http", PlainSocketFactory
+        .getSocketFactory(), 80));
+    this.httpclient =
+        new DefaultHttpClient(new ThreadSafeClientConnManager(httpParams,
+            schemeRegistry), httpParams);
+
+    this.transport = new Transport(this, baseUrl, httpclient, httpParams);
+    this.factory = new CachableResourcesFactory(transport);
+
+    GsonBuilder builder = new GsonBuilder();
+    builder.registerTypeAdapter(RtcWorkItem.class, new RtcWorkItemDeserializer(
+        factory));
+    builder.registerTypeAdapter(RtcComment.class, new RtcCommentDeserializer());
+    builder.registerTypeAdapter(RtcEntity.class, new RtcEntityDeserializer());
+    builder.registerTypeAdapter(RtcRelatedLink.class,
+        new RtcRelatedLinkDeserializer());
+    builder.registerTypeAdapter(RtcWorkflowAction.class,
+        new RtcWorkflowActionDeserializer());
+    gson = builder.create();
+    transport.setGson(gson);
+
+    setCookieStore();
+    setRedirectStategy();
+    setSSLTrustStrategy(sslVerify);
+
+    sessionApi = new SessionApiImpl(this, transport);
+    workItemsApi = new WorkItemsApiImpl(this, transport);
+  }
+
+  public boolean isLoggedIn() {
+    return loggedIn;
+  }
+
+  public SessionApi sessionApi() throws IOException {
+    collectSessionCookie();
+    return sessionApi;
+  }
+
+  public WorkItemsApi workItemsApi() throws IOException {
+    collectSessionCookie();
+    return workItemsApi;
+  }
+
+  private void collectSessionCookie() throws IOException {
+    if (cookieStore.getCookies().size() <= 0) {
+      LOG.debug("Initial collecting of session cookie...");
+      transport.get(baseUrl);
+      LOG.debug("Succesfully collected cookies: " + cookieStore.getCookies());
+    }
+  }
+
+  private void setCookieStore() {
+    cookieStore = new BasicCookieStore();
+    httpclient.setCookieStore(cookieStore);
+  }
+
+  private void setSSLTrustStrategy(boolean sslVerify) throws IOException {
+    try {
+      TrustManager[] trustAllCerts =
+          new TrustManager[] {new X509TrustManager() {
+            public X509Certificate[] getAcceptedIssuers() {
+              return new X509Certificate[0];
+            }
+
+            public void checkClientTrusted(X509Certificate[] certs,
+                String authType) {
+            }
+
+            public void checkServerTrusted(X509Certificate[] certs,
+                String authType) {
+            }
+          }};
+      SSLContext sc;
+
+      if (sslVerify) {
+        sc = SSLContext.getDefault();
+      } else {
+        sc = SSLContext.getInstance("SSL");
+        sc.init(null, trustAllCerts, new SecureRandom());
+      }
+
+      SSLSocketFactory sf = new SSLSocketFactory(sc);
+      sf.setHostnameVerifier(new AllowAllHostnameVerifier());
+      SchemeRegistry schemeRegistry =
+          httpclient.getConnectionManager().getSchemeRegistry();
+      schemeRegistry.register(new Scheme("https", sf, 443));
+    } catch (Exception any) {
+      throw new IOException(any);
+    }
+  }
+
+  private void setRedirectStategy() {
+    httpclient.setRedirectHandler(new DefaultRedirectHandler() {
+      @Override
+      public boolean isRedirectRequested(HttpResponse response,
+          HttpContext context) {
+        boolean isRedirect = super.isRedirectRequested(response, context);
+        if (!isRedirect) {
+          int responseCode = response.getStatusLine().getStatusCode();
+          if (responseCode == 301 || responseCode == 302) {
+            return true;
+          }
+        }
+        return isRedirect;
+      }
+    });
+  }
+
+  public Transport getTransportForTest() {
+    return transport;
+  }
+
+  public void setLoginCredentials(String rtcUser, String rtcPassword) throws IOException {
+    this.rtcUser = rtcUser;
+    this.rtcPassword = rtcPassword;
+  }
+
+  public void login() throws IOException {
+    sessionApi().login(rtcUser, rtcPassword);
+    loggedIn = true;
+  }
+
+  public void ping() throws IOException {
+    if (loggedIn) {
+      sessionApi().ping();
+    }
+  }
+
+  public void setLoggedIn(boolean b) {
+    loggedIn = false;
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/network/RTCHttpParams.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/network/RTCHttpParams.java
new file mode 100644
index 0000000..ed0d839
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/network/RTCHttpParams.java
@@ -0,0 +1,209 @@
+// 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.rtc.network;
+
+import java.util.HashMap;
+
+import org.apache.http.client.params.ClientPNames;
+import org.apache.http.conn.params.ConnManagerPNames;
+import org.apache.http.params.CoreConnectionPNames;
+import org.apache.http.params.CoreProtocolPNames;
+import org.apache.http.params.HttpParams;
+import org.eclipse.jgit.lib.Config;
+
+import com.google.common.base.Objects;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.inject.Inject;
+import com.googlesource.gerrit.plugins.hooks.rtc.RTCItsFacade;
+
+@SuppressWarnings("deprecation")
+public class RTCHttpParams implements HttpParams {
+  private static interface ParameterParser {
+    public Object parse(String value);
+  }
+
+  private static final ParameterParser TYPE_STRING = new ParameterParser() {
+    @Override
+    public Object parse(String value) {
+      return value;
+    }
+  };
+
+  private static final ParameterParser TYPE_LONG = new ParameterParser() {
+    @Override
+    public Object parse(String value) {
+      if (value == null) {
+        return null;
+      } else {
+        return Long.parseLong(value);
+      }
+    }
+  };
+
+  private static final ParameterParser TYPE_INT = new ParameterParser() {
+    @Override
+    public Object parse(String value) {
+      if (value == null) {
+        return null;
+      } else {
+        return Integer.parseInt(value);
+      }
+    }
+  };
+
+  private static final ParameterParser TYPE_BOOL = new ParameterParser() {
+    @Override
+    public Object parse(String value) {
+      if (value == null) {
+        return null;
+      } else {
+        return Boolean.parseBoolean(value);
+      }
+    }
+  };
+
+  private static final HashMap<String, ParameterParser> TYPES =
+      new HashMap<String, RTCHttpParams.ParameterParser>();
+  static {
+
+    TYPES.put(CoreConnectionPNames.SO_TIMEOUT, TYPE_INT);
+    TYPES.put(CoreConnectionPNames.TCP_NODELAY, TYPE_BOOL);
+    TYPES.put(CoreConnectionPNames.SOCKET_BUFFER_SIZE, TYPE_INT);
+    TYPES.put(CoreConnectionPNames.SO_LINGER, TYPE_INT);
+    TYPES.put(CoreConnectionPNames.SO_REUSEADDR, TYPE_BOOL);
+    TYPES.put(CoreConnectionPNames.CONNECTION_TIMEOUT, TYPE_INT);
+    TYPES.put(CoreConnectionPNames.STALE_CONNECTION_CHECK, TYPE_BOOL);
+    TYPES.put(CoreConnectionPNames.MAX_LINE_LENGTH, TYPE_INT);
+    TYPES.put(CoreConnectionPNames.MAX_HEADER_COUNT, TYPE_INT);
+    TYPES.put(CoreConnectionPNames.MIN_CHUNK_LIMIT, TYPE_INT);
+    TYPES.put(CoreConnectionPNames.SO_KEEPALIVE, TYPE_BOOL);
+
+    TYPES.put(ClientPNames.HANDLE_REDIRECTS, TYPE_BOOL);
+    TYPES.put(ClientPNames.REJECT_RELATIVE_REDIRECT, TYPE_BOOL);
+    TYPES.put(ClientPNames.MAX_REDIRECTS, TYPE_INT);
+    TYPES.put(ClientPNames.ALLOW_CIRCULAR_REDIRECTS, TYPE_BOOL);
+    TYPES.put(ClientPNames.HANDLE_AUTHENTICATION, TYPE_BOOL);
+    TYPES.put(ClientPNames.CONN_MANAGER_TIMEOUT, TYPE_LONG);
+
+    TYPES.put(CoreProtocolPNames.STRICT_TRANSFER_ENCODING, TYPE_BOOL);
+    TYPES.put(CoreProtocolPNames.USE_EXPECT_CONTINUE, TYPE_BOOL);
+    TYPES.put(CoreProtocolPNames.WAIT_FOR_CONTINUE, TYPE_INT);
+
+    TYPES.put(ConnManagerPNames.TIMEOUT, TYPE_LONG);
+    TYPES.put(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, TYPE_INT);
+  }
+  private Config config;
+
+  @Inject
+  public RTCHttpParams(@GerritServerConfig Config config) {
+    this.config = config;
+  }
+
+  private String getParamName(String name) {
+    StringBuilder camelisedName = new StringBuilder();
+    for (String namePart : name.split("[\\.-]")) {
+      if (camelisedName.length() > 0) {
+        camelisedName.append(Character.toUpperCase(namePart.charAt(0)));
+        camelisedName.append(namePart.substring(1, namePart.length()));
+      } else {
+        camelisedName.append(namePart);
+      }
+    }
+    return camelisedName.toString();
+  }
+
+  @Override
+  public Object getParameter(String name) {
+    String value =
+        config.getString(RTCItsFacade.ITS_NAME_RTC, null, getParamName(name));
+    return getParameterParser(name).parse(value);
+  }
+
+  private ParameterParser getParameterParser(String name) {
+    return Objects.firstNonNull(TYPES.get(name), TYPE_STRING);
+  }
+
+  @Override
+  public HttpParams setParameter(String name, Object value) {
+    return throwsNotSupported(HttpParams.class);
+  }
+
+  private <T> T throwsNotSupported(Class<T> clazz) {
+    throw new IllegalArgumentException("Method not supported");
+  }
+
+  @Override
+  @Deprecated
+  public HttpParams copy() {
+    return throwsNotSupported(HttpParams.class);
+  }
+
+  @Override
+  public boolean removeParameter(String name) {
+    return throwsNotSupported(Boolean.class);
+  }
+
+  @Override
+  public long getLongParameter(String name, long defaultValue) {
+    return config.getLong(RTCItsFacade.ITS_NAME_RTC, null, getParamName(name),
+        defaultValue);
+  }
+
+  @Override
+  public HttpParams setLongParameter(String name, long value) {
+    return throwsNotSupported(HttpParams.class);
+  }
+
+  @Override
+  public int getIntParameter(String name, int defaultValue) {
+    return config.getInt(RTCItsFacade.ITS_NAME_RTC, null, getParamName(name),
+        defaultValue);
+  }
+
+  @Override
+  public HttpParams setIntParameter(String name, int value) {
+    return throwsNotSupported(HttpParams.class);
+  }
+
+  @Override
+  public double getDoubleParameter(String name, double defaultValue) {
+    return throwsNotSupported(Double.class);
+  }
+
+  @Override
+  public HttpParams setDoubleParameter(String name, double value) {
+    return throwsNotSupported(HttpParams.class);
+  }
+
+  @Override
+  public boolean getBooleanParameter(String name, boolean defaultValue) {
+    return config.getBoolean(RTCItsFacade.ITS_NAME_RTC, null,
+        getParamName(name), defaultValue);
+  }
+
+  @Override
+  public HttpParams setBooleanParameter(String name, boolean value) {
+    return throwsNotSupported(HttpParams.class);
+  }
+
+  @Override
+  public boolean isParameterTrue(String name) {
+    return getBooleanParameter(name, false);
+  }
+
+  @Override
+  public boolean isParameterFalse(String name) {
+    return !isParameterTrue(name);
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/network/Transport.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/network/Transport.java
new file mode 100644
index 0000000..fb50058
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/network/Transport.java
@@ -0,0 +1,282 @@
+// 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.rtc.network;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.net.MalformedURLException;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HTTP;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.googlesource.gerrit.plugins.hooks.rtc.api.ResourceNotFoundException;
+
+public class Transport {
+
+  public static final String APP_JSON = "application/json";
+  public static final String ANY = "*/*";
+  public static final String APP_OSLC =
+      "application/x-oslc-cm-change-request+json";
+
+  private static final Log log = LogFactory.getLog(Transport.class);
+
+  public static final ThreadLocal<String> etag = new ThreadLocal<String>();
+
+  protected HttpClient httpclient;
+  protected Gson gson;
+  protected String baseUrl;
+  private HttpParams httpParams;
+  private RTCClient rtcClient;
+
+  public Transport(RTCClient rtcClient, String baseUrl, DefaultHttpClient httpclient, HttpParams httpParams) {
+    this.rtcClient = rtcClient;
+    this.baseUrl = baseUrl;
+    this.httpclient = httpclient;
+    this.httpParams = httpParams;
+  }
+
+  public void setGson(Gson gson) {
+    this.gson = gson;
+  }
+
+  public <T> T put(final String path, final Type typeOrClass, JsonObject data,
+      String etag) throws IOException {
+    HttpPut request = new HttpPut(toUri(path));
+    if (log.isDebugEnabled())
+      log.debug("Preparing PUT against " + request.getURI() + " using etag "
+          + etag + " and data " + data);
+    request.setEntity(new StringEntity(data.toString(), HTTP.UTF_8));
+    if (etag != null) request.addHeader("If-Match", etag);
+    return invoke(request, typeOrClass, APP_OSLC, APP_OSLC);
+  }
+
+  public <T> T patch(final String path, final Type typeOrClass,
+      JsonObject data, String etag) throws IOException {
+    HttpPatch request = newHttpPatch(path);
+    if (log.isDebugEnabled())
+      log.debug("Preparing PATCH against " + request.getURI() + " using etag "
+          + etag + " and data " + data);
+    request.setEntity(new StringEntity(data.toString(), HTTP.UTF_8));
+    if (etag != null) request.addHeader("If-Match", etag);
+    return invoke(request, typeOrClass, APP_OSLC, APP_OSLC);
+  }
+
+  public <T> T post(final String path, final Type typeOrClass,
+      String contentType,
+      final NameValuePair... params) throws IOException {
+    HttpPost request = newHttpPost(path);
+    if (log.isDebugEnabled())
+      log.debug("Preparing POST against " + request.getURI() + " using params "
+          + Arrays.asList(params));
+    List<NameValuePair> nameValuePairs = Arrays.asList(params);
+    request.setEntity(new UrlEncodedFormEntity(nameValuePairs, HTTP.UTF_8));
+    return invoke(request, typeOrClass, contentType, null);
+  }
+
+  public <T> T get(final String path, final Type typeOrClass)
+      throws IOException, MalformedURLException {
+    final HttpGet request = newHttpGet(path);
+    if (log.isDebugEnabled())
+      log.debug("Preparing GET against " + request.getURI());
+    return invoke(request, typeOrClass, APP_JSON, null);
+  }
+
+  public String get(final String path) throws IOException,
+      MalformedURLException {
+    final HttpGet request = newHttpGet(path);
+    if (log.isDebugEnabled())
+      log.debug("Preparing GET against " + request.getURI());
+    return invoke(request, null, ANY, null);
+  }
+
+  @SuppressWarnings("unchecked")
+  private synchronized <T> T invoke(HttpRequestBase request,
+      Object typeOrClass, String acceptType, String contentType)
+      throws IOException, ClientProtocolException, ResourceNotFoundException {
+
+    if (contentType != null) {
+      request.addHeader("Content-Type", contentType);
+    }
+
+    if (acceptType != null) {
+      request.addHeader("Accept", acceptType);
+    }
+
+    HttpResponse response = httpclient.execute(request);
+    try {
+      final int code = response.getStatusLine().getStatusCode();
+      if (code / 100 != 2) {
+        if (code == 404) {
+          log.debug("API call failed: " + response.getStatusLine());
+          throw new ResourceNotFoundException(request.getURI());
+        } else {
+          log.debug("API call failed: " + response.getStatusLine());
+          throw new IOException("API call failed! " + response.getStatusLine());
+        }
+      }
+
+      String responseContentTypeString = getResponseContentType(response);
+      String entityString = readEntityAsString(response);
+
+      if (!assertValidContentType(acceptType, responseContentTypeString)) {
+        log.error("Request to " + request.getURI()
+            + " failed because of an invalid content returned:\n"
+            + entityString);
+        rtcClient.setLoggedIn(false);
+        throw new InvalidContentTypeException("Wrong content type '"
+            + responseContentTypeString + "' in HTTP response (Expected: "
+            + acceptType + ")");
+      }
+
+      if (typeOrClass != null && acceptType.endsWith("json")
+          && responseContentTypeString.endsWith("json")) {
+        Transport.etag.set(extractEtag(response));
+        if (typeOrClass instanceof ParameterizedType) {
+          return gson.fromJson(entityString, (Type) typeOrClass);
+        } else {
+          return gson.fromJson(entityString, (Class<T>) typeOrClass);
+        }
+      } else if (typeOrClass != null && typeOrClass.equals(String.class)) {
+        return (T) entityString;
+      } else {
+        if (log.isDebugEnabled()) log.debug(entityString);
+        return null;
+      }
+    } finally {
+      consumeHttpEntity(response.getEntity());
+      Transport.etag.set(null);
+    }
+  }
+
+  private boolean assertValidContentType(String acceptType,
+      String responseContentTypeString) throws IOException {
+    if (acceptType == null) {
+      return true;
+    }
+    if (acceptType.endsWith("/*")) {
+      return true;
+    }
+    if (acceptType.split("/")[1].equalsIgnoreCase(responseContentTypeString
+        .split("/")[1])) {
+      return true;
+    }
+    return false;
+  }
+
+  public String getResponseContentType(HttpResponse response) {
+    Header contentType = response.getEntity().getContentType();
+    if (contentType == null) {
+      return null;
+    }
+
+    String contentTypeValue = contentType.getValue();
+    if (contentTypeValue == null) {
+      return null;
+    }
+
+    for (String contentTypeItem : contentTypeValue.split(";")) {
+      if (contentTypeItem.indexOf('/') >= 0) {
+        return contentTypeItem;
+      }
+    }
+    return null;
+  }
+
+  private String readEntityAsString(HttpResponse response)
+      throws IllegalStateException, IOException {
+    String charset = "utf-8";
+    Header[] contentTypes = response.getHeaders("Content-Type");
+    for (Header header : contentTypes) {
+      if (header.getName().equalsIgnoreCase("charset")) {
+        charset = header.getValue();
+      }
+    }
+
+    ByteArrayOutputStream responseOut = new ByteArrayOutputStream();
+    try {
+      IOUtils.copy(response.getEntity().getContent(), responseOut);
+    } finally {
+      responseOut.close();
+    }
+
+    return new String(responseOut.toByteArray(), Charset.forName(charset));
+  }
+
+  // We can't use the HTTP Client 4.2 EntityUtils.consume()
+  // because of compatibility issues with gerrit-pgm 2.5
+  // that includes httpclient 4.0
+  private void consumeHttpEntity(final HttpEntity entity) throws IOException {
+    if (entity == null) {
+      return;
+    }
+    if (entity.isStreaming()) {
+      InputStream instream = entity.getContent();
+      if (instream != null) {
+        instream.close();
+      }
+    }
+  }
+
+  private String extractEtag(HttpResponse response) {
+    final Header etagHeader = response.getFirstHeader("ETag");
+    return etagHeader == null ? null : etagHeader.getValue().substring(1,
+        etagHeader.getValue().length() - 1);
+  }
+
+  private HttpGet newHttpGet(final String path) throws MalformedURLException {
+    HttpGet get = new HttpGet(toUri(path));
+    get.setParams(httpParams);
+    return get;
+  }
+
+  private HttpPost newHttpPost(final String path) throws MalformedURLException {
+    return new HttpPost(toUri(path));
+  }
+
+  private HttpPatch newHttpPatch(final String path)
+      throws MalformedURLException {
+    return new HttpPatch(toUri(path));
+  }
+
+  private String toUri(final String path) throws MalformedURLException {
+    if (path.startsWith(baseUrl))
+      return path;
+    else
+      return baseUrl + path;
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/session/RtcSession.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/session/RtcSession.java
new file mode 100644
index 0000000..3648d5e
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/session/RtcSession.java
@@ -0,0 +1,44 @@
+// 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.rtc.session;
+
+import java.util.Arrays;
+import java.util.List;
+
+import com.googlesource.gerrit.plugins.hooks.rtc.api.RtcObject;
+
+
+public class RtcSession extends RtcObject {
+
+  private String userId;
+  private String[] roles;
+  
+  private RtcSession() {
+    super();
+  }
+
+  public RtcSession(String userId, String[] roles) {
+    this();
+    this.userId = userId;
+    this.roles = roles;
+  }
+
+  public String getUserId() {
+    return userId;
+  }
+
+  public List<String> getRoles() {
+    return Arrays.asList(roles);
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/session/SessionApi.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/session/SessionApi.java
new file mode 100644
index 0000000..3d464ab
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/session/SessionApi.java
@@ -0,0 +1,24 @@
+// 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.rtc.session;
+
+import java.io.IOException;
+
+public interface SessionApi {
+
+  void login(String username, String password) throws IOException;
+
+  void ping() throws IOException;
+
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/session/SessionApiImpl.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/session/SessionApiImpl.java
new file mode 100644
index 0000000..1eb5d68
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/session/SessionApiImpl.java
@@ -0,0 +1,53 @@
+// 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.rtc.session;
+
+import java.io.IOException;
+
+import org.apache.http.message.BasicNameValuePair;
+
+import com.googlesource.gerrit.plugins.hooks.rtc.network.AuthenticationException;
+import com.googlesource.gerrit.plugins.hooks.rtc.network.InvalidContentTypeException;
+import com.googlesource.gerrit.plugins.hooks.rtc.network.RTCClient;
+import com.googlesource.gerrit.plugins.hooks.rtc.network.Transport;
+import com.googlesource.gerrit.plugins.hooks.rtc.workitems.AbstractApiImpl;
+
+public class SessionApiImpl extends AbstractApiImpl implements SessionApi {
+
+  private final Transport transport;
+
+  public SessionApiImpl(RTCClient rtcClient, Transport transport) {
+    super(rtcClient);
+    this.transport=transport;
+  }
+
+  @Override
+  public synchronized void login(String username, String password)
+      throws IOException {
+    String result = transport.post("/authenticated/j_security_check", String.class,
+        Transport.ANY,
+        new BasicNameValuePair("j_username", username), 
+        new BasicNameValuePair("j_password", password));
+    
+    if(result.indexOf("net.jazz.web.app.authfailed") > 0) {
+      throw new AuthenticationException("User authentication failed");
+    }
+  }
+
+  @Override
+  public void ping() throws IOException {
+    loginIfNeeded();
+    transport.get("/authenticated/identity");
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/AbstractApiImpl.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/AbstractApiImpl.java
new file mode 100644
index 0000000..8fa6d93
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/AbstractApiImpl.java
@@ -0,0 +1,32 @@
+// 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.rtc.workitems;
+
+import java.io.IOException;
+
+import com.googlesource.gerrit.plugins.hooks.rtc.network.RTCClient;
+
+public class AbstractApiImpl {
+  protected RTCClient client;
+
+  public AbstractApiImpl(RTCClient client) {
+    this.client = client;
+  }
+  
+  protected void loginIfNeeded() throws IOException {
+    if(!client.isLoggedIn()) {
+      client.login();
+    }
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/RtcComment.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/RtcComment.java
new file mode 100644
index 0000000..cc8b779
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/RtcComment.java
@@ -0,0 +1,47 @@
+// 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.rtc.workitems;
+
+import java.util.Calendar;
+
+import com.googlesource.gerrit.plugins.hooks.rtc.api.RtcObject;
+
+public class RtcComment extends RtcObject {
+
+  String creator;
+  String contents;
+  Calendar created;
+  
+  RtcComment(String rdf) {
+    super(rdf);
+  }
+
+  public RtcComment(String creator, String contents) {
+    super();
+    this.creator = creator;
+    this.contents = contents;
+  }
+
+  public String getCreator() {
+    return creator;
+  }
+
+  public String getContents() {
+    return contents;
+  }
+
+  public Calendar getCreated() {
+    return created;
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/RtcCommentDeserializer.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/RtcCommentDeserializer.java
new file mode 100644
index 0000000..b088c77
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/RtcCommentDeserializer.java
@@ -0,0 +1,39 @@
+// 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.rtc.workitems;
+
+import java.lang.reflect.Type;
+
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.googlesource.gerrit.plugins.hooks.rtc.api.AbstractDeserializer;
+
+public class RtcCommentDeserializer extends AbstractDeserializer implements JsonDeserializer<RtcComment> {
+
+  @Override
+  public RtcComment deserialize(JsonElement json, Type typeOfT,
+      JsonDeserializationContext context) throws JsonParseException {
+    
+    JsonObject root = json.getAsJsonObject();
+
+    RtcComment result = new RtcComment(extractRdf(root));
+    result.contents = extractString(root, "dc:description");
+    result.creator = extractIdenFromRdfResource(root, "dc:creator");
+    
+    return result;
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/RtcRelatedLink.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/RtcRelatedLink.java
new file mode 100644
index 0000000..0016f3f
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/RtcRelatedLink.java
@@ -0,0 +1,30 @@
+// 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.rtc.workitems;
+
+import java.net.URL;
+
+import com.googlesource.gerrit.plugins.hooks.rtc.api.RtcObject;
+
+public class RtcRelatedLink extends RtcObject {
+
+  public RtcRelatedLink(URL resourceUrl, String label2) {
+    this.resource = resourceUrl;
+    this.label = label2;
+  }
+
+  public URL resource;
+  public String label;
+
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/RtcRelatedLinkDeserializer.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/RtcRelatedLinkDeserializer.java
new file mode 100644
index 0000000..9f542d0
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/RtcRelatedLinkDeserializer.java
@@ -0,0 +1,47 @@
+// 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.rtc.workitems;
+
+import java.lang.reflect.Type;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.googlesource.gerrit.plugins.hooks.rtc.api.AbstractDeserializer;
+
+public class RtcRelatedLinkDeserializer extends AbstractDeserializer
+    implements JsonDeserializer<RtcRelatedLink> {
+
+  @Override
+  public RtcRelatedLink deserialize(JsonElement json, Type typeOfT,
+      JsonDeserializationContext context) throws JsonParseException {
+    
+    JsonObject jsonObj = json.getAsJsonObject();
+    String resourceUrlString = jsonObj.get("rdf:resource").getAsString();
+    try {
+      URL resourceUrl = new URL(resourceUrlString);
+      String label = jsonObj.get("oslc_cm:label").getAsString();
+      
+      return new RtcRelatedLink(resourceUrl, label);
+      
+    } catch (MalformedURLException e) {
+      throw new JsonParseException("Invalid rdf:resource URL '" + resourceUrlString + "'", e);
+    }
+  }
+
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/RtcWorkItem.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/RtcWorkItem.java
new file mode 100644
index 0000000..0963dc5
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/RtcWorkItem.java
@@ -0,0 +1,84 @@
+// 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.rtc.workitems;
+
+import java.util.Calendar;
+
+import com.googlesource.gerrit.plugins.hooks.rtc.api.RtcEntity;
+import com.googlesource.gerrit.plugins.hooks.rtc.api.RtcObject;
+
+
+public class RtcWorkItem extends RtcObject {
+
+  Long id;
+  String title;
+  String subject;
+  String creator;
+  String ownedby;
+  String description;
+  Calendar created;
+  RtcEntity status;
+  RtcEntity severity;
+  RtcEntity priority;
+  RtcEntity type;
+//  private List<RtcComment> comments;    //
+  
+  RtcWorkItem(String rdf) {
+    super(rdf);
+  }
+
+  public Long getId() {
+    return id;
+  }
+
+  public String getTitle() {
+    return title;
+  }
+
+  public String getSubject() {
+    return subject;
+  }
+
+  public String getCreator() {
+    return creator;
+  }
+
+  public String getOwnedby() {
+    return ownedby;
+  }
+
+  public String getDescription() {
+    return description;
+  }
+
+  public Calendar getCreated() {
+    return created;
+  }
+
+  public RtcEntity getStatus() {
+    return status;
+  }
+
+  public RtcEntity getSeverity() {
+    return severity;
+  }
+
+  public RtcEntity getPriority() {
+    return priority;
+  }
+
+  public RtcEntity getType() {
+    return type;
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/RtcWorkItemDeserializer.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/RtcWorkItemDeserializer.java
new file mode 100644
index 0000000..c7123e2
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/RtcWorkItemDeserializer.java
@@ -0,0 +1,67 @@
+// 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.rtc.workitems;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.googlesource.gerrit.plugins.hooks.rtc.api.AbstractDeserializer;
+import com.googlesource.gerrit.plugins.hooks.rtc.api.RtcEntity;
+import com.googlesource.gerrit.plugins.hooks.rtc.network.CachableResourcesFactory;
+
+public class RtcWorkItemDeserializer extends AbstractDeserializer implements JsonDeserializer<RtcWorkItem> {
+
+  private CachableResourcesFactory factory;
+
+  public RtcWorkItemDeserializer(CachableResourcesFactory factory) {
+    this.factory = factory;
+  }
+
+  @Override
+  public RtcWorkItem deserialize(JsonElement json, Type typeOfT,
+      JsonDeserializationContext context) throws JsonParseException {
+    
+    JsonObject root = json.getAsJsonObject();
+
+    RtcWorkItem result = new RtcWorkItem(extractRdf(root));
+    result.id = extractLong(root, "dc:identifier");
+    result.title = extractString(root,"dc:title");
+    result.subject = extractString(root,"dc:subject");
+    result.creator = extractIdenFromRdfResource(root, "dc:creator");
+    result.ownedby = extractIdenFromRdfResource(root, "rtc_cm:ownedBy");
+    result.status = extractResource(root, "rtc_cm:state", RtcEntity.class);
+    result.severity = extractResource(root, "oslc_cm:severity", RtcEntity.class);
+    result.priority = extractResource(root, "oslc_cm:priority", RtcEntity.class);
+    result.type = extractResource(root, "dc:type", RtcEntity.class);
+    
+    
+    return result;
+  }
+
+  private RtcEntity extractResource(JsonObject root, final String memberName,
+      final Class<RtcEntity> clazz) {
+    String url = extractRdfResourceUrl(root, memberName);
+    try {
+      return factory.get(url, clazz);
+    } catch (IOException e) {
+      throw new JsonParseException("Unable to parse resource from url "+url+" using class "+clazz, e);
+    }
+  }
+
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/RtcWorkflowAction.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/RtcWorkflowAction.java
new file mode 100644
index 0000000..e322a3f
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/RtcWorkflowAction.java
@@ -0,0 +1,32 @@
+// 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.rtc.workitems;
+
+import com.googlesource.gerrit.plugins.hooks.rtc.api.RtcEntity;
+
+public class RtcWorkflowAction extends RtcEntity {
+
+  String resultStateRdf;
+  
+  public RtcWorkflowAction(String id, String title) {
+    super(id, title);
+  }
+  public RtcWorkflowAction(String rdf, String id, String title) {
+    super(rdf, id, title);
+  }
+  
+  public String getResultStateRdf() {
+    return resultStateRdf;
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/RtcWorkflowActionDeserializer.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/RtcWorkflowActionDeserializer.java
new file mode 100644
index 0000000..3fcc10f
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/RtcWorkflowActionDeserializer.java
@@ -0,0 +1,41 @@
+// 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.rtc.workitems;
+
+import java.lang.reflect.Type;
+
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.googlesource.gerrit.plugins.hooks.rtc.api.AbstractDeserializer;
+
+public class RtcWorkflowActionDeserializer extends AbstractDeserializer implements JsonDeserializer<RtcWorkflowAction> {
+
+  @Override
+  public RtcWorkflowAction deserialize(JsonElement json, Type typeOfT,
+      JsonDeserializationContext context) throws JsonParseException {
+    
+    JsonObject root = json.getAsJsonObject();
+
+    final String rdf = extractRdf(root);
+    final String id = extractString(root, "dc:identifier");
+    final String title = extractString(root, "dc:title");
+    RtcWorkflowAction result = new RtcWorkflowAction(rdf, id, title);
+    result.resultStateRdf = extractRdf(root.get("rtc_cm:resultState").getAsJsonObject());
+    
+    return result;
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/WorkItemsApi.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/WorkItemsApi.java
new file mode 100644
index 0000000..9706341
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/WorkItemsApi.java
@@ -0,0 +1,37 @@
+// 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.rtc.workitems;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.List;
+
+import com.googlesource.gerrit.plugins.hooks.rtc.api.RtcEntity;
+
+public interface WorkItemsApi {
+
+  public RtcWorkItem getWorkItem(long id) throws IOException;
+
+  public RtcComment addComment(long id, String text) throws IOException;
+
+  public RtcRelatedLink addRelated(long id, URL relatedUrl, String text)
+      throws IOException;
+
+  public List<RtcEntity> getAvailableStatuses(RtcWorkItem wip) throws IOException;
+
+  public List<RtcWorkflowAction> getAvailableActions(RtcWorkItem wip) throws IOException;
+
+  public RtcWorkItem performAction(RtcWorkItem wip, String newStatusTitle) throws IOException;
+
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/WorkItemsApiImpl.java b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/WorkItemsApiImpl.java
new file mode 100644
index 0000000..7e7af5b
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/hooks/rtc/workitems/WorkItemsApiImpl.java
@@ -0,0 +1,141 @@
+// 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.rtc.workitems;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.net.URL;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.http.message.BasicNameValuePair;
+
+import com.google.gson.JsonObject;
+import com.google.gson.reflect.TypeToken;
+import com.googlesource.gerrit.plugins.hooks.rtc.api.ResourceInvalidException;
+import com.googlesource.gerrit.plugins.hooks.rtc.api.RtcEntity;
+import com.googlesource.gerrit.plugins.hooks.rtc.network.RTCClient;
+import com.googlesource.gerrit.plugins.hooks.rtc.network.Transport;
+
+public class WorkItemsApiImpl extends AbstractApiImpl implements WorkItemsApi {
+
+  private Transport transport;
+  public WorkItemsApiImpl(RTCClient rtcClient, Transport transport) {
+    super(rtcClient);
+    this.transport = transport;
+  }
+
+  @Override
+  public synchronized RtcWorkItem getWorkItem(long id) throws IOException {
+    loginIfNeeded();
+    return transport.get("/oslc/workitems/" + id + ".json", RtcWorkItem.class);
+  }
+
+  @Override
+  public synchronized RtcComment addComment(long id, String text)
+      throws IOException {
+    loginIfNeeded();
+    return transport.post("/oslc/workitems/" + id + "/rtc_cm:comments",
+        RtcComment.class, 
+        Transport.APP_JSON,
+        new BasicNameValuePair("dc:description", text));
+  }
+
+  @Override
+  public synchronized RtcRelatedLink addRelated(long id, URL relatedUrl,
+      String text) throws IOException {
+    loginIfNeeded();
+    return transport
+        .post(
+            "/oslc/workitems/"
+                + id
+                + "/rtc_cm:com.ibm.team.workitem.linktype.relatedartifact.relatedArtifact",
+            RtcRelatedLink.class, 
+            Transport.APP_JSON,
+            new BasicNameValuePair("rdf:resource",
+                relatedUrl.toExternalForm()), new BasicNameValuePair(
+                "oslc_cm:label", text));
+  }
+
+  @Override
+  public List<RtcEntity> getAvailableStatuses(RtcWorkItem wip)
+      throws IOException {
+    loginIfNeeded();
+    final String rdf = wip.getStatus().getRdf();
+    final String url = rdf.substring(0, rdf.lastIndexOf('/')) + ".json";
+    final Type type = new TypeToken<Collection<RtcEntity>>() {}.getType();
+    return transport.get(url, type);
+  }
+
+  @Override
+  public List<RtcWorkflowAction> getAvailableActions(RtcWorkItem wip)
+      throws IOException {
+    loginIfNeeded();
+    final String rdf = wip.getStatus().getRdf();
+    final String url = rdf.substring(0, rdf.lastIndexOf('/')).replace("states", "actions")+ ".json";
+    final Type type = new TypeToken<Collection<RtcWorkflowAction>>() {}.getType();
+    return transport.get(url, type);
+  }
+
+  @Override
+  public RtcWorkItem performAction(RtcWorkItem wip, String actionTitle)
+      throws IOException {
+    loginIfNeeded();
+    RtcWorkflowAction action = null;
+    List<RtcWorkflowAction> allActions = getAvailableActions(wip);
+    for (RtcWorkflowAction anAction : allActions) {
+      if (anAction.getTitle().equalsIgnoreCase(actionTitle)) {
+        action = anAction;
+        break;
+      }
+    }
+
+    if (action == null) {
+      throw new ResourceInvalidException(actionTitle);
+    }
+    
+    final String url = "/oslc/workitems/" + wip.getId()+"?_action="+action.getId();
+
+    JsonObject rdf = new JsonObject();
+    rdf.addProperty("rdf:resource", action.getResultStateRdf());
+    JsonObject data = new JsonObject();
+    data.add("rtc_cm:state", rdf);
+
+    return transport.patch(url, RtcWorkItem.class, data, wip.getEtag());
+  }
+  
+/*
+  Object foo()  
+  {   
+//    String s = wip.getStatus().getRdf();
+//    JsonObject rdf = new JsonObject();
+//    rdf.addProperty("rdf:resource", s);
+//    JsonObject data = new JsonObject();
+//    data.add("rtc_cm:state", rdf);
+
+    JsonObject rdf = new JsonObject();
+    final String url = newStatus.getRdf();
+    rdf.addProperty("rdf:resource", url);
+    JsonObject data = new JsonObject();
+    data.add("rtc_cm:state", rdf);
+
+//    JsonObject data = new JsonObject();
+//    data.addProperty("dc:title", "Hey, wombats! Is this so called 'patch' really working???");
+
+    System.err.println(data);
+    return transport.patch("/oslc/workitems/" + wip.getId(), RtcWorkItem.class, data, wip.getEtag());
+//    return transport.put("/oslc/workitems/" + wip.getId()+"?oslc_cm.properties=rtc_cm:state", RtcWorkItem.class, data, wip.getEtag());
+  }
+*/
+}
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
new file mode 100644
index 0000000..0c0cece
--- /dev/null
+++ b/src/main/resources/Documentation/config.md
@@ -0,0 +1,211 @@
+Plugin @PLUGIN@
+===============
+
+This plugin allows to associate IBM Rational Team Concert (RTC) issues to Git commits / Gerrit
+Changes using the Gerrit ChangeListener and CommitValidator interfaces.
+
+Main integration points provided are:
+
+1. Commit validation (synchronous). It is the ability to introduce an additional validation
+step to the Git commits pushed to Gerrit either directly to the target branch or submitted
+for Code Review.
+2. Commit association and workflow (asynchronous). It is the bi-directional integration between
+the RTC issue and its corresponding Git commit / Gerrit Change. Additionally the workflow of
+the RTC issue and Gerrit Review can be linked together using an external actions mapping file.
+3. Configuration of comment links (init). It is an additional step included in the Gerrit init
+configuration that allows to configure the RTC issue syntax and formatting all the references
+rendered in Gerrit as hyperlinks to the corresponding issue target URL in RTC.
+
+Comment links
+----------------
+
+Git commits are associated to RTC issues reusing the existing Gerrit
+[commitLink configuration]i[1] to extract the issue ID from commit comments.
+
+[1]: ../../../Documentation/config-gerrit.html#_a_id_commentlink_a_section_commentlink
+
+Additionally you need to specify the enforcement policy for git commits
+with regards to issue-tracker associations; the following values are supported:
+
+`MANDATORY`
+:	One or more issue-ids are required in the git commit message, otherwise
+	the git push will be rejected. 
+	NOTE: triggers a *synchronous API call* to RTC for the issue-id lookup, push
+	is blocked until the operation is completed.
+
+`SUGGESTED`
+:	Whenever git commit message does not contain one or more issue-ids,
+	a warning message is displayed as a suggestion on the client.
+	NOTE: triggers a *synchronous API call* to RTC for the issue-id lookup, push
+	is blocked until the operation is completed.
+
+`OPTIONAL`
+:	 Issues-ids are liked when found on git commit message, no warning are
+	 displayed otherwise.
+
+**Example:**
+
+    [commentLink "RTC"]
+    match = RTC#([0-9]*)
+    html = "<a href=\"https://rtc.gerritforge.com:9443/ccm/browse/$1\">$1</a>"
+    association = OPTIONAL
+
+Once a Git commit with a comment link is detected, the RTC issue ID
+is extracted and a new comment added to the issue, pointing back to
+the original Git commit.
+
+RTC connectivity
+-----------------
+
+In order for Gerrit to connect to RTC REST-API, url and credentials
+are required in your gerrit.config / secure.config under the [rtc] section.
+
+**Example:**
+
+    [rtc]
+    url=https://rtc.gerritforge.com:9443/ccm
+    username=rtcuser
+    passsword=rtcpass
+
+RTC credentials and connectivity details are asked and verified during the Gerrit init.
+
+HTTP/S and network settings
+---------------------------
+
+There are no additional settings required for a default connectivity from Gerrit
+to RTC and the default JVM settings are automatically taken for opening outbound 
+connections.
+
+However connectivity to RTC could be highly customised for defining the protocol
+security level, pooling and network settings. This allows the administrator
+to have full control of the output pipe to RTC and the propagation of the Change events
+to the associated issues in a high-loaded production environment.
+
+All settings are defined in gerrit.config under the same [rtc] section.
+See below the list of the most important parameters and their associated meaning.
+
+`sslVerify`
+:	`[TRUE|FALSE]`. When using HTTP/S to connect to RTC (the most typical scenario)
+	allows to enforce (recommended) or disable (**ONLY FOR TEST ENVIRONMENTS**) the 
+	X.509 Certificates validation during SSL handshake. If unsure say TRUE.
+	
+`httpSocketTimeout`
+:	`<number>` Defines the socket timeout in milliseconds,
+    which is the timeout for waiting for data  or, put differently,
+    a maximum period inactivity between two consecutive data packets).
+    A timeout value of zero is interpreted as an infinite timeout.
+
+`httpSocketBufferSize`
+:	`<number>` Determines the size of the internal socket buffer used to buffer data
+    while receiving / transmitting HTTP messages.
+
+`httpSocketReuseaddr`
+:	`[TRUE|FALSE]` Defines whether the socket can be bound even though a previous connection is
+    still in a timeout state.
+
+`httpConnectionTimeout`
+:	`<number>` Determines the timeout in milliseconds until a connection is established.
+    A timeout value of zero is interpreted as an infinite timeout.
+
+`httpConnectionStalecheck`
+:	`[TRUE|FALSE]` Determines whether stale connection check is to be used. The stale
+    connection check can cause up to 30 millisecond overhead per request and
+    should be used only when appropriate. For performance critical
+    operations this check should be disabled.
+
+`httpSocketKeepalive`
+:	`[TRUE|FALSE]` Defines whether or not TCP is to send automatically a keepalive probe to the peer
+	after an interval of inactivity (no data exchanged in either direction) between this
+	host and the peer. The purpose of this option is to detect if the peer host crashes.
+	
+`httpConnManagerTimeout`
+:	`<number>` Defines the timeout in milliseconds used when retrieving a free connection from the pool
+	of outbound HTTP connections allocated.
+	
+`httpConnManagerMaxTotal`
+:	`<number>` Defines the maximum number of outbound HTTP connections in total.
+    This limit is interpreted by client connection managers and applies to individual manager instances.
+
+**NOTE**: The full list of all available HTTP network connectivity parameters can be found under
+the [Apache Commons HTTP Client 4.2.x documentation](http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/index.html?org/apache/http/client/params/ClientPNames.html). Gerrit parameters names are the [CamelCase](http://en.wikipedia.org/wiki/Camelcase) version of the string
+values of the Apache HTTP Client ones.
+
+
+Gerrit init integration
+-----------------------
+
+RTC plugin is integrated as a Gerrit init step in order to simplify and guide
+through the configuration of RTC integration and connectivity check, avoiding
+bogus settings to prevent Gerrit plugin to start correctly.
+
+**Gerrit init example:**
+
+    *** IBM Rational Team Concert connectivity
+	*** 
+
+	RTC CCM URL (empty to skip)    : https://rtc.gerritforge.com:9443/ccm
+	RTC username                   []: gerrit
+	Change luca's password         [y/N]? y
+	luca's password                : ******
+	              confirm password : ******
+	Verify SSL Certificates        [TRUE/?]: false
+	Test connectivity to https://rtc.gerritforge.com:9443/ccm [N/?]: y
+	Checking IBM Rational Team Concert connectivity ... [OK]
+
+	*** Rational Team Concert issue-tracking association
+	*** 
+
+	RTC issue-Id regex             [RTC#([0-9]+)]: 
+	RTC-Id enforced in commit message [OPTIONAL/?]: suggested
+	
+Issues workflow automation
+--------------------------
+
+RTC plugin is able to automate status transition on the issues based on
+code-review actions performed on Gerrit; actions are performed on RTC using
+the username/password provided during Gerrit init.
+Transition automation is driven by `$GERRIT_SITE/issue-state-transition.config` file.
+
+Syntax of the status transition configuration file is the following:
+
+    [action "<issue-status-action>"]
+    change=<change-action>
+    verified=<verified-value>
+    code-review=<code-review-value>
+
+`<issue-status-action>`
+:	Action to perform on RTC issue when all the condition in the stanza are met.
+
+`<change-action>`
+:	Action performed on Gerrit change-id, possible values are:
+	`created, commented, merged, abandoned, restored`
+
+`<verified-value>`
+:	Verified flag added on Gerrit with values from -1 to +1
+
+`<code-review-value>`
+:	Code-Review flag added on Gerrit with values from -2 to +2
+
+Note: multiple conditions in the action stanza are optional but at least one must be present.
+
+Example:
+
+    [action "Start Progress"]
+    change=created
+
+    [action "Resolve Issue"]
+    verified=+1
+    code-review=+2
+
+    [action "Close Issue"]
+    change=merged
+
+    [action "Stop Progress"]
+    change=abandoned
+
+The above example defines four status transition on RTC, based on the following conditions:
+
+* Whenever a new Change-set is created on Gerrit, start progress on the RTC issue
+* Whenever a change is verified and reviewed with +2, transition the RTC issue to resolved
+* Whenever a change is merged to branch, mark the RTC transition the RTC issue to closed
+* Whenever a change is abandoned, stop the progress on RTC issue
\ No newline at end of file
diff --git a/src/test/java/com/googlesource/gerrit/plugins/hooks/rtc/ChangeListenerAsyncDecoratorTest.java b/src/test/java/com/googlesource/gerrit/plugins/hooks/rtc/ChangeListenerAsyncDecoratorTest.java
new file mode 100644
index 0000000..764f5f5
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/hooks/rtc/ChangeListenerAsyncDecoratorTest.java
@@ -0,0 +1,134 @@
+// 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.rtc;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.*;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.mockito.stubbing.Answer;
+
+import com.google.gerrit.common.ChangeListener;
+import com.google.gerrit.server.events.ChangeEvent;
+import com.googlesource.gerrit.plugins.hooks.rtc.filters.ChangeListenerAsyncDecorator;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ChangeListenerAsyncDecoratorTest {
+
+  @Mock
+  ChangeListener listener;
+  @Mock
+  ChangeEvent event;
+  @Mock
+  ExecutorService executor;
+  @Mock
+  ExecutorService immediateExecutor;
+
+  ChangeListenerAsyncDecorator<ChangeListener> asyncListener;
+
+  @Before
+  public void setUp() {
+    when(immediateExecutor.submit(any(Runnable.class))).thenAnswer(
+        new Answer<Future<?>>() {
+
+          @Override
+          public Future<?> answer(InvocationOnMock invocation) throws Throwable {
+            Runnable task = (Runnable) invocation.getArguments()[0];
+            task.run();
+            return null;
+
+          }
+        });
+    asyncListener =
+        new ChangeListenerAsyncDecorator<ChangeListener>(listener, executor);
+  }
+
+  @Test
+  public void testQueueShouldBeEmptyWhenCreated() {
+    assertTrue(asyncListener.getQueue().isEmpty());
+  }
+
+  @Test
+  public void testQueueShouldNotBeEmptyWhenOneEventSubmitted() {
+    asyncListener.onChangeEvent(event);
+    assertFalse(asyncListener.getQueue().isEmpty());
+  }
+
+  @Test
+  public void testChangeEventShouldBeQueuedWhenSubmitted() {
+    asyncListener.onChangeEvent(event);
+    assertEquals(event, asyncListener.getQueue().peek());
+  }
+
+  @Test
+  public void testChangeEventShouldBeSentToExecutor() {
+    asyncListener.onChangeEvent(event);
+    verify(executor).submit(
+        any(ChangeListenerAsyncDecorator.ChangeRunner.class));
+  }
+
+  @Test
+  public void testChangeEventShouldBePropagatedToListenerWhenImmediatelyExecuted() {
+    asyncListener =
+        new ChangeListenerAsyncDecorator<ChangeListener>(listener,
+            immediateExecutor);
+    asyncListener.onChangeEvent(event);
+    verify(listener).onChangeEvent(event);
+    assertTrue(asyncListener.getQueue().isEmpty());
+  }
+
+  @Test
+  public void testChangeEventShouldStayInQueueWhenExecutionFailed() {
+    asyncListener =
+        new ChangeListenerAsyncDecorator<ChangeListener>(listener,
+            immediateExecutor);
+    doThrow(new IllegalArgumentException()).when(listener).onChangeEvent(
+        any(ChangeEvent.class));
+
+    asyncListener.onChangeEvent(event);
+    verify(listener).onChangeEvent(event);
+    assertFalse(asyncListener.getQueue().isEmpty());
+  }
+  
+  @Test
+  public void testChangeShouldProcessAllPreviouslyFailedEventsInQueue() {
+    asyncListener =
+        new ChangeListenerAsyncDecorator<ChangeListener>(listener,
+            immediateExecutor);
+    
+    doThrow(new IllegalArgumentException()).when(listener).onChangeEvent(
+        any(ChangeEvent.class));
+    asyncListener.onChangeEvent(event);
+    verify(listener).onChangeEvent(event);
+    assertFalse(asyncListener.getQueue().isEmpty());
+    
+    doNothing().when(listener).onChangeEvent(any(ChangeEvent.class));
+    asyncListener.onChangeEvent(event);
+    verify(listener, times(3)).onChangeEvent(event);
+    
+    assertTrue(asyncListener.getQueue().isEmpty());
+  }
+
+}