How does Grails resolve Controller name conflicts? - grails

What is the recommended approach when an application Controller name conflicts with the name of a plugin's Controller?
I've seen these Grails JIRAs:
GRAILS-4240
GRAILS-1243
...and Burt Beckwith's replies to these two threads imply that the only recourse is to rename one of the Controllers (presumably the application Controller since hacking plugin code is not desirable)
How to use the package name to differentiate between classes in grails?
How to extend/override controller actions of plugins?
However, Burt's own spring-security-ui plugin advocates the exact approach of naming an application Controller the same as a plugin Controller - see spring-security-ui docs.
This approach actually seems to work in both development mode (grails run-app) and when the app is deployed as a WAR. So can this functionality be depended on? If so, what is the Controller conflict resolution rule? The grails docs do not make any mention of it. Perhasps Burt can share his insight?
Having a "plugin" architecture like grails' without even a basic namespacing facility to handle conflicts like this seems pretty broken to me...

The problem is that while you can use packages for any artifact, the convention for controllers is to remove the package and "Controller" to create URLs, e.g. PersonController -> /appname/person/action_name. So in effect everything gets flattened.
In 1.2 and more so in 1.3 things were changed so plugins are compiled separately from application code (and are compiled first) and this gives you the opportunity to replace a plugin artifact with the application's version. Since you shouldn't edit plugin code, this gives you the flexibility to extend or replace a plugin artifact just by using the same name.
I tend to use UrlMappings to get around stuff like this when there are two similarly named controllers. For example say you have an admin UserController that allows low-level CRUD actions and a regular UserController that users work with. I'd name the admin controller AdminUserController and map it to /admin/user/* and leave UserController as is. The admin GSPs will be in views/adminUser and the others will be in views/user so there's no conflict there. This has the added benefit of being able to easily secure - map /admin/** -> ROLE_ADMIN. The conventions are convenient, but this is a simple configuration step that solves this issue for me.
The good news is that GRAILS-1243 will definitely be implemented in 2.0 and possibly in 1.4. And the plugin that Kim Betti references in the comments of GRAILS-1243 looks interesting.

Related

Overwrite a plugin GSP and Controller within another Plugin

I have a fairly complicated grails plugin dependency structure within my project and I am having problems overriding classes from the security plugin.
My structure is a little something like this:
Web App
|_ Audit Plugin
|_ Spring Security Core Plugin
|_ Security Wrapper Plugin
|_ Audit Plugin
|_ Spring Security Core Plugin
The reason it is like this is audit is shared between some apps which have the security wrapper, and some what don't, which is why it pulls in Security-Core (it needs at least the ability to get the current principal).
Similarly the wrapper is shared between multiple web apps therefore we put it in a plugin. My problem comes after upgrading Spring-Security-Core to version 2.
My wrapper has a customer auth.gsp and LoginController.groovy. In the older version of security this was fine, as the plugin templated those and made them available in the source of the installing plugin.
However now these files are internal to the plugin, and although I know you can override them within the main app, when trying to override them within another plugin I get some bizarre results.
The Spring-Security-Core version of the login page always overrides my custom login page. I cannot get mine to take precedence.
The second problem is that the LoginController.groovy from the Spring-Security-Core plugin sometimes takes precedence over my one from the wrapper. It seems almost random between builds as to which one will be in use.
Is there any correct way to go about making sure my views and controllers take precedence?
OK playing around with things I found a solution that seems to work for me:
Firstly I couldn't change the order in which the plugins load because the security wrapper does a lot with spring beans and it has to load after the core plugin for this to work. So after a bit of digging in the (DefaultSecurityConfig.groovy) I noticed that you can set the following properties:
grails.plugin.springsecurity.failureHandler.defaultFailureUrl = '/login/authfail? login_error=1'
grails.plugin.springsecurity.failureHandler.ajaxAuthFailUrl = '/login/authfail?ajax=true'
grails.plugin.springsecurity.auth.loginFormUrl = '/login/auth'
So I created a custom controller and login page which have a different name to the ones use in the core plugin and changed these properties to point to my locations.
To neaten this up, in the UrlMappings for the wrapper (named: SecWrapperUrlMappings) I put a mapping from /login/** to /seclogin/**.
Make sure that these new locations aren't locked down so that people can access them and that seems to work well. I now reliable know, whichever order they load in my login page and login controller are used.
In Grails-4.013 and spring-security-core-4.0.4, I did the following trick.
In my custom plugin instead of LoginController and LogoutController I named them as SigninController and SignoutController respectively. And in UrlMappings.groovy of App mapped them like..
static mappings = {
"/login/$action?"(controller: "signin")
"/logout/$action?"(controller: "signout")
....
....
}

Grails Plugin Development - override domain class

Plugins in Grails are great method to modularise an application.The documentation suggest to override the artifacts from the plugin in the application, which uses this plugin.
Is it realy the best approach?
Let's describe it by example: There is a domain class "org.User" defined in the plugin. The application overrides this domain class. If I use "grails run-app" then there are no warnings and it works. But Eclipse (GGTS) complains about "Invalid duplicate class definition of class org.User". For some developers it wouldn't matter, but I like the IDE helping on coding by stuf like "autocomplete".
At the end both classes are compiled an put on the java class loader. The application version of the class is loaded before the version of the plugin. The class resolver finds it first and that's why it works. Please correct me if I'm wrong at this point. Is it realy a good idea to have two versions of a class in one class loader?
What are the alternatives?
You can do like Spring Security Core plugin does, provide the User class as a template, so your application that use this plugin can choose between creating his own class or installing your default User class.
The plugin user template is here, and the script responsible to create this in the application is here.
You will need also a config value to know the class to use, and use it dynamic.
P.S: there are good security plugins like Shiro and Spring Security, maybe it's easier to check them instead of create your own.

Where do all methods, attributes get wired into grails controller?

I'm looking at grails (2.2), and it's all beautiful and even magical, how it all works. I'm looking at a Controller class which is created with grails create-controller and out of the box it has many methods and properties available, like render(), redirect(), params, request and I presume it goes on and on.
where does all this get wired in? Where in the code/project/framework do I see that render() is made available as a method? And how is it implemented?
As a java developer I'm used to inheritance and code injection and reflection. And in javascript prototypes can do some black magic. But the XXController.groovy is just a standalone object. Is it the name (XXController) or the location (grails-app/controllers?) or is there some injection happening which the IDE can pick up?
Welcome to the wonderfull world of Grails,
Here you have a couple of links that may help you:
The Section of Controllers in the Web Layer docs.
And the docs of the render method. Check it out the "Quick reference" column at the right for more methods avaliable at the Controllers.
If you are wondering how that magic is done, Grails is an open source project, so as usual, go and serve yourself at Github (warning, it is quite large project).
Grails works on the top of Groovy, which is a Dynamic Language with a powerful support of meta programming. Tha is basically the trick of all the magic of Grails
Finally, Grails is a framework based on CoC (Convection over configuration), So the Controllers will be any class under the directory "grails-app/controllers" and with the suffix "Controller". (In the folder of controllers may be "commandObjects as well).
The integration with well-know ides is quite powerful as well, you should check it out
EDIT
You may also found how the render methods behaves here at github.
And more inyected stuff at the Controllers metaClass package
As of Grails 2.0+ it's implemented using an AST transformation - previously it was done by adding the methods to the Groovy MetaClass. The benefits of the new approach are that things will be a bit faster and use less memory.
GORM domain class methods now use this approach too (except for dynamic methods like findByFooAndBar which have to be added dynamically to the metaclass) and those have the benefit of being callable from Java since the AST adds the methods to the bytecode. This doesn't help controllers though since they're only called from Grails itself as the result of a web request.
For the gory details, ControllersApi is where the methods are, and they're mixed into each controller class by a combination of ControllerTransformer and the code in the doWithDynamicMethods closure in ControllersGrailsPlugin

Is it possible to add Grails MVC classes at deployment time?

I'm writing a Grails app which I'd like 3rd parties to augment at runtime. Ideally they would be able to add a JAR/WAR to the webapp directory which contains new domain, controller and service classes, new views, and other content.
Is there a simple way to do this within grails? Would it be simplest to create a startup script which copies the new classes etc. into the relevant directories and then updates grails.xml and web.xml?
You will be able to do this in version 2 of grails in which plugins will be also OSGI plugins http://jira.codehaus.org/browse/GRAILS/fixforversion/15421
It seems that the Grails plugins will actually fit quite well for this: http://www.grails.org/Understanding+Plugins
A plugin can do just about anything... One thing a plugin cannot do though is modify the web-app/WEB-INF/web.xml or web-app/WEB-INF/applicationContext.xml files. A plugin can participate in web.xml generation, but not modify the file or provide a replacement. A plugin can NEVER change the applicationContext.xml file, but can provide runtime bean definitions

Re-using model in multiple applications

Due to hosting constraints I am porting an ASP.NET MVC to Ruby On Rails. In my ASP.NET application I had 2 web applications. One for the Admin section, and one for the public section.
Both referenced a class library that held all my Business Logic and Data.
Now I want to accomplish the same thing in Ruby On Rails. How do I use my model in two applications?
First shot: put the business logic and associated models in a plugin.
Second shot: use SVN externals or Git submodules to replicate the exact code in your app, for example under APP_ROOT/lib/ and then require it in your environment.
And last... why not putting the admin and public section in the same app?
I would create a plugin. Call it IceheatSharedLogic or something. Add the model and supporting code to the plugin and commit it to its own source control location. Add that plugin via svn:externals or equivalent to both apps.
But given your use case, is it really necessary to have 2 apps? It seems that would make things complicated if you ever needed any interaction between them (such as putting an edit link on a page, if you are logged in as an admin).

Resources