grails 2.5+ Duplicate class definition error when implementing serializable on a controller - grails

Upgrading a legacy system of grails. One of the controllers implements Serializable. This is throwing the following error in newer versions of grails:
Invalid duplicate class definition of class com.regional.ScheduleController :
The source contains at least two definitions of the class.
One of the classes is an explicit generated class using the class statement,
the other is a class generated from the script body based on the file name.
Solutions are to change the file name or to change the class name.
The solution mentioned would break (previous) grails convention. Anyone know how to handle this in grails 2.5+?
EDIT
Serializable is not the issue. I tried removing it and got the same error.
I found this explanation from another question:
IN groovy.. class B{} is a class structure and defines a class B.
Scripts are classes too.
Now you may create a B.groovy, with the content "class B{}; def b = new B()".
There would be one class named B, and a script with the very same name.
This is a conflict.
However this does not explain why it runs fine below grails 2.5 and not above it. And I can't find a def conflict like the one mentioned above in that controller.
ANSWER:
One of the imports was what was actually failing- in a way that caused groovy to generate a class definition based on the current file name. When it hit the class definition, there was already an auto generated class name to collide with.

Related

Grails 3.1.1 - Dirty checking not working when model class extends another groovy class

I have a domain class which extends another groovy class with same name but in different package in a different library.
The problem is when I modify instances on the domain class, it is not marked as dirty & hence changes are not persisted.
I have read that grails 3 release has some enhancements to dirty checking & this could be a bug or I am missing something.
New objects are saved properly without any issues, I have used isDirty() method on modified domain object as well as modified properties & both return false. Objects are attached to the session, confirmed with isAttached().
To reproduce, I created a test project with following code & tried updating the object from default grails views that are generated using scaffolding, but still the changes are not persisted.
Note: I have done similar stuff in Grails 2.4 & it used to work.
Domain class is as follows:
package com.perseus
class Derived extends Base{
static constraints = {
name blank: false, nullable: false
}
}
Base class in src/main/groovy:
package com.perseus
class Base implements Serializable {
private static final long serialVersionUID = 1L
String name
}
Controller
package com.perseus
class DerivedController {
static scaffold = Derived
}
Link to github project.
Isssue: Model is not marked dirty, even if it has been modified. This happens when a model class extends another groovy class.
How to reproduce:
Run the app.
Create a new model object (model name is Derived)
Modify the object using edit view & click Update.
You will see that modifications are not persisted.
Finally I found a reference here which explains the reason for failure to update dirty check status.
I added #DirtyCheck annotation and it solved the problem.
However this has a negative impact on the design of our libraries.
We have separate projects for each category:
Business model classes
Business logic classes
User Interface
The idea is to have modules completely independent of each other. So model & business logic liraries can be used by any UI technology. Now the problem is that I had to add gorm dependency to my business model project for the annotation.
compile 'org.grails:grails-datastore-gorm:6.1.7.RELEASE'
Design wise we like our model classes in a project completely independent of UI or persistent technologies like hibernate. So that these model classes can be used in variety of different projects without any additional dependencies. And we achieved this with grails so far by creating a new class that extends actual model class in our library
Is there any way I can solve this problem, without modifying the base class?
In short, model classes (POJO) are now dependent on grails framework, whereas in earlier versions they were not.

swagger-inflector and its use of x-swagger-router-controller and x-swagger-router-model

I am using swagger-inflector to design an API. I am trying to use x-swagger-router-controller and x-swagger-router-model as specified in the docs, but it is not working as specified.
The way I read the docs, these vendor extensions are supposed to create the class if it does not exist and then create a method named with the "operationId". However, they do not work as specified. (I looked at the code that I think is supposed to process this vendor extension, and it looks like the extension should be processed, but the classes are not created as expected. Well, the x-swagger-router-model generates the desired class name, but not the desired package).
If I use the inflector.yaml file and specify modelPackage and controllerPackage, the classes get created in those packages, but I need more granular control over what package is used for the generated classes.
Am i doing something wrong, or is this broken?
Here is an example:
definitions:
SomeObject:
type: object
x-swagger-router-model: com.example.api.dto.SomeObjectDTO
...
paths:
/mypath:
x-swagger-router-controller: com.example.api.controller.subpkg1.MyController
get:
...
From the above example
I do not get a model class named SomeObjectDTO created in com/example/api/dto. If no modelPackage is specified in inflector.yaml, I get a model class named SomeObject created in the default package (io/swagger/...). The model class name that is generated in either case is SomeObject.java
I do not get a controller class named MyController created in com/example/api/controller/subpkg1. If no controllerPackage is specified in inflector.yaml, I get a controller class named "MyPathController" created in the default package (io/swagger/...). The controller class that is generated in either case is MyPathController.java.
It looks like this is a bug, or that I am missing something really obvious. Any pointers here?
the swagger-inflector code will not generate models for you--it will attempt to connect the routing information (via x-swagger-router-model) or via modelPackage + schema name. If that model cannot be loaded in the class loader, it will treat this model as a jackson JsonNode for all input (put/post) methods.

What its the first, the annotated class (egg) or used class (chicken)?

