Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Initial commit.

  • Loading branch information...
commit c251fb5a7e9bac53c9ff8bd2e1be767f67def5d4 0 parents
bflorian authored March 22, 2012

Showing 43 changed files with 3,651 additions and 0 deletions. Show diff stats Hide diff stats

  1. 14  .classpath
  2. 5  .gitignore
  3. 19  .project
  4. 67  CassandraOrmGrailsPlugin.groovy
  5. 22  application.properties
  6. 47  grails-app/conf/BuildConfig.groovy
  7. 40  grails-app/conf/Config.groovy
  8. 48  grails-app/conf/DataSource.groovy
  9. 29  grails-app/conf/UrlMappings.groovy
  10. 48  grails-app/services/com/reachlocal/grails/plugins/cassandra/orm/CassandraOrmService.groovy
  11. 70  grails-app/views/error.gsp
  12. 26  scripts/_Install.groovy
  13. 21  scripts/_Uninstall.groovy
  14. 26  scripts/_Upgrade.groovy
  15. BIN  src/groovy/.DS_Store
  16. BIN  src/groovy/com/reachlocal/grails/.DS_Store
  17. BIN  src/groovy/com/reachlocal/grails/plugins/cassandra/.DS_Store
  18. 58  src/groovy/com/reachlocal/grails/plugins/cassandra/OrmPersistenceMethods.groovy
  19. 33  src/groovy/com/reachlocal/grails/plugins/cassandra/mapping/CassandraMappingException.groovy
  20. 218  src/groovy/com/reachlocal/grails/plugins/cassandra/mapping/ClassMethods.groovy
  21. 260  src/groovy/com/reachlocal/grails/plugins/cassandra/mapping/DataMapping.groovy
  22. 346  src/groovy/com/reachlocal/grails/plugins/cassandra/mapping/InstanceMethods.groovy
  23. 406  src/groovy/com/reachlocal/grails/plugins/cassandra/mapping/MappingUtils.groovy
  24. 153  src/groovy/com/reachlocal/grails/plugins/cassandra/test/MockPersistenceMethods.groovy
  25. 33  src/groovy/com/reachlocal/grails/plugins/cassandra/test/orm/Car.groovy
  26. 35  src/groovy/com/reachlocal/grails/plugins/cassandra/test/orm/Conference.groovy
  27. 33  src/groovy/com/reachlocal/grails/plugins/cassandra/test/orm/Course.groovy
  28. 40  src/groovy/com/reachlocal/grails/plugins/cassandra/test/orm/Grade.groovy
  29. 43  src/groovy/com/reachlocal/grails/plugins/cassandra/test/orm/Household.groovy
  30. 34  src/groovy/com/reachlocal/grails/plugins/cassandra/test/orm/Person.groovy
  31. 35  src/groovy/com/reachlocal/grails/plugins/cassandra/test/orm/School.groovy
  32. 39  src/groovy/com/reachlocal/grails/plugins/cassandra/test/orm/Student.groovy
  33. 38  src/groovy/com/reachlocal/grails/plugins/cassandra/test/orm/Transcript.groovy
  34. 42  src/groovy/com/reachlocal/grails/plugins/cassandra/test/orm/User.groovy
  35. 35  src/groovy/com/reachlocal/grails/plugins/cassandra/test/orm/UserGroup.groovy
  36. 32  src/groovy/com/reachlocal/grails/plugins/cassandra/test/orm/UserGroupMeeting.groovy
  37. 147  test/unit/com/reachlocal/grails/plugins/cassandra/test/ClassMethodsTests.groovy
  38. 69  test/unit/com/reachlocal/grails/plugins/cassandra/test/InstanceMethodTests.groovy
  39. 59  test/unit/com/reachlocal/grails/plugins/cassandra/test/OrmTestCase.groovy
  40. 58  web-app/WEB-INF/applicationContext.xml
  41. 30  web-app/WEB-INF/sitemesh.xml
  42. 566  web-app/WEB-INF/tld/grails.tld
  43. 327  web-app/WEB-INF/tld/spring.tld
14  .classpath
... ...
@@ -0,0 +1,14 @@
  1
+<classpath>
  2
+    <classpathentry kind="src" path="src/java"/>
  3
+    <classpathentry kind="src" path="src/groovy"/>
  4
+    <classpathentry kind="src" path="grails-app/conf"/>
  5
+    <classpathentry kind="src" path="grails-app/controllers"/>
  6
+    <classpathentry kind="src" path="grails-app/domain"/>
  7
+    <classpathentry kind="src" path="grails-app/services"/>
  8
+    <classpathentry kind="src" path="grails-app/taglib"/>
  9
+    <classpathentry kind="src" path="test/integration"/>
  10
+    <classpathentry kind="src" path="test/unit"/>
  11
+    <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
  12
+    <classpathentry kind="con" path="com.springsource.sts.grails.core.CLASSPATH_CONTAINER"/>
  13
+    <classpathentry kind="output" path="web-app/WEB-INF/classes"/>
  14
+</classpath>
5  .gitignore
... ...
@@ -0,0 +1,5 @@
  1
+.idea/
  2
+.settings/
  3
+*.iml
  4
+stacktrace.log
  5
+target/
19  .project
... ...
@@ -0,0 +1,19 @@
  1
+<?xml version="1.0" encoding="UTF-8"?>
  2
+<projectDescription>
  3
+	<name>cassandra-orm</name>
  4
+	<comment></comment>
  5
+	<projects>
  6
+	</projects>
  7
+	<buildSpec>
  8
+		<buildCommand>
  9
+			<name>org.eclipse.jdt.core.javabuilder</name>
  10
+			<arguments>
  11
+			</arguments>
  12
+		</buildCommand>
  13
+	</buildSpec>
  14
+	<natures>
  15
+	    <nature>com.springsource.sts.grails.core.nature</nature>
  16
+		<nature>org.eclipse.jdt.groovy.core.groovyNature</nature>
  17
