0

I am converting existing Grails 2.5.6 project to Grails 3.3.11.

With my existing application (Grails 2.5.6), the plugin descriptor is having code like below:

def doWithApplicationContext = { applicationContext ->
    def config = applicationContext.grailsApplication.config
    def key = config.property.key
    key.put(Constants.RESULT_CONST, [controller: "results", action: "showData", templatePath: "/results/data"])
}

This code works fine with earlier version of grails. But after I have upgraded to grails 3.3.11 then it throws exception: java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

This is at line:

key.put(Constants.RESULT_CONST, [controller: "results", action: "showData", templatePath: "/results/data"])

After looking at the type of key i.e. config.property.key it is showing type as org.grails.config.NavigableMap$NullSafeNavigator.

Which with older version was LinkedHashMap. property.key is set on application.groovy fine under /grails-app/conf/application.groovy property.key = [:]

I've also tried setting type of property.key in plugin descriptor externally to java.util.HashMap. But it seems like not adopting new type.

What I am doing wrong here?

Parth Bhagat
  • 509
  • 1
  • 11
  • 24
  • Is your goal simply to dynamically add entries to the application's config? – Jeff Scott Brown Mar 27 '20 at 13:54
  • Yes. To add entries based on rules. These entries will be referred from that plugin and other part of the application. – Parth Bhagat Mar 27 '20 at 13:58
  • Where do the values for `results` and `showData` come from? – Jeff Scott Brown Mar 27 '20 at 13:59
  • I am sorry, it is actually values and not member variable. Updating question... – Parth Bhagat Mar 27 '20 at 14:00
  • With your latest update to the question, there isn't anything dynamic. The values are all hardcoded so you could eliminate all this code and put the config settings in the plugin's config file. – Jeff Scott Brown Mar 27 '20 at 14:01
  • Well, the dynamic code is actually that only. This is part of one plugin. But if there is another plugin included (based on properties file) then that plugin descriptor will also have this entry added to `property.key` map. – Parth Bhagat Mar 27 '20 at 14:03
  • Also, in a Grails 3 plugin, instead of `applicationContext.grailsApplication.config` you could simply refer to `config` and there is no longer a `doWithApplicationContext` property whose value is a `Closure`. In Grails 3 it is simply a no arg method named `doWithApplicationContext()`. You don't need the `applicationContext` parameter because there is a `applicationContext` property in the plugin class. – Jeff Scott Brown Mar 27 '20 at 14:04
  • "Well, the dynamic code is actually that only." - It isn't dynamic though. I am saying that those values could simply be defined in `grails-app/conf/plugin.yml`. – Jeff Scott Brown Mar 27 '20 at 14:05
  • Can we refer static final value as key? Like `key.put(Constants.RESULT_CONST, [controller: "results", action: "showData", templatePath: "/results/data"])` in plugin.yml file? – Parth Bhagat Mar 27 '20 at 14:13
  • Are there other requirements that are not expressed in the question? Knowing what you are trying to do will help propose the right solution. – Jeff Scott Brown Mar 27 '20 at 14:19
  • Sorry. Updated the code in question with what we wants. – Parth Bhagat Mar 27 '20 at 14:22

2 Answers2

2

Instead of trying to do this dynamically with this:

def doWithApplicationContext = { applicationContext ->
    def config = applicationContext.grailsApplication.config
    def key = config.property.key
    key.put(2, [controller: "results", action: "showData", templatePath: "/results/data"]) 
}

You could define those values in grails-app/conf/plugin.yml like this:

---
property:
  key:
    '2':
      controller: results
      action: showData
      templatePath: '/results/data`

EDIT

The question has changed such that the above is no longer valid.

Instead of doing this:

def config = applicationContext.grailsApplication.config
def key = config.property.key
key.put(Constants.RESULT_CONST, [controller: "results", action: "showData", templatePath: "/results/data"])

You can simplify that to this:

config.merge([property: [key: [Constants.RESULT_CONST, [controller: "results", action: "showData", templatePath: "/results/data"]]]])
Jeff Scott Brown
  • 26,804
  • 2
  • 30
  • 47
  • With `config.merge`, there is no error when war deployed. But when I try to access values from config it does not gives values set to `property.key`. Here is how I am trying to access: `grailsApplication.config.property.key.get(Constants.RESULT_CONST);` & `grailsApplication.config.property.key.Constants.RESULT_CONST;`. Trying to access from main App controller (and not from plugin controller). – Parth Bhagat Mar 27 '20 at 15:32
  • You should not do `grailsApplication.config.property.key.get(...)`. See the video at https://guides.grails.org/grails-quickcasts-retrieving-config-values/guide/index.html or the article at https://objectcomputing.com/resources/publications/sett/retrieving-config-values-in-grails-3. – Jeff Scott Brown Mar 27 '20 at 17:05
  • Thank you @Jeff. This solution works. Have posted consolidated code snippets to set and retrieve config parameters below. – Parth Bhagat Apr 14 '20 at 05:52
0

Thank you @Jeff for your input and suggestion.

Here is the consolidated code for setting config parameters and retrieve them in controller or any other grails component.

Plugin Descriptor:

class ResultGrailsPlugin extends Plugin {
   void doWithApplicationContext() { 
      config.merge(['property': ['key': ["${Constants.RESULT_CONST}": [controller: "results", action: "showData", templatePath: "/results/data"]]]])
   }
}

Controller:

class ResultController {
   def index() {
      def resultConfigMap = grailsApplication.config.get('property.key.' + Constants.RESULT_CONST)
      ...
   }
}
Parth Bhagat
  • 509
  • 1
  • 11
  • 24
  • That controller code is not a great idea. That is retrieving the config value during request processing time, which means it will happen over and over again. – Jeff Scott Brown Apr 14 '20 at 15:10
  • Yes. this is actually for representation purpose only. The actual use of config parameters would be in service. – Parth Bhagat Apr 15 '20 at 11:45
  • If you move that method into a service that is called during request processing, you will have the same issue. Why would you show a Controller if actual use would be in a service anyway? – Jeff Scott Brown Apr 15 '20 at 14:05