I assume that the issue involves the creation of the Groovy console inside the Grails instance; normal Domain object queries work fine, but attempting to query into a self-referential one-to-one causes issues.
I have a Domain class like the followng:
package demo
class Foo {
String name
static hasMany = [substitutes: Foo]
static constraints = {
}
}
During bootstrap, I create a Console to be able to inspect the domain objects:
import demo.Foo
import org.springframework.web.context.support.WebApplicationContextUtils
import org.codehaus.groovy.grails.commons.GrailsApplication
import grails.util.GrailsUtil
import groovy.ui.Console
class BootStrap {
def init = { servletContext ->
def one = new Foo([name: 'one'])
def two = new Foo([name: 'two'])
[one,two].each { it.save(failOnError: true, flush: true)}
one.addToSubstitutes(two)
one.save(failOnError: true, flush: true)
def appCtx = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext)
def grailsApp = appCtx.getBean(GrailsApplication.APPLICATION_ID);
Binding b = new Binding();
b.setVariable("ctx", appCtx);
def console = new Console(grailsApp.classLoader, b);
console.run()
Foo.list().each{println it.name}
Foo.list().each{println it.substitutes}
println '--------------------------'
}
def destroy = {
}
}
This prints the following in the console:
one
two
[demo.Foo : 2]
null
--------------------------
Within the console, I attempt to repeat the two queries, which throws an exception:
groovy> import demo.Foo
groovy> demo.Foo.list().each{println it.name}
groovy> demo.Foo.list().each{println it.substitutes}
one
two
Exception thrown
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: demo.Foo.substitutes, no session or session was closed
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:383)
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:375)
at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:368)
at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:111)
at org.hibernate.collection.PersistentSet.iterator(PersistentSet.java:186)
at org.springsource.loaded.ri.ReflectiveInterceptor.jlrMethodInvoke(ReflectiveInterceptor.java:1243)
at ConsoleScript2$_run_closure2.doCall(ConsoleScript2:3)
at org.springsource.loaded.ri.ReflectiveInterceptor.jlrMethodInvoke(ReflectiveInterceptor.java:1243)
at ConsoleScript2.run(ConsoleScript2:3)
at org.springsource.loaded.ri.ReflectiveInterceptor.jlrMethodInvoke(ReflectiveInterceptor.java:1243)
Related
I have the following in a groovy file...
// jenkins/Application.groovy
package jenkins
class Application{
String name
Application(name){
this.name = name
}
}
I am trying to import it and I have tried these but none seem to work. These are all in jenkins/Test.groovy
final GroovyScriptEngineImpl engine = (GroovyScriptEngineImpl) this.scriptingEngines.getEngineByName("groovy");
GroovyClassLoader classLoader = engine.getClassLoader();
classLoader.parseClass(new GroovyCodeSource("./jenkins/Application.groovy"))
engine.setClassLoader(classLoader)
This gives..
Script1.groovy: 17: unable to resolve class Application
Then I tried...
// jenkins/Application.groovy
// Added
return { Application }
// jenkins/Test.groovy
app = load "./jenkins/Application.groovy"
def dna = new app.Application("blah")
and I get...
Script1.groovy: 11: unable to resolve class app.Application
How do I import a call in a Jenkins GroovyScript?
Update
I changed my code to the following (and moved into a domain folder)...
app = load "./jenkins/domain/Application.groovy"
def dna = app.newInstance([name:"blah"] as Object[])
When I run I get...
org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: Scripts not permitted to use staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods newInstance java.lang.Class java.lang.Object[]
the idea
you could return from loaded script - the class (not an instance)
then to create new instance you could call class.newInstance( Object [] argList )
http://docs.groovy-lang.org/latest/html/groovy-jdk/java/lang/Class.html#newInstance(java.lang.Object[])
so, theoretically this should work:
./jenkins/Application.groovy
class Application{
String name
Application(name){
this.name = name
}
}
return Application.class
pipeline:
def app = load "./jenkins/Application.groovy"
def dna = app.newInstance( "blah" )
I'm working with the following environment:
- Grails 3.0.3, Groovy 2.4.3, JVM 1.8.0.31 ... all running on OSX 10.10.4
After tracing through with a debugger, Grails appears to be seeing the one-to-many self association (parent --> children) as a many-to-many. The result is that when one tries to remove an object from the children, grails tries to clean up the other end of the association by calling a remove() method on something that is, of course, not a collection. Not finding an applicable remove() method causes Grails to throw a MissingMethodException... I've isolated the case down to a two level inheritance model where the abstract base class defines the parent/children self association. NOTE, the problem does not occur if I collapse the two classes into a single instantiable class. Here is the domain model:
// CLASS 1 - Abstract Base Class
package com.sandbox
abstract class AbstractBaseClass {
static hasMany = [ children: AbstractBaseClass ]
static belongsTo = [ parent: AbstractBaseClass ]
static constraints = {
}
}
// CLASS 2 - Instantiable Sub Class
package com.sandbox
class IllustrationCase extends AbstractBaseClass {
String name
static constraints = {
}
}
Then, my test case where the exception occurs is as follows:
package com.sandbox
import grails.test.mixin.integration.Integration
import grails.transaction.*
import spock.lang.*
#Integration
#Rollback
class IllustrationCaseSpec extends Specification {
def setup() {
}
def cleanup() {
}
void "Deleting Nodes From a Hierarchy"() {
given:
// Create the various nodes
def theParent = new IllustrationCase(
name: "Parent Node"
)
def theChild1 = new IllustrationCase(
name: "Child Node 1"
)
def theChild2 = new IllustrationCase(
name: "Child Node 2"
)
def theChild3 = new IllustrationCase(
name: "Child Node 3"
)
// Create the hierarchy structure
theParent.addToChildren(
theChild1
).addToChildren(
theChild2
).addToChildren(
theChild3
)
// Perform a DEEP save
theParent.save( failOnError: true )
// Remove a child --- This is where the error occurs...
IllustrationCase.get( theParent.id ).removeFromChildren( IllustrationCase.get( theChild2.id ) )
expect:
// Find the parent nodes...
def targetNode = IllustrationCase.findByName( "Parent Node" )
targetNode.children.size() == 2
!targetNode.children.find { it.name == "Child Node 2" }
targetNode.children.find { it.name == "Child Node 1" }
targetNode.children.find { it.name == "Child Node 3" }
}
}
Executing the above test case results in the following exception being thrown...
groovy.lang.MissingMethodException: No signature of method: com.sandbox.IllustrationCase.remove() is applicable for argument types: (com.sandbox.IllustrationCase) values: [com.sandbox.IllustrationCase : 1]
Possible solutions: merge(com.sandbox.IllustrationCase), merge(), merge(), create(), delete(), save()
at org.grails.orm.hibernate.AbstractHibernateGormEnhancer.addRelationshipManagementMethods_closure5(AbstractHibernateGormEnhancer.groovy:183)
at com.sandbox.IllustrationCaseSpec.$tt__$spock_feature_0_0(IllustrationCaseSpec.groovy:46)
at com.sandbox.IllustrationCaseSpec.Deleting Nodes From a Hierarchy_closure1(IllustrationCaseSpec.groovy)
at groovy.lang.Closure.call(Closure.java:423)
at groovy.lang.Closure.call(Closure.java:439)
at grails.transaction.GrailsTransactionTemplate$1.doInTransaction(GrailsTransactionTemplate.groovy:67)
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133)
at grails.transaction.GrailsTransactionTemplate.executeAndRollback(GrailsTransactionTemplate.groovy:64)
at com.sandbox.IllustrationCaseSpec.Deleting Nodes From a Hierarchy(IllustrationCaseSpec.groovy)
Considering the error goes away if I collapse the two classes (i.e., remove the abstract class and inheritance from the equation) this is really looking like a potential bug in Grails 3 so I opened a bug report with the Grails team......
I'm trying to use the Grails shopping cart plugin found here: http://grails.org/plugin/shopping-cart
I was able to successfully install the plugin in my application, as well as inject the service in my Controller:
class TestController {
def shoppingCartService
def index() {
def s = new DomainObj(name: "A Plain Ole Domain Object")
s.addToShoppingCart()
}
}
This appears to be adding the product to my shopping cart, as I expected. However, the problem I'm encountering now is actually listing the items out from the cart. According to the debugger, after running the above code, the shopping cart does indeed have an item (s) in it, as it returns:
com.metasieve.shoppingcart.ShoppingItem : 1
The item is properly being added to the cart, but now I would like to actually list the name of the item out again, so in this case, I want to display the name A Plain Ole Domain Object. How do I do this?
I'm unsure of the syntax for getting the actual objects back from the cart. The documentation doesn't describe how to do this clearly, as it merely states that the following should work:
def checkedOutItems = shoppingCartService.checkOut()
checkedOutItems.each {
println it['item']
println it['qty']
}
But that outputs
com.metasieve.shoppingcart.ShoppingItem : 1 , which is only a reference to some arbitrary item in the cart. I want to get back the actual name of my item.
Thanks in advance.
EDIT:
My domain class (DomainObj) is defined as follows:
class DomainObj extends com.metasieve.shoppingcart.Shoppable {
String name
static constraints = {
name blank: false
}
}
EDIT #2:
def index() {
def s = new DomainObj(name: "A Plain Ole Domain Object")
s.addToShoppingCart()
def r = new DomainObj(name: "Second Plain Ole Domain Object")
r.addToShoppingCart()
def checkedOutItems = shoppingCartService.checkOut()
println currentItems
println "-----"
checkedOutItems.each {
println it['item']
println it['qty']
}
}
The output of this is:
[com.metasieve.shoppingcart.ShoppingItem : 1, com.metasieve.shoppingcart.ShoppingItem : 2]
com.metasieve.shoppingcart.ShoppingItem : 2
1
com.metasieve.shoppingcart.ShoppingItem : 1
1
According to the documentation it["item"] gives you back the entity of a domain class that extends Shoppable. So in this case when you are printing it out it's calling the toString() method of that domain class. If you want that to return the value of the name property you need to implement your own toString(). Here is such an example
#Override
String toString() {
return name
}
EDIT:
Well as it's not clear from the documentation it['item'] is a pointer to the Shoppable instance which you can then use to query for the actual product in your cart like this:
com.metasieve.shoppingcart.Shoppable.findByShoppingItem(it['item'])
Thus the following will print out the toString() value of your products
checkedOutItems.each {
println com.metasieve.shoppingcart.Shoppable.findByShoppingItem(it['item'])
println it['qty']
}
For testing I created the following domain and controller.
Domain:
package com.test
class MyProduct extends com.metasieve.shoppingcart.Shoppable {
String name
static constraints = {
name(blank: false)
}
#Override
String toString() {
return name
}
}
Controller:
package com.test
class MyProductController {
def shoppingCartService
def index() {
def p1 = new MyProduct(name: 'one')
p1.save(flush: true, failOnError: true)
p1.addToShoppingCart()
def p2 = new MyProduct(name: 'two')
p2.save(flush: true, failOnError: true)
p2.addToShoppingCart()
def checkedOutItems = shoppingCartService.checkOut()
checkedOutItems.each {
println com.metasieve.shoppingcart.Shoppable.findByShoppingItem(it['item'])
println it['qty']
}
}
}
I have static method in a domain class that returns a url. I need to build that url dynamically but g.link isn't working.
static Map options() {
// ...
def url = g.link( controller: "Foo", action: "bar" )
// ...
}
I get the following errors:
Apparent variable 'g' was found in a static scope but doesn't refer to a local variable, static field or class. Possible causes:
You attempted to reference a variable in the binding or an instance variable from a static context.
You misspelled a classname or statically imported field. Please check the spelling.
You attempted to use a method 'g' but left out brackets in a place not allowed by the grammar.
# line 17, column 19.
def url = g.link( controller: "Foo", action: "bar" )
^
1 error
Obviously my problem is that I am trying to access g from static context, so how do I get around this?
The g object is a taglib, which is not available inside a domain class like it would be in a controller. You can get at it through the grailsApplication as shown here: How To Call A Taglib As A Function In A Domain Class
A better way to do this in Grails 2+ is through the grailsLinkGenerator service, like so:
def grailsLinkGenerator
def someMethod() {
def url = grailsLinkGenerator.link(controller: 'foo', action: 'bar')
}
In both cases, you'll need to do some extra work to get grailsApplication/grailsLinkGenerator from a static context. The best way is probably to grab it off the domainClass property of your domain class:
def grailsApplication = new MyDomain().domainClass.grailsApplication
def grailsLinkGenerator = new MyDomain().domainClass.grailsApplication.mainContext.grailsLinkGenerator
If you're using Grails 2.x you can use the LinkGenerator API. Here's an example, I am re-using a domain class I was testing with earlier so ignore the non-url related functionality.
class Parent {
String pName
static hasMany = [children:Child]
static constraints = {
}
static transients = ['grailsLinkGenerator']
static Map options() {
def linkGen = ContextUtil.getLinkGenerator();
return ['url':linkGen.link(controller: 'test', action: 'index')]
}
}
Utility Class with Static Method
#Singleton
class ContextUtil implements ApplicationContextAware {
private ApplicationContext context
void setApplicationContext(ApplicationContext context) {
this.context = context
}
static LinkGenerator getLinkGenerator() {
getInstance().context.getBean("grailsLinkGenerator")
}
}
Bean Def for New Utility Bean
beans = {
contextUtil(ContextUtil) { bean ->
bean.factoryMethod = 'getInstance'
}
}
If you need the base URL, add absolute:true to the link call.
Given this relationship:
class A {
String name
static hasMany = [b:B]
}
class B {
String name
static belongsTo = [a:A]
}
I have an record b that I want to save. I've already discovered via working Grails reflection (omitted in the code example below) that it needs to be an instance of class B. Beyond that, record b only knows:
it has a relation "a"
relation "a"'s key
Since it's a dynamic case, we do not know and must discover:
relation "a" is to an instance of class A (so we can call A.find(a's key))
the "other side" of the relation - class A's perspective - is relation "b" (so we can call .addToB(b))
So how do I save b to the database? Here's how I'm doing it:
class AssocTests extends GrailsUnitTestCase {
protected void setUp() {
super.setUp()
// I don't know this part, but it's in the DB
def a = new A(name:"al")
a.save()
}
void testAssociation() {
// I want to create a new B such that name="bob"
// I also had to discover "class B" using (working) Grails reflection
// but omitted it for this example.
def b = new B(name:"bob")
// ... and the relation is as "given" below
def given = [a:[name:"al"]]
// So I need to call A.find([name:"al"]).addToB(b). But "A" and
// "addToB" are unknown so need to be found via reflection
def gdc = new DefaultGrailsDomainClass(B)
given.each { give ->
def prop = gdc.getPropertyByName(give.key)
if (prop.isAssociation() && !prop.isOwningSide()) {
println "I want to use otherSide, but it's ${prop.otherSide}"
def os = reallyGetOtherSide(B, give)
def object = os.parent.find(
os.parent.newInstance(give.value))
object."${os.method}"(b)
}
}
def bFound = B.findByName("bob")
assertEquals "al", bFound.a.name
}
def reallyGetOtherSide(clazz, relation) {
def parent=clazz.belongsTo[relation.key]
def addTo=parent.hasMany.find { (clazz == it.value) }.key
[parent:parent, method:"addTo${addTo.capitalize()}"]
}
}
...with otherSide returning null, unfortunately. This can't be the best way to do this, can it?
If I understood you correctly, You can refer to these docs here. You can try the following:
`new A(name:"Gatwick")
.addToB(new B(name:"BA3430"))
.addToB(new B(name:"EZ0938"))
.save()`