+		<nature>org.eclipse.jdt.core.javanature</nature>
  18
+	</natures>
  19
+</projectDescription>
67  CassandraOrmGrailsPlugin.groovy
... ...
@@ -0,0 +1,67 @@
  1
+/*
  2
+ * Copyright 2012 ReachLocal Inc.
  3
+ *
  4
+ * Licensed under the Apache License, Version 2.0 (the "License");
  5
+ * you may not use this file except in compliance with the License.
  6
+ * You may obtain a copy of the License at
  7
+ *
  8
+ *      http://www.apache.org/licenses/LICENSE-2.0
  9
+ *
  10
+ * Unless required by applicable law or agreed to in writing, software
  11
+ * distributed under the License is distributed on an "AS IS" BASIS,
  12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13
+ * See the License for the specific language governing permissions and
  14
+ * limitations under the License.
  15
+ */
  16
+
  17
+class CassandraOrmGrailsPlugin
  18
+{
  19
+	// the plugin version
  20
+	def version = "0.1"
  21
+	// the version or versions of Grails the plugin is designed for
  22
+	def grailsVersion = "1.3.7 > *"
  23
+	// the other plugins this plugin depends on
  24
+	def dependsOn = [:]
  25
+	// resources that are excluded from plugin packaging
  26
+	def pluginExcludes = [
  27
+			"grails-app/views/error.gsp"
  28
+	]
  29
+
  30
+	// TODO Fill in these fields
  31
+	def author = "Your name"
  32
+	def authorEmail = ""
  33
+	def title = "Plugin summary/headline"
  34
+	def description = '''\\
  35
+Brief description of the plugin.
  36
+'''
  37
+
  38
+	// URL to the plugin's documentation
  39
+	def documentation = "http://grails.org/plugin/cassandra-orm"
  40
+
  41
+	def doWithWebDescriptor = { xml ->
  42
+		// TODO Implement additions to web.xml (optional), this event occurs before
  43
+	}
  44
+
  45
+	def doWithSpring = {
  46
+		// TODO Implement runtime spring config (optional)
  47
+	}
  48
+
  49
+	def doWithDynamicMethods = { ctx ->
  50
+		// TODO Implement registering dynamic methods to classes (optional)
  51
+	}
  52
+
  53
+	def doWithApplicationContext = { applicationContext ->
  54
+		// TODO Implement post initialization spring config (optional)
  55
+	}
  56
+
  57
+	def onChange = { event ->
  58
+		// TODO Implement code that is executed when any artefact that this plugin is
  59
+		// watching is modified and reloaded. The event contains: event.source,
  60
+		// event.application, event.manager, event.ctx, and event.plugin.
  61
+	}
  62
+
  63
+	def onConfigChange = { event ->
  64
+		// TODO Implement code that is executed when the project configuration changes.
  65
+		// The event is the same as for 'onChange'.
  66
+	}
  67
+}
22  application.properties
... ...
@@ -0,0 +1,22 @@
  1
+#
  2
+# Copyright 2012 ReachLocal Inc.
  3
+#
  4
+# Licensed under the Apache License, Version 2.0 (the "License");
  5
+# you may not use this file except in compliance with the License.
  6
+# You may obtain a copy of the License at
  7
+#
  8
+#      http://www.apache.org/licenses/LICENSE-2.0
  9
+#
  10
+# Unless required by applicable law or agreed to in writing, software
  11
+# distributed under the License is distributed on an "AS IS" BASIS,
  12
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13
+# See the License for the specific language governing permissions and
  14
+# limitations under the License.
  15
+#
  16
+
  17
+#Grails Metadata file
  18
+#Wed Mar 21 14:39:42 EDT 2012
  19
+app.grails.version=1.3.7
  20
+app.name=cassandra-orm
  21
+plugins.hibernate=1.3.7
  22
+plugins.tomcat=1.3.7
47  grails-app/conf/BuildConfig.groovy
... ...
@@ -0,0 +1,47 @@
  1
+grails.project.class.dir = "target/classes"
  2
+grails.project.test.class.dir = "target/test-classes"
  3
+grails.project.test.reports.dir = "target/test-reports"
  4
+/*
  5
+ * Copyright 2012 ReachLocal Inc.
  6
+ *
  7
+ * Licensed under the Apache License, Version 2.0 (the "License");
  8
+ * you may not use this file except in compliance with the License.
  9
+ * You may obtain a copy of the License at
  10
+ *
  11
+ *      http://www.apache.org/licenses/LICENSE-2.0
  12
+ *
  13
+ * Unless required by applicable law or agreed to in writing, software
  14
+ * distributed under the License is distributed on an "AS IS" BASIS,
  15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16
+ * See the License for the specific language governing permissions and
  17
+ * limitations under the License.
  18
+ */
  19
+
  20
+//grails.project.war.file = "target/${appName}-${appVersion}.war"
  21
+grails.project.dependency.resolution = {
  22
+    // inherit Grails' default dependencies
  23
+    inherits("global") {
  24
+        // uncomment to disable ehcache
  25
+        // excludes 'ehcache'
  26
+    }
  27
+    log "warn" // log level of Ivy resolver, either 'error', 'warn', 'info', 'debug' or 'verbose'
  28
+    repositories {
  29
+        grailsPlugins()
  30
+        grailsHome()
  31
+        grailsCentral()
  32
+
  33
+        // uncomment the below to enable remote dependency resolution
  34
+        // from public Maven repositories
  35
+        //mavenLocal()
  36
+        //mavenCentral()
  37
+        //mavenRepo "http://snapshots.repository.codehaus.org"
  38
+        //mavenRepo "http://repository.codehaus.org"
  39
+        //mavenRepo "http://download.java.net/maven/2/"
  40
+        //mavenRepo "http://repository.jboss.com/maven2/"
  41
+    }
  42
+    dependencies {
  43
+        // specify dependencies here under either 'build', 'compile', 'runtime', 'test' or 'provided' scopes eg.
  44
+
  45
+        // runtime 'mysql:mysql-connector-java:5.1.13'
  46
+    }
  47
+}
40  grails-app/conf/Config.groovy
... ...
@@ -0,0 +1,40 @@
  1
