Where's located the declaration of messageSource in Grails? - grails

Background
We have some legacy internationalization for field labels that are stored in the database, so I tried to make a "merged" messageSource. If the code exists in database, return, if not, use PluginAwareResourceBundleMessageSource to look in the i18n.
Problem
For some reason the cachedMergedPluginProperties is caching the wrong file for the locale. For example, if I search for en_US, I receive pt_BR messages (the key of the Map is en_US, but the properties are pt_BR).
I declared my messageSource as follows:
messageSource(DatabaseMessageSource) {
messageBundleMessageSource = { org.codehaus.groovy.grails.context.support.PluginAwareResourceBundleMessageSource m ->
basenames = "WEB-INF/grails-app/i18n/messages"
}
}
The inner bean is beacause of Grails won't let me have two beans of type MessageSource.
Am I declaring PluginAwareResourceBundleMessageSource different from the default of Grails? In which file of Grails I can see this bean declaration?

I found the declaration inside I18nGrailsPlugin, and it's a bit more detailed then mine:
String baseDir = "grails-app/i18n"
String version = GrailsUtil.getGrailsVersion()
String watchedResources = "file:./${baseDir}/**/*.properties".toString()
...
Set baseNames = []
def messageResources
if (application.warDeployed) {
messageResources = parentCtx?.getResources("**/WEB-INF/${baseDir}/**/*.properties")?.toList()
}
else {
messageResources = plugin.watchedResources
}
if (messageResources) {
for (resource in messageResources) {
// Extract the file path of the file's parent directory
// that comes after "grails-app/i18n".
String path
if (resource instanceof ContextResource) {
path = StringUtils.substringAfter(resource.pathWithinContext, baseDir)
}
else {
path = StringUtils.substringAfter(resource.path, baseDir)
}
// look for an underscore in the file name (not the full path)
String fileName = resource.filename
int firstUnderscore = fileName.indexOf('_')
if (firstUnderscore > 0) {
// grab everyting up to but not including
// the first underscore in the file name
int numberOfCharsToRemove = fileName.length() - firstUnderscore
int lastCharacterToRetain = -1 * (numberOfCharsToRemove + 1)
path = path[0..lastCharacterToRetain]
}
else {
// Lop off the extension - the "basenames" property in the
// message source cannot have entries with an extension.
path -= ".properties"
}
baseNames << "WEB-INF/" + baseDir + path
}
}
LOG.debug "Creating messageSource with basenames: $baseNames"
messageSource(PluginAwareResourceBundleMessageSource) {
basenames = baseNames.toArray()
fallbackToSystemLocale = false
pluginManager = manager
if (Environment.current.isReloadEnabled() || GrailsConfigUtils.isConfigTrue(application, GroovyPagesTemplateEngine.CONFIG_PROPERTY_GSP_ENABLE_RELOAD)) {
def cacheSecondsSetting = application?.flatConfig?.get('grails.i18n.cache.seconds')
if (cacheSecondsSetting != null) {
cacheSeconds = cacheSecondsSetting as Integer
} else {
cacheSeconds = 5
}
}
}
Since Grails don't let you have two beans of type MessageSource I had to copy this code and adapt to mine "merged" messageSource.

Related

How to replace Servlet Filters already defined (replacing legacy doWithWebDescriptor with doWithSpring)

I'm trying to upgrade a Grails plugin from version 2.3.4 to 4.0.11. It uses a syntax that is no longer supported to replace filters with names sitemesh and urlMapping with its own filters.
The code below uses a DSL for xml. It replaces xml nodes in the final generated web.xml.
def doWithWebDescriptor = { xml ->
def pageFilter = xml.filter.find { it.'filter-name'.text() == 'sitemesh' }
def urlMappingFilter = xml.filter.find { it.'filter-name'.text() == 'urlMapping' }
def grailsVersion = GrailsUtil.grailsVersion
// Grails 1.3.x & Grails 2.0.x
def pageFilterClass = "org.zkoss.zk.grails.web.ZKGrailsPageFilter"
def urlMappingFilterClass = "org.zkoss.zk.grails.web.ZULUrlMappingsFilter"
if(grailsVersion.startsWith("2")) {
pageFilter.'filter-class'.replaceNode {
'filter-class'(pageFilterClass)
}
urlMappingFilter.'filter-class'.replaceNode {
'filter-class'(urlMappingFilterClass)
}
//
// Require a legacy config for servlet version
//
if(application.metadata.getServletVersion() >= '3.0') {
pageFilter.'filter-class' + {
'async-supported'('true')
}
urlMappingFilter.'filter-class' + {
'async-supported'('true')
}
}
} else {
pageFilter.'filter-class'.replaceBody(pageFilterClass)
urlMappingFilter.'filter-class'.replaceBody(urlMappingFilterClass)
}
}
What I tried so far
The code below uses Grails plugin configuration to register filters with spring's FilterRegistrationBean. I'm following Grails official documentation.
Closure doWithSpring() { { ->
boolean supportsAsync = this.grailsApplication.metadata.getServletVersion() >= "3.0"
pageFilter(FilterRegistrationBean) {
name = "sitemesh"
filter = bean(org.zkoss.zk.grails.web.ZKGrailsPageFilter)
urlPatterns = ["/*"]
order = Ordered.HIGHEST_PRECEDENCE
asyncSupported = supportsAsync
}
urlMappingFilter(FilterRegistrationBean) {
name = "urlMapping"
filter = bean(org.zkoss.zk.grails.web.ZULUrlMappingsFilter)
urlPatterns = ["/*"]
order = Ordered.HIGHEST_PRECEDENCE
asyncSupported = supportsAsync
}
}}
How can I replicate the legacy code with RegistrationBeans?
Also, I don't know if any of these filters got deprecated by Grails. I would like to know if there are any other replacements, if possible.
Here's the project in case anyone wants more context.
Debugging the older version of the plugin I came up with the following:
pageFilter(FilterRegistrationBean) {
name = "sitemesh"
filter = bean(ZKGrailsPageFilter)
urlPatterns = ["/*"]
order = OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER + 50
asyncSupported = supportsAsync
dispatcherTypes = EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR)
}
urlMappingFilter(FilterRegistrationBean) {
name = "urlMapping"
filter = bean(ZULUrlMappingsFilter)
urlPatterns = ["/*"]
order = OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER + 60
asyncSupported = supportsAsync
dispatcherTypes = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD)
}
Added the dispatcherTypes and changed the order assuming these would be the last filters where the 'pageFilter' should be placed before the 'urlMappingFilter' in the filter chain.

Xtext project JDT Independence

I created editor with Xtext 2.9.1 and now I want to make it independent of JDT. I followed this guide https://eclipse.org/Xtext/documentation/307_special_languages.html
but it does not seem to work. This is my ErrmsgUiModule.xtend
#FinalFieldsConstructor
class ErrmsgUiModule extends AbstractErrmsgUiModule {
override configure(Binder binder) {
super.configure(binder);
binder.bind(DefaultHighlightingConfiguration).to(ErrMsgHighlightingConfiguration);
binder.bind(DefaultSemanticHighlightingCalculator).to(ErrorSemanticHighlightingCalculator);
}
override bindIResourceForEditorInputFactory() {
return ResourceForIEditorInputFactory
}
override bindIResourceSetProvider() {
return SimpleResourceSetProvider
}
override provideIAllContainersState() {
return Access.getWorkspaceProjectsState()
}
}
I checked every overwritten method with debugger and all 3 methods are called. I did not created my own project wizard so this should be enough.
But still after plugin installating the Java project wizard becomes available...
/Edit:
To provide more information, this is project's mwe2 file
module com.xxx.lang.errmsg.GenerateErrmsg
import org.eclipse.xtext.xtext.generator.*
import org.eclipse.xtext.xtext.generator.model.project.*
import org.eclipse.xtext.ui.generator.*
var rootPath = ".."
Workflow {
component = XtextGenerator {
configuration = {
project = StandardProjectConfig {
baseName = "com.xxx.lang.errmsg"
rootPath = rootPath
runtimeTest = {
enabled = true
}
eclipsePlugin = {
enabled = true
}
eclipsePluginTest = {
enabled = true
}
createEclipseMetaData = true
}
code = {
encoding = "windows-1250"
fileHeader = "/*\n * generated by Xtext \${version}\n */"
}
}
language = StandardLanguage {
name = "com.xxx.lang.errmsg.Errmsg"
fileExtensions = "msg"
fragment = formatting.Formatter2Fragment2 auto-inject {}
serializer = {
generateStub = false
}
validator = {
// composedCheck = "org.eclipse.xtext.validation.NamesAreUniqueValidator"
}
}
}
}
and this is list of Dependencies from plugin.xml for various projects:
project com.xxx.lang.errmsg
org.eclipse.xtext,
org.eclipse.xtext.xbase,
org.eclipse.equinox.common;bundle-version="3.5.0",
org.eclipse.emf.ecore,
org.eclipse.xtext.xbase.lib,
org.antlr.runtime,
org.eclipse.xtext.util,
org.eclipse.xtend.lib,
org.eclipse.emf.common,
org.objectweb.asm;bundle-version="[5.0.1,6.0.0)";resolution:=optional,
org.eclipse.xtext.ui
project com.xxx.lang.errmsg.ui
com.xxx.lang.errmsg,
com.xxx.lang.errmsg.ide,
org.eclipse.xtext.ui,
org.eclipse.xtext.ui.shared,
org.eclipse.xtext.ui.codetemplates.ui,
org.eclipse.ui.editors;bundle-version="3.5.0",
org.eclipse.ui.ide;bundle-version="3.5.0",
org.eclipse.ui,
org.eclipse.compare,
org.eclipse.xtext.builder,
org.eclipse.xtend.lib;resolution:=optional,
org.eclipse.xtext.xbase.lib,
org.eclipse.xtext.xbase.ui
/Edit2: According to this topic https://bugs.eclipse.org/bugs/show_bug.cgi?id=336217, I also tried to disable all org.eclipse.jdt* plugins in run configuration. This is what i get http://pastebin.com/Wi0gzceM
You have to remove the dependencies on org.eclipse.xtext.xbase and org.eclipse.xtext.xbase.ui from your runtime and UI project.
Make sure if you open the Plug-in Dependencies in the Package Explorer that you do not see org.eclipse.jdt.core in the list. If so, there is another plug-in having a (transitive) dependency. Find out which and remove.

How to append to the URL Path Grails

I'm using Grails 1.3.7 and I'm trying to figure out how to append a state (region) abbreviation (NM, AZ, UT) in the path of my URL. So it looks like this:
localhost/NM/action/
or
localhost/UT/action2/3/
This has to be done for all urls on the site. I also don't want to change the URLMappings.groovy file, would a filter help with this?
I created this filter definition:
def filters = {
...
regionAppender(uri:'/**') {
before = {
if (request.region) {
if (!request.forwardURI.contains(request.region)) {
String[] split = s.split("/");
String redirectUri = split[0] + "/" + request.region
if (split.length > 1) {
for (int i = 1; i < split.length; i++){
redirectUri = redirectUri + "/" + split[i]
}
}
redirect(uri: redirectUri)
return false
}
return true
}
return true
}
}
}
But it doesn't seem to be working. Is there a way to do this, or do I have change all my URLMappings?
I haven't test this but maybe it is to do with your declaration of request... try params.region instead
Take a look here
def filters = {
....
before = {
if(params?.id) {

Why am I getting this error in a basic Rails+Ember app?

I am trying to do a simple CRUD app using Ember + Rails and I'm getting the following error when trying to go to the /workouts route.
Error while loading route: TypeError {} ember.js?body=1:415
Uncaught TypeError: Object function () {
if (!wasApplied) {
Class.proto(); // prepare prototype...
}
o_defineProperty(this, GUID_KEY, undefinedDescriptor);
o_defineProperty(this, '_super', undefinedDescriptor);
var m = meta(this), proto = m.proto;
m.proto = this;
if (initMixins) {
// capture locally so we can clear the closed over variable
var mixins = initMixins;
initMixins = null;
this.reopen.apply(this, mixins);
}
if (initProperties) {
// capture locally so we can clear the closed over variable
var props = initProperties;
initProperties = null;
var concatenatedProperties = this.concatenatedProperties;
for (var i = 0, l = props.length; i < l; i++) {
var properties = props[i];
Ember.assert("Ember.Object.create no longer supports mixing in other definitions, use createWithMixins instead.", !(properties instanceof Ember.Mixin));
for (var keyName in properties) {
if (!properties.hasOwnProperty(keyName)) { continue; }
var value = properties[keyName],
IS_BINDING = Ember.IS_BINDING;
if (IS_BINDING.test(keyName)) {
var bindings = m.bindings;
if (!bindings) {
bindings = m.bindings = {};
} else if (!m.hasOwnProperty('bindings')) {
bindings = m.bindings = o_create(m.bindings);
}
bindings[keyName] = value;
}
var desc = m.descs[keyName];
Ember.assert("Ember.Object.create no longer supports defining computed properties.", !(value instanceof Ember.ComputedProperty));
Ember.assert("Ember.Object.create no longer supports defining methods that call _super.", !(typeof value === 'function' && value.toString().indexOf('._super') !== -1));
Ember.assert("`actions` must be provided at extend time, not at create time, when Ember.ActionHandler is used (i.e. views, controllers & routes).", !((keyName === 'actions') && Ember.ActionHandler.detect(this)));
if (concatenatedProperties && indexOf(concatenatedProperties, keyName) >= 0) {
var baseValue = this[keyName];
if (baseValue) {
if ('function' === typeof baseValue.concat) {
value = baseValue.concat(value);
} else {
value = Ember.makeArray(baseValue).concat(value);
}
} else {
value = Ember.makeArray(value);
}
}
if (desc) {
desc.set(this, keyName, value);
} else {
if (typeof this.setUnknownProperty === 'function' && !(keyName in this)) {
this.setUnknownProperty(keyName, value);
} else if (MANDATORY_SETTER) {
Ember.defineProperty(this, keyName, null, value); // setup mandatory setter
} else {
this[keyName] = value;
}
}
}
}
}
finishPartial(this, m);
this.init.apply(this, arguments);
m.proto = proto;
finishChains(this);
sendEvent(this, "init");
} has no method 'find'
My code is located here: https://github.com/ecl1pse/ember-workouts
What am I doing wrong?
Edit: Upon further investigation I believe the culprit is
EmberWorkouts.WorkoutsRoute = Ember.Route.extend(
model: -> EmberWorkouts.Workout.find()
This doesn't actually return anything. How do I debug from there?
If I replace that with this
EmberWorkouts.WorkoutsRoute = Ember.Route.extend
model: -> [{title: 'hi'}, {title: 'damn'}]
The view actually renders content.
How do I get the model to collect from Rails properly?
Ember Data's interface has changed a little with the current release:
You can clear out the store.js file entirely. Ember Data will automatically set up a data store for you using the REST Adapter (unless you tell it otherwise).
Use model: -> #store.find('workout') instead.
I tested this with your app and it works.
If you haven't read through the Ember Data Guide in the last week or two (it's changed a lot), I would spend a few minutes on it.
The fix for this error (as of ember-data 1.0.0.beta.6) for me was to make sure that the JSON returned from the server included an "id" field for each model, BUT not to explicitly declare the id when setting up the Ember DS.Model.
jbuilder template:
json.scans do
json.array! #scans do |scan|
json.id scan.id # This prop has to be there
json.name scan.name
end
end
Ember model:
EmberApp.Scan = DS.Model.extend(
// Don't include the id prop here
name: DS.attr("string")
)

displaying errors in grails without refreshing the page

I have a page with dynamic list boxes(selecting value from the first list populates the values in the second list box).
The validation errors for the list boxes are working fine, but while displaying the error messages the page is getting refreshed and the selected values are been set to initial status(need to select the values again in the list boxes)
The page is designed to add any number of list boxes using ajax calls, so adding and selecting the values again is going to be a rework.
Could you help me in displaying the validation errors and keeping the selected values as they are(previously I faced a similar situation which was resolved by replacing local variables of preprocess and postprocess with a global variable, this time no luck with that approach)
Any hints/help would be great
static constraints = {
deviceMapping(
validator: {val, obj ->
Properties dm = (Properties) val;
def deviceCheck = [:];
if (obj.customErrorMessage == null) {
for (def device : dm) {
if (device.key == null || "null".equalsIgnoreCase(device.key)) {
return ["notSelected"];
}
deviceCheck.put(device.key, "");
}
if (deviceCheck.size() != obj.properties["numberOfDevices"]) {
return ["multipleDevicesError"];
}
}
}
)
customErrorMessage (
validator: {
if ("sameDeviceMultipleTimes".equals(it)) {
return ['sameDeviceMultipleTimes']
}
}
)
}
public LinkedHashMap<String, Object> preProcess(sessionObject, params, request) {
Submission submission = (Submission) sessionObject;
def selectedFileName = sessionObject.fileName;
logger.debug("submission.deviceMapping :"+submission.deviceMapping)
try {
Customer customer = Customer.get(submission.customerId);
OperatingSystem operatingSystem = OperatingSystem.get(submission.operatingSystemId)
def ftpClientService = new FtpClientService();
def files = ftpClientService.listFilesInZip(customer.ftpUser, customer.ftpPassword, customer.ftpHost, customer.ftpToPackageDirectory, selectedFileName, operatingSystem, customer.ftpCustomerTempDirectory);
def terminalService = new TerminalService();
OperatingSystem os = OperatingSystem.get(submission.getOperatingSystemId());
def manufacturers = terminalService.getAllDeviceManufacturersForType(os.getType());
logger.debug("manufacturers after os type :"+manufacturers)
logger.debug("files in preprocess :"+files)
def devicesForFiles = [:]
files.each { file ->
def devicesForThisFile = [];
submission.deviceMapping.each { device ->
if (device.value == file.fileName) {
String manufacturer = terminalService.getManufacturerFromDevice("${device.key}");
def devicesForManufacturer = terminalService.getDevicesForManufacturerAndType(manufacturer, os.getType());
devicesForThisFile.push([device:device.key, manufacturer: manufacturer, devicesForManufacturer: devicesForManufacturer]);
}
}
devicesForFiles.put(file.fileName,devicesForThisFile);
}
logger.debug("devicesForFiles :"+devicesForFiles)
return [command: this, devicesForFiles: devicesForFiles, files: files, manufacturers: manufacturers];
} catch (Exception e) {
logger.warn("FTP threw exception");
logger.error("Exception", e);
this.errors.reject("mapGameToDeviceCommand.ftp.connectionTimeOut","A temporary FTP error occurred");
return [command: this];
}
}
public LinkedHashMap<String, Object> postProcess(sessionObject, params, request) {
Submission submission = (Submission) sessionObject;
Properties devices = params.devices;
Properties files = params.files;
mapping = devices.inject( [:] ) { map, dev ->
// Get the first part of the version (up to the first dot)
def v = dev.key.split( /\./ )[ 0 ]
map << [ (dev.value): files[ v ] ]
}
deviceMapping = new Properties();
params.files.eachWithIndex { file, i ->
def device = devices["${file.key}"];
if (deviceMapping.containsKey("${device}")) {
this.errors.reject("You cannot use the same device more than once");
return [];
//customErrorMessage = "sameDeviceMultipleTimes";
}
deviceMapping.put("${device}", "${file.value}");
}
if (params.devices != null) {
this.numberOfDevices = params.devices.size(); //Used for the custom validator later on
} else {
this.numberOfDevices = 0;
}
//logger.debug("device mapping :"+deviceMapping);
submission.deviceMapping = mapping;
return [command: this, deviceMapping: mapping, devicesForFiles: devicesForFiles ];
}
}
The problem is in your gsp page. Be sure that all field are initialised with a value
<g:text value="${objectInstance.fieldname}" ... />
Also the way it is selecting values is through id, so be sure to set it as well:
<g:text value="${objectInstance.fieldname}" id=${device.manufacturer.id} ... />

Resources