Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'groovy2'

Conflicts:
	core/build.gradle
	core/src/main/groovyx/gaelyk/GaelykCategory.groovy
	core/src/main/groovyx/gaelyk/GaelykServletContextListener.groovy
	core/src/main/groovyx/gaelyk/routes/Route.groovy
	core/src/test/groovyx/gaelyk/DatastoreShortcutsTest.groovy
	core/src/test/groovyx/gaelyk/MailSupportTest.groovy
	core/src/test/groovyx/gaelyk/XGTransactionsTest.groovy
	core/src/test/groovyx/gaelyk/plugins/PluginsHandlerTest.groovy
	core/src/test/groovyx/gaelyk/search/SearchShortcutsTest.groovy
	template-project/build.gradle
	website/build.gradle
	website/war/WEB-INF/pages/download.gtpl
  • Loading branch information...
commit 7eb1539114b2089ad453552e4477789a98ec0414 2 parents 8917925 + 28911e3
@musketyr musketyr authored
Showing with 6,225 additions and 5,107 deletions.
  1. +5 −1 build.gradle
  2. 0  {core → }/codenarc.groovy
  3. +10 −0 common-codenarc-ci.gradle
  4. +28 −0 common-nexus.gradle
  5. +43 −0 common.gradle
  6. +2 −0  core/.groovy/suggestions.xdsl
  7. +41 −42 core/build.gradle
  8. BIN  core/lib/appengine-api-1.0-sdk-1.6.6.jar
  9. BIN  core/lib/appengine-api-labs-1.6.6.jar
  10. BIN  core/lib/appengine-api-stubs.jar
  11. BIN  core/lib/appengine-local-runtime.jar
  12. BIN  core/lib/appengine-testing.jar
  13. BIN  core/lib/groovy-all-1.8.6.jar
  14. BIN  core/lib/jsp-api.jar
  15. BIN  core/lib/junit-4.8.2.jar
  16. BIN  core/lib/servlet-api.jar
  17. +4 −2 core/src/main/groovyx/gaelyk/GaelykBindingEnhancer.groovy
  18. +1 −1  core/src/main/groovyx/gaelyk/GaelykBindings.groovy
  19. +3 −1 core/src/main/groovyx/gaelyk/GaelykBindingsTransformation.groovy
  20. +0 −2,893 core/src/main/groovyx/gaelyk/GaelykCategory.groovy
  21. +0 −60 core/src/main/groovyx/gaelyk/GaelykCategoryBase.java
  22. +15 −21 core/src/main/groovyx/gaelyk/GaelykServlet.groovy
  23. +21 −1 core/src/main/groovyx/gaelyk/GaelykServletContextListener.groovy
  24. +52 −26 core/src/main/groovyx/gaelyk/GaelykTemplateServlet.groovy
  25. +1 −1  core/src/main/groovyx/gaelyk/ImagesServiceWrapper.groovy
  26. +3 −1 core/src/main/groovyx/gaelyk/QueueAccessor.groovy
  27. +20 −3 core/src/main/groovyx/gaelyk/UnindexedEntityWrapper.groovy
  28. +15 −13 core/src/main/groovyx/gaelyk/cache/CacheHandler.groovy
  29. +10 −8 core/src/main/groovyx/gaelyk/cache/CachedResponse.groovy
  30. +15 −0 core/src/main/groovyx/gaelyk/datastore/Entity.groovy
  31. +40 −8 core/src/main/groovyx/gaelyk/datastore/EntityTransformation.groovy
  32. +6 −6 core/src/main/groovyx/gaelyk/datastore/EntityTransformationHelper.groovy
  33. +15 −0 core/src/main/groovyx/gaelyk/datastore/Ignore.groovy
  34. +15 −0 core/src/main/groovyx/gaelyk/datastore/Indexed.groovy
  35. +15 −0 core/src/main/groovyx/gaelyk/datastore/Key.groovy
  36. +104 −45 core/src/main/groovyx/gaelyk/datastore/PogoEntityCoercion.groovy
  37. +15 −0 core/src/main/groovyx/gaelyk/datastore/Unindexed.groovy
  38. +15 −0 core/src/main/groovyx/gaelyk/datastore/Version.groovy
  39. +54 −0 core/src/main/groovyx/gaelyk/extensions/BackendExtensions.groovy
  40. +324 −0 core/src/main/groovyx/gaelyk/extensions/BlobstoreExtensions.groovy
  41. +57 −0 core/src/main/groovyx/gaelyk/extensions/CapabilitiesExtensions.groovy
  42. +39 −0 core/src/main/groovyx/gaelyk/extensions/ChannelExtensions.groovy
  43. +879 −0 core/src/main/groovyx/gaelyk/extensions/DatastoreExtensions.groovy
  44. +13 −10 ...main/groovyx/gaelyk/{ExpirationTimeCategory.groovy → extensions/ExpirationTimeExtensionMethods.groovy}
  45. +248 −0 core/src/main/groovyx/gaelyk/extensions/FilesExtensions.groovy
  46. +233 −0 core/src/main/groovyx/gaelyk/extensions/ImageExtensions.groovy
  47. +106 −0 core/src/main/groovyx/gaelyk/extensions/MailExtensions.groovy
  48. +374 −0 core/src/main/groovyx/gaelyk/extensions/MemcacheExtensions.groovy
  49. +216 −0 core/src/main/groovyx/gaelyk/extensions/MiscExtensions.groovy
  50. +51 −0 core/src/main/groovyx/gaelyk/extensions/NamespaceStaticExtensions.groovy
  51. +217 −0 core/src/main/groovyx/gaelyk/extensions/SearchExtensions.groovy
  52. +64 −0 core/src/main/groovyx/gaelyk/extensions/ServletExtensions.groovy
  53. +149 −0 core/src/main/groovyx/gaelyk/extensions/TaskQueueExtensions.groovy
  54. +198 −0 core/src/main/groovyx/gaelyk/extensions/UrlFetchExtensions.groovy
  55. +280 −0 core/src/main/groovyx/gaelyk/extensions/XmppExtensions.groovy
  56. +17 −9 core/src/main/groovyx/gaelyk/logging/GroovyLogger.groovy
  57. +1 −1  core/src/main/groovyx/gaelyk/logging/LoggerAccessor.groovy
  58. +1 −1  core/src/main/groovyx/gaelyk/plugins/LazyBinding.groovy
  59. +4 −14 core/src/main/groovyx/gaelyk/plugins/PluginBaseScript.groovy
  60. +11 −15 core/src/main/groovyx/gaelyk/plugins/PluginsHandler.groovy
  61. +4 −1 core/src/main/groovyx/gaelyk/plugins/PluginsListBaseScript.groovy
  62. +15 −0 core/src/main/groovyx/gaelyk/query/Clause.groovy
  63. +37 −13 core/src/main/groovyx/gaelyk/query/QueryBuilder.groovy
  64. +15 −0 core/src/main/groovyx/gaelyk/query/QueryDslTransformation.groovy
  65. +15 −0 core/src/main/groovyx/gaelyk/query/QuerySyntaxException.groovy
  66. +15 −0 core/src/main/groovyx/gaelyk/query/QueryType.groovy
  67. +15 −0 core/src/main/groovyx/gaelyk/query/SortClause.groovy
  68. +15 −0 core/src/main/groovyx/gaelyk/query/WhereClause.groovy
  69. +3 −1 core/src/main/groovyx/gaelyk/routes/CapabilityAwareDestination.groovy
  70. +1 −1  core/src/main/groovyx/gaelyk/routes/HttpMethod.groovy
  71. +57 −0 core/src/main/groovyx/gaelyk/routes/OptionalRoutesHelper.groovy
  72. +1 −1  core/src/main/groovyx/gaelyk/routes/RedirectionType.groovy
  73. +2 −9 core/src/main/groovyx/gaelyk/routes/Route.groovy
  74. +11 −3 core/src/main/groovyx/gaelyk/routes/RoutesBaseScript.groovy
  75. +40 −34 core/src/main/groovyx/gaelyk/routes/RoutesFilter.groovy
  76. +3 −1 core/src/main/groovyx/gaelyk/routes/RoutingRule.groovy
  77. +20 −0 core/src/main/groovyx/gaelyk/search/DocumentDefinitions.groovy
  78. +69 −17 core/src/main/groovyx/gaelyk/search/FieldDefinitions.groovy
  79. +4 −0 core/src/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule
  80. 0  core/src/{main → resources}/META-INF/services/org.codehaus.groovy.transform.ASTTransformation
  81. +9 −13 core/src/test/groovyx/gaelyk/BackendServiceTest.groovy
  82. +142 −161 core/src/test/groovyx/gaelyk/BlobstoreServiceTest.groovy
  83. +11 −13 core/src/test/groovyx/gaelyk/CapabilitiesTest.groovy
  84. +4 −5 core/src/test/groovyx/gaelyk/ChannelServiceTest.groovy
  85. +24 −27 core/src/test/groovyx/gaelyk/ClosureMemoizationTest.groovy
  86. +0 −281 core/src/test/groovyx/gaelyk/DatastoreMetadataCategoryTest.groovy
  87. +247 −0 core/src/test/groovyx/gaelyk/DatastoreMetadataExtensionMethodsTest.groovy
  88. +141 −155 core/src/test/groovyx/gaelyk/DatastoreShortcutsTest.groovy
  89. +10 −16 core/src/test/groovyx/gaelyk/EntityPOGOCoercionTest.groovy
  90. +0 −72 core/src/test/groovyx/gaelyk/GaelykCategoryTest.groovy
  91. +13 −0 core/src/test/groovyx/gaelyk/GaelykServletContextListenerTest.groovy
  92. +8 −2 core/src/test/groovyx/gaelyk/GaelykServletTest.groovy
  93. +20 −26 core/src/test/groovyx/gaelyk/ImagesServiceTest.groovy
  94. +6 −8 core/src/test/groovyx/gaelyk/MailParsingTest.groovy
  95. +6 −8 core/src/test/groovyx/gaelyk/MailSupportTest.groovy
  96. +4 −6 core/src/test/groovyx/gaelyk/MailToAdminSupportTest.groovy
  97. +58 −0 core/src/test/groovyx/gaelyk/MiscExtensionMethodsTest.groovy
  98. +0 −51 core/src/test/groovyx/gaelyk/NamespaceCategoryMethodsTest.groovy
  99. +42 −0 core/src/test/groovyx/gaelyk/NamespaceExtensionMethodsTest.groovy
  100. +0 −43 core/src/test/groovyx/gaelyk/NewServletCategoryMethodsTest.groovy
  101. +41 −0 core/src/test/groovyx/gaelyk/NewServletExtensionMethodsTest.groovy
  102. +18 −20 core/src/test/groovyx/gaelyk/QueueTaskTest.groovy
  103. +4 −6 core/src/test/groovyx/gaelyk/SearchServiceTest.groovy
  104. +31 −49 core/src/test/groovyx/gaelyk/UrlFetchServiceEnhancementsTest.groovy
  105. +8 −12 core/src/test/groovyx/gaelyk/XGTransactionsTest.groovy
  106. +125 −141 core/src/test/groovyx/gaelyk/XmppTest.groovy
  107. +11 −12 core/src/test/groovyx/gaelyk/cache/CacheHandlerTest.groovy
  108. +0 −117 core/src/test/groovyx/gaelyk/cache/MemcacheCategoryMethodsTest.groovy
  109. +94 −0 core/src/test/groovyx/gaelyk/cache/MemcacheExtensionMethodsTest.groovy
  110. +183 −171 core/src/test/groovyx/gaelyk/datastore/EntityTransformationSpec.groovy
  111. +108 −70 core/src/test/groovyx/gaelyk/datastore/PogoEntityCoercionTest.groovy
  112. +5 −0 core/src/test/groovyx/gaelyk/datastore/UserDetailsSocial.java
  113. +1 −7 core/src/test/groovyx/gaelyk/plugins/PluginBaseScriptTest.groovy
  114. +5 −13 core/src/test/groovyx/gaelyk/plugins/PluginsHandlerTest.groovy
  115. +190 −214 core/src/test/groovyx/gaelyk/querydsl/QueryDslTest.groovy
  116. +0 −23 core/src/test/groovyx/gaelyk/routes/ExpirationTimeCategoryTest.groovy
  117. +18 −0 core/src/test/groovyx/gaelyk/routes/ExpirationTimeExtensionMethodsTest.groovy
  118. +71 −0 core/src/test/groovyx/gaelyk/routes/OptionalRoutesHelperSpec.groovy
  119. +3 −2 core/src/test/groovyx/gaelyk/routes/RoutesBaseScriptTest.groovy
  120. +49 −60 core/src/test/groovyx/gaelyk/search/SearchShortcutsTest.groovy
  121. +21 −6 template-project/build.gradle
  122. +4 −2 website/build.gradle
  123. +40 −6 website/war/WEB-INF/pages/download.gtpl
  124. +28 −2 website/war/WEB-INF/pages/tutorial/flexibleUrlRouting.gtpl
  125. +1 −1  website/war/WEB-INF/pages/tutorial/gaeShortcuts/backend.gtpl
  126. +8 −0 website/war/WEB-INF/pages/tutorial/gaeShortcuts/datastore.gtpl
  127. +7 −0 website/war/WEB-INF/pages/tutorial/gaeShortcuts/images.gtpl
  128. +2 −2 website/war/WEB-INF/pages/tutorial/gaeShortcuts/jabber.gtpl
  129. +8 −5 website/war/WEB-INF/pages/tutorial/gaeShortcuts/search.gtpl
  130. +4 −15 website/war/WEB-INF/pages/tutorial/plugins.gtpl
View
6 build.gradle
@@ -4,7 +4,11 @@ final groovyVersion = '1.8.9-SNAPSHOT'
allprojects {
apply plugin: 'idea'
- version = '1.3-SNAPSHOT'
+
+ ext.groovyVersion = '2.1.0'
+ ext.appEngineVersion = '1.7.4'
+
+ version = '2.0-SNAPSHOT'
group = 'org.gaelyk'
}
View
0  core/codenarc.groovy → codenarc.groovy
File renamed without changes
View
10 common-codenarc-ci.gradle
@@ -0,0 +1,10 @@
+/**
+ * Configures codenarc for Jenkins
+ *
+ */
+
+ext.codenarcRuleSetFiles = [ 'codenarc.groovy' ]
+ext.codenarcSources = [ 'src/main', 'src/test' ]
+ext.codenarcReportType = 'xml'
+ext.codenarcReportFile = 'codenarc.xml'
+apply from: 'https://raw.github.com/evgeny-goldin/gradle-plugins/master/codenarc/CodeNarc.gradle'
View
28 common-nexus.gradle
@@ -0,0 +1,28 @@
+/** Add following snippet to your build script to enable nexus plugin */
+
+
+/*
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'org.gradle.api.plugins:gradle-nexus-plugin:0.2'
+ }
+}
+*/
+
+apply plugin: 'maven'
+apply plugin: 'nexus'
+
+tasks.withType(Upload) { // map the groovy configuration to compile in the pom
+ repositories.withType(MavenResolver) {
+ pom.scopeMappings.addMapping(1, configurations.groovy, 'compile')
+ }
+}
+
+boolean signingEnabled = !hasProperty('skipSigning') || skipSigning != 'true'
+nexus {
+ sign = signingEnabled
+}
View
43 common.gradle
@@ -0,0 +1,43 @@
+ext.gaelykGroovyVersion = '2.1.0'
+ext.gaelykSpockVersion = '0.7-groovy-2.0'
+ext.gaelykAppEngineVersion = '1.7.4'
+ext.gaelykLatestVersion = '2.0-SNAPSHOT'
+ext.gaelykSpockLatestVersion = '0.4-SNAPSHOT'
+ext.gaelykGradleVersion = '1.2'
+
+repositories {
+ mavenCentral()
+ maven { // for Gaelyk Snapshots
+ url 'https://oss.sonatype.org/content/repositories/snapshots'
+ }
+ maven { // for Spock Groovy 2.0 Support
+ url "http://oss.sonatype.org/content/repositories/snapshots/"
+ }
+ maven { // for Groovy Snapshots
+ url "http://snapshots.repository.codehaus.org/"
+ }
+}
+
+apply plugin: 'groovy'
+apply plugin: 'eclipse'
+apply plugin: 'idea'
+
+sourceCompatibility = 1.6
+targetCompatibility = 1.6
+
+task wrapper(type: Wrapper) {
+ gradleVersion = gaelykGradleVersion
+}
+
+configurations.all {
+ resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
+}
+
+println "Using following common Gaelyk setting:"
+
+println "Groovy Version: $ext.gaelykGroovyVersion"
+println "Spock Version: $ext.gaelykSpockVersion"
+println "App Engine Version: $ext.gaelykAppEngineVersion"
+println "Gaelyk Version: $ext.gaelykLatestVersion"
+println "Gaelyk Spock Version: $ext.gaelykSpockLatestVersion"
+println "Gradle Version: $ext.gaelykGradleVersion"
View
2  core/.groovy/suggestions.xdsl
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<projectSuggestions/>
View
83 core/build.gradle
@@ -1,32 +1,16 @@
-apply plugin: 'groovy'
-apply plugin: 'maven'
-apply plugin: 'eclipse'
-
-/**
- * http://evgeny-goldin.com/wiki/Gradle-CodeNarc-plugin
- */
-ext.codenarcPriority1Violations = 0 // Work
-ext.codenarcPriority2Violations = 213 // in
-ext.codenarcPriority3Violations = 985 // progress ..
-ext.codenarcRuleSetFiles = [ 'codenarc.groovy' ]
-ext.codenarcSources = [ 'src/main', 'src/test' ]
-
-// apply from: 'https://raw.github.com/evgeny-goldin/gradle-plugins/master/src/main/groovy/CodeNarc.gradle'
+apply from: '../common.gradle'
+apply plugin: 'maven'
-sourceCompatibility = 1.6
-targetCompatibility = 1.6
defaultTasks 'clean', 'codenarc', 'build'
test {
maxHeapSize = '320m'
}
-
// various directory places and file names
final tmpProj = '../template-project'
final zipName = "${buildDir}/gaelyk-template-project-${version}.zip"
-final projLib = "${tmpProj}/src/main/webapp/WEB-INF/lib"
final website = '../website'
final apiDir = "${website}/war/api"
@@ -37,6 +21,9 @@ sourceSets {
groovy {
srcDir 'src/main'
}
+ resources {
+ srcDir 'src/resources'
+ }
}
test {
@@ -50,14 +37,20 @@ sourceSets {
}
dependencies {
- compile "com.google.appengine:appengine-api-1.0-sdk:$appEngineVersion"
- compile "com.google.appengine:appengine-api-labs:$appEngineVersion"
+ groovy "org.codehaus.groovy:groovy-all:${ext.gaelykGroovyVersion}", {
+ force = true
+ }
+// compile "org.codehaus.groovy:groovy-servlet:${ext.gaelykGroovyVersion}"
+// groovy "org.codehaus.groovy:groovy-ant:${ext.gaelykGroovyVersion}" // for groovy-doc only
+ compile "com.google.appengine:appengine-api-1.0-sdk:${ext.gaelykAppEngineVersion}"
+ compile "com.google.appengine:appengine-api-labs:${ext.gaelykAppEngineVersion}"
compile 'javax.servlet:servlet-api:2.5'
+// groovy "org.codehaus.groovy:groovy-test:${ext.gaelykGroovyVersion}" // for tests only
testCompile 'javax.servlet.jsp:jsp-api:2.2'
testCompile 'junit:junit:4.8.2'
- testCompile "com.google.appengine:appengine-testing:$appEngineVersion"
- testCompile "com.google.appengine:appengine-api-stubs:$appEngineVersion"
- testCompile 'org.spockframework:spock-core:0.7-groovy-1.8', {
+ testCompile "com.google.appengine:appengine-testing:${ext.gaelykAppEngineVersion}"
+ testCompile "com.google.appengine:appengine-api-stubs:${ext.gaelykAppEngineVersion}"
+ testCompile "org.spockframework:spock-core:${ext.gaelykSpockVersion}", {
exclude group: 'org.codehaus.groovy', module: 'groovy-all'
}
testCompile 'cglib:cglib-nodep:2.2.2'
@@ -127,19 +120,25 @@ tasks.withType(Upload) { // map the groovy configuration to compile in the pom
}
}
-if(hasProperty('sonatypeUsername') && hasProperty('sonatypePassword')){
- apply plugin: 'signing'
- signing {
- sign configurations.archives
+if(hasProperty('nexusUsername') && hasProperty('nexusPassword')){
+ boolean signingEnabled = !hasProperty('skipSigning') || skipSigning != 'true'
+ if(signingEnabled){
+ apply plugin: 'signing'
+ signing {
+ sign configurations.archives
+ }
}
+
uploadArchives {
repositories {
mavenDeployer {
- beforeDeployment { MavenDeployment deployment -> signPom(deployment) }
+ if(signingEnabled){
+ beforeDeployment { deployment -> signPom(deployment) }
+ }
def auth = {
- authentication(userName: sonatypeUsername, password: sonatypePassword)
+ authentication(userName: nexusUsername, password: nexusPassword)
}
repository(url: 'https://oss.sonatype.org/service/local/staging/deploy/maven2/', auth)
@@ -199,19 +198,19 @@ if(hasProperty('sonatypeUsername') && hasProperty('sonatypePassword')){
}
//mess with the generated pom to set the 'packaging' tag
- pom.withXml { XmlProvider xmlProvider ->
- def xml = xmlProvider.asString()
- def pomXml = new XmlParser().parse(new ByteArrayInputStream(xml.toString().bytes))
-
- pomXml.version[0] + { packaging('jar') }
-
- def newXml = new StringWriter()
- def printer = new XmlNodePrinter(new PrintWriter(newXml))
- printer.preserveWhitespace = true
- printer.print(pomXml)
- xml.setLength(0)
- xml.append(newXml.toString())
- }
+// pom.withXml { XmlProvider xmlProvider ->
+// def xml = xmlProvider.asString()
+// def pomXml = new XmlParser().parse(xml.toString())
+//
+// pomXml.version[0] + { packaging('jar') }
+//
+// def newXml = new StringWriter()
+// def printer = new XmlNodePrinter(new PrintWriter(newXml))
+// printer.preserveWhitespace = true
+// printer.print(pomXml)
+// xml.setLength(0)
+// xml.append(newXml.toString())
+// }
}
}
}
View
BIN  core/lib/appengine-api-1.0-sdk-1.6.6.jar
Binary file not shown
View
BIN  core/lib/appengine-api-labs-1.6.6.jar
Binary file not shown
View
BIN  core/lib/appengine-api-stubs.jar
Binary file not shown
View
BIN  core/lib/appengine-local-runtime.jar
Binary file not shown
View
BIN  core/lib/appengine-testing.jar
Binary file not shown
View
BIN  core/lib/groovy-all-1.8.6.jar
Binary file not shown
View
BIN  core/lib/jsp-api.jar
Binary file not shown
View
BIN  core/lib/junit-4.8.2.jar
Binary file not shown
View
BIN  core/lib/servlet-api.jar
Binary file not shown
View
6 core/src/main/groovyx/gaelyk/GaelykBindingEnhancer.groovy
@@ -1,5 +1,5 @@
/*
- * Copyright 2009-2011 the original author or authors.
+ * Copyright 2009-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,6 +38,7 @@ import com.google.appengine.api.prospectivesearch.ProspectiveSearchServiceFactor
import com.google.appengine.api.log.LogServiceFactory
import com.google.appengine.api.search.SearchServiceFactory
import javax.servlet.http.HttpServletRequest
+import groovy.transform.CompileStatic
/**
* Class responsible for adding adding Google App Engine related services into the binding of Groovlets and Templates.
@@ -46,6 +47,7 @@ import javax.servlet.http.HttpServletRequest
* @author Guillaume Laforge
* @author Benjamin Muschko
*/
+@CompileStatic
class GaelykBindingEnhancer {
/**
@@ -147,7 +149,7 @@ class GaelykBindingEnhancer {
version: SystemProperty.version.get(),
],
gaelyk: [
- version: '1.2'
+ version: '2.0'
],
id: SystemProperty.applicationId.get(),
version: SystemProperty.applicationVersion.get()
View
2  core/src/main/groovyx/gaelyk/GaelykBindings.groovy
@@ -1,5 +1,5 @@
/*
- * Copyright 2009-2011 the original author or authors.
+ * Copyright 2009-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
View
4 core/src/main/groovyx/gaelyk/GaelykBindingsTransformation.groovy
@@ -1,5 +1,5 @@
/*
- * Copyright 2009-2011 the original author or authors.
+ * Copyright 2009-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -43,6 +43,7 @@ import com.google.appengine.api.users.UserService
import com.google.appengine.api.users.UserServiceFactory
import com.google.appengine.api.xmpp.XMPPService
import com.google.appengine.api.xmpp.XMPPServiceFactory
+import groovy.transform.CompileStatic
import groovyx.gaelyk.logging.LoggerAccessor
import org.codehaus.groovy.ast.ASTNode
import org.codehaus.groovy.ast.AnnotationNode
@@ -75,6 +76,7 @@ import com.google.appengine.api.search.SearchServiceFactory
* @author Guillaume Laforge
*/
@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
+@CompileStatic
class GaelykBindingsTransformation implements ASTTransformation {
void visit(ASTNode[] nodes, SourceUnit unit) {
View
2,893 core/src/main/groovyx/gaelyk/GaelykCategory.groovy
0 additions, 2,893 deletions not shown
View
60 core/src/main/groovyx/gaelyk/GaelykCategoryBase.java
@@ -1,60 +0,0 @@
-/*
- * Copyright 2009-2011 the original author or authors.
- *
- * 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 groovyx.gaelyk;
-
-
-import com.google.appengine.api.memcache.Expiration;
-import com.google.appengine.api.memcache.MemcacheService;
-import com.google.appengine.api.memcache.MemcacheServiceException;
-
-/**
- * Base Category methods decorating the Google App Engine SDK classes
- * adding new shortcut methods to simplify the usage of the SDK
- * from within Groovy servlets and templates.
- *
- * @author Guillaume Laforge
- * @author Scott Murphy
- */
-public class GaelykCategoryBase {
-
- /**
- * Get an object from the cache, with a object key, ignoring any exceptions.
- *
- * @param key the Object key
- * @return the value stored under that key
- */
- static Object get(MemcacheService memcache, Object key) {
- try {
- return memcache.get(key);
- } catch (MemcacheServiceException mse) {}
- return null;
- }
-
- /**
- * Put an object in the cache under a Object key, coerced to a String, with an expiration and a SetPolicy, ignoring any exceptions.
- *
- * @param key a Object key
- * @param value the value to put in the cache
- * @param expiration expiration of the key/value
- * @param policy a SetPolicy
- */
- static void put(MemcacheService memcache, Object key, Object value, Expiration expiration, MemcacheService.SetPolicy policy) {
- try {
- memcache.put(key, value, expiration, policy);
- } catch (MemcacheServiceException mse) {}
- }
-
-}
View
36 core/src/main/groovyx/gaelyk/GaelykServlet.groovy
@@ -1,5 +1,5 @@
/*
- * Copyright 2009-2011 the original author or authors.
+ * Copyright 2009-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,13 +15,8 @@
*/
package groovyx.gaelyk
-import java.io.IOException
-
import groovy.servlet.GroovyServlet
import groovy.servlet.ServletBinding
-import groovy.servlet.ServletCategory
-import groovy.util.GroovyScriptEngine
-import groovy.util.ResourceException
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
@@ -29,11 +24,11 @@ import javax.servlet.http.HttpServletResponse
import javax.servlet.ServletConfig
import javax.servlet.ServletRequest
-
-
import groovyx.gaelyk.plugins.PluginsHandler
import groovyx.gaelyk.logging.GroovyLogger
+import groovy.transform.CompileStatic
+
/**
* The Gaelyk servlet extends Groovy's own Groovy servlet
* to inject Google App Engine dedicated services in the binding of the Groolets.
@@ -52,6 +47,7 @@ class GaelykServlet extends GroovyServlet {
@Override
+ @CompileStatic
void init(ServletConfig config) {
super.init(config)
// Set up the scripting engine
@@ -65,10 +61,11 @@ class GaelykServlet extends GroovyServlet {
* @param binding the binding to enhance
*/
@Override
+ @CompileStatic
protected void setVariables(ServletBinding binding) {
GaelykBindingEnhancer.bind(binding)
PluginsHandler.instance.enrich(binding)
- binding.setVariable("log", getLog(binding.request))
+ binding.setVariable("log", getLog((ServletRequest)binding.getVariable('request')))
}
private GroovyLogger getLog(ServletRequest request){
@@ -76,29 +73,24 @@ class GaelykServlet extends GroovyServlet {
}
/**
- * Service incoming requests applying the <code>GaelykCategory</code>
- * and the other categories defined by the installed plugins.
+ * Service incoming requests and executing before/after actions defined by plugins.
*
* @param request the request
* @param response the response
* @throws IOException when anything goes wrong
*/
@Override
+ @CompileStatic
void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
- use([
- ServletCategory,
- GaelykCategory,
- * PluginsHandler.instance.categories
- ]) {
- PluginsHandler.instance.executeBeforeActions(request, response)
- doService(request, response)
- PluginsHandler.instance.executeAfterActions(request, response)
- }
+ PluginsHandler.instance.executeBeforeActions(request, response)
+ doService(request, response)
+ PluginsHandler.instance.executeAfterActions(request, response)
}
/**
* Handle web requests to the GroovyServlet
*/
+ @CompileStatic
private void doService(HttpServletRequest request, HttpServletResponse response) throws IOException {
// Set it to HTML by default
response.contentType = "text/html; charset="+encoding
@@ -156,12 +148,14 @@ class GaelykServlet extends GroovyServlet {
}
}
+ @CompileStatic
private runGroovlet(String scriptUri, ServletBinding binding) {
gse.run(scriptUri, binding)
}
+ @CompileStatic
private runPrecompiled(String precompiledClassName, ServletBinding binding) {
- Class precompiledClass = Class.forName(precompiledClassName)
+ Class<Script> precompiledClass = Class.forName(precompiledClassName)
Script precompiled = precompiledClass.newInstance([binding]as Object[])
precompiled.run()
}
View
22 core/src/main/groovyx/gaelyk/GaelykServletContextListener.groovy
@@ -1,5 +1,5 @@
/*
- * Copyright 2011 the original author or authors.
+ * Copyright 2011-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,8 @@
*/
package groovyx.gaelyk
+import groovy.transform.CompileStatic
+
import javax.servlet.ServletContextListener
import javax.servlet.ServletContextEvent
import groovyx.gaelyk.plugins.PluginsHandler
@@ -26,6 +28,7 @@ import groovyx.gaelyk.plugins.PluginsHandler
* @author Guillaume Laforge
* @author Marcin Erdmann
*/
+@CompileStatic
class GaelykServletContextListener implements ServletContextListener {
/**
@@ -33,10 +36,27 @@ class GaelykServletContextListener implements ServletContextListener {
* @param servletContextEvent
*/
void contextInitialized(ServletContextEvent servletContextEvent) {
+ verifyGroovyVersion()
PluginsHandler.instance.initPlugins(servletContextEvent.servletContext)
}
void contextDestroyed(ServletContextEvent servletContextEvent) {
// nothing special to be done
}
+
+ /**
+ * Verifies if proper version of Gaelyk is used.
+ * Currently Groovy 2.x.x versions are supported but this may change
+ * until final Gaelyk 2.0 version will be released.
+ */
+ static void verifyGroovyVersion(){
+ if(!verifyGroovyVersionInternal(GroovySystem.version)){
+ throw new IllegalStateException("You must use Groovy 2.x to run Gaelyk ${GaelykBindingEnhancer.app['gaelyk']['version']} application.")
+ }
+ }
+
+ private static boolean verifyGroovyVersionInternal(String version){
+ version.startsWith('2.')
+ }
+
}
View
78 core/src/main/groovyx/gaelyk/GaelykTemplateServlet.groovy
@@ -1,5 +1,5 @@
/*
- * Copyright 2009-2011 the original author or authors.
+ * Copyright 2009-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,18 +16,20 @@
package groovyx.gaelyk
import groovy.servlet.ServletBinding
-import groovy.servlet.ServletCategory
import groovy.servlet.TemplateServlet
import groovy.text.Template
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
-
-import groovyx.gaelyk.plugins.PluginsHandler
import javax.servlet.ServletConfig
+import org.codehaus.groovy.runtime.InvokerInvocationException;
+
+import groovyx.gaelyk.plugins.PluginsHandler
import groovyx.gaelyk.logging.GroovyLogger
+import groovy.transform.CompileStatic
+
/**
* The Gaelyk template servlet extends Groovy's own template servlet
* to inject Google App Engine dedicated services in the binding of the Groolets.
@@ -44,19 +46,36 @@ class GaelykTemplateServlet extends TemplateServlet {
private Closure serviceClosure = {}
@Override
+ @CompileStatic
void init(ServletConfig config) {
if (config.getInitParameter('preferPrecompiled') != 'false' && (config.getInitParameter('preferPrecompiled') == 'true' || !GaelykBindingEnhancer.localMode)) {
serviceClosure = { HttpServletRequest request, HttpServletResponse response, ServletBinding binding ->
try {
try {
runPrecompiled(getPrecompiledClassName(request), binding, response)
- } catch(ClassNotFoundException e){
- runTemplate(request, response, binding)
+ } catch(InvokerInvocationException e){
+ if(e.cause instanceof ClassNotFoundException){
+ try {
+ runTemplate(request, response, binding)
+ } catch(InvokerInvocationException iie){
+ throw iie.cause ?: iie
+ }
+ } else {
+ throw e.cause ?: e
+ }
}
} catch(FileNotFoundException te){
response.setStatus(HttpServletResponse.SC_NOT_FOUND)
+ log('Exception serving template', te)
+ throw te
} catch(IllegalAccessException te){
response.setStatus(HttpServletResponse.SC_FORBIDDEN)
+ log('Exception serving template', te)
+ throw te
+ } catch(e){
+ response.setStatus(HttpServletResponse.SC_NOT_FOUND)
+ log('Exception serving template', e)
+ throw e
}
}
} else {
@@ -64,13 +83,21 @@ class GaelykTemplateServlet extends TemplateServlet {
try {
try {
runTemplate(request, response, binding)
- } catch(FileNotFoundException e){
- runPrecompiled(getPrecompiledClassName(request), binding, response)
- } catch(IllegalAccessException e){
- runPrecompiled(getPrecompiledClassName(request), binding, response)
+ } catch(InvokerInvocationException e){
+ if(e.cause instanceof FileNotFoundException){
+ try {
+ runPrecompiled(getPrecompiledClassName(request), binding, response)
+ } catch(InvokerInvocationException iie){
+ throw iie.cause ?: iie
+ }
+ } else {
+ throw e.cause
+ }
}
- } catch(ClassNotFoundException te){
+ } catch(e){
response.setStatus(HttpServletResponse.SC_NOT_FOUND)
+ log('Exception serving template', e)
+ throw e
}
}
}
@@ -91,24 +118,18 @@ class GaelykTemplateServlet extends TemplateServlet {
}
/**
- * Service incoming requests applying the <code>GaelykCategory</code>
- * and the other categories defined by the installed plugins.
+ * Service incoming requests and executing before/after actions defined by plugins
*
* @param request the request
* @param response the response
* @throws IOException when anything goes wrong
*/
@Override
+ @CompileStatic
void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
- use([
- ServletCategory,
- GaelykCategory,
- * PluginsHandler.instance.categories
- ]) {
- PluginsHandler.instance.executeBeforeActions(request, response)
- doService(request, response)
- PluginsHandler.instance.executeAfterActions(request, response)
- }
+ PluginsHandler.instance.executeBeforeActions(request, response)
+ doService(request, response)
+ PluginsHandler.instance.executeAfterActions(request, response)
}
/**
@@ -125,6 +146,7 @@ class GaelykTemplateServlet extends TemplateServlet {
serviceClosure(request, response, binding)
}
+ @CompileStatic
private runTemplate(HttpServletRequest request, HttpServletResponse response, ServletBinding binding) {
Template template = tryFindTemplate(request)
response.setStatus(HttpServletResponse.SC_OK)
@@ -135,6 +157,7 @@ class GaelykTemplateServlet extends TemplateServlet {
template.make(binding.getVariables()).writeTo(out)
}
+ @CompileStatic
private Template tryFindTemplate(HttpServletRequest request) {
String uri = getScriptUri(request)
File file = super.getScriptUriAsFile(request)
@@ -145,7 +168,8 @@ class GaelykTemplateServlet extends TemplateServlet {
}
return getTemplate(file)
}
- throw new FileNotFoundException()
+ String message = file ? "Cannot find template: $file.absolutePath" : "Cannot find template for URI $uri"
+ throw new FileNotFoundException(message)
}
/**
@@ -167,14 +191,16 @@ class GaelykTemplateServlet extends TemplateServlet {
ret += match[0][3]
ret
}
-
+
+ @CompileStatic
static String packageToDir(String pkg){
return pkg.replaceAll(/[^a-zA-Z0-9\/]/, '_').replace('/', '.').toLowerCase()
}
+ @CompileStatic
private runPrecompiled(String precompiledClassName, ServletBinding binding, HttpServletResponse response) {
try {
- Class precompiledClass = Class.forName(precompiledClassName)
+ Class<Script> precompiledClass = Class.forName(precompiledClassName)
Script precompiled = precompiledClass.newInstance([binding]as Object[])
precompiled.run()
response.setStatus(HttpServletResponse.SC_OK)
@@ -183,7 +209,7 @@ class GaelykTemplateServlet extends TemplateServlet {
throw e
}
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR)
- e.printStackTrace(binding.out)
+ e.printStackTrace((PrintWriter)binding.getVariable('out'))
}
}
}
View
2  core/src/main/groovyx/gaelyk/ImagesServiceWrapper.groovy
@@ -1,5 +1,5 @@
/*
- * Copyright 2009-2011 the original author or authors.
+ * Copyright 2009-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
View
4 core/src/main/groovyx/gaelyk/QueueAccessor.groovy
@@ -1,5 +1,5 @@
/*
- * Copyright 2009-2011 the original author or authors.
+ * Copyright 2009-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
package groovyx.gaelyk
import com.google.appengine.api.taskqueue.QueueFactory
+import groovy.transform.CompileStatic
/**
* Holder for queues providing a Groovy map-like syntax for accessing queues.
@@ -23,6 +24,7 @@ import com.google.appengine.api.taskqueue.QueueFactory
*
* @author Guillaume Laforge
*/
+@CompileStatic
class QueueAccessor {
/**
View
23 core/src/main/groovyx/gaelyk/UnindexedEntityWrapper.groovy
@@ -1,7 +1,23 @@
+/*
+ * Copyright 2009-2012 the original author or authors.
+ *
+ * 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 groovyx.gaelyk
+import groovyx.gaelyk.extensions.DatastoreExtensions
import com.google.appengine.api.datastore.Entity
-import static GaelykCategory.transformValueForStorage
+import groovy.transform.CompileStatic
/**
* Wrapper around an entity, so as to be able to set unindexed properties, with:
@@ -12,6 +28,7 @@ import static GaelykCategory.transformValueForStorage
*
* @author Guillaume Laforge
*/
+@CompileStatic
class UnindexedEntityWrapper {
Entity entity
@@ -30,7 +47,7 @@ class UnindexedEntityWrapper {
* @param value of the unindexed property
*/
void setProperty(String name, value) {
- entity.setUnindexedProperty(name, transformValueForStorage(value))
+ entity.setUnindexedProperty(name, DatastoreExtensions.transformValueForStorage(value))
}
/**
@@ -40,6 +57,6 @@ class UnindexedEntityWrapper {
* @param value of the unindexed property
*/
void setAt(String name, value) {
- entity.setUnindexedProperty(name, transformValueForStorage(value))
+ entity.setUnindexedProperty(name, DatastoreExtensions.transformValueForStorage(value))
}
}
View
28 core/src/main/groovyx/gaelyk/cache/CacheHandler.groovy
@@ -1,5 +1,5 @@
/*
- * Copyright 2009-2011 the original author or authors.
+ * Copyright 2009-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@ import groovyx.gaelyk.logging.GroovyLogger
*
* @author Guillaume Laforge
*/
+@groovy.transform.CompileStatic
class CacheHandler {
// Date formatter for caching headers date creation
@@ -77,11 +78,11 @@ class CacheHandler {
return
}
}
- serveAndCacheOrServeFromCache(request, response, result.destination, uri, route.cacheExpiration)
+ serveAndCacheOrServeFromCache(request, response, (String)result['destination'], uri, route.cacheExpiration)
} else {
log.config "Route not cacheable"
- request.getRequestDispatcher(result.destination).forward request, response
+ request.getRequestDispatcher((String)result['destination']).forward request, response
}
}
@@ -93,9 +94,10 @@ class CacheHandler {
String lastModifiedKey = "last-modified-$uri"
def memcache = MemcacheServiceFactory.memcacheService
+ def asyncMemcache = MemcacheServiceFactory.asyncMemcacheService
- def content = memcache.get(contentKey)
- def type = memcache.get(typeKey)
+ byte[] content = (byte[])memcache.get(contentKey)
+ String type = memcache.get(typeKey)
// the resource is still present in the cache
if (content && type) {
@@ -120,20 +122,20 @@ class CacheHandler {
log.config "Wrapping a response for caching and forwarding to resource to be cached"
def cachedResponse = new CachedResponse(response)
request.getRequestDispatcher(destination).forward request, cachedResponse
- def byteArray = cachedResponse.output.toByteArray()
-
- log.config "Byte array of wrapped response will be put in memcache: ${new String(byteArray)}"
-
- // put the output in memcache
- memcache.put(contentKey, byteArray, duration)
- memcache.put(typeKey, cachedResponse.contentType, duration)
- memcache.put(lastModifiedKey, lastModifiedString, duration)
+ byte[] byteArray = cachedResponse.output.toByteArray()
log.config "Serving content-type and byte array"
// output back to the screen
response.contentType = cachedResponse.contentType
response.outputStream << byteArray
+
+ log.config "Byte array of wrapped response will be put in memcache: ${new String(byteArray)}"
+
+ // put the output in memcache
+ asyncMemcache.put(contentKey, byteArray as byte[], duration)
+ asyncMemcache.put(typeKey, cachedResponse.contentType, duration)
+ asyncMemcache.put(lastModifiedKey, lastModifiedString, duration)
}
}
}
View
18 core/src/main/groovyx/gaelyk/cache/CachedResponse.groovy
@@ -1,5 +1,5 @@
/*
- * Copyright 2009-2011 the original author or authors.
+ * Copyright 2009-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,14 +24,16 @@ import javax.servlet.http.HttpServletResponse
*
* @author Guillaume Laforge
*/
+@groovy.transform.CompileStatic
class CachedResponse extends HttpServletResponseWrapper {
ByteArrayOutputStream output = new ByteArrayOutputStream(8192)
- CustomServletOutputStream stream = new CustomServletOutputStream(output: output)
+ CustomServletOutputStream stream = new CustomServletOutputStream()
PrintWriter writer = new PrintWriter(new OutputStreamWriter(stream, "UTF-8"))
CachedResponse(HttpServletResponse response) {
super(response)
+ stream.out = output
}
/**
@@ -48,26 +50,26 @@ class CachedResponse extends HttpServletResponseWrapper {
* Custom extension of <code>CustomServletOutpuStream</code>
*/
static class CustomServletOutputStream extends ServletOutputStream {
- OutputStream output
+ OutputStream out
void write(int i) {
- output.write(i)
+ out.write(i)
}
void write(byte[] bytes) {
- output.write(bytes)
+ out.write(bytes)
}
void write(byte[] bytes, int offset, int length) {
- output.write(bytes, offset, length)
+ out.write(bytes, offset, length)
}
void flush() {
- output.flush()
+ out.flush()
}
void close() {
- output.close()
+ out.close()
}
}
}
View
15 core/src/main/groovyx/gaelyk/datastore/Entity.groovy
@@ -1,3 +1,18 @@
+/*
+ * Copyright 2009-2012 the original author or authors.
+ *
+ * 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 groovyx.gaelyk.datastore
import java.lang.annotation.ElementType
View
48 core/src/main/groovyx/gaelyk/datastore/EntityTransformation.groovy
@@ -1,3 +1,18 @@
+/*
+ * Copyright 2009-2012 the original author or authors.
+ *
+ * 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 groovyx.gaelyk.datastore
import groovyx.gaelyk.query.QueryBuilder
@@ -23,6 +38,7 @@ import org.codehaus.groovy.ast.stmt.ExpressionStatement
import org.codehaus.groovy.ast.stmt.ReturnStatement
import org.codehaus.groovy.control.CompilePhase
import org.codehaus.groovy.control.SourceUnit
+import org.codehaus.groovy.syntax.SyntaxException;
import org.codehaus.groovy.transform.ASTTransformation
import org.codehaus.groovy.transform.GroovyASTTransformation
@@ -38,11 +54,11 @@ class EntityTransformation implements ASTTransformation {
AnnotationNode anno = (AnnotationNode) nodes[0]
ClassNode parent = (ClassNode) nodes[1]
- handleKey(parent, source)
+ ClassNode keyType = handleKey(parent, source)
parent.addMethod(addDelegatedMethod('save', ClassHelper.makeWithoutCaching(Key).plainNodeReference))
parent.addMethod(addDelegatedMethod('delete'))
- parent.addMethod(addStaticDelegatedMethod(parent, "get", [key: Object], parent.plainNodeReference))
- parent.addMethod(addStaticDelegatedMethod(parent, "delete", [key: Object]))
+ parent.addMethod(addStaticDelegatedMethod(parent, "get", [key: keyType], parent.plainNodeReference))
+ parent.addMethod(addStaticDelegatedMethod(parent, "delete", [key: keyType]))
parent.addMethod(addStaticDelegatedMethod(parent, "find", [key: Closure], parent.plainNodeReference))
parent.addMethod(addStaticDelegatedMethod(parent, "count", [:], ClassHelper.int_TYPE))
parent.addMethod(addStaticDelegatedMethod(parent, "count", [query: Closure], ClassHelper.int_TYPE))
@@ -69,7 +85,7 @@ class EntityTransformation implements ASTTransformation {
return pogoListNode
}
- private handleKey(ClassNode parent, SourceUnit source) {
+ private ClassNode handleKey(ClassNode parent, SourceUnit source) {
ClassNode keyAnnoClassNode = ClassHelper.makeWithoutCaching(groovyx.gaelyk.datastore.Key)
PropertyNode existingKeyProperty = parent.properties.find { PropertyNode prop ->
@@ -77,6 +93,11 @@ class EntityTransformation implements ASTTransformation {
anno.classNode == keyAnnoClassNode
}
}
+
+ if(existingKeyProperty && !(existingKeyProperty.type in [ClassHelper.long_TYPE, ClassHelper.STRING_TYPE])){
+ source.addError(new SyntaxException("Only long or String are allowed as a key properties!", existingKeyProperty.lineNumber, existingKeyProperty.columnNumber))
+ return
+ }
if (!existingKeyProperty) {
existingKeyProperty = new PropertyNode(new FieldNode('id', Modifier.PUBLIC, ClassHelper.long_TYPE, parent, null), Modifier.PUBLIC, null, null)
@@ -85,7 +106,7 @@ class EntityTransformation implements ASTTransformation {
}
BlockStatement getKeyBlock = new BlockStatement()
- getKeyBlock.addStatement(new ExpressionStatement(new VariableExpression(existingKeyProperty.name, existingKeyProperty.type)))
+ getKeyBlock.addStatement(new ExpressionStatement(new VariableExpression(existingKeyProperty)))
parent.addMethod new MethodNode(
'get$key',
@@ -96,8 +117,9 @@ class EntityTransformation implements ASTTransformation {
getKeyBlock
)
+ def mce = new MethodCallExpression(new VariableExpression('this'), 'setProperty', new ArgumentListExpression(new ConstantExpression(existingKeyProperty.name), new VariableExpression(existingKeyProperty)))
BlockStatement setKeyBlock = new BlockStatement()
- setKeyBlock.addStatement(new ExpressionStatement(new MethodCallExpression(new VariableExpression('this'), 'setProperty', new ArgumentListExpression(new ConstantExpression(existingKeyProperty.name), new VariableExpression(existingKeyProperty.name, existingKeyProperty.type)))))
+ setKeyBlock.addStatement(new ExpressionStatement(mce))
parent.addMethod new MethodNode(
'set$key',
@@ -107,6 +129,7 @@ class EntityTransformation implements ASTTransformation {
ClassNode.EMPTY_ARRAY,
setKeyBlock
)
+ existingKeyProperty.type
}
private MethodNode addDelegatedMethod(String name, ClassNode returnType = ClassHelper.DYNAMIC_TYPE) {
@@ -130,19 +153,28 @@ class EntityTransformation implements ASTTransformation {
private MethodNode addStaticDelegatedMethod(ClassNode parent, String name, Map<String, Class> parameters, ClassNode returnType = ClassHelper.DYNAMIC_TYPE) {
def helper = ClassHelper.makeWithoutCaching(EntityTransformationHelper).plainNodeReference
+ def methodParams = parameters.collect { String n, cls ->
+ ClassNode clsNode = cls instanceof ClassNode ? cls : ClassHelper.makeWithoutCaching(cls)
+ new Parameter(clsNode.plainNodeReference, n)
+
+ }
+ def variables = methodParams.collect {
+ new VariableExpression(it)
+ }
+
BlockStatement block = new BlockStatement()
block.addStatement(new ReturnStatement(new MethodCallExpression(
new ClassExpression(helper), name,
new ArgumentListExpression(
new ClassExpression(parent),
- * parameters.collect { String n, Class cls -> new VariableExpression(n)}
+ * variables
))))
new MethodNode(
name,
Modifier.PUBLIC | Modifier.STATIC,
returnType,
- parameters.collect { String n, Class cls -> new Parameter(ClassHelper.makeWithoutCaching(cls).plainNodeReference, n)} as Parameter[],
+ methodParams as Parameter[],
ClassNode.EMPTY_ARRAY,
block
)
View
12 core/src/main/groovyx/gaelyk/datastore/EntityTransformationHelper.groovy
@@ -1,6 +1,5 @@
package groovyx.gaelyk.datastore
-import groovyx.gaelyk.GaelykCategory
import groovyx.gaelyk.query.QueryBuilder
import groovyx.gaelyk.query.QueryType
@@ -12,6 +11,7 @@ import com.google.appengine.api.datastore.FetchOptions
import com.google.appengine.api.datastore.Key
import com.google.appengine.api.datastore.KeyFactory
import com.google.appengine.api.datastore.Query
+import groovyx.gaelyk.extensions.DatastoreExtensions
/**
* Utility class used for delegating on from classes annotated with {@link groovyx.gaelyk.datastore.Entity}.
@@ -22,7 +22,7 @@ import com.google.appengine.api.datastore.Query
class EntityTransformationHelper {
static Key save(Object pogo) {
- Key key = GaelykCategory.save(GaelykCategory.asType(pogo, Entity))
+ Key key = DatastoreExtensions.save(DatastoreExtensions.asType(pogo, Entity))
if (CharSequence.class.isAssignableFrom(pogo.getClass().getMethod('get$key').returnType)) {
pogo.set$key(key.name)
} else {
@@ -32,12 +32,12 @@ class EntityTransformationHelper {
}
static void delete(Object pogo) {
- GaelykCategory.delete(GaelykCategory.asType(pogo, Entity))
+ DatastoreExtensions.delete(DatastoreExtensions.asType(pogo, Entity))
}
static <P> P get(Class<P> pogoClass, long key) {
try {
- return GaelykCategory.asType(GaelykCategory.get(KeyFactory.createKey(pogoClass.simpleName, key)), pogoClass)
+ return DatastoreExtensions.asType(DatastoreExtensions.get(KeyFactory.createKey(pogoClass.simpleName, key)), pogoClass)
} catch (EntityNotFoundException e) {
return null;
}
@@ -45,14 +45,14 @@ class EntityTransformationHelper {
static <P> P get(Class<P> pogoClass, String key) {
try {
- return GaelykCategory.asType(GaelykCategory.get(KeyFactory.createKey(pogoClass.simpleName, key)), pogoClass)
+ return DatastoreExtensions.asType(DatastoreExtensions.get(KeyFactory.createKey(pogoClass.simpleName, key)), pogoClass)
} catch (EntityNotFoundException e) {
return null;
}
}
static <P> void delete(Class<P> pogoClass, key) {
- GaelykCategory.delete(KeyFactory.createKey(pogoClass.simpleName, key))
+ DatastoreExtensions.delete(KeyFactory.createKey(pogoClass.simpleName, key))
}
static int count(Class<?> pogoClass) {
View
15 core/src/main/groovyx/gaelyk/datastore/Ignore.groovy
@@ -1,3 +1,18 @@
+/*
+ * Copyright 2009-2012 the original author or authors.
+ *
+ * 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 groovyx.gaelyk.datastore
import java.lang.annotation.Retention
View
15 core/src/main/groovyx/gaelyk/datastore/Indexed.groovy
@@ -1,3 +1,18 @@
+/*
+ * Copyright 2009-2012 the original author or authors.
+ *
+ * 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 groovyx.gaelyk.datastore
import java.lang.annotation.Retention
View
15 core/src/main/groovyx/gaelyk/datastore/Key.groovy
@@ -1,3 +1,18 @@
+/*
+ * Copyright 2009-2012 the original author or authors.
+ *
+ * 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 groovyx.gaelyk.datastore
import java.lang.annotation.Retention
View
149 core/src/main/groovyx/gaelyk/datastore/PogoEntityCoercion.groovy
@@ -1,22 +1,47 @@
+/*
+ * Copyright 2009-2012 the original author or authors.
+ *
+ * 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 groovyx.gaelyk.datastore
-import groovyx.gaelyk.GaelykCategory
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Map.Entry;
import com.google.appengine.api.datastore.Entities
import com.google.appengine.api.datastore.Entity
import com.google.appengine.api.datastore.EntityNotFoundException
+import groovy.transform.Canonical;
+import groovy.transform.CompileStatic;
+import groovy.transform.TypeCheckingMode;
+import groovyx.gaelyk.extensions.DatastoreExtensions
+
/**
* Utility class handling the POGO to Entity coercion, and Entity to POGO coercion as well.
*
* @author Guillaume Laforge
*/
+@CompileStatic
class PogoEntityCoercion {
/**
* Cached information about annotations present on POGO classes
*/
- private static Map<Class, Map> cachedProps = [:]
+ private static Map<Class, Map<String, PropertyDescriptor>> cachedProps = [:]
+
/**
* Goes through all the properties and finds how they are annotated.
@@ -25,36 +50,56 @@ class PogoEntityCoercion {
* and whose values are maps of ignore/unindexed/key/value keys and
* values of closures returning booleans
*/
- static Map props(Object p) {
+ // XXX: if the static compilation is skipped, props[property] = value works good
+ // @CompileStatic(TypeCheckingMode.SKIP)
+ static Map<String, PropertyDescriptor> props(Object p) {
def clazz = p.class
- boolean defaultIndexed = true;
+ boolean defaultIndexed = true
if(clazz.isAnnotationPresent(groovyx.gaelyk.datastore.Entity.class)){
- defaultIndexed = ! clazz.getAnnotation(groovyx.gaelyk.datastore.Entity).unindexed()
+ defaultIndexed = !clazz.getAnnotation(groovyx.gaelyk.datastore.Entity).unindexed()
}
if (!cachedProps.containsKey(clazz)) {
- cachedProps[clazz] = p.properties.findAll { String k, v -> !(k in ['class', 'metaClass']) && !(k.startsWith('$') || k.startsWith('_')) }
- .collectEntries { String k, v ->
- def annos
- try {
- annos = p.class.getDeclaredField(k).annotations
- } catch (e) {
- try {
- annos = p.class.getDeclaredMethod("get${k.capitalize()}").annotations
- } catch (NoSuchMethodException nsme){
- return [(k), [ignore: {true}, unindexed: {false}, key: {false}]]
- }
- }
- [(k), [
- ignore: { annos.any { it instanceof Ignore } },
- unindexed: { defaultIndexed ? annos.any { it instanceof Unindexed } : !annos.any { it instanceof Indexed } },
- key: { annos.any { it instanceof Key } },
- version: { annos.any { it instanceof Version } }
- ]]
- }
+ Map<String, PropertyDescriptor> props = [:]
+ for(String property in p.properties.keySet()){
+ if(!(property in ['class', 'metaClass']) && !(property.startsWith('$') || property.startsWith('_'))){
+ def descriptor = getPropertyDescriptorFor(clazz, property, defaultIndexed)
+ // XXX: This does not work. If it is Groovy bug, it's really nasty!
+ // props[property] = descriptor
+ props.put property, descriptor
+ } else {
+ // XXX: This does not work. If it is Groovy bug, it's really nasty!
+ // props[property] = PropertyDescriptor.IGNORED
+ props.put property, PropertyDescriptor.IGNORED
+ }
+ }
+ cachedProps[clazz] = props
}
return cachedProps[clazz]
}
+
+ static PropertyDescriptor getPropertyDescriptorFor(Class clazz, String property, boolean defaultIndexed){
+ def annos
+ def isStatic = false
+ try {
+ Field field = clazz.getDeclaredField(property)
+ isStatic = Modifier.isStatic(field.modifiers)
+ annos = field.annotations
+ } catch (e) {
+ try {
+ Method method = clazz.getDeclaredMethod("get${property.capitalize()}")
+ annos = method.annotations
+ isStatic = Modifier.isStatic(method.modifiers)
+ } catch (NoSuchMethodException nsme){
+ return PropertyDescriptor.IGNORED
+ }
+ }
+ if(isStatic || annos.any { it instanceof Ignore }) return PropertyDescriptor.IGNORED
+ if(annos.any { it instanceof Key }) return PropertyDescriptor.KEY
+ if(annos.any { it instanceof Version }) return PropertyDescriptor.VERSION
+ if(defaultIndexed ? annos.any { it instanceof Unindexed } : !annos.any { it instanceof Indexed }) return PropertyDescriptor.UNINDEXED
+ PropertyDescriptor.INDEXED
+ }
/**
* Find the key in the properties
@@ -62,20 +107,20 @@ class PogoEntityCoercion {
* @param props the properties
* @return the name of the key or null if none is found
*/
- static String findKey(Map props) {
- props.findResult { String prop, Map m ->
+ static String findKey(Map<String, PropertyDescriptor> props) {
+ props.findResult { String prop, PropertyDescriptor m ->
if (m.key()) return prop
}
}
/**
- * Find the key in the properties
+ * Find the version in the properties
*
* @param props the properties
* @return the name of the key or null if none is found
*/
- static String findVersion(Map props) {
- props.findResult { String prop, Map m ->
+ static String findVersion(Map<String, PropertyDescriptor> props) {
+ props.findResult { String prop, PropertyDescriptor m ->
if (m.version()) return prop
}
}
@@ -89,26 +134,25 @@ class PogoEntityCoercion {
static Entity convert(Object p) {
Entity entity
- Map props = props(p)
+ Map<String, PropertyDescriptor> props = props(p)
String key = findKey(props)
- def value = key ? p."$key" : null
+ def value = key ? p.metaClass.getProperty(p, key) : null
if (key && value) {
- entity = new Entity(p.class.simpleName, value)
+ if(value instanceof CharSequence){
+ entity = new Entity(p.class.simpleName, value?.toString())
+ } else {
+ entity = new Entity(p.class.simpleName, ((Number)value).longValue())
+ }
} else {
entity = new Entity(p.class.simpleName)
}
- props.each { String propName, Map m ->
+ props.each { String propName, PropertyDescriptor m ->
if (propName != key) {
if (!props[propName].ignore() && !props[propName].version()) {
- def val = p."$propName"
+ def val = p.metaClass.getProperty(p, propName)
if (props[propName].unindexed()) {
- // TODO: decide the correct behaviour
-// if(!val){
-// entity.removeProperty(propName)
-// } else {
- entity.setUnindexedProperty(propName, p."$propName")
-// }
+ entity.setUnindexedProperty(propName, val)
} else {
if (val instanceof Enum) val = val as String
@@ -132,18 +176,18 @@ class PogoEntityCoercion {
def entityProps = e.getProperties()
def o = clazz.newInstance()
- entityProps.each { k, v ->
+ entityProps.each { String k, v ->
if (o.metaClass.hasProperty(o, k)) {
o[k] = v
}
}
- def classProps = props(o)
+ Map<String, PropertyDescriptor> classProps = props(o)
String key = findKey(classProps)
if (key) {
- o."$key" = e.key.name ?: e.key.id
+ o.metaClass.setProperty(o, key, e.key.name ?: e.key.id)
}
String version = findVersion(classProps)
@@ -151,13 +195,28 @@ class PogoEntityCoercion {
if (version) {
try {
if(e.key) {
- o."$version" = Entities.getVersionProperty(GaelykCategory.get(Entities.createEntityGroupKey(e.key)))
+ o.metaClass.setProperty(o, version, Entities.getVersionProperty(DatastoreExtensions.get(Entities.createEntityGroupKey(e.key))))
}
} catch (EntityNotFoundException ex){
- o."$version" = 0
+ o.metaClass.setProperty(o, version, 0)
}
}
return o
}
}
+
+@CompileStatic
+enum PropertyDescriptor {
+ // ignore,unindex,key, version
+ IGNORED { boolean ignore() { true } },
+ KEY { boolean key() { true } },
+ VERSION { boolean version() { true } },
+ INDEXED,
+ UNINDEXED { boolean unindexed() { true } },
+
+ boolean ignore() { false }
+ boolean unindexed() { false }
+ boolean key() { false }
+ boolean version() { false }
+}
View
15 core/src/main/groovyx/gaelyk/datastore/Unindexed.groovy
@@ -1,3 +1,18 @@
+/*
+ * Copyright 2009-2012 the original author or authors.
+ *
+ * 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 groovyx.gaelyk.datastore
import java.lang.annotation.Retention
View
15 core/src/main/groovyx/gaelyk/datastore/Version.groovy
@@ -1,3 +1,18 @@
+/*
+ * Copyright 2009-2012 the original author or authors.
+ *
+ * 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 groovyx.gaelyk.datastore
import java.lang.annotation.Retention
View
54 core/src/main/groovyx/gaelyk/extensions/BackendExtensions.groovy
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2009-2012 the original author or authors.
+ *
+ * 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 groovyx.gaelyk.extensions
+
+import groovy.transform.CompileStatic
+import com.google.appengine.api.LifecycleManager
+import com.google.appengine.api.backends.BackendService
+import com.google.appengine.api.ThreadManager
+
+/**
+ * Backend service extension methods
+ *
+ * @author Guillaume Laforge
+ */
+class BackendExtensions {
+
+ /**
+ * Shortcut to use closures as shutdown hooks.
+ * <pre><code>
+ * lifecycle.shutdownHook = { ...shutdown logic... }
+ * </code></pre>
+ *
+ * @param manager the lifecycle manager
+ * @param c the closure as shutdown hook
+ */
+ static void shutdownHook(LifecycleManager manager, @DelegatesTo(LifecycleManager.ShutdownHook) Closure c) {
+ manager.setShutdownHook(c as LifecycleManager.ShutdownHook)
+ }
+
+ /**
+ * Runs code in the background thread.
+ *
+ * @param the code supposed to run in background thread
+ */
+ @CompileStatic
+ static Thread run(BackendService backends, Runnable code){
+ Thread thread = ThreadManager.createBackgroundThread(code);
+ thread.start()
+ thread
+ }
+}
View
324 core/src/main/groovyx/gaelyk/extensions/BlobstoreExtensions.groovy
@@ -0,0 +1,324 @@
+/*
+ * Copyright 2009-2012 the original author or authors.
+ *
+ * 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 groovyx.gaelyk.extensions
+
+import groovy.transform.CompileStatic
+import com.google.appengine.api.blobstore.BlobKey
+import com.google.appengine.api.blobstore.BlobstoreInputStream
+import com.google.appengine.api.blobstore.BlobInfo
+import com.google.appengine.api.blobstore.BlobInfoFactory
+import com.google.appengine.api.blobstore.BlobstoreServiceFactory
+import javax.servlet.http.HttpServletResponse
+import com.google.appengine.api.blobstore.ByteRange
+import com.google.appengine.api.images.Image
+import com.google.appengine.api.images.ImagesServiceFactory
+import com.google.appengine.api.images.ImagesService
+import com.google.apphosting.api.ApiProxy
+import com.google.appengine.api.images.ImagesServiceFailureException
+import com.google.appengine.api.blobstore.BlobstoreService
+
+/**
+ * Blobstore extension module methods
+ *
+ * @author Guillaume Laforge
+ */
+class BlobstoreExtensions {
+ /**
+ * Creates an <code>InputStream</code> over the blob.
+ * The stream is passed as parameter of the closure.
+ * This methods takes care of properly opening and closing the stream.
+ * You can use this method as follows:
+ * <pre><code>
+ * blobKey.withStream { inputstream -> ... }
+ * </code></pre>
+ *
+ * @param selfKey a BlobKey
+ * @param c the closure to execute, passing in the stream as parameter of the closure
+ * @return the return value of the closure execution
+ */
+ @CompileStatic
+ static Object withStream(BlobKey selfKey, Closure c) {
+ def stream = new BlobstoreInputStream(selfKey)
+ stream.withStream(c)
+ }
+
+ /**
+ * Creates a (buffered) <code>Reader</code> over the blob with a specified encoding.
+ * The reader is passed as parameter of the closure.
+ * This methods takes care of properly opening and closing the reader and underlying stream.
+ * You can use this method as follows:
+ * <pre><code>
+ * blobKey.withReader("UTF-8") { reader -> ... }
+ * </code></pre>
+ *
+ * @param selfKey a BlobKey
+ * @param encoding the encoding used to read from the stream (UTF-8, etc.)
+ * @param c the closure to execute, passing in the stream as parameter of the closure
+ * @return the return value of the closure execution
+ */
+ @CompileStatic
+ static Object withReader(BlobKey selfKey, String encoding, Closure c) {
+ def stream = new BlobstoreInputStream(selfKey)
+ stream.withReader(encoding, c)
+ }
+
+ /**
+ * Creates a (buffered) <code>Reader</code> over the blob using UTF-8 as default encoding.
+ * The reader is passed as parameter of the closure.
+ * This methods takes care of properly opening and closing the reader and underlying stream.
+ * You can use this method as follows:
+ * <pre><code>
+ * blobKey.withReader { reader -> ... }
+ * </code></pre>
+ *
+ * @param selfKey a BlobKey
+ * @param encoding the encoding used to read from the stream (UTF-8, etc.)
+ * @param c the closure to execute, passing in the stream as parameter of the closure
+ * @return the return value of the closure execution
+ */
+ @CompileStatic
+ static Object withReader(BlobKey selfKey, Closure c) {
+ withReader(selfKey, "UTF-8", c)
+ }
+
+ /**
+ * Get the <code>BlobInfo</code> associated with a blob key with:
+ * <pre><code>
+ * blobKey.info
+ * </code></pre>
+ * @param selfKey the blob key to get information from
+ * @return an instance of <code>BlobInfo</code>
+ */
+ @CompileStatic
+ static BlobInfo getInfo(BlobKey selfKey) {
+ new BlobInfoFactory().loadBlobInfo(selfKey)
+ }
+
+ /**
+ * @return the name of the file stored in the blob
+ */
+ @CompileStatic
+ static String getFilename(BlobKey selfKey) {
+ getInfo(selfKey).filename
+ }
+
+ /**
+ * @return the content-type of the blob
+ */
+ @CompileStatic
+ static String getContentType(BlobKey selfKey) {
+ getInfo(selfKey).contentType
+ }
+
+ /**
+ * @return the creation date of the file stored in the blob
+ */
+ @CompileStatic
+ static Date getCreation(BlobKey selfKey) {
+ getInfo(selfKey).creation
+ }
+
+ /**
+ * @return the size of the blob
+ */
+ @CompileStatic
+ static long getSize(BlobKey selfKey) {
+ getInfo(selfKey).size
+ }
+
+ /**
+ * Delete the blob associated with this blob key.
+ *
+ * @param selfKey the blob to delete, identified by its key
+ */
+ @CompileStatic
+ static void delete(BlobKey selfKey) {
+ BlobstoreServiceFactory.blobstoreService.delete selfKey
+ }
+
+ /**
+ * Serve a range of the blob to the response
+ *
+ * @param selfKey the blob to serve
+ * @param the response on which to serve the blob
+ * @param range the range of the blob (parameter can be ommitted)
+ */
+ @CompileStatic
+ static void serve(BlobKey selfKey, HttpServletResponse response, ByteRange range = null) {
+ if (range)
+ BlobstoreServiceFactory.blobstoreService.serve selfKey, range, response
+ else
+ BlobstoreServiceFactory.blobstoreService.serve selfKey, response
+ }
+
+ /**
+ *
+ * @param selfKey
+ * @param response
+ * @param range
+ */
+ static void serve(BlobKey selfKey, HttpServletResponse response, IntRange range) {
+ BlobstoreServiceFactory.blobstoreService.serve selfKey, new ByteRange(range.fromInt, range.toInt), response
+ }
+
+ /**
+ * Fetch a segment of a blob
+ *
+ * @param selfKey the blob key identifying the blob
+ * @param start the beginning of the segment
+ * @param end the end of the segment
+ * @return an array of bytes
+ */
+ @CompileStatic
+ static byte[] fetchData(BlobKey selfKey, long start, long end) {
+ BlobstoreServiceFactory.blobstoreService.fetchData selfKey, start, end
+ }
+
+ /**
+ * Fetch a segment of a blob
+ * <pre><code>
+ * blobKey.fetchData 1000..2000
+ * </code></pre>
+ *
+ * @param selfKey the blob key identifying the blob
+ * @param a Groovy int range
+ * @return an array of bytes
+ */
+ static byte[] fetchData(BlobKey selfKey, IntRange intRange) {
+ fetchData(selfKey, intRange.fromInt, intRange.toInt)
+ }
+
+ /**
+ * Fetch a segment of a blob
+ *
+ * @param selfKey the blob key identifying the blob
+ * @param byteRange a <code>ByteRange</code> representing the segment
+ * @return an array of bytes
+ */
+ @CompileStatic
+ static byte[] fetchData(BlobKey selfKey, ByteRange byteRange) {
+ fetchData(selfKey, byteRange.start, byteRange.end)
+ }
+
+ /**
+ * Fetch an image stored in the blobstore.
+ * <pre><code>
+ * def image = blobKey.image
+ * // equivalent of ImagesServiceFactory.makeImageFromBlob(selfKey)
+ * </code></pre>
+ *
+ * <p>
+ * Note that this creates an image object only with the blob key set,
+ * it's not retrieving the image data right away, nor the dimensions of the image.
+ * </p>
+ *
+ * @param selfKey the key
+ * @return an Image
+ */
+ @CompileStatic
+ static Image getImage(BlobKey selfKey) {
+ ImagesServiceFactory.makeImageFromBlob(selfKey)
+ }
+
+ /**
+ * Obtains a URL that can serve the image stored as a blob dynamically.
+ *
+ * Note: getServingUrl can be time consuming so this should only be
+ * done once per blobkey and the result should be stored for future use.
+ *
+ * <pre><code>
+ * image.url = blobKey.getServingUrl(retry: 2, onRetry: { ex, i ->
+ * // do something... log exception? Thread.sleep(1000*i) ?
+ * true // must return true in order to continue next retry
+ * }, onFail: { ex -> // do something
+ * })
+ * </code></pre>
+ *
+ * @param selfKey the key
+ * @param a Map of options
+ * retries - the number of times to retry upon failure.
+ * onRetry - a closure that is called upon each retry attempt.
+ * Takes 2 parameters: 1. causing exception 2. # retries
+ * Closure must return true in order to continue otherwise
+ * no more retries will be attempted and onFail will be
+ * returned. If no onFail is specified, null will be
+ * returned as the URL.
+ * onFail - a closure that is called if serving url could not
+ * be retrieved successfully.
+ * Takes 1 parameter: causing exception
+ * Note: if you don't pass an onFail closure, the
+ * underlying exception will propagate out otherwise
+ * the result of onFail will be returned as the URL.
+ * @return a URL that can serve the image dynamically.
+ */
+ static String getServingUrl(BlobKey blobKey, Map options) {
+ ImagesService images = ImagesServiceFactory.getImagesService()
+ int retries = options.retry?:0
+ while (true) {
+ Exception ex = null
+ try {
+ return images.getServingUrl(blobKey)
+ } catch (ApiProxy.ApiDeadlineExceededException adee) {
+ ex = adee
+ } catch (IllegalArgumentException iae) {
+ ex = iae
+ } catch (ImagesServiceFailureException isfe) {
+ ex = isfe
+ }
+ if (retries-- == 0) {
+ if (options.onFail) {
+ return options.onFail(ex)
+ }
+ throw ex
+ } else {
+ if (options.onRetry) {
+ if (!options.onRetry(ex, options.retry - (retries + 1)))
+ return options.onFail? options.onFail(ex) : null
+ }
+ }
+ }
+ }
+
+ /**
+ * Collect all the BlobInfos of the blobs stored in the blobstore.
+ * <pre><code>
+ * blobstore.each { BlobInfo info -> ... }
+ * </code></pre>
+ *
+ * @param blobstore the blobstore service
+ * @param c the closure passed to the collect method
+ * @return a List of BlobInfos
+ */
+ @CompileStatic
+ static List<BlobInfo> collect(BlobstoreService blobstore, Closure<BlobInfo> c) {
+ new BlobInfoFactory().queryBlobInfos().collect c
+ }
+
+ /**
+ * Iterates over all the BlobInfos of the blobs stored in the blobstore.
+ * <pre><code>
+ * def filenames = blobstore.collect { BlobInfo info -> info.filename }
+ * </code></pre>
+ *
+ * @param blobstore the blobstore service
+ * @param c the closure passed to the each method
+ * @return an iterator over BlobInfos
+ */
+ @CompileStatic
+ static Iterator<BlobInfo> each(BlobstoreService blobstore, Closure<BlobInfo> c) {
+ new BlobInfoFactory().queryBlobInfos().each c
+ }
+}
View
57 core/src/main/groovyx/gaelyk/extensions/CapabilitiesExtensions.groovy
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2009-2012 the original author or authors.
+ *
+ * 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 groovyx.gaelyk.extensions
+
+import groovy.transform.CompileStatic
+import com.google.appengine.api.capabilities.CapabilityStatus
+import com.google.appengine.api.capabilities.CapabilitiesService
+import com.google.appengine.api.capabilities.Capability
+
+/**
+ * Capabilities method extensions
+ *
+ * @author Guillaume Laforge
+ */
+@CompileStatic
+class CapabilitiesExtensions {
+
+ /**
+ * Query the status of the various App Engine services.
+ *
+ * <pre><code>
+ * import static com.google.appengine.api.capabilities.Capability.*
+ * import static com.google.appengine.api.capabilities.CapabilityStatus.*
+ *
+ * capabilities[DATASTORE] == ENABLED
+ * </code></pre>
+ *
+ * @param capa the capability to know the status of
+ * @return a status
+ */
+ static CapabilityStatus getAt(CapabilitiesService capabilities, Capability capa) {
+ return capabilities.getStatus(capa).getStatus()
+ }
+
+ /**
+ * Coerces a capability status into a boolean.
+ * This mechanism is used by the "Groovy Truth".
+ *
+ * @return true if the capability status is ENABLED, otherwise false.
+ */
+ static boolean asBoolean(CapabilityStatus capabilityStatus) {
+ capabilityStatus == CapabilityStatus.ENABLED || capabilityStatus == CapabilityStatus.SCHEDULED_MAINTENANCE
+ }
+}
View
39 core/src/main/groovyx/gaelyk/extensions/ChannelExtensions.groovy
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2009-2012 the original author or authors.
+ *
+ * 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 groovyx.gaelyk.extensions
+
+import groovy.transform.CompileStatic
+import com.google.appengine.api.channel.ChannelService
+import com.google.appengine.api.channel.ChannelMessage
+
+/**
+ * Channel service extension methods
+ *
+ * @author Guillaume Laforge
+ */
+@CompileStatic
+class ChannelExtensions {
+
+ /**
+ * Send a message through the Channel service
+ *
+ * @param clientId the client ID
+ * @param message the message to send
+ */
+ static void send(ChannelService channel, String clientId, String message) {
+ channel.sendMessage(new ChannelMessage(clientId, message))
+ }
+}
View
879 core/src/main/groovyx/gaelyk/extensions/DatastoreExtensions.groovy
@@ -0,0 +1,879 @@
+/*
+ * Copyright 2009-2012 the original author or authors.
+ *
+ * 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 groovyx.gaelyk.extensions
+
+import groovy.lang.Closure;
+import groovy.transform.CompileStatic
+import com.google.appengine.api.datastore.Entity
+import groovy.transform.PackageScope
+import com.google.appengine.api.datastore.Text
+import com.google.appengine.api.datastore.Key
+import com.google.appengine.api.datastore.DatastoreServiceFactory
+import java.util.concurrent.Future
+import com.google.appengine.api.datastore.Transaction
+import com.google.appengine.api.datastore.DatastoreService
+import com.google.appengine.api.datastore.AsyncDatastoreService
+import com.google.appengine.api.datastore.KeyFactory
+import com.google.appengine.api.datastore.Query
+import groovyx.gaelyk.query.QueryBuilder
+import groovyx.gaelyk.UnindexedEntityWrapper
+import com.google.appengine.api.datastore.FetchOptions
+import com.google.appengine.api.datastore.PreparedQuery
+