certainly I have not read something fundamental, and it seems very strange, but I wonder.
Suppose you use
#SharedPref
public interface SharedPreferencesInterface {
#DefaultBoolean(true)
boolean showDeviceName();
I have the IDE (idea) configured with Gradle, and I generated the SharedPreferencesInterface_ class that I can use in another class as
#Pref
SharedPreferencesInterface_ prefs;
But suppose someone now download the project, how can the use? Because the class where used SharedPreferencesInterface_ not compile because the class does not exist, and the class does not exist because compilation errors ...
How it's made? Surely there is a way ... configured to compile certain classes first?
Help is appreciated.
A greeting.
But suppose someone now download the project, how can the use? Because
the class where used SharedPreferencesInterface_ not compile because
the class does not exist, and the class does not exist because
compilation errors ...
This is the same situation when you compile a project in a full build (when no classes are generated yet). Actually Gradle always does a full build currently in Android projects. No configuration is needed at all in addition to the standard AndroidAnnotaions config.
Actually this works because the compiler does not fully compiles your class before passing it to annotations processing. It is clear it should not to, because the class may reference generated classes, which are only available after the processing. So first the compiler creates a model of the classes, only parses the structure of the them (fields, methods, return types, parameter types, etc), but not the implementations. Also it allows missing types even on fields. If it finds a missing type, it assigns to TypeKind.ERROR, but the name of the type is still available for the annotation processor. After the processor is done, it generates the missing class, so the kind of the class is no longer TypeKind.ERROR, and the compilation can succeed.

Grails Logging - Exclude One Class

I have a project consisting of hundreds of various classes. One of these extends a class located in a JAR library and produces tons of log info. I would like to exclude this one class from producing all this logging information.
Here's my config.groovy logging section:
trace('grails.plugin.springsecurity.web.filter.DebugFilter',
'grails.app.conf.com.myrootpackage',
'grails.app.controllers.com.myrootpackage',
'grails.app.domain.com.myrootpackage',
'grails.app.filters.com.myrootpackage',
'grails.app.services.com.myrootpackage',
'grails.app.taglib.com.myrootpackage',
'com.myrootpackage')
Since all of my classes are located in the com.myrootpackage package or sub packages of that, I'm not sure how to exclude the one class. As far as I can tell, the logging setup in config.groovy only allows specifying the beginning of class names so I would have to specify by name all other classes and omit the one I want to omit, or move the one I want to omit to a separate root package. Both of these seem silly to have to do to just omit one class from producing log output.
As far as I can tell, the logging setup in config.groovy only allows specifying the beginning of class names
No, you can use the full class name to configure an individual class, so the following should work
trace('grails.plugin.springsecurity.web.filter.DebugFilter',
'grails.app.conf.com.myrootpackage',
'grails.app.controllers.com.myrootpackage',
'grails.app.domain.com.myrootpackage',
'grails.app.filters.com.myrootpackage',
'grails.app.services.com.myrootpackage',
'grails.app.taglib.com.myrootpackage',
'com.myrootpackage')
error 'com.myrootpackage.ExcludeMe'
I've assumed
com.myrootpackage.ExcludeMe is the fully-qualified name of the class in question
this class should log at the error level instead of trace

ConversionNotSupportedException with similarly named classes

UPDATED: made some wrong assumptions about classes etc. The following occurs now when I have a 'demo' project:
I have two classes, both named 'Company'.
One is placed in grails-app/domain/my.classes.domain.Company
the other is in src/groovy/my.clazz.Company
The last one has a #Validateable annotation, and the Config.groovy contains grails.validateable.packages = ['my.clazz']
I also have an Account class, in grails-app/domain/my.classes.domain.Account:
package my.classes.domain
import java.io.Serializable;
class Account implements Serializable { Company company }
Then I use the following code (in the bootstrap.groovy):
import my.classes.domain.Company
import my.classes.domain.Account
...
Company company = new Company
Account acccount = new Account(company: company)
When running this app, the following error is shown:
Caused by: org.springframework.beans.ConversionNotSupportedException: Cannot convert value of type [my.clazz.Company] to required type [my.classes.domain.Company] for property 'company': no matching editors or conversion strategy found
... 33 more
Caused by: java.lang.IllegalStateException:
Cannot convert value of type [my.clazz.Company] to required type [my.classes.domain.Company] for property 'company': no matching editors or conversion strategy found
This is a very strange exception, as everything seems to be fine.
Some testing proved the following 'hints':
This error does NOT occur when I modify the config.groovy to explicitly name the classes (i.e. use grails.validateable.classes = ['my.classes.domain.Company']),
This error does NOT occur when I modify the Account's company property to be named differently (and modify the bootstrap accordingly), i.e.:
class Account extends Serializable {
Company cmp
}
However, these are workarounds. I'm really interested in WHY this is happening. Anybody got a clue?
Just to be on the safe side, I did the following to create this problem:
create domain class: my.classes.domain.Company
create domain class: my.classes.domain.Account
modify the domain class as above
create a groovy class: my.clazz.Company
give this groovy class the Validatable annotation.
add the my.clazz package to the validateable packages
in the bootstrap, create a new Account with new Account(company:company)
Aside from the various typos in your code which makes the problem hard to determine, the problem would seem to be that you are trying to set the Account.company property which is of type my.class.Company with the my.class.domain.Company type. Your bootstrap would need to be changed to:
import my.class.Company
import my.class.Account
Company company = new Company
Account acccount = new Account(company: company)
Note, the correct import statement for Company.
According to the error you describe, I think that Grails have determined wrong: the company you transfer into Account was determined as a my.clazz.Company, not my.classes.domain.Company.
You can put a simple check to know for sure what's the type of company in bootstrap:
import java.lang.Class;
...
println company.getClass().getName()

Resources