+/*
  2
+ * Copyright 2012 ReachLocal Inc.
  3
+ *
  4
+ * Licensed under the Apache License, Version 2.0 (the "License");
  5
+ * you may not use this file except in compliance with the License.
  6
+ * You may obtain a copy of the License at
  7
+ *
  8
+ *      http://www.apache.org/licenses/LICENSE-2.0
  9
+ *
  10
+ * Unless required by applicable law or agreed to in writing, software
  11
+ * distributed under the License is distributed on an "AS IS" BASIS,
  12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13
+ * See the License for the specific language governing permissions and
  14
+ * limitations under the License.
  15
+ */
  16
+
  17
+// configuration for plugin testing - will not be included in the plugin zip
  18
+ 
  19
+log4j = {
  20
+    // Example of changing the log pattern for the default console
  21
+    // appender:
  22
+    //
  23
+    //appenders {
  24
+    //    console name:'stdout', layout:pattern(conversionPattern: '%c{2} %m%n')
  25
+    //}
  26
+
  27
+    error  'org.codehaus.groovy.grails.web.servlet',  //  controllers
  28
+           'org.codehaus.groovy.grails.web.pages', //  GSP
  29
+           'org.codehaus.groovy.grails.web.sitemesh', //  layouts
  30
+           'org.codehaus.groovy.grails.web.mapping.filter', // URL mapping
  31
+           'org.codehaus.groovy.grails.web.mapping', // URL mapping
  32
+           'org.codehaus.groovy.grails.commons', // core / classloading
  33
+           'org.codehaus.groovy.grails.plugins', // plugins
  34
+           'org.codehaus.groovy.grails.orm.hibernate', // hibernate integration
  35
+           'org.springframework',
  36
+           'org.hibernate',
  37
+           'net.sf.ehcache.hibernate'
  38
+
  39
+    warn   'org.mortbay.log'
  40
+}
48  grails-app/conf/DataSource.groovy
... ...
@@ -0,0 +1,48 @@
  1
+dataSource {
  2
+    pooled = true
  3
+    driverClassName = "org.hsqldb.jdbcDriver"
  4
+    username = "sa"
  5
+    password = ""
  6
+}
  7
+hibernate {
  8
+    cache.use_second_level_cache = true
  9
+    cache.use_query_cache = true
  10
+    cache.provider_class = 'net.sf.ehcache.hibernate.EhCacheProvider'
  11
+}
  12
+/*
  13
+ * Copyright 2012 ReachLocal Inc.
  14
+ *
  15
+ * Licensed under the Apache License, Version 2.0 (the "License");
  16
+ * you may not use this file except in compliance with the License.
  17
+ * You may obtain a copy of the License at
  18
+ *
  19
+ *      http://www.apache.org/licenses/LICENSE-2.0
  20
+ *
  21
+ * Unless required by applicable law or agreed to in writing, software
  22
+ * distributed under the License is distributed on an "AS IS" BASIS,
  23
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  24
+ * See the License for the specific language governing permissions and
  25
+ * limitations under the License.
  26
+ */
  27
+
  28
+// environment specific settings
  29
+environments {
  30
+    development {
  31
+        dataSource {
  32
+            dbCreate = "create-drop" // one of 'create', 'create-drop','update'
  33
+            url = "jdbc:hsqldb:mem:devDB"
  34
+        }
  35
+    }
  36
+    test {
  37
+        dataSource {
  38
+            dbCreate = "update"
  39
+            url = "jdbc:hsqldb:mem:testDb"
  40
+        }
  41
+    }
  42
+    production {
  43
+        dataSource {
  44
+            dbCreate = "update"
  45
+            url = "jdbc:hsqldb:file:prodDb;shutdown=true"
  46
+        }
  47
+    }
  48
+}
29  grails-app/conf/UrlMappings.groovy
... ...
@@ -0,0 +1,29 @@
  1
+/*
  2
+ * Copyright 2012 ReachLocal Inc.
  3
+ *
  4
+ * Licensed under the Apache License, Version 2.0 (the "License");
  5
+ * you may not use this file except in compliance with the License.
  6
+ * You may obtain a copy of the License at
  7
+ *
  8
+ *      http://www.apache.org/licenses/LICENSE-2.0
  9
+ *
  10
+ * Unless required by applicable law or agreed to in writing, software
  11
+ * distributed under the License is distributed on an "AS IS" BASIS,
  12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13
+ * See the License for the specific language governing permissions and
  14
+ * limitations under the License.
  15
+ */
  16
+
  17
+class UrlMappings {
  18
+
  19
+	static mappings = {
  20
+		"/$controller/$action?/$id?"{
  21
+			constraints {
  22
+				// apply constraints here
  23
+			}
  24
+		}
  25
+
  26
+		"/"(view:"/index")
  27
+		"500"(view:'/error')
  28
+	}
  29
+}
48  grails-app/services/com/reachlocal/grails/plugins/cassandra/orm/CassandraOrmService.groovy
... ...
@@ -0,0 +1,48 @@
  1
+/*
  2
+ * Copyright 2012 ReachLocal Inc.
  3
+ *
  4
+ * Licensed under the Apache License, Version 2.0 (the "License");
  5
+ * you may not use this file except in compliance with the License.
  6
+ * You may obtain a copy of the License at
  7
+ *
  8
+ *      http://www.apache.org/licenses/LICENSE-2.0
  9
+ *
  10
+ * Unless required by applicable law or agreed to in writing, software
  11
+ * distributed under the License is distributed on an "AS IS" BASIS,
  12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13
+ * See the License for the specific language governing permissions and
  14
+ * limitations under the License.
  15
+ */
  16
+
  17
+package com.reachlocal.grails.plugins.cassandra.orm
  18
+
  19
+import org.springframework.beans.factory.InitializingBean
  20
+import org.codehaus.groovy.grails.commons.ConfigurationHolder
  21
+import com.reachlocal.grails.plugins.cassandra.mapping.DataMapping
  22
+
  23
+/**
  24
+ * @author: Bob Florian
  25
+ */
  26
+class CassandraOrmService implements InitializingBean
  27
+{
  28
+	boolean transactional = false
  29
+	def applicationContext
  30
+
  31
+	def ormClientServiceName = ConfigurationHolder.config?.cassandra?.ormClientServiceName ?: "astyanaxService"
  32
+		
  33
+	def client
  34
+	def persistence
  35
+	def mapping
  36
+	
  37
+	void afterPropertiesSet () 
  38
+	{
  39
+		client = applicationContext.getBean(ormClientServiceName)
  40
+		persistence = client.orm
  41
+		mapping = new DataMapping(persistence: persistence)
  42
+	}
  43
+
  44
+	def execute(keyspace, block) throws Exception
  45
+	{
  46
+		client.execute(keyspace, block)
  47
+	}
  48
+}
70  grails-app/views/error.gsp
... ...
@@ -0,0 +1,70 @@
  1
+<html>
  2
+  <head>
  3
+	  <title>Grails Runtime Exception</title>
  4
+	  <style type="text/css">
  5
+	  		.message {
  6
+	  			border: 1px solid black;
  7
+	  			padding: 5px;
  8
+	  			background-color:#E9E9E9;
  9
+	  		}
  10
+	  		.stack {
  11
+	  			border: 1px solid black;
  12
+	  			padding: 5px;
  13
+	  			overflow:auto;
  14
+	  			height: 300px;
  15
+	  		}
  16
+	  		.snippet {
  17
+	  			padding: 5px;
  18
+	  			background-color:white;
  19
+	  			border:1px solid black;
  20
+	  			margin:3px;
  21
+	  			font-family:courier;
  22
+	  		}
  23
+	  </style>
  24
+  </head>
  25
+
  26
+  <body>
  27
+    <h1>Grails Runtime Exception</h1>
  28
+    <h2>Error Details</h2>
  29
+
  30
+  	<div class="message">
  31
+		<strong>Error %{--
  32
+  - Copyright 2012 ReachLocal Inc.
  33
+  -
  34
+  - Licensed under the Apache License, Version 2.0 (the "License");
  35
+  - you may not use this file except in compliance with the License.
  36
+  - You may obtain a copy of the License at
  37
+  -
  38
+  -      http://www.apache.org/licenses/LICENSE-2.0
  39
+  -
  40
+  - Unless required by applicable law or agreed to in writing, software
  41
+  - distributed under the License is distributed on an "AS IS" BASIS,
  42
+  - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  43
+  - See the License for the specific language governing permissions and
  44
+  - limitations under the License.
  45
+  --}%
  46
+
  47
+${request.'javax.servlet.error.status_code'}:</strong> ${request.'javax.servlet.error.message'.encodeAsHTML()}<br/>
  48
+		<strong>Servlet:</strong> ${request.'javax.servlet.error.servlet_name'}<br/>
  49
+		<strong>URI:</strong> ${request.'javax.servlet.error.request_uri'}<br/>
  50
+		<g:if test="${exception}">
  51
+	  		<strong>Exception Message:</strong> ${exception.message?.encodeAsHTML()} <br />
  52
+	  		<strong>Caused by:</strong> ${exception.cause?.message?.encodeAsHTML()} <br />
  53
+	  		<strong>Class:</strong> ${exception.className} <br />
  54
+	  		<strong>At Line:</strong> [${exception.lineNumber}] <br />
  55
+	  		<strong>Code Snippet:</strong><br />
  56
+	  		<div class="snippet">
  57
+	  			<g:each var="cs" in="${exception.codeSnippet}">
  58
+	  				${cs?.encodeAsHTML()}<br />
  59
+	  			</g:each>
  60
+	  		</div>
  61
+		</g:if>
  62
+  	</div>
  63
+	<g:if test="${exception}">
  64
+	    <h2>Stack Trace</h2>
  65
+	    <div class="stack">
  66
+	      <pre><g:each in="${exception.stackTraceLines}">${it.encodeAsHTML()}<br/></g:each></pre>
  67
+	    </div>
  68
+	</g:if>
  69
+  </body>
  70
+</html>
26  scripts/_Install.groovy
... ...
@@ -0,0 +1,26 @@
  1
+/*
  2
+ * Copyright 2012 ReachLocal Inc.
  3
+ *
  4
+ * Licensed under the Apache License, Version 2.0 (the "License");
  5
+ * you may not use this file except in compliance with the License.
  6
+ * You may obtain a copy of the License at
  7
+ *
  8
+ *      http://www.apache.org/licenses/LICENSE-2.0
  9
+ *
  10
+ * Unless required by applicable law or agreed to in writing, software
  11
+ * distributed under the License is distributed on an "AS IS" BASIS,
  12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13
+ * See the License for the specific language governing permissions and
  14
+ * limitations under the License.
  15
+ */
  16
+
  17
+//
  18
+// This script is executed by Grails after plugin was installed to project.
  19
+// This script is a Gant script so you can use all special variables provided
  20
+// by Gant (such as 'baseDir' which points on project base dir). You can
  21
+// use 'ant' to access a global instance of AntBuilder
  22
+//
  23
+// For example you can create directory under project tree:
  24
+//
  25
+//    ant.mkdir(dir:"${basedir}/grails-app/jobs")
  26
+//
21  scripts/_Uninstall.groovy
... ...
@@ -0,0 +1,21 @@
  1
+/*
  2
+ * Copyright 2012 ReachLocal Inc.
  3
+ *
  4
+ * Licensed under the Apache License, Version 2.0 (the "License");
  5
+ * you may not use this file except in compliance with the License.
  6
+ * You may obtain a copy of the License at
  7
+ *
  8
+ *      http://www.apache.org/licenses/LICENSE-2.0
  9
+ *
  10
+ * Unless required by applicable law or agreed to in writing, software
  11
+ * distributed under the License is distributed on an "AS IS" BASIS,
  12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13
+ * See the License for the specific language governing permissions and
  14
+ * limitations under the License.
  15
+ */
  16
+
  17
+//
  18
+// This script is executed by Grails when the plugin is uninstalled from project.
  19
+// Use this script if you intend to do any additional clean-up on uninstall, but
  20
+// beware of messing up SVN directories!
  21
+//
26  scripts/_Upgrade.groovy
... ...
@@ -0,0 +1,26 @@
  1
+/*
  2
+ * Copyright 2012 ReachLocal Inc.
  3
+ *
  4
+ * Licensed under the Apache License, Version 2.0 (the "License");
  5
+ * you may not use this file except in compliance with the License.
  6
+ * You may obtain a copy of the License at
  7
+ *
  8
+ *      http://www.apache.org/licenses/LICENSE-2.0
  9
+ *
  10
+ * Unless required by applicable law or agreed to in writing, software
  11
+ * distributed under the License is distributed on an "AS IS" BASIS,
  12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13
+ * See the License for the specific language governing permissions and
  14
+ * limitations under the License.
  15
+ */
  16
+
  17
+//
  18
+// This script is executed by Grails during application upgrade ('grails upgrade'
  19
+// command). This script is a Gant script so you can use all special variables
  20
+// provided by Gant (such as 'baseDir' which points on project base dir). You can
  21
+// use 'ant' to access a global instance of AntBuilder
  22
+//
  23
+// For example you can create directory under project tree:
  24
+//
  25
+//    ant.mkdir(dir:"${basedir}/grails-app/jobs")
  26
+//
BIN  src/groovy/.DS_Store
Binary file not shown
BIN  src/groovy/com/reachlocal/grails/.DS_Store
Binary file not shown
BIN  src/groovy/com/reachlocal/grails/plugins/cassandra/.DS_Store
Binary file not shown
58  src/groovy/com/reachlocal/grails/plugins/cassandra/OrmPersistenceMethods.groovy
... ...
@@ -0,0 +1,58 @@
  1
+/*
  2
+ * Copyright 2012 ReachLocal Inc.
  3
+ *
  4
+ * Licensed under the Apache License, Version 2.0 (the "License");
  5
+ * you may not use this file except in compliance with the License.
  6
+ * You may obtain a copy of the License at
  7
+ *
  8
+ *      http://www.apache.org/licenses/LICENSE-2.0
  9
+ *
  10
+ * Unless required by applicable law or agreed to in writing, software
  11
+ * distributed under the License is distributed on an "AS IS" BASIS,
  12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13
+ * See the License for the specific language governing permissions and
  14
+ * limitations under the License.
  15
+ */
  16
+
  17
+package com.reachlocal.grails.plugins.cassandra
  18
+
  19
+/**
  20
+ * @author: Bob Florian
  21
+ */
  22
+interface OrmPersistenceMethods
  23
+{
  24
+	def columnFamily(String name);
  25
+
  26
+	def getRow(Object client, Object columnFamily, Object rowKey);
  27
+
  28
+	def getRows(Object client, Object columnFamily, Collection rowKeys);
  29
+
  30
+	def getRowsColumnSlice(Object client, Object columnFamily, Collection rowKeys, Collection columnNames);
  31
+
  32
+	def getRowsWithEqualityIndex(client, columnFamily, properties, max);
  33
+
  34
+	def getColumnRange(Object client, Object columnFamily, Object rowKey, Object start, Object finish, Boolean reversed, Integer max);
  35
+
  36
+	def getColumnSlice(Object client, Object columnFamily, Object rowKey, Collection columnNames);
  37
+
  38
+	def prepareMutationBatch(client);
  39
+
  40
+	void deleteColumn(mutationBatch, columnFamily, rowKey, columnName);
  41
+
  42
+	void putColumns(mutationBatch, columnFamily, rowKey, columnMap);
  43
+
  44
+	void deleteRow(mutationBatch, columnFamily, rowKey);
  45
+
  46
+	def execute(mutationBatch);
  47
+
  48
+	def getRow(rows, key);
  49
+
  50
+	def getColumn(row, name);
  51
+
  52
+	def name(column);
  53
+
  54
+	String stringValue(column);
  55
+
  56
+	byte[] byteArrayValue(column);
  57
+}
  58
+
33  src/groovy/com/reachlocal/grails/plugins/cassandra/mapping/CassandraMappingException.groovy
... ...
@@ -0,0 +1,33 @@
  1
+/*
  2
+ * Copyright 2012 ReachLocal Inc.
  3
+ *
  4
+ * Licensed under the Apache License, Version 2.0 (the "License");
  5
+ * you may not use this file except in compliance with the License.
  6
+ * You may obtain a copy of the License at
  7
+ *
  8
+ *      http://www.apache.org/licenses/LICENSE-2.0
  9
+ *
  10
+ * Unless required by applicable law or agreed to in writing, software
  11
+ * distributed under the License is distributed on an "AS IS" BASIS,
  12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13
+ * See the License for the specific language governing permissions and
  14
+ * limitations under the License.
  15
+ */
  16
+
  17
+package com.reachlocal.grails.plugins.cassandra.mapping
  18
+
  19
+/**
  20
+ * @author: Bob Florian
  21
+ */
  22
+class CassandraMappingException extends Exception
  23
+{
  24
+	public CassandraMappingException()
  25
+	{
  26
+		super()
  27
+	}
  28
+
  29
+	public CassandraMappingException(String message)
  30
+	{
  31
+		super(message)
  32
+	}
  33
+}
218  src/groovy/com/reachlocal/grails/plugins/cassandra/mapping/ClassMethods.groovy
... ...
@@ -0,0 +1,218 @@
  1
+/*
  2
+ * Copyright 2012 ReachLocal Inc.
  3
+ *
  4
+ * Licensed under the Apache License, Version 2.0 (the "License");
  5
+ * you may not use this file except in compliance with the License.
  6
+ * You may obtain a copy of the License at
  7
+ *
  8
+ *      http://www.apache.org/licenses/LICENSE-2.0
  9
+ *
  10
+ * Unless required by applicable law or agreed to in writing, software
  11
+ * distributed under the License is distributed on an "AS IS" BASIS,
  12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13
+ * See the License for the specific language governing permissions and
  14
+ * limitations under the License.
  15
+ */
  16
+
  17
+package com.reachlocal.grails.plugins.cassandra.mapping
  18
+
  19
+import org.codehaus.groovy.grails.commons.ConfigurationHolder
  20
+
  21
+/**
  22
+ * @author: Bob Florian
  23
+ */
  24
+class ClassMethods extends MappingUtils
  25
+{
  26
+	static final int MAX_ROWS = 1000
  27
+
  28
+	static void addDynamicOrmMethods(clazz, ctx) throws CassandraMappingException
  29
+	{
  30
+		if (!clazz.metaClass.hasMetaProperty('cassandraMapping')) {
  31
+			throw new CassandraMappingException("cassandraMapping not specified")
  32
+		} else if (!safeGetStaticProperty(clazz, 'cassandraMapping')?.primaryKey) {
  33
+			throw new CassandraMappingException("cassandraMapping.primaryKey not specified")
  34
+		}
  35
+
  36
+		// cassandra
  37
+		clazz.metaClass.'static'.getCassandra = { ctx.getBean("cassandraOrmService") }
  38
+
  39
+		// keySpace
  40
+		clazz.metaClass.'static'.getKeySpace = {
  41
+			if (clazz.metaClass.hasMetaProperty('cassandraMapping') && cassandraMapping?.keySpace) {
  42
+				return cassandraMapping?.keySpace
  43
+			}
  44
+			else {
  45
+				return ConfigurationHolder.config.cassandra.keySpace
  46
+			}
  47
+		}
  48
+
  49
+		// columnFamilyName
  50
+		clazz.metaClass.'static'.getColumnFamilyName = {
  51
+			if (clazz.metaClass.hasMetaProperty('cassandraMapping') && cassandraMapping?.columnFamily) {
  52
+				cassandraMapping?.columnFamily
  53
+			}
  54
+			else {
  55
+				clazz.name.split("\\.")[-1]
  56
+			}
  57
+		}
  58
+
  59
+		// columnFamily
  60
+		clazz.metaClass.'static'.getColumnFamily = {
  61
+			def cf = cassandraMapping.columnFamily_object
  62
+			if (cf == null) {
  63
+				cf = cassandra.persistence.columnFamily(columnFamilyName)
  64
+				cassandraMapping.columnFamily_object = cf
  65
+			}
  66
+			return cf
  67
+		}
  68
+
  69
+		// indexColumnFamily
  70
+		clazz.metaClass.'static'.getIndexColumnFamily = {
  71
+			def cf = cassandraMapping.indexColumnFamily_object
  72
+			if (cf == null) {
  73
+				cf = cassandra.persistence.columnFamily("${columnFamilyName}_IDX".toString())
  74
+				cassandraMapping.indexColumnFamily_object = cf
  75
+			}
  76
+			return cf
  77
+		}
  78
+
  79
+		// belongsToClass(clazz2)
  80
+		clazz.metaClass.'static'.belongsToClass = { clazz2 ->
  81
+			if (clazz.metaClass.hasMetaProperty("belongsTo")) {
  82
+			    return belongsTo.find{it.value == clazz2} != null
  83
+			}
  84
+			return false
  85
+		}
  86
+
  87
+		// get(id, options?)
  88
+		clazz.metaClass.'static'.get = {id, opts=[:] ->
  89
+			def result = null
  90
+			def rowKey = primaryRowKey(id)
  91
+			cassandra.execute(keySpace) {ks ->
  92
+				def data = cassandra.persistence.getRow(ks, columnFamily, rowKey)
  93
+				result = cassandra.mapping.newObject(data)
  94
+			}
  95
+			return result
  96
+		}
  97
+
  98
+		// list(opts?)
  99
+		// list(max: max_rows)
  100
+		// list(start: id1, max: max_rows)
  101
+		// list(start: id1, finish: id1)
  102
+		// list(start: id1, finish: id1, max: max_rows)
  103
+		// list(start: id1, finish: id1, reversed: true)
  104
+		// list(start: id1, finish: id1, reversed: true, max: max_rows)
  105
+		clazz.metaClass.'static'.list = {opts=[:] ->
  106
+
  107
+			def options = addOptionDefaults(opts, MAX_ROWS)
  108
+			cassandra.execute(keySpace) {ks ->
  109
+				def columns = cassandra.persistence.getColumnRange(
  110
+						ks,
  111
+						indexColumnFamily,
  112
+						primaryKeyIndexRowKey(),
  113
+						options.start,
  114
+						options.finish,
  115
+						options.reversed,
  116
+						options.max)
  117
+
  118
+				def keys = columns.collect{cassandra.persistence.name(it)}
  119
+				def rows = cassandra.persistence.getRows(ks, columnFamily, keys)
  120
+				def result = cassandra.mapping.makeResult(keys, rows, options)
  121
+				return result
  122
+			}
  123
+		}
  124
+
  125
+		// findAllWhere(params, opts?)
  126
+		// findAllWhere(state: 'MD')
  127
+		// findAllWhere(phone: '301-555-1212')
  128
+		// findAllWhere(phone: ['301-555-1212','410-555-1234'])
  129
+		// findAllWhere(scope:'DP', scopeId:'text-account-1', eventType:'Radar')
  130
+		// findAllWhere(scope:'DP', scopeId:'text-account-1', eventType:'Radar', subType:['Review','Mention'])
  131
+		// findAllWhere(scope:'DP', scopeId:'text-account-1', categories: 'My Business', eventType:'Radar', subType:['Review','Mention'])
  132
+		clazz.metaClass.'static'.findAllWhere = {params, opts=[:] ->
  133
+
  134
+			def filterList = expandFilters(params)
  135
+			def index = findIndex(cassandraMapping.explicitIndexes, filterList)
  136
+			if (index) {
  137
+				return queryByExplicitIndex(clazz, filterList, index, opts)
  138
+			}
  139
+			else if (cassandraMapping.secondaryIndexes) {
  140
+				// TODO - handle secondary indexes
  141
+			}
  142
+			else {
  143
+				throw new CassandraMappingException("No index found for specified arguments")
  144
+			}
  145
+		}
  146
+
  147
+		// findWhere(params, opts?)
  148
+		clazz.metaClass.'static'.findWhere = {params, opts=[:] ->
  149
+			def options = opts.clone()
  150
+			options.max = 1
  151
+
  152
+			def result = null
  153
+			def filterList = expandFilters(params)
  154
+			def index = findIndex(cassandraMapping.explicitIndexes, filterList)
  155
+			if (index) {
  156
+				result = queryByExplicitIndex(clazz, filterList, index, options)
  157
+			}
  158
+			else if (cassandraMapping.secondaryIndexes) {
  159
+				// TODO - handle secondary indexes
  160
+			}
  161
+			else {
  162
+				throw new CassandraMappingException("No index found for specified arguments")
  163
+			}
  164
+			return result ? result[0] : null
  165
+		}
  166
+
  167
+		// findBy...(value)
  168
+		// findBy...And...(value1, value2)
  169
+		// findBy...And...And...(value1, value2, value3) ...
  170
+		//
  171
+		// findAllBy...(value)
  172
+		// findAllBy...(value, rowCount)
  173
+		// findAllBy...And...(value1, value2, rowCount)
  174
+		// findAllBy...And...And...(value1, value2, value3, rowCount) ...
  175
+		clazz.metaClass.'static'.methodMissing = {String name, args ->
  176
+			def single = false
  177
+			def str = null
  178
+			def result = null
  179
+			def opts = (args[-1] instanceof Map) ? args[-1].clone() : [:]
  180
+			if (name.startsWith("findAllBy") && name.size() > 9 && args.size() > 0) {
  181
+				str = name - "findAllBy"
  182
+			}
  183
+			else if (name.startsWith("findBy") && name.size() > 6 && args.size() > 0) {
  184
+				str = name - "findBy"
  185
+				opts.max = 1
  186
+				single = true
  187
+			}
  188
+			if (str) {
  189
+				def propertyList = propertyListFromMethodName(str)
  190
+				def params = [:]
  191
+				propertyList.eachWithIndex {it, i ->
  192
+					params[it] = args[i]
  193
+				}
  194
+				def filterList = expandFilters(params)
  195
+				def index = findIndex(cassandraMapping.explicitIndexes, filterList)
  196
+				if (index) {
  197
+					result = queryByExplicitIndex(clazz, filterList, index, opts)
  198
+				}
  199
+				else {
  200
+					// find by query expression
  201
+					def options = addOptionDefaults(opts, MAX_ROWS)
  202
+					cassandra.execute(keySpace) {ks ->
  203
+						def properties = [:]
  204
+						propertyListFromMethodName(str).eachWithIndex {it, i ->
  205
+							properties[it] = args[i]
  206
+						}
  207
+						def rows = cassandra.persistence.getRowsWithEqualityIndex(ks, columnFamily, properties, options.max)
  208
+						result = cassandra.mapping.makeResult(rows, options)
  209
+					}
  210
+				}
  211
+				return single ? (result ? result[0] : null) : result
  212
+			}
  213
+			else {
  214
+				throw new MissingPropertyException(name, clazz)
  215
+			}
  216
+		}
  217
+	}
  218
+}
260  src/groovy/com/reachlocal/grails/plugins/cassandra/mapping/DataMapping.groovy
... ...
@@ -0,0 +1,260 @@
  1
+/*
  2
+ * Copyright 2012 ReachLocal Inc.
  3
+ *
  4
+ * Licensed under the Apache License, Version 2.0 (the "License");
  5
+ * you may not use this file except in compliance with the License.
  6
+ * You may obtain a copy of the License at
  7
+ *
  8
+ *      http://www.apache.org/licenses/LICENSE-2.0
  9
+ *
  10
+ * Unless required by applicable law or agreed to in writing, software
  11
+ * distributed under the License is distributed on an "AS IS" BASIS,
  12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13
+ * See the License for the specific language governing permissions and
  14
+ * limitations under the License.
  15
+ */
  16
+
  17
+package com.reachlocal.grails.plugins.cassandra.mapping
  18
+
  19
+import grails.converters.JSON
  20
+import java.nio.ByteBuffer
  21
+import com.reachlocal.grails.plugins.cassandra.OrmPersistenceMethods
  22
+
  23
+/**
  24
+ * @author: Bob Florian
  25
+ */
  26
+class DataMapping 
  27
+{
  28
+	static final CLASS_NAME_KEY = '_class_name_'
  29
+	static final KEY_SUFFIX = InstanceMethods.KEY_SUFFIX
  30
+	static final DIRTY_SUFFIX = InstanceMethods.DIRTY_SUFFIX
  31
+
  32
+	OrmPersistenceMethods persistence
  33
+
  34
+	def dataProperties(data)
  35
+	{
  36
+		def map = [:]
  37
+		if (data instanceof Map) {
  38
+			data.each() {name, value ->
  39
+				if (value != null) {
  40
+					map[name] = dataProperty(value)
  41
+				}
  42
+			}
  43
+		}
  44
+		else {
  45
+			def transients = MappingUtils.safeGetProperty(data, 'transients', List, [])
  46
+			def hasMany = MappingUtils.safeGetProperty(data, 'hasMany', Map, [:])
  47
+			def clazz = data.getClass()
  48
+			map[CLASS_NAME_KEY] = clazz.getName()
  49
+			data.metaClass.properties.each() {
  50
+				if (!it.name.endsWith(DIRTY_SUFFIX)) {
  51
+					def prop = data.getProperty(it.name)
  52
+					if (prop != null &&
  53
+							it.getter &&
  54
+							!it.getter.isStatic() &&
  55
+							!transients.contains(it.name) &&
  56
+							!hasMany[it.name])
  57
+					{
  58
+						if (MappingUtils.isMappedClass(prop.class)) {
  59
+							map["${it.name}${KEY_SUFFIX}"] = prop.id
  60
+						}
  61
+						else {
  62
+							def value = dataProperty(prop)
  63
+							if (value != null) {
  64
+								map[it.name] = value
  65
+							}
  66
+						}
  67
+					}
  68
+				}
  69
+			}
  70
+		}
  71
+		return map
  72
+	}
  73
+
  74
+	def makeResult(rows, options)
  75
+	{
  76
+		if (options.columns) {
  77
+			rows.collect{newColumnMap(it.columns, options.columns)}
  78
+		}
  79
+		else if (options.column) {
  80
+			rows.collect{it.columns.getColumnByName(options.column)}
  81
+		}
  82
+		else {
  83
+			rows.collect{newObject(it.columns)}
  84
+		}
  85
+	}
  86
+
  87
+	def makeResult(keys, rows, options)
  88
+	{
  89
+		if (options.columns) {
  90
+			keys.collect{newColumnMap(persistence.getRow(rows, it), options.columns)}
  91
+		}
  92
+		else if (options.rawColumns) {
  93
+			keys.collect{newRawColumnMap(persistence.getRow(rows, it), options.rawColumns)}
  94
+		}
  95
+		else if (options.column) {
  96
+			keys.collect{persistence.stringValue(persistence.getColumn(persistence.getRow(rows, it), options.column))}
  97
+		}
  98
+		else if (options.rawColumn) {
  99
+			keys.collect{persistence.byteArrayValue(persistence.getColumn(persistence.getRow(rows, it), options.rawColumn))}
  100
+		}
  101
+		else {
  102
+			keys.collect{newObject(persistence.getRow(rows, it))}
  103
+		}
  104
+	}
  105
+
  106
+	def newColumnMap(cols, names)
  107
+	{
  108
+		def map = [:]
  109
+		names.each {
  110
+			map[it] = persistence.stringValue(persistence.getColumn(cols, it))
  111
+		}
  112
+		return map
  113
+	}
  114
+
  115
+	def newRawColumnMap(cols, names)
  116
+	{
  117
+		def map = [:]
  118
+		names.each {
  119
+			map[it] = persistence.byteArrayValue(persistence.getColumn(cols, it))
  120
+		}
  121
+		return map
  122
+	}
  123
+
  124
+	def newObject(cols)
  125
+	{
  126
+		def obj = null
  127
+		if (cols.size()) {
  128
+			def className = persistence.stringValue(persistence.getColumn(cols, CLASS_NAME_KEY))
  129
+			def asClass = Class.forName(className, false, DataMapping.class.classLoader)
  130
+			obj = asClass.newInstance()
  131
+
  132
+			cols.each() {col ->
  133
+				def name = col.name
  134
+				def metaProperty = obj.metaClass.getMetaProperty(name)
  135
+				if (metaProperty) {
  136
+					if (metaProperty.setter && !metaProperty.setter.isStatic()) {
  137
+						metaProperty.setProperty(obj, objectProperty(metaProperty.type, col))
  138
+					}
  139
+				}
  140
+			}
  141
+		}
  142
+		return obj
  143
+	}
  144
+
  145
+	def objectProperty(Class clazz, column)
  146
+	{
  147
+		switch (clazz) {
  148
+			case Integer:
  149
+				return new Integer(persistence.stringValue(column))
  150
+			case Long:
  151
+				return new Long(persistence.stringValue(column))
  152
+			case Float:
  153
+				return new Float(persistence.stringValue(column))
  154
+			case Double:
  155
+				return new Double(persistence.stringValue(column))
  156
+			case BigDecimal:
  157
+				return new BigDecimal(persistence.stringValue(column))
  158
+			case BigInteger:
  159
+				return new BigInteger(persistence.stringValue(column))
  160
+			case Boolean:
  161
+				return column.booleanValue
  162
+			case Date:
  163
+				return new Date(new Long(persistence.stringValue(column)))
  164
+			case String:
  165
+				return persistence.stringValue(column)
  166
+			case UUID:
  167
+				return UUID.fromString(persistence.stringValue(column))
  168
+			case byte[]:
  169
+				return persistence.byteArrayValue(column)
  170
+			default:
  171
+				if (Collection.isAssignableFrom(clazz) || Map.isAssignableFrom(clazz)) {
  172
+					return JSON.parse(persistence.stringValue(column))
  173
+				}
  174
+				else if (clazz.isEnum()) {
  175
+					clazz.values().each() {
  176
+						if (it.toString() == persistence.stringValue(column)) {
  177
+							return persistence.stringValue(column)
  178
+						}
  179
+					}
  180
+					return null
  181
+				}
  182
+		}
  183
+	}
  184
+
  185
+	def dataProperty(Date value)
  186
+	{
  187
+		return value.time.toString()
  188
+	}
  189
+
  190
+	def dataProperty(Integer value)
  191
+	{
  192
+		return value.toString()
  193
+	}
  194
+
  195
+	def dataProperty(Long value)
  196
+	{
  197
+		return value.toString()
  198
+	}
  199
+
  200
+	def dataProperty(Double value)
  201
+	{
  202
+		return value.toString()
  203
+	}
  204
+
  205
+	def dataProperty(BigDecimal value)
  206
+	{
  207
+		return value.toString()
  208
+	}
  209
+
  210
+	def dataProperty(BigInteger value)
  211
+	{
  212
+		return value.toString()
  213
+	}
  214
+