Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Initial commit

  • Loading branch information...
commit df65a085a13c4efe5c00ace81d45d1a91b12bc11 0 parents
@0x6e6562 0x6e6562 authored
Showing with 35,354 additions and 0 deletions.
  1. +30 −0 .gitignore
  2. +3 −0  .gitmodules
  3. +202 −0 LICENSE
  4. +5 −0 NOTICE
  5. +13 −0 README
  6. +21 −0 README.maven_opts.txt
  7. +15 −0 agent/README.docgen
  8. +330 −0 agent/pom.xml
  9. +20 −0 agent/src/main/resources/diffa.properties
  10. +18 −0 agent/src/main/resources/docgen/md/rest_collections.st
  11. +13 −0 agent/src/main/resources/docgen/md/rest_menu.st
  12. +61 −0 agent/src/main/resources/docgen/md/rest_resource.st
  13. +34 −0 agent/src/main/resources/logback.xml
  14. +9 −0 agent/src/main/resources/notifications/mail_body.st
  15. +1 −0  agent/src/main/resources/notifications/mail_subject.st
  16. +84 −0 agent/src/main/scala/net/lshift/diffa/agent/http/FrontendRequestHandler.scala
  17. +95 −0 agent/src/main/scala/net/lshift/diffa/agent/notifications/SmtpNotifier.scala
  18. +80 −0 agent/src/main/scala/net/lshift/diffa/agent/rest/AbstractRestResource.scala
  19. +57 −0 agent/src/main/scala/net/lshift/diffa/agent/rest/ActionsResource.scala
  20. +133 −0 agent/src/main/scala/net/lshift/diffa/agent/rest/ConfigurationResource.scala
  21. +127 −0 agent/src/main/scala/net/lshift/diffa/agent/rest/DifferencesResource.scala
  22. +69 −0 agent/src/main/scala/net/lshift/diffa/agent/rest/UsersResource.scala
  23. +38 −0 agent/src/main/scala/net/lshift/diffa/agent/util/HibernatePropertiesFactory.scala
  24. +31 −0 agent/src/main/webapp/WEB-INF/RESTful-context.xml
  25. +256 −0 agent/src/main/webapp/WEB-INF/applicationContext.xml
  26. +115 −0 agent/src/main/webapp/WEB-INF/web.xml
  27. +101 −0 agent/src/main/webapp/css/grid.css
  28. +140 −0 agent/src/main/webapp/css/grid24.css
  29. +305 −0 agent/src/main/webapp/css/jbase.css
  30. +318 −0 agent/src/main/webapp/css/jquery-ui-slider.1.8.5.css
  31. +63 −0 agent/src/main/webapp/css/reset.css
  32. +30 −0 agent/src/main/webapp/css/stickyfooter.css
  33. +152 −0 agent/src/main/webapp/css/style.css
  34. +101 −0 agent/src/main/webapp/css/styles.css
  35. 0  agent/src/main/webapp/images/sample
  36. +136 −0 agent/src/main/webapp/index.jsp
  37. +878 −0 agent/src/main/webapp/js/app.js
  38. +6,078 −0 agent/src/main/webapp/js/jquery-1.4.1.js
  39. +83 −0 agent/src/main/webapp/js/jquery-ui-slider.1.8.5.min.js
  40. +329 −0 agent/src/main/webapp/js/jquery.ajax.js
  41. +123 −0 agent/src/main/webapp/js/jquery.color.js
  42. +3,725 −0 agent/src/main/webapp/js/raphael.js
  43. +79 −0 agent/src/main/webapp/js/test_data.js
  44. +88 −0 agent/src/test/conf/jetty-env.xml
  45. +67 −0 agent/src/test/conf/jetty.xml
  46. +39 −0 agent/src/test/js-client/crud-test-new.html
  47. +124 −0 agent/src/test/js-client/crud-test.html
  48. +144 −0 agent/src/test/js-client/crud-test.js
  49. +52 −0 agent/src/test/js-client/diffa-rest.js
  50. +6,240 −0 agent/src/test/js-client/jquery-1.4.2.js
  51. +44 −0 agent/src/test/js-client/json2.js
  52. +155 −0 agent/src/test/js-client/qunit.css
  53. +1,261 −0 agent/src/test/js-client/qunit.js
  54. +20 −0 agent/src/test/resources/diffa.itest.properties
  55. +21 −0 agent/src/test/resources/log4j.properties
  56. +43 −0 agent/src/test/resources/logback-test.xml
  57. +35 −0 agent/src/test/scala/net/lshift/diffa/agent/itest/AbstractEnvironmentTest.scala
  58. +50 −0 agent/src/test/scala/net/lshift/diffa/agent/itest/CommonActionTests.scala
  59. +175 −0 agent/src/test/scala/net/lshift/diffa/agent/itest/CommonDifferenceTests.scala
  60. +31 −0 agent/src/test/scala/net/lshift/diffa/agent/itest/CommonMatchingTests.scala
  61. +30 −0 agent/src/test/scala/net/lshift/diffa/agent/itest/CorrelatedEnvironmentTest.scala
  62. +29 −0 agent/src/test/scala/net/lshift/diffa/agent/itest/SameEnvironmentTest.scala
  63. +38 −0 agent/src/test/scala/net/lshift/diffa/agent/itest/SmokeTest.scala
  64. +51 −0 agent/src/test/scala/net/lshift/diffa/agent/itest/support/Participants.scala
  65. +28 −0 agent/src/test/scala/net/lshift/diffa/agent/itest/support/TestConstants.scala
  66. +110 −0 agent/src/test/scala/net/lshift/diffa/agent/itest/support/TestEnvironment.scala
  67. +25 −0 agent/src/test/scala/net/lshift/diffa/agent/itest/support/TestEnvironments.scala
  68. +132 −0 dist/pom.xml
  69. +131 −0 dist/src/main/assembly/jetty-bundle.xml
  70. +24 −0 dist/src/main/assembly/resources/bin/agent.sh
  71. +29 −0 dist/src/main/assembly/resources/bin/configure-sample.sh
  72. +24 −0 dist/src/main/assembly/resources/bin/declare.sh
  73. +24 −0 dist/src/main/assembly/resources/bin/diff.sh
  74. +176 −0 dist/src/main/assembly/resources/etc/jetty.xml
  75. +1 −0  dist/src/main/assembly/resources/start.ini
  76. +55 −0 docgen-maven-plugin/pom.xml
  77. +142 −0 docgen-maven-plugin/src/main/java/net/lshift/diffa/docgen/DocGenMojo.java
  78. +41 −0 docgen-maven-plugin/src/main/java/net/lshift/diffa/docgen/MandatoryParameter.java
  79. +41 −0 docgen-maven-plugin/src/main/java/net/lshift/diffa/docgen/OptionalParameter.java
  80. +60 −0 docgen-maven-plugin/src/main/java/net/lshift/diffa/docgen/ResourceCollection.java
  81. +140 −0 docgen-maven-plugin/src/main/java/net/lshift/diffa/docgen/ResourceDescriptor.java
  82. +166 −0 docgen-maven-plugin/src/main/java/net/lshift/diffa/docgen/RestGen.java
  83. +29 −0 docgen-maven-plugin/src/main/java/net/lshift/diffa/docgen/annotations/Description.java
  84. +34 −0 docgen-maven-plugin/src/main/java/net/lshift/diffa/docgen/annotations/MandatoryParams.java
  85. +34 −0 docgen-maven-plugin/src/main/java/net/lshift/diffa/docgen/annotations/OptionalParams.java
  86. +13 −0 etc/header.txt
  87. +1 −0  javamail-file-transport
  88. +70 −0 json-messaging/pom.xml
  89. +102 −0 json-messaging/src/main/scala/net/lshift/diffa/messaging/json/AbstractJSONHandler.scala
  90. +70 −0 json-messaging/src/main/scala/net/lshift/diffa/messaging/json/AbstractRestClient.scala
  91. +37 −0 json-messaging/src/main/scala/net/lshift/diffa/messaging/json/ActionsRestClient.scala
  92. +51 −0 json-messaging/src/main/scala/net/lshift/diffa/messaging/json/ChangesHandler.scala
  93. +49 −0 json-messaging/src/main/scala/net/lshift/diffa/messaging/json/ChangesRestClient.scala
  94. +54 −0 json-messaging/src/main/scala/net/lshift/diffa/messaging/json/ConfigurationRestClient.scala
  95. +104 −0 json-messaging/src/main/scala/net/lshift/diffa/messaging/json/DifferencesRestClient.scala
  96. +44 −0 json-messaging/src/main/scala/net/lshift/diffa/messaging/json/JSONEncodingUtils.scala
  97. +34 −0 json-messaging/src/main/scala/net/lshift/diffa/messaging/json/JSONRestParticipantProtocolFactory.scala
  98. +98 −0 json-messaging/src/main/scala/net/lshift/diffa/messaging/json/ParticipantHandler.scala
  99. +94 −0 json-messaging/src/main/scala/net/lshift/diffa/messaging/json/ParticipantRestClient.scala
  100. +30 −0 json-messaging/src/main/scala/net/lshift/diffa/messaging/json/UsersRestClient.scala
  101. +140 −0 kernel/pom.xml
  102. +21 −0 kernel/src/main/java/net/lshift/diffa/kernel/differencing/MatchState.java
  103. +80 −0 kernel/src/main/resources/net/lshift/diffa/kernel/config/Config.hbm.xml
  104. +47 −0 kernel/src/main/resources/net/lshift/diffa/kernel/differencing/Correlations.hbm.xml
  105. +26 −0 kernel/src/main/scala/net/lshift/diffa/kernel/alerting/AlertKey.scala
  106. +45 −0 kernel/src/main/scala/net/lshift/diffa/kernel/alerting/Alerter.scala
  107. +30 −0 kernel/src/main/scala/net/lshift/diffa/kernel/alerting/KernelAlerts.scala
  108. +59 −0 kernel/src/main/scala/net/lshift/diffa/kernel/client/ActionsClient.scala
  109. +29 −0 kernel/src/main/scala/net/lshift/diffa/kernel/client/ChangesClient.scala
  110. +29 −0 kernel/src/main/scala/net/lshift/diffa/kernel/client/ConfigurationClient.scala
  111. +81 −0 kernel/src/main/scala/net/lshift/diffa/kernel/client/DifferencesClient.scala
  112. +25 −0 kernel/src/main/scala/net/lshift/diffa/kernel/client/UsersClient.scala
  113. +94 −0 kernel/src/main/scala/net/lshift/diffa/kernel/config/ConfigStore.scala
  114. +117 −0 kernel/src/main/scala/net/lshift/diffa/kernel/config/HibernateConfigStore.scala
  115. +133 −0 kernel/src/main/scala/net/lshift/diffa/kernel/differencing/BaseSynchingVersionPolicy.scala
  116. +64 −0 kernel/src/main/scala/net/lshift/diffa/kernel/differencing/CorrelatedVersionPolicy.scala
  117. +42 −0 kernel/src/main/scala/net/lshift/diffa/kernel/differencing/Correlation.scala
  118. +73 −0 kernel/src/main/scala/net/lshift/diffa/kernel/differencing/DateConstraint.scala
  119. +320 −0 kernel/src/main/scala/net/lshift/diffa/kernel/differencing/DefaultSessionManager.scala
  120. +23 −0 kernel/src/main/scala/net/lshift/diffa/kernel/differencing/DifferenceHandler.scala
  121. +44 −0 kernel/src/main/scala/net/lshift/diffa/kernel/differencing/DifferencingListener.scala
  122. +97 −0 kernel/src/main/scala/net/lshift/diffa/kernel/differencing/DigestBuilder.scala
  123. +121 −0 kernel/src/main/scala/net/lshift/diffa/kernel/differencing/DigestDifferencingUtils.scala
  124. +178 −0 kernel/src/main/scala/net/lshift/diffa/kernel/differencing/HibernateVersionCorrelationStore.scala
  125. +112 −0 kernel/src/main/scala/net/lshift/diffa/kernel/differencing/LocalSessionCache.scala
  126. +62 −0 kernel/src/main/scala/net/lshift/diffa/kernel/differencing/SameVersionPolicy.scala
  127. +107 −0 kernel/src/main/scala/net/lshift/diffa/kernel/differencing/SessionCache.scala
  128. +132 −0 kernel/src/main/scala/net/lshift/diffa/kernel/differencing/SessionManager.scala
  129. +43 −0 kernel/src/main/scala/net/lshift/diffa/kernel/differencing/SessionScope.scala
  130. +71 −0 kernel/src/main/scala/net/lshift/diffa/kernel/differencing/VersionCorrelationStore.scala
  131. +38 −0 kernel/src/main/scala/net/lshift/diffa/kernel/differencing/VersionPolicy.scala
  132. +43 −0 kernel/src/main/scala/net/lshift/diffa/kernel/differencing/VersionPolicyManager.scala
  133. +50 −0 kernel/src/main/scala/net/lshift/diffa/kernel/events/ChangeEvent.scala
  134. +48 −0 kernel/src/main/scala/net/lshift/diffa/kernel/events/PairChangeEvent.scala
  135. +29 −0 kernel/src/main/scala/net/lshift/diffa/kernel/events/VersionID.scala
  136. +57 −0 kernel/src/main/scala/net/lshift/diffa/kernel/frontend/ActionsProxy.scala
  137. +64 −0 kernel/src/main/scala/net/lshift/diffa/kernel/frontend/Changes.scala
  138. +120 −0 kernel/src/main/scala/net/lshift/diffa/kernel/frontend/Configuration.scala
  139. +56 −0 kernel/src/main/scala/net/lshift/diffa/kernel/frontend/Matches.scala
  140. +57 −0 kernel/src/main/scala/net/lshift/diffa/kernel/matching/EventMatcher.scala
  141. +251 −0 kernel/src/main/scala/net/lshift/diffa/kernel/matching/LocalEventMatcher.scala
  142. +91 −0 kernel/src/main/scala/net/lshift/diffa/kernel/matching/LocalEventMatchingManager.scala
  143. +45 −0 kernel/src/main/scala/net/lshift/diffa/kernel/matching/MatchingManager.scala
  144. +41 −0 kernel/src/main/scala/net/lshift/diffa/kernel/matching/MatchingStatusListener.scala
  145. +61 −0 kernel/src/main/scala/net/lshift/diffa/kernel/notifications/EventNotifier.scala
  146. +36 −0 kernel/src/main/scala/net/lshift/diffa/kernel/notifications/NotificationEvent.scala
  147. +30 −0 kernel/src/main/scala/net/lshift/diffa/kernel/notifications/NotificationProvider.scala
  148. +30 −0 kernel/src/main/scala/net/lshift/diffa/kernel/participants/ActionResult.scala
  149. +33 −0 kernel/src/main/scala/net/lshift/diffa/kernel/participants/DownstreamMemoryParticipant.scala
  150. +68 −0 kernel/src/main/scala/net/lshift/diffa/kernel/participants/MemoryParticipantBase.scala
  151. +53 −0 kernel/src/main/scala/net/lshift/diffa/kernel/participants/Participant.scala
  152. +45 −0 kernel/src/main/scala/net/lshift/diffa/kernel/participants/ParticipantFactory.scala
  153. +41 −0 kernel/src/main/scala/net/lshift/diffa/kernel/participants/ParticipantProtocolFactory.scala
  154. +25 −0 kernel/src/main/scala/net/lshift/diffa/kernel/participants/ProcessingResponse.scala
  155. +27 −0 kernel/src/main/scala/net/lshift/diffa/kernel/participants/RangeGranularity.scala
  156. +31 −0 kernel/src/main/scala/net/lshift/diffa/kernel/participants/UpstreamMemoryParticipant.scala
  157. +28 −0 kernel/src/main/scala/net/lshift/diffa/kernel/participants/VersionDigest.scala
  158. +43 −0 kernel/src/main/scala/net/lshift/diffa/kernel/protocol/ProtocolHandler.scala
  159. +55 −0 kernel/src/main/scala/net/lshift/diffa/kernel/protocol/ProtocolMapper.scala
  160. +24 −0 kernel/src/main/scala/net/lshift/diffa/kernel/protocol/TransportRequest.scala
  161. +31 −0 kernel/src/main/scala/net/lshift/diffa/kernel/protocol/TransportResponse.scala
  162. +34 −0 kernel/src/main/scala/net/lshift/diffa/kernel/util/DateUtils.scala
  163. +46 −0 kernel/src/main/scala/net/lshift/diffa/kernel/util/DocExamplesFactory.scala
  164. +66 −0 kernel/src/main/scala/net/lshift/diffa/kernel/util/HibernateQueryUtils.scala
  165. +23 −0 kernel/src/main/scala/net/lshift/diffa/kernel/util/MissingObjectException.scala
  166. +27 −0 kernel/src/main/scala/net/lshift/diffa/kernel/util/Placeholders.scala
  167. +44 −0 kernel/src/main/scala/net/lshift/diffa/kernel/util/SessionHelper.scala
  168. +34 −0 kernel/src/test/java/net/lshift/diffa/kernel/util/Concurrent.java
  169. +87 −0 kernel/src/test/java/net/lshift/diffa/kernel/util/ConcurrentJunitRunner.java
  170. +21 −0 kernel/src/test/resources/log4j.properties
  171. +41 −0 kernel/src/test/resources/logback-test.xml
  172. +321 −0 kernel/src/test/scala/net/lshift/diffa/kernel/config/HibernateConfigStoreTest.scala
  173. +281 −0 kernel/src/test/scala/net/lshift/diffa/kernel/differencing/AbstractPolicyTest.scala
  174. +156 −0 kernel/src/test/scala/net/lshift/diffa/kernel/differencing/CorrelatedVersionPolicyTest.scala
  175. +185 −0 kernel/src/test/scala/net/lshift/diffa/kernel/differencing/DefaultSessionManagerTest.scala
  176. +97 −0 kernel/src/test/scala/net/lshift/diffa/kernel/differencing/DigestBuilderTest.scala
  177. +161 −0 kernel/src/test/scala/net/lshift/diffa/kernel/differencing/DigestDifferencingUtilsTest.scala
  178. +301 −0 kernel/src/test/scala/net/lshift/diffa/kernel/differencing/HibernateVersionCorrelationStoreTest.scala
  179. +74 −0 kernel/src/test/scala/net/lshift/diffa/kernel/differencing/LocalSessionCacheProviderTest.scala
  180. +149 −0 kernel/src/test/scala/net/lshift/diffa/kernel/differencing/LocalSessionCacheTest.scala
  181. +29 −0 kernel/src/test/scala/net/lshift/diffa/kernel/differencing/NullDifferencingListener.scala
  182. +92 −0 kernel/src/test/scala/net/lshift/diffa/kernel/differencing/SameVersionPolicyTest.scala
  183. +44 −0 kernel/src/test/scala/net/lshift/diffa/kernel/differencing/VersionPolicyManagerTest.scala
  184. +36 −0 kernel/src/test/scala/net/lshift/diffa/kernel/frontend/ConfigurationTest.scala
  185. +341 −0 kernel/src/test/scala/net/lshift/diffa/kernel/matching/AbstractMatcherTest.scala
  186. +37 −0 kernel/src/test/scala/net/lshift/diffa/kernel/matching/LocalEventMatcherTest.scala
  187. +92 −0 kernel/src/test/scala/net/lshift/diffa/kernel/matching/LocalEventMatchingManagerTest.scala
  188. +69 −0 kernel/src/test/scala/net/lshift/diffa/kernel/notifications/EventNotifierTest.scala
  189. +112 −0 kernel/src/test/scala/net/lshift/diffa/kernel/participants/ParticipantFactoryTest.scala
  190. +85 −0 kernel/src/test/scala/net/lshift/diffa/kernel/protocol/ProtocolMapperTest.scala
  191. +72 −0 kernel/src/test/scala/net/lshift/diffa/kernel/util/Dates.scala
  192. +58 −0 kernel/src/test/scala/net/lshift/diffa/kernel/util/EasyMockScalaUtils.scala
  193. +185 −0 participants-web/pom.xml
  194. +24 −0 participants-web/src/main/resources/demo.properties
  195. +61 −0 participants-web/src/main/resources/mvcContext.xml
  196. +42 −0 participants-web/src/main/scala/net/lshift/diffa/participants/DownstreamWebParticipant.scala
  197. +45 −0 participants-web/src/main/scala/net/lshift/diffa/participants/UpstreamWebParticipant.scala
  198. +35 −0 participants-web/src/main/scala/net/lshift/diffa/participants/WebParticipant.scala
  199. +27 −0 participants-web/src/main/scala/net/lshift/diffa/participants/domain/ParticipantEntity.scala
  200. +39 −0 participants-web/src/main/scala/net/lshift/diffa/participants/web/ParticipantRequestHandler.scala
  201. +75 −0 participants-web/src/main/scala/net/lshift/diffa/participants/web/ParticipantUIController.scala
  202. +39 −0 participants-web/src/main/scala/net/lshift/diffa/util/DefaultWrapperServlet.scala
  203. +36 −0 participants-web/src/main/scala/net/lshift/diffa/util/ResultOnlyMappingJacksonJsonView.scala
  204. +116 −0 participants-web/src/main/webapp/WEB-INF/applicationContext.xml
  205. +184 −0 participants-web/src/main/webapp/WEB-INF/views/home/index.jsp
  206. +20 −0 participants-web/src/main/webapp/WEB-INF/views/json/empty.jsp
  207. +90 −0 participants-web/src/main/webapp/WEB-INF/web.xml
  208. +104 −0 participants-web/src/main/webapp/javascript/date.js
  209. +154 −0 participants-web/src/main/webapp/javascript/jquery-1.4.2.min.js
  210. +9 −0 participants-web/src/main/webapp/javascript/md5-min.js
  211. +40 −0 participants-web/src/main/webapp/javascript/scenarios.js
  212. +21 −0 participants-web/src/test/conf/jetty-env.xml
  213. +60 −0 participants-web/src/test/conf/jetty.xml
  214. +19 −0 participants/bin/file-pair.sh
  215. +54 −0 participants/pom.xml
  216. +43 −0 participants/src/main/scala/net/lshift/diffa/participants/DirWalker.scala
  217. +57 −0 participants/src/main/scala/net/lshift/diffa/participants/DirWatcher.scala
  218. +37 −0 participants/src/main/scala/net/lshift/diffa/participants/DownstreamFileParticipant.scala
  219. +47 −0 participants/src/main/scala/net/lshift/diffa/participants/FilePair.scala
  220. +69 −0 participants/src/main/scala/net/lshift/diffa/participants/FileParticipant.scala
  221. +47 −0 participants/src/main/scala/net/lshift/diffa/participants/ParticipantRpcServer.scala
  222. +40 −0 participants/src/main/scala/net/lshift/diffa/participants/UpstreamFileParticipant.scala
  223. +323 −0 pom.xml
  224. +12 −0 setup/Makefile
  225. +19 −0 tools/bin/declare.sh
  226. +19 −0 tools/bin/difftail.sh
  227. +53 −0 tools/pom.xml
  228. +19 −0 tools/samples/declare-same.sh
  229. +19 −0 tools/samples/diff-same.sh
  230. +19 −0 tools/samples/match-same.sh
  231. +92 −0 tools/src/main/scala/net/lshift/diffa/tools/Declare.scala
  232. +52 −0 tools/src/main/scala/net/lshift/diffa/tools/DiffTail.scala
  233. +62 −0 tools/src/main/scala/net/lshift/diffa/tools/DiffaTool.scala
30 .gitignore
@@ -0,0 +1,30 @@
+/*.hgignore
+*.suo
+/*.5.0.ReSharper.user
+/_ReSharper*/
+*.xss
+*.xsc
+bin/*
+
+*/obj/
+/Mockup.bmml
+/DifferenceEngine.png
+/*.lnk.bmml
+/.idea
+diffa*.iml
+*.iml
+/setup/*.tar.gz
+/setup/protobuf-*
+target/
+derby.log
+glob:**/bin/
+agent/target/*
+kernel/target/*
+participants/target/*
+protocol/target/*
+target/*
+tools/target/*
+/*.iws
+/*.ipr
+/*.7z
+/*.tmproj
3  .gitmodules
@@ -0,0 +1,3 @@
+[submodule "javamail-file-transport"]
+ path = javamail-file-transport
+ url = git://github.com/lshift/javamail-file-transport
202 LICENSE
@@ -0,0 +1,202 @@
+
+ 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.
5 NOTICE
@@ -0,0 +1,5 @@
+LShift diffa
+Copyright [2010] LShift Ltd.
+
+This product includes software developed at
+LShift Ltd. (http://www.lshift.net/).
13 README
@@ -0,0 +1,13 @@
+License Checking
+----------------
+
+Diffa uses the maven license plugin (http://code.google.com/p/maven-license-plugin/) to check that the license files are up to date.
+
+To run the license check:
+
+mvn -e license:format -Dyear=2010
+
+To reformat each file according to the header file:
+
+mvn -e license:format -Dyear=2010
+
21 README.maven_opts.txt
@@ -0,0 +1,21 @@
+====
+ Copyright (C) 2010 LShift Ltd.
+
+ 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.
+====
+
+Due to the way that we're booting the webapps into the Maven JVM, the Permgen
+space is tending to blow out when we run the integration test suite. If
+you set the following environment variable, things should come good again:
+
+MAVEN_OPTS=-XX:MaxPermSize=512m
15 agent/README.docgen
@@ -0,0 +1,15 @@
+The REST API documentation in markdown is auto-generated from annotations in the code. This is linked to the 'process-classes' phase.
+
+The result should normally go to the 'doc/rest' subdirectory of a 'gh-pages' branch checkout.
+
+You can specify the target directory by overriding the docs.targetDir property. Thus a full example would look as follows:
+
+ cd DIFFA-MASTER-CHECKOUT/agent
+ mvn install -Ddocs.targetDir=../../DIFFA-GH-PAGES-CHECKOUT/doc/rest
+
+...assuming you have your checkouts of 'gh-pages' and 'master' in a common directory.
+
+After checking the generated content you need to commit both the docgen logic/templates on the master branch and the generated files on the gh-pages branch separately.
+
+Also see README in the root of the 'gh-pages' branch.
+
330 agent/pom.xml
@@ -0,0 +1,330 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>net.lshift.diffa</groupId>
+ <artifactId>diffa-parent</artifactId>
+ <version>0.8-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>diffa-agent</artifactId>
+ <name>LShift Diffa Agent</name>
+
+ <packaging>war</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>net.lshift.diffa</groupId>
+ <artifactId>diffa-json-messaging</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.scala-lang</groupId>
+ <artifactId>scala-library</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.scala-lang</groupId>
+ <artifactId>scala-compiler</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-web</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-orm</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>javassist</groupId>
+ <artifactId>javassist</artifactId>
+ </dependency>
+ <!-- Hibernate imports 1.5.2 of slf4j, which is incompatible with the version
+ of logback that we're using. Force an upgrade.
+ -->
+ <dependency>
+ <groupId>com.sun.jersey</groupId>
+ <artifactId>jersey-server</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.sun.jersey.contribs</groupId>
+ <artifactId>jersey-spring</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.antlr</groupId>
+ <artifactId>stringtemplate</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-jaxrs</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-xc</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>javax.mail</groupId>
+ <artifactId>mail</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jettison</groupId>
+ <artifactId>jettison</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-continuation</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymockclassextension</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymockclassextension</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.derby</groupId>
+ <artifactId>derby</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>net.lshift.diffa</groupId>
+ <artifactId>diffa-participants</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>net.lshift.diffa</groupId>
+ <artifactId>diffa-docgen-maven-plugin</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <finalName>${project.artifactId}-${buildVersion}</finalName>
+ <sourceDirectory>src/main/scala</sourceDirectory>
+ <testSourceDirectory>src/test/scala</testSourceDirectory>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.scala-tools</groupId>
+ <artifactId>maven-scala-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.mortbay.jetty</groupId>
+ <artifactId>jetty-maven-plugin</artifactId>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.derby</groupId>
+ <artifactId>derby</artifactId>
+ <version>${derby.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-dbcp</groupId>
+ <artifactId>commons-dbcp</artifactId>
+ <version>${dbcp.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>net.lshift.javamail</groupId>
+ <artifactId>javamail-file-transport</artifactId>
+ <version>0.8-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+
+ <configuration>
+ <useTestClasspath>true</useTestClasspath>
+ <systemProperties>
+ <systemProperty>
+ <name>JETTY_NO_SHUTDOWN_HOOK</name>
+ <value>true</value>
+ </systemProperty>
+ <systemProperty>
+ <name>basedir</name>
+ <value>${basedir}</value>
+ </systemProperty>
+ <systemProperty>
+ <name>diffa.maildir</name>
+ <value>${basedir}/target/messages</value>
+ </systemProperty>
+ </systemProperties>
+ <connectors>
+ <connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">
+ <port>19093</port>
+ <maxIdleTime>60000</maxIdleTime>
+ </connector>
+ </connectors>
+ <scanIntervalSeconds>600</scanIntervalSeconds>
+ <stopKey>foo</stopKey>
+ <stopPort>19094</stopPort>
+ <jettyConfig>${basedir}/src/test/conf/jetty.xml</jettyConfig>
+ <webAppXml>${basedir}/src/test/conf/jetty-env.xml</webAppXml>
+ <webAppConfig>
+ <contextPath>/diffa-agent</contextPath>
+ </webAppConfig>
+ </configuration>
+ <executions>
+ <execution>
+ <id>start-jetty</id>
+ <phase>pre-integration-test</phase>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ <configuration>
+ <scanIntervalSeconds>0</scanIntervalSeconds>
+ <daemon>true</daemon>
+ </configuration>
+ </execution>
+ <execution>
+ <id>stop-jetty</id>
+ <phase>post-integration-test</phase>
+ <goals>
+ <goal>stop</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <!--<forkMode>never</forkMode>-->
+ <excludes>
+ <exclude>**/itest/**</exclude>
+ </excludes>
+ </configuration>
+ <executions>
+ <execution>
+ <id>surefire-itest</id>
+ <phase>integration-test</phase>
+ <goals>
+ <goal>test</goal>
+ </goals>
+ <configuration>
+ <excludes>
+ <exclude>none</exclude>
+ </excludes>
+ <includes>
+ <include>**/itest/**/*Test.class</include>
+ </includes>
+ <systemPropertyVariables>
+ <diffa.maildir>${basedir}/target/messages</diffa.maildir>
+ </systemPropertyVariables>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.moyrax.qunit</groupId>
+ <artifactId>qunit-maven-plugin</artifactId>
+ <version>1.2.4.2</version>
+
+ <executions>
+ <execution>
+ <phase>integration-test</phase>
+ <goals>
+ <goal>test</goal>
+ </goals>
+ </execution>
+ </executions>
+
+ <configuration>
+ <contextPath>
+ <entry>
+ <files>
+ <directory>${basedir}/src/test/js-client</directory>
+ </files>
+ </entry>
+ </contextPath>
+
+ <remoteTestResources>
+ <baseUrl>http://localhost:19093/js-tests/</baseUrl>
+ <urlFiles>
+ <urlFile>crud-test.html</urlFile>
+ </urlFiles>
+ </remoteTestResources>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>net.lshift.diffa</groupId>
+ <artifactId>diffa-docgen-maven-plugin</artifactId>
+ <configuration>
+ <templateDir>${basedir}/src/main/resources/docgen/md</templateDir>
+ <examplesFactoryClass>net.lshift.diffa.kernel.util.DocExamplesFactory</examplesFactoryClass>
+ <resources>
+ <resource>net.lshift.diffa.agent.rest.ConfigurationResource</resource>
+ <resource>net.lshift.diffa.agent.rest.DifferencesResource</resource>
+ <resource>net.lshift.diffa.agent.rest.ActionsResource</resource>
+ <resource>net.lshift.diffa.agent.rest.UsersResource</resource>
+ </resources>
+ <targetDir>${docs.targetDir}</targetDir>
+ <consoleOutput>false</consoleOutput>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>process-classes</phase>
+ <goals>
+ <goal>generate-docs</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <reporting>
+ <plugins>
+ <plugin>
+ <groupId>org.scala-tools</groupId>
+ <artifactId>maven-scala-plugin</artifactId>
+ <configuration>
+ <scalaVersion>${scala.version}</scalaVersion>
+ </configuration>
+ </plugin>
+ </plugins>
+ </reporting>
+ <properties>
+ <docs.targetDir>${project.build.directory}/rest-docs</docs.targetDir>
+ </properties>
+</project>
+
20 agent/src/main/resources/diffa.properties
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2010 LShift Ltd.
+#
+# 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.
+#
+
+diffa.host=localhost
+diffa.port=19093
+
+event.notifier.quiet.time=30
18 agent/src/main/resources/docgen/md/rest_collections.st
@@ -0,0 +1,18 @@
+---
+title: REST API Documentation
+layout: default
+---
+
+REST API Documentation
+======================
+
+<div id="collections" markdown="1">
+$collections: { coll |
+$coll.className$
+-----------
+
+$coll.resources: { res |
+* [$res.method$ $res.nicePath$]($res.docPath$)
+}$
+}$
+</div>
13 agent/src/main/resources/docgen/md/rest_menu.st
@@ -0,0 +1,13 @@
+Contents
+--------
+
+$collections: { coll |
+### $coll.className$$\n$
+$coll.resources: { res |
+$if(res.active)$
+* $res.method$ $res.nicePath$$\n$$! currently selected !$
+$else$
+* [$res.method$ $res.nicePath$]($basePath$/$res.docPath$)$\n$
+$endif$
+}$
+}$
61 agent/src/main/resources/docgen/md/rest_resource.st
@@ -0,0 +1,61 @@
+---
+title: $resource.method$ $resource.nicePath$ | REST API Documentation
+layout: default
+---
+
+<div id="menu" markdown="1">
+$rest_menu()$
+</div>
+
+<div id="resources" markdown="1">
+$resource.method$ $resource.nicePath$
+=======================================================
+
+<em>$resource.description$</em>
+
+Entity Type
+-----------
+$resource.entityName$
+
+URL
+---
+http://server:port/diffa-agent/rest/$resource.nicePath$
+
+$if(resource.mandatoryParameters)$
+Mandatory Parameters
+--------------------
+$resource.mandatoryParameters: { p |
+
+### $p.name$
+
+*$p.datatype$*
+
+$p.description$
+}$
+$endif$
+
+$if(resource.optionalParameters)$
+Optional Parameters
+-------------------
+$resource.optionalParameters: { p |
+
+### $p.name$
+
+*$p.datatype$*
+
+$p.description$
+}$
+$endif$
+
+Requires Authentication
+-----------------------
+no $! TODO Make dynamic !$
+
+Example
+-------
+``$resource.example$ ``
+
+JSON Schema
+-----------
+``$resource.schema$ ``
+</div>
34 agent/src/main/resources/logback.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright (C) 2010 LShift Ltd.
+
+ 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.
+
+-->
+
+<configuration>
+
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <layout class="ch.qos.logback.classic.PatternLayout">
+ <Pattern>%d{HH:mm:ss.SSS} [%thread] %file:%line - %msg%n</Pattern>
+ </layout>
+ </appender>
+
+ <logger name="org.hibernate" level="WARN" />
+
+ <root level="debug">
+ <appender-ref ref="STDOUT" />
+ </root>
+
+</configuration>
9 agent/src/main/resources/notifications/mail_body.st
@@ -0,0 +1,9 @@
+You are receiving this mail because a conflict has occurred:
+
+Pairing: $pairKey$
+Id: $entityId$
+Detected At: $timestamp$
+Upstream: $upstream$
+Downstream: $downstream$
+
+Click <a href="$url$">here</a> to see the error in the UI
1  agent/src/main/resources/notifications/mail_subject.st
@@ -0,0 +1 @@
+Conflict detected for pairing $pairKey$
84 agent/src/main/scala/net/lshift/diffa/agent/http/FrontendRequestHandler.scala
@@ -0,0 +1,84 @@
+/**
+ * Copyright (C) 2010 LShift Ltd.
+ *
+ * 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 net.lshift.diffa.agent.http
+
+import org.springframework.web.HttpRequestHandler
+import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
+import org.springframework.http.HttpStatus
+import net.lshift.diffa.kernel.protocol.{TransportResponse, TransportRequest, ProtocolHandler, ProtocolMapper}
+import org.eclipse.jetty.continuation.{ContinuationSupport, Continuation}
+import java.io.OutputStream
+
+/**
+ * Handler for adapting HTTP requests to frontends via protocol handlers.
+ */
+class FrontendRequestHandler(mapper:ProtocolMapper, endpointGroup:String) extends HttpRequestHandler {
+ def handleRequest(request:HttpServletRequest, response:HttpServletResponse) = {
+ val tRequest = new TransportRequest(request.getPathInfo.substring(1), request.getInputStream)
+ val tResponse = new HttpTransportResponse(request, response)
+
+ mapper.lookupHandler(endpointGroup, request.getContentType) match {
+ case Some(handler:ProtocolHandler) =>
+ if (!handler.handleRequest(tRequest, tResponse)) {
+ // Request hasn't finished. Make it asynchronous
+ tResponse.makeAsync
+ }
+ case None =>
+ response.setStatus(HttpStatus.BAD_REQUEST.value)
+ response.getWriter.println("No protocol handlers available for content type")
+ }
+ }
+
+ class HttpTransportResponse(val servletRequest:HttpServletRequest, val servletResponse:HttpServletResponse)
+ extends TransportResponse {
+ var async = false
+
+ val os = servletResponse.getOutputStream
+ def continuation = ContinuationSupport.getContinuation(servletRequest)
+
+ def setStatusCode(code: Int) = servletResponse.setStatus(code)
+ def withOutputStream(f: (OutputStream) => Unit) = {
+ // If the response is marked as async, then we'll need to resume and suspend a continuation
+ // around it.
+ if (async) {
+ // TODO: Standard Jetty model would have continuation.resume called, which would re-trigger the request
+ // handling thread with an empty-ish request. This currently breaks the request handler, since
+ // the post data is null, and therefore the protocol parser crashes out. Turns out that the output
+ // stream is still valid though, so you can actually write to it from other threads even if you don't
+ // resume the connection.
+// continuation.resume
+ }
+ f(os)
+
+ if (async) {
+ os.flush
+
+ // TODO: Unused due to the above comment.
+// continuation.suspend
+ }
+ }
+
+ def makeAsync {
+ val cont = continuation
+ os.flush
+
+ cont.setTimeout(0)
+ cont.suspend
+ async = true
+ }
+ }
+}
95 agent/src/main/scala/net/lshift/diffa/agent/notifications/SmtpNotifier.scala
@@ -0,0 +1,95 @@
+/**
+ * Copyright (C) 2010 LShift Ltd.
+ *
+ * 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 net.lshift.diffa.agent.notifications
+
+import net.lshift.diffa.kernel.config.User
+import net.lshift.diffa.kernel.notifications.{NotificationEvent, NotificationProvider}
+import org.slf4j.{Logger, LoggerFactory}
+import javax.mail.internet.{MimeMessage, InternetAddress}
+import javax.mail.{MessagingException, Message, Session}
+import org.antlr.stringtemplate.StringTemplateGroup
+import java.net.{URISyntaxException, URI}
+
+class SmtpNotifier(val session:Session,
+ val ctx:String,
+ val host:String) extends NotificationProvider {
+
+ val log:Logger = LoggerFactory.getLogger(getClass)
+
+ val templateGroup = new StringTemplateGroup("mail")
+ val bodyMaster = templateGroup.getInstanceOf("notifications/mail_body")
+ val subjectMaster = templateGroup.getInstanceOf("notifications/mail_subject")
+
+ var url:String = null
+
+ try {
+ url = new URI("http://" + host + "/" + ctx).toString
+ }
+ catch {
+ case e:URISyntaxException => log.error("Could not setup link, host = " + host + "; ctx = " + ctx)
+ }
+
+ log.info("Setting the notification URL to: " + url)
+
+
+ def notify(event:NotificationEvent, user:User) = {
+ log.debug("Sending notification about " + event + " to " + user)
+ try {
+
+ val subject = subjectMaster.getInstanceOf
+ subject.setAttribute("pairKey", event.id.pairKey)
+
+ val body = bodyMaster.getInstanceOf
+ body.setAttribute("pairKey", event.id.pairKey)
+ body.setAttribute("entityId", event.id.id)
+ body.setAttribute("timestamp", event.lastUpdated.toString())
+ body.setAttribute("upstream", event.upstreamVsn)
+ body.setAttribute("downstream", event.downstreamVsn)
+ body.setAttribute("url", url)
+
+ var to = new InternetAddress(user.email)
+
+ val message = new MimeMessage(session)
+ message.addRecipient(Message.RecipientType.TO, to)
+ message.setSubject(subject.toString)
+ message.setText(body.toString)
+
+ session.getTransport.sendMessage(message, message.getAllRecipients)
+ }
+ catch {
+ case m:MessagingException => {
+ log.error("SMTP error: " + m.getMessage)
+ }
+ case n:NoSuchFieldException => {
+ log.error("ST error: " + n.getMessage)
+ val clazz = classOf[NotificationEvent]
+ try {
+ val field = clazz.getField("id")
+ }
+ catch {
+ case e:Exception => {
+ log.error("What is going on here?", e)
+ }
+ }
+ }
+ case e:Exception => {
+ log.error("Unknown error", e)
+ }
+ }
+ }
+
+}
80 agent/src/main/scala/net/lshift/diffa/agent/rest/AbstractRestResource.scala
@@ -0,0 +1,80 @@
+/**
+ * Copyright (C) 2010 LShift Ltd.
+ *
+ * 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 net.lshift.diffa.agent.rest
+
+import javax.ws.rs.WebApplicationException
+import javax.ws.rs.core.{Context, UriInfo, Response}
+import org.slf4j.{Logger, LoggerFactory}
+import net.lshift.diffa.kernel.util.MissingObjectException
+
+abstract class AbstractRestResource {
+
+ private val log:Logger = LoggerFactory.getLogger(getClass)
+
+ @Context var uriInfo:UriInfo = null
+
+ def maybe[T](f: String => T, key:String) = {
+ try {
+ f(key)
+ }
+ catch {
+ case e:MissingObjectException => {
+ log.debug("Attempt to perform an operation with a non-existent key:" + key)
+ throw new WebApplicationException(404)
+ }
+ }
+ }
+
+ def maybe[T](f: Seq[String] => T, keys:Seq[String]) = {
+ try {
+ f(keys)
+ }
+ catch {
+ case e:MissingObjectException => {
+ log.debug("Attempt to perform an operation with a non-existent key:" + e.objName)
+ throw new WebApplicationException(404)
+ }
+ }
+ }
+
+ def maybeReturn[T](t:T, f: T => Unit) = {
+ try {
+ f(t)
+ }
+ catch {
+ case e:MissingObjectException => {
+ log.debug("Attempt to perform an operation with a non-existent entity: " + t)
+ throw new WebApplicationException(404)
+ }
+ }
+ t
+ }
+
+ def create[T] (t:T, f: T => Unit, id: T => Any) = {
+ try {
+ f(t)
+ val uri = uriInfo.getAbsolutePathBuilder().path(id(t) + "").build()
+ Response.created(uri).build()
+ }
+ catch {
+ case e:Exception => {
+ log.error("Could not execute function:" + t, e)
+ throw new WebApplicationException(500)
+ }
+ }
+ }
+}
57 agent/src/main/scala/net/lshift/diffa/agent/rest/ActionsResource.scala
@@ -0,0 +1,57 @@
+/**
+ * Copyright (C) 2010 LShift Ltd.
+ *
+ * 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 net.lshift.diffa.agent.rest
+
+import javax.ws.rs._
+import org.springframework.stereotype.Component
+import org.springframework.beans.factory.annotation.Autowired
+import net.lshift.diffa.kernel.client.{ActionRequest, ActionsClient, Actionable}
+import net.lshift.diffa.docgen.annotations.{MandatoryParams, Description}
+import net.lshift.diffa.docgen.annotations.MandatoryParams.MandatoryParam
+import net.lshift.diffa.kernel.participants.ActionResult
+
+@Path("/actions")
+@Component
+class ActionsResource extends AbstractRestResource {
+
+ @Autowired var proxy:ActionsClient = null
+
+ @GET
+ @Path("/{pairId}")
+ @Produces(Array("application/json"))
+ @Description("Returns a list of actions that can be invoked on the pair. ")
+ @MandatoryParams(Array(new MandatoryParam(name="pairId", datatype="string", description="The identifier of the pair")))
+ def listActions(@PathParam("pairId") pairId:String) : Array[Actionable] = {
+ maybe( (x:String) => proxy.listActions(x).toArray, pairId )
+ }
+
+ @POST
+ @Path("/{pairId}/{actionId}/{entityId}")
+ @Produces(Array("application/json"))
+ @Description("Invokes a named action on the pair and passes the id of the entity to which the action should be applied. ")
+ @MandatoryParams(Array(
+ new MandatoryParam(name="pairId", datatype="string", description="The indentifier of the pair"),
+ new MandatoryParam(name="actionId", datatype="string", description="The name of the action to invoke"),
+ new MandatoryParam(name="entityId", datatype="string", description="The id of the pair to perform the action on")
+ ))
+ def invokeAction(@PathParam("pairId") pairId:String,
+ @PathParam("actionId") actionId:String,
+ @PathParam("entityId") entityId:String) : ActionResult = {
+ maybe( (x:String) => proxy.invoke(ActionRequest(x, actionId, entityId)), pairId )
+ }
+
+}
133 agent/src/main/scala/net/lshift/diffa/agent/rest/ConfigurationResource.scala
@@ -0,0 +1,133 @@
+/**
+ * Copyright (C) 2010 LShift Ltd.
+ *
+ * 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 net.lshift.diffa.agent.rest
+
+import org.springframework.stereotype.Component
+import org.springframework.beans.factory.annotation.Autowired
+import org.slf4j.{Logger, LoggerFactory}
+import javax.ws.rs._
+import net.lshift.diffa.kernel.config._
+import net.lshift.diffa.kernel.frontend.Configuration
+import net.lshift.diffa.docgen.annotations.{MandatoryParams, Description}
+import net.lshift.diffa.docgen.annotations.MandatoryParams.MandatoryParam
+
+/**
+ * This is a REST interface the Configuration abstraction.
+ * @see Configuration
+ */
+@Path("/config")
+@Component
+class ConfigurationResource extends AbstractRestResource {
+
+ private val log:Logger = LoggerFactory.getLogger(getClass)
+
+ @Autowired var config:Configuration = null
+
+ @GET
+ @Path("/endpoints")
+ @Produces(Array("application/json"))
+ @Description("Returns a list of all the endpoints registered with the agent.")
+ def listEndpoints() = config.listEndpoints.toArray
+
+ @GET
+ @Path("/groups")
+ @Produces(Array("application/json"))
+ @Description("Returns a list of all the endpoint groups registered with the agent.")
+ def listGroups: Array[GroupContainer] = config.listGroups.toArray
+
+ @GET
+ @Produces(Array("application/json"))
+ @Path("/endpoints/{id}")
+ @Description("Returns an endpoint by its identifier.")
+ @MandatoryParams(Array(new MandatoryParam(name="id", datatype="string", description="Endpoint ID")))
+ def getEndpoint(@PathParam("id") id:String) = maybe[Endpoint]( x => config.getEndpoint(x), id )
+
+ @POST
+ @Path("/endpoints")
+ @Consumes(Array("application/json"))
+ @Description("Registers a new endpoint with the agent.")
+ def createEndpoint(e:Endpoint) = create[Endpoint] (e, (x:Endpoint) => config.createOrUpdateEndpoint(x), (x:Endpoint) => x.name )
+
+ @PUT
+ @Consumes(Array("application/json"))
+ @Produces(Array("application/json"))
+ @Path("/endpoints/{id}")
+ @Description("Updates the attributes of an endpoint that is registered with the agent.")
+ @MandatoryParams(Array(new MandatoryParam(name="id", datatype="string", description="Endpoint ID")))
+ def updateEndpoint(@PathParam("id") id:String, e:Endpoint) = maybeReturn[Endpoint]( e, x => config.createOrUpdateEndpoint(x) )
+
+ @DELETE
+ @Path("/endpoints/{id}")
+ @Description("Removes an endpoint that is registered with the agent.")
+ @MandatoryParams(Array(new MandatoryParam(name="id", datatype="string", description="Endpoint ID")))
+ def deleteEndpoint(@PathParam("id") id:String) = maybe[Unit]( x => config.deleteEndpoint(x), id )
+
+ @POST
+ @Path("/pairs")
+ @Consumes(Array("application/json"))
+ @Description("Creates a new pairing between two endpoints that are already registered with the agent.")
+ def createPair(p:PairDef) = create[PairDef] (p, (x:PairDef) => config.createOrUpdatePair(x), (x:PairDef) => x.pairKey )
+
+ @PUT
+ @Consumes(Array("application/json"))
+ @Produces(Array("application/json"))
+ @Path("/pairs/{id}")
+ @Description("Updates the attributes of a pairing between two endpoints that are already registered with the agent.")
+ @MandatoryParams(Array(new MandatoryParam(name="id", datatype="string", description="Pair ID")))
+ def updatePair(@PathParam("id") id:String, p:PairDef) = maybeReturn[PairDef]( p, x => config.createOrUpdatePair(x) )
+
+ @DELETE
+ @Path("/pairs/{id}")
+ @Description("Removes a pairing between two endpoints that are registered with the agent.")
+ @MandatoryParams(Array(new MandatoryParam(name="id", datatype="string", description="Pair ID")))
+ def deletePair(@PathParam("id") id:String) = maybe[Unit]( x => config.deletePair(x), id )
+
+ @POST
+ @Path("/groups")
+ @Consumes(Array("application/json"))
+ @Description("Creates a new group that can be used to aggregate pairings of endpoints.")
+ def createGroup(g:PairGroup) = create[PairGroup] (g, (x:PairGroup) => config.createOrUpdateGroup(x), (x:PairGroup) => x.key )
+
+ @PUT
+ @Consumes(Array("application/json"))
+ @Produces(Array("application/json"))
+ @Path("/groups/{id}")
+ @Description("Updates the attributes of a group of endpoint pairings.")
+ @MandatoryParams(Array(new MandatoryParam(name="id", datatype="string", description="Group ID")))
+ def updateGroup(@PathParam("id") id:String, g:PairGroup) = maybeReturn[PairGroup]( g, x => config.createOrUpdateGroup(x) )
+
+ @DELETE
+ @Path("/groups/{id}")
+ @Description("Removes a group of endpoint pairings.")
+ @MandatoryParams(Array(new MandatoryParam(name="id", datatype="string", description="Group ID")))
+ def deleteGroup(@PathParam("id") id:String) = maybe[Unit]( x => config.deleteGroup(x), id )
+
+ @GET
+ @Produces(Array("application/json"))
+ @Path("/pairs/{id}")
+ @Description("Returns an endpoint pairing by its identifier.")
+ @MandatoryParams(Array(new MandatoryParam(name="id", datatype="string", description="Pair ID")))
+ def getPair(@PathParam("id") id:String) = maybe[Pair]( x => config.getPair(x),id )
+
+ @GET
+ @Produces(Array("application/json"))
+ @Path("/groups/{id}")
+ @Description("Returns a group of endpoint pairings by its identifier.")
+ @MandatoryParams(Array(new MandatoryParam(name="id", datatype="string", description="Group ID")))
+ def getGroup(@PathParam("id") id:String) = maybe[PairGroup]( x => config.getGroup(x), id )
+
+}
127 agent/src/main/scala/net/lshift/diffa/agent/rest/DifferencesResource.scala
@@ -0,0 +1,127 @@
+/**
+ * Copyright (C) 2010 LShift Ltd.
+ *
+ * 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 net.lshift.diffa.agent.rest
+
+import org.springframework.stereotype.Component
+import javax.ws.rs._
+import core.{EntityTag, Context, Request, Response}
+import org.slf4j.{Logger, LoggerFactory}
+import org.springframework.beans.factory.annotation.Autowired
+import org.joda.time.format.DateTimeFormat
+import net.lshift.diffa.docgen.annotations.{OptionalParams, MandatoryParams, Description}
+import net.lshift.diffa.docgen.annotations.MandatoryParams.MandatoryParam
+import net.lshift.diffa.docgen.annotations.OptionalParams.OptionalParam
+import net.lshift.diffa.kernel.differencing.{SessionScope, SessionManager, SessionEvent}
+@Path("/diffs")
+@Component
+class DifferencesResource extends AbstractRestResource {
+
+ private val log:Logger = LoggerFactory.getLogger(getClass)
+
+ val parser = DateTimeFormat.forPattern("dd/MMM/yyyy:HH:mm:ss Z");
+
+ @Autowired var session:SessionManager = null
+
+ @POST
+ @Path("/sessions")
+ @Description("Returns the URL of an endpoint that can be polled to receive outstanding differences. " +
+ "If a requested pair does not exist, a 404 will be returned.")
+ @OptionalParams(Array(
+ new OptionalParam(name="pairs", datatype="string", description="Comma-separated list of pair IDs"),
+ new OptionalParam(name="start", datatype="date", description="This is the lower bound of the date range for analysis"),
+ new OptionalParam(name="end", datatype="date", description="This is the upper bound of the date range for analysis")
+ ))
+ def subscribe(@QueryParam("pairs") pairs:String,
+ @QueryParam("start") start:String,
+ @QueryParam("end") end:String) = {
+ val scope = pairs match {
+ case null => SessionScope.all
+ case _ => SessionScope.forPairs(pairs.split(","):_*)
+ }
+
+ log.debug("Creating a subscription for this scope: " + scope)
+ val sessionId = maybe((_:Seq[String]) => session.start(scope), scope.includedPairs)
+ val uri = uriInfo.getBaseUriBuilder.path("diffs/sessions/" + sessionId).build()
+ Response.created(uri).build()
+ }
+
+ @GET
+ @Path("/sessions/{sessionId}")
+ @Produces(Array("application/json"))
+ @Description("Returns a list of outstanding differences in the current session. ")
+ @MandatoryParams(Array(new MandatoryParam(name="sessionId", datatype="string", description="Session ID")))
+ @OptionalParams(Array(new OptionalParam(name="since", datatype="integer",
+ description="This will return differences subsequent to the given sequence number.")))
+ def getDifferences(@PathParam("sessionId") sessionId:String,
+ @QueryParam("since") since:String,
+ @Context request:Request) : Response = {
+ try {
+ // Evaluate whether the version of the session has changed
+ val sessionVsn = new EntityTag(session.retrieveSessionVersion(sessionId))
+ request.evaluatePreconditions(sessionVsn) match {
+ case null => // We'll continue with the request
+ case r => throw new WebApplicationException(r.build)
+ }
+
+ val diffs = since match {
+ case null => session.retrieveAllEvents(sessionId)
+ case _ => session.retrieveEventsSince(sessionId, since)
+ }
+
+ Response.ok(diffs.toArray).tag(sessionVsn).build
+ }
+ catch {
+ case e:NoSuchElementException => {
+ log.error("Unsucessful query on sessionId = " + sessionId + "; since = " + since)
+ throw new WebApplicationException(404)
+ }
+ }
+ }
+
+ @GET
+ @Path("/events/{sessionId}/{evtSeqId}")
+ @Produces(Array("text/plain"))
+ @Description("Returns the verbatim detail from each participant for the event that corresponds to the sequence id.")
+ @MandatoryParams(Array(
+ new MandatoryParam(name="sessionId", datatype="string", description="Session ID"),
+ new MandatoryParam(name="evtSeqId", datatype="string", description="Event Sequence ID")
+ ))
+ def getDetail(@PathParam("sessionId") sessionId:String,
+ @PathParam("evtSeqId") evtSeqId:String) : String = {
+ log.info("Detail params sessionId = " + sessionId + "; sequence = " + evtSeqId)
+ try{
+ session.retrieveEventDetail(sessionId, evtSeqId)
+ }
+ catch {
+ case e:Exception => {
+ log.error("Unsucessful query on sessionId = " + sessionId + "; sequence = " + evtSeqId)
+ throw new WebApplicationException(404)
+ }
+ }
+ }
+
+
+ def maybe(s:String) = {
+ try {
+ parser.parseDateTime(s)
+ }
+ catch {
+ case e:Exception => null
+ }
+ }
+
+}
69 agent/src/main/scala/net/lshift/diffa/agent/rest/UsersResource.scala
@@ -0,0 +1,69 @@
+/**
+ * Copyright (C) 2010 LShift Ltd.
+ *
+ * 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 net.lshift.diffa.agent.rest
+
+import org.springframework.stereotype.Component
+import org.springframework.beans.factory.annotation.Autowired
+import net.lshift.diffa.kernel.frontend.Configuration
+import net.lshift.diffa.docgen.annotations.{MandatoryParams, Description}
+import net.lshift.diffa.docgen.annotations.MandatoryParams.MandatoryParam
+import net.lshift.diffa.kernel.config.User
+import javax.ws.rs._
+
+/**
+ * This handles all of the user specific admin
+ */
+@Path("/security")
+@Component
+class UsersResource extends AbstractRestResource {
+
+ @Autowired var config:Configuration = null
+
+ @GET
+ @Path("/users")
+ @Produces(Array("application/json"))
+ @Description("Returns a list of all the users registered with the agent.")
+ def listUsers() = config.listUsers.toArray
+
+ @GET
+ @Produces(Array("application/json"))
+ @Path("/users/{name}")
+ @Description("Returns a user by its name.")
+ @MandatoryParams(Array(new MandatoryParam(name="name", datatype="string", description="Username")))
+ def getUser(@PathParam("name") name:String) = maybe[User]( x => config.getUser(x), name )
+
+ @POST
+ @Path("/users")
+ @Consumes(Array("application/json"))
+ @Description("Registers a new user with the agent.")
+ def createEndpoint(e:User) = create[User] (e, (x:User) => config.createOrUpdateUser(x), (x:User) => x.name )
+
+ @PUT
+ @Consumes(Array("application/json"))
+ @Produces(Array("application/json"))
+ @Path("/users/{name}")
+ @Description("Updates the attributes of a user that is registered with the agent.")
+ @MandatoryParams(Array(new MandatoryParam(name="name", datatype="string", description="Username")))
+ def updateEndpoint(@PathParam("name") name:String, u:User) = maybeReturn[User]( u, x => config.createOrUpdateUser(u) )
+ // TODO This PUT is buggy
+
+ @DELETE
+ @Path("/users/{name}")
+ @Description("Removes an endpoint that is registered with the agent.")
+ @MandatoryParams(Array(new MandatoryParam(name="name", datatype="string", description="Username")))
+ def deleteEndpoint(@PathParam("name") name:String) = maybe[Unit]( x => config.deleteUser(x), name )
+}
38 agent/src/main/scala/net/lshift/diffa/agent/util/HibernatePropertiesFactory.scala
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) 2010 LShift Ltd.
+ *
+ * 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 net.lshift.diffa.agent.util
+
+import org.springframework.beans.factory.FactoryBean
+import java.util.Properties
+
+/**
+ * Factory for creating the configuration to run Hibernate. Abstracted since we'd like
+ * to be able to configure Hibernate via the context.xml, and this just doesn't seem
+ * to be possible via the provided mechanisms.
+ */
+class HibernatePropertiesFactory(dialect:String, createDB:Boolean)
+ extends FactoryBean[Properties] {
+ private val props = new Properties
+ props.setProperty("hibernate.dialect", dialect)
+ if (createDB) {
+ props.setProperty("hibernate.hbm2ddl.auto", "create-drop")
+ }
+
+ def isSingleton = true
+ def getObjectType = classOf[Properties]
+ def getObject = props
+}
31 agent/src/main/webapp/WEB-INF/RESTful-context.xml
@@ -0,0 +1,31 @@
+<!--
+
+ Copyright (C) 2010 LShift Ltd.
+
+ 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.
+
+-->
+
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:context="http://www.springframework.org/schema/context"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/aop
+ http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
+ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
+
+ <context:annotation-config/>
+
+ <context:component-scan base-package="net.lshift.diffa.agent.rest"/>
+
+</beans>
256 agent/src/main/webapp/WEB-INF/applicationContext.xml
@@ -0,0 +1,256 @@
+<!--
+
+ Copyright (C) 2010 LShift Ltd.
+
+ 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.
+
+-->
+
+<beans
+ xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
+
+
+ <bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+ <property name="properties">
+ <bean class="org.springframework.jndi.JndiObjectFactoryBean">
+ <property name="jndiName" value="java:comp/env/diffaProperties"/>
+ </bean>
+ </property>
+ </bean>
+
+ <bean id="configurationRequestHandler" class="net.lshift.diffa.agent.http.FrontendRequestHandler">
+ <constructor-arg ref="protocolMapper" />
+ <constructor-arg value="configuration" />
+ </bean>
+ <bean id="changesRequestHandler" class="net.lshift.diffa.agent.http.FrontendRequestHandler">
+ <constructor-arg ref="protocolMapper" />
+ <constructor-arg value="changes" />
+ </bean>
+ <bean id="differencesRequestHandler" class="net.lshift.diffa.agent.http.FrontendRequestHandler">
+ <constructor-arg ref="protocolMapper" />
+ <constructor-arg value="differences" />
+ </bean>
+ <bean id="matchesRequestHandler" class="net.lshift.diffa.agent.http.FrontendRequestHandler">
+ <constructor-arg ref="protocolMapper" />
+ <constructor-arg value="matches" />
+ </bean>
+
+
+ <!--
+ =============
+ Protocols
+ =============
+ -->
+ <bean id="protocolMapper" class="net.lshift.diffa.kernel.protocol.ProtocolMapper">
+ <constructor-arg>
+ <map>
+ <entry key="changes">
+ <list>
+ <bean class="net.lshift.diffa.messaging.json.ChangesHandler">
+ <constructor-arg ref="changesFrontend" />
+ </bean>
+ </list>
+ </entry>
+ </map>
+ </constructor-arg>
+ </bean>
+
+
+ <!--
+ ============
+ Policies
+ ============
+ -->
+ <bean id="versionPolicyManager" class="net.lshift.diffa.kernel.differencing.VersionPolicyManager"/>
+
+ <bean id="registerSamePolicy" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
+ <property name="targetObject" ref="versionPolicyManager"/>
+ <property name="targetMethod" value="registerPolicy"/>
+ <property name="arguments">
+ <list>
+ <value>same</value>
+ <bean class="net.lshift.diffa.kernel.differencing.SameVersionPolicy">
+ <constructor-arg ref="versionCorrelationStore" />
+ <constructor-arg ref="sessionManager" />
+ </bean>
+ </list>
+ </property>
+ </bean>
+
+ <bean id="registerCorrelatedPolicy" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
+ <property name="targetObject" ref="versionPolicyManager"/>
+ <property name="targetMethod" value="registerPolicy"/>
+ <property name="arguments">
+ <list>
+ <value>correlated</value>
+ <bean class="net.lshift.diffa.kernel.differencing.CorrelatedVersionPolicy">
+ <constructor-arg ref="versionCorrelationStore" />
+ <constructor-arg ref="sessionManager" />
+ </bean>
+ </list>
+ </property>
+ </bean>
+
+ <!--
+ ============
+ Matching
+ ============
+ -->
+ <bean id="matchingManager" class="net.lshift.diffa.kernel.matching.LocalEventMatchingManager" destroy-method="close">
+ <constructor-arg ref="diffaConfigStore" />
+ </bean>
+
+
+ <!--
+ ============
+ Sessions
+ ============
+ -->
+ <bean id="sessionManager" class="net.lshift.diffa.kernel.differencing.DefaultSessionManager">
+ <constructor-arg ref="diffaConfigStore" />
+ <constructor-arg ref="sessionCacheProvider" />
+ <constructor-arg ref="matchingManager" />
+ <constructor-arg ref="versionPolicyManager" />
+ <constructor-arg ref="participantFactory" />
+ </bean>
+ <bean id="sessionCacheProvider" class="net.lshift.diffa.kernel.differencing.LocalSessionCacheProvider" />
+
+ <!--
+ ============
+ Notifications
+ ============
+ -->
+
+ <bean id="notifier" class="net.lshift.diffa.kernel.notifications.EventNotifier" destroy-method="destroy">
+ <constructor-arg ref="sessionManager" />
+ <constructor-arg ref="diffaConfigStore" />
+ <constructor-arg>
+ <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
+ <property name="targetClass" value="org.joda.time.Period"/>
+ <property name="targetMethod" value="minutes"/>
+ <property name="arguments">
+ <list>
+ <value>${event.notifier.quiet.time}</value>
+ </list>
+ </property>
+ </bean>
+ </constructor-arg>
+ </bean>
+
+ <bean id="registerSmtpProvider" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
+ <property name="targetObject" ref="notifier"/>
+ <property name="targetMethod" value="registerProvider"/>
+ <property name="arguments">
+ <bean class="net.lshift.diffa.agent.notifications.SmtpNotifier">
+ <constructor-arg index="0">
+ <bean class="org.springframework.jndi.JndiObjectFactoryBean">
+ <property name="jndiName" value="java:comp/env/mail/Session"/>
+ </bean>
+ </constructor-arg>
+ <constructor-arg index="1">
+ <bean class="org.springframework.jndi.JndiObjectFactoryBean">
+ <property name="jndiName" value="java:comp/env/diffaCustomRoot"/>
+ </bean>
+ </constructor-arg>
+ <constructor-arg index="2" value="${diffa.host}:${diffa.port}"/>
+ </bean>
+ </property>
+ </bean>
+
+ <!--
+ ================
+ Participants
+ ================
+ -->
+ <bean id="participantFactory" class="net.lshift.diffa.kernel.participants.ParticipantFactory">
+ <constructor-arg>
+ <list>
+ <bean class="net.lshift.diffa.messaging.json.JSONRestParticipantProtocolFactory" />
+ </list>
+ </constructor-arg>
+ </bean>
+
+
+ <!--
+ =============
+ Frontends
+ =============
+ -->
+ <bean id="configurationFrontend" class="net.lshift.diffa.kernel.frontend.Configuration">
+ <constructor-arg ref="diffaConfigStore"/>
+ <constructor-arg ref="matchingManager"/>
+ <constructor-arg ref="sessionManager"/>
+ </bean>
+ <bean id="changesFrontend" class="net.lshift.diffa.kernel.frontend.Changes">
+ <constructor-arg ref="diffaConfigStore"/>
+ <constructor-arg ref="versionPolicyManager"/>
+ <constructor-arg ref="matchingManager"/>
+ </bean>
+ <bean id="matchesFrontend" class="net.lshift.diffa.kernel.frontend.Matches">
+ <constructor-arg ref="matchingManager"/>
+ </bean>
+ <bean id="actionsFrontend" class="net.lshift.diffa.kernel.frontend.ActionsProxy">
+ <constructor-arg ref="diffaConfigStore"/>
+ <constructor-arg ref="participantFactory" />
+ </bean>
+
+ <!--
+ ========
+ Data
+ ========
+ -->
+ <bean id="diffaSessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
+ <property name="dataSource">
+ <bean class="org.springframework.jndi.JndiObjectFactoryBean">
+ <property name="jndiName" value="java:comp/env/jdbc/diffaDS"/>
+ </bean>
+ </property>
+ <property name="hibernateProperties">
+ <bean class="net.lshift.diffa.agent.util.HibernatePropertiesFactory">
+ <constructor-arg index="0">
+ <bean class="org.springframework.jndi.JndiObjectFactoryBean">
+ <property name="jndiName" value="java:comp/env/diffaHibernateDialect"/>
+ </bean>
+ </constructor-arg>
+ <constructor-arg index="1">
+ <bean class="org.springframework.jndi.JndiObjectFactoryBean">
+ <property name="jndiName" value="java:comp/env/diffaCreateDB"/>
+ </bean>
+ </constructor-arg>
+ </bean>
+ </property>
+ <property name="mappingResources">
+ <list>
+ <value>net/lshift/diffa/kernel/differencing/Correlations.hbm.xml</value>
+ <value>net/lshift/diffa/kernel/config/Config.hbm.xml</value>
+ </list>
+ </property>
+ </bean>
+ <bean id="diffaConfigStore" class="net.lshift.diffa.kernel.config.HibernateConfigStore">
+ <constructor-arg index="0" ref="diffaSessionFactory" />
+ </bean>
+ <bean id="versionCorrelationStore" class="net.lshift.diffa.kernel.differencing.HibernateVersionCorrelationStore">
+ <constructor-arg index="0" ref="diffaSessionFactory" />
+ </bean>
+
+ <!--
+ ========
+ Boot the REST proxy
+ ========
+ -->
+
+ <import resource="RESTful-context.xml"/>
+
+</beans>
115 agent/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright (C) 2010 LShift Ltd.
+
+ 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.
+
+-->
+
+<web-app version="2.4">
+
+ <listener>
+ <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+ </listener>
+
+ <listener>
+ <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
+ </listener>
+
+ <servlet>
+ <servlet-name>jerseyspring</servlet-name>
+ <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
+ <load-on-startup>1</load-on-startup>
+ <init-param>
+ <param-name>com.sun.jersey.config.property.packages</param-name>
+ <param-value>net.lshift.diffa.agent.rest;org.codehaus.jackson.jaxrs</param-value>
+ </init-param>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>jerseyspring</servlet-name>
+ <url-pattern>/rest/*</url-pattern>
+ </servlet-mapping>
+
+ <servlet>
+ <servlet-name>configurationRequestHandler</servlet-name>
+ <servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class>
+ </servlet>
+ <servlet-mapping>
+ <servlet-name>configurationRequestHandler</servlet-name>
+ <url-pattern>/configuration/*</url-pattern>
+ </servlet-mapping>
+
+ <servlet>
+ <servlet-name>changesRequestHandler</servlet-name>
+ <servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class>
+ </servlet>
+ <servlet-mapping>
+ <servlet-name>changesRequestHandler</servlet-name>
+ <url-pattern>/changes/*</url-pattern>
+ </servlet-mapping>
+
+ <servlet>
+ <servlet-name>differencesRequestHandler</servlet-name>
+ <servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class>
+ </servlet>
+ <servlet-mapping>
+ <servlet-name>differencesRequestHandler</servlet-name>
+ <url-pattern>/differences/*</url-pattern>
+ </servlet-mapping>
+
+ <servlet>
+ <servlet-name>matchesRequestHandler</servlet-name>
+ <servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class>
+ </servlet>
+ <servlet-mapping>
+ <servlet-name>matchesRequestHandler</servlet-name>
+ <url-pattern>/matches/*</url-pattern>
+ </servlet-mapping>
+
+ <resource-ref>
+ <description>Diff Engine DataSource</description>
+ <res-ref-name>jdbc/diffaDS</res-ref-name>
+ <res-type>javax.sql.DataSource</res-type>
+ <res-auth>Container</res-auth>
+ </resource-ref>
+
+ <resource-ref>
+ <description>diffa Mail</description>
+ <res-ref-name>mail/Session</res-ref-name>
+ <res-type>javax.mail.Session</res-type>
+ <res-auth>Container</res-auth>
+ </resource-ref>
+
+ <env-entry>
+ <env-entry-name>diffaCustomRoot</env-entry-name>
+ <env-entry-value>__context__</env-entry-value>
+ <env-entry-type>java.lang.String</env-entry-type>
+ </env-entry>
+ <env-entry>
+ <env-entry-name>diffaProperties</env-entry-name>
+ <env-entry-type>java.io.Properties</env-entry-type>
+ </env-entry>
+ <env-entry>