I'm running a test with Grails 2.3.8 and an external configuration, yet the value doesn't seem to be coming through. I've tried this in older versions and am not seeing the same error. Did something change or am I missing something that I fat fingered? I am using an absolute path and the file definitely exists because Grails sees the key.
Config.groovy
reliable.string = "This string is working"
grails.config.locations = ["file:/home/howes/Project/project/test-config.groovy"]
/home/howes/Project/project/test-config.groovy
test.externalstring = "this is a test"
To test it I made a controller and just called grailsApplication to pull out the values. The first time I load the page I get a blank map, and when I refresh I see the key but no value. To make sure I am pulling everything correctly I am outputting the test-config.groovy item and one from the basic Config.groovy.
TestContoller.groovy
class TestController {
def grailsApplication
def index() {
println grailsApplication.config.test
println grailsApplication.config.reliable
return [ext:grailsApplication.config.test.externalstring, ext2:grailsApplication.config.reliable.string]
}
}
Console Output (First Load)
[:]
[string:This string is working]
Console Output (Page Refresh)
[externalstring:[:]]
[string:This string is working]
What am I missing here?
Related
I was looking into logging actions for example when you create a new user it sends it to the logger etc.. so every action is logged. I can see how the logger.info sends information into the development.log file.
I was wondering how I Would set-up a different file e.g. users.log and then when I log a line or variable, it saves inside that log file instead of the development.log?
Ruby has a Logger class in its standard lib: http://ruby-doc.org/stdlib-2.1.0/libdoc/logger/rdoc/Logger.html
You would instantiate that and pass it the file path of your new log file, like this:
user_log = File.open('logs/users.log', File::WRONLY | File::APPEND)
You can place that in a controller method that your controllers can use. The first string argument is the path to the log file, and the following are opening the file for writing and appending only (so that each log line is added to the log rather than overwriting it each time).
You can customize the format of each log line by setting a formatter:
user_log.formatter = proc { |severity, datetime, progname, msg|
"#{severity}, #{datetime}, #{progname}, #{msg.dump}"
}
You can specify the file path used in the config file, which can vary according to the environment, as so:
config.paths.log = "/some/path/#{Rails.env}.log"
If you want to create different log files for each model, you can simply create a logger object when needed, as explained in this answer.
However if you just want to somehow mark different logs according to where they were generated, it may be easier to use tagged logging:
logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
logger.tagged("BCX") { logger.info "Stuff" } # Logs "[BCX] Stuff"
logger.tagged("BCX", "Jason") { logger.info "Stuff" } # Logs "[BCX] [Jason] Stuff"
logger.tagged("BCX") { logger.tagged("Jason") { logger.info "Stuff" } } # Logs "[BCX] [Jason] Stuff"
I was trying to follow the step-by-step instructions in Grails In Action (http://www.manning.com/gsmith2/GiA2E_meap_ch01.pdf), and the scaffolding explained in section 1.5.1, Pg. 21-23 does not seem to be working for me.
I added the static scaffold = true in the QuoteController.groovy as suggested. Then did grails run-app, and when I head to localhost:8080/qotd/quote/list I get a 404 error (instead of the Figure 1.11 in the pdf) as follows:
HTTP Status 404 - /qotd/quote/list
type Status report
message /qotd/quote/list
description The requested resource is not available.
Apache Tomcat/7.0.42
Here is the QuoteController.groovy:
package qotd
class QuoteController {
static scaffold = true
def index() {
redirect(action: "home")
}
def home() {
render "Real Programmers do not eat Quiche"
}
def random() {
def allQuotes = Quote.list()
def randomQuote
def n = allQuotes.size()
if (n > 0){
def randomIdx = new Random().nextInt(n)
randomQuote = allQuotes[randomIdx]
} else{
String str = "Real Programmers Don't Eat Quiche" + n
randomQuote = new Quote(author: "Anonymous",
content: str)
}
[quote: randomQuote]
}
}
However, going to localhost:8080/qotd/quote/create works fine (matches with Figure 1.12 in the pdf), and I am able to create a new quote.
The versions I am using are:
App version: 0.1
Grails version: 2.3.1
Groovy version: 2.1.8
JVM version: 1.7.0_45
Is this a bug in Grails or I am missing something?
I am new to Groovy and Grails, and any help would be highly appreciated.
Thank you!
The list action has been removed for some reason. Use index instead.
There are more changes now with version 2.4.2.
The following url explains how scaffolding has been moved to the plugin model:
http://grails.org/doc/latest/guide/scaffolding.html
"As of Grails 2.3, the scaffolding feature has been moved to a plugin. By default this is configured for installation in new applications, but if you are upgrading from a previous version of Grails you will need to add the following configuration to your BuildConfig.groovy file..."
So, inside the plugins { } section add this line:
compile ":scaffolding:2.0.0"
Also, use the action 'create' to force data into your database if it's still empty.
For example:
localhost:8080/myapp/mycont/create
Then try to see if you can load it with:
localhost:8080/myapp/mycont/show/1
Replace:
myapp --> with your application name (used in 'grails create-app')
mycont --> your controller name (used in 'grails create-controller')
When I try run this script to secure my web services on Grails / CXF client I get
"Cannot invoke method getInInterceptors() on null object" on secureServiceFactory
Does secureServiceFactory need to be set somewhere else?
Any ideas:
Code :
class BootStrap {
def secureServiceFactory
def init = { servletContext ->
Map<String, Object> inProps = [:]
inProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
inProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
Map<QName, Validator> validatorMap = new HashMap<QName, Validator>();
validatorMap.put(WSSecurityEngine.USERNAME_TOKEN, new UsernameTokenValidator() {
#Override
protected void verifyPlaintextPassword(org.apache.ws.security.message.token.UsernameToken usernameToken, org.apache.ws.security.handler.RequestData data)
throws org.apache.ws.security.WSSecurityException {
if(data.username == "wsuser" && usernameToken.password == "secret") {
println "username and password are correct!"
} else {
println "username and password are NOT correct..."
throw new WSSecurityException("user and/or password mismatch")
}
}
});
inProps.put(WSS4JInInterceptor.VALIDATOR_MAP, validatorMap);
secureServiceFactory.getInInterceptors().add(new WSS4JInInterceptor(inProps))
}
Not sure this is a total answer, but, I receive the same errors and I understand that the cxf plugin is meant to wire up service factories that will match the name of your exposed service. I have verified that out of the box, running the grails-cxf plugin using grails run-app the application works. however, by executing grails war on the project creates a war that when deployed to tc server [vfabric-tc-server-developer-2.9.4.RELEASE] tomcat 7 [tomcat-7.0.47.A.RELEASE], this error occurs.
It is also useful to note that out of the box, as the plugin author has noted in other references [http://www.christianoestreich.com/2012/04/grails-cxf-interceptor-injection/] the generated war won't work unless you change test('org.apache.ws.security:wss4j:1.6.7') to compile('org.apache.ws.security:wss4j:1.6.7') and I note that I was unable to make that work, I had to use compile('org.apache.ws.security:wss4j:1.6.9')
Unfortunately, after surpassing this, I run into a third error when deploying the war that doesn't occur in grails run-app:
22-Aug-2014 11:46:05.062 SEVERE [tomcat-http--1] org.apache.catalina.core.StandardWrapperValve.invoke Allocate exception for servlet CxfServlet
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'cxf' is defined
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:641)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1159)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:282)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:273)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:979)
at org.apache.cxf.transport.servlet.CXFServlet.loadBus(CXFServlet.java:75)
I'll continue looking at it, but perhaps this war isn't meant to really deploy, but is more meant just for development of the plugin itself. however, if that is the case, it would still be better to work in TC because then we can leverage the code in our own projects with confidence.
just started with the codenarc plugin for grails - seems to be great!
But now I try to configure a dynamic report title like this:
"code quality report for ${appName}"
Unfortunately, it seems that the appName property is not available at the right time - I just get a null value.
Any ideas?
PS: using Grails 2.0.4 with ":codenarc:0.19"
appName is by default bound in BuildConfig.groovy but is not available to the configuration closure for codenarc. I am yet to become cognizant of an absolute reasoning behind that behavior, unless anyone here throws some light on it. But you can very well read application.properties directly to get the appName as below:
codenarc.reports = {
def props = new Properties()
new File("application.properties").withInputStream {props.load(it)}
MyHtmlReport('html') {
outputFile = "${props['app.name']} - CodeNarc-Report.html"
title = "${props['app.name']} - Sample Report"
}
}
My requirement is to invoke some processing from a Jenkins build server, to determine whether the domain model has changed since the last build. I've come to the conclusion that the way forward is to write a script that will invoke a sequence of existing scripts from the db-migration plugin. Then I can invoke it in the step that calls test-app and war.
I've looked in the Grails doc, and at some of the db-migration scripts, and I find I'm stuck - have no idea where to start trying things. I'd be really grateful if someone could point me at any suitable sources. BTW, I'm a bit rusty in Grails. Started to teach myself two years ago via proof of concept project, which lasted 6 months. Then it was back to Eclipse rich client work. That might be part of my problem, though I never go involved in scripts.
One thing I need in the Jenkins evt is to get hold of the current SVN revision number being used for the build. Suggestions welcome.
Regards, John
Create a new script by running grails create-script scriptname. The database-migration plugins scripts are configured to be easily reused. There are is a lot of shared code in _DatabaseMigrationCommon.groovy and each script defines one target with a unique name. So you can import either the shared script or any standalone script (or multiple scripts) and call the targets like they're methods.
By default the script generated by create-script "imports" the _GrailsInit script via includeTargets << grailsScript("_GrailsInit") and you can do the same, taking advantage of the magic variables that point at installed plugins' directories:
includeTargets << new File("$databaseMigrationPluginDir/scripts/DbmGenerateChangelog.groovy")
If you do this you can remove the include of _GrailsInit since it's already included, but if you don't that's fine since Grails only includes files once.
Then you can define your target and call any of the plugin's targets. The targets cannot accept parameters, but you can add data to the argsMap (this is a map Grails creates from the parsed commandline arguments) to simulate user-specified args. Note that any args passed to your script will be seen by the database-migration plugin's scripts since they use the same argsMap.
Here's an example script that just does the same thing as dbm-generate-changelog but adds a before and after message:
includeTargets << new File("$databaseMigrationPluginDir/scripts/DbmGenerateChangelog.groovy")
target(foo: "Just calls dbmGenerateChangelog") {
println 'before'
dbmGenerateChangelog()
println 'after'
}
setDefaultTarget foo
Note that I renamed the target from main to foo so it's unique, in case you want to call this from another script.
As an example of working with args, here's a modified version that specifies a default changelog name if none is provided:
println 'before'
if (!argsMap.params) {
argsMap.params = ['foo2.groovy']
}
dbmGenerateChangelog()
println 'after'
Edit: Here's a fuller example that captures the output of dbm-gorm-diff to a string:
includeTargets << new File("$databaseMigrationPluginDir/scripts/_DatabaseMigrationCommon.groovy")
target(foo: "foo") {
depends dbmInit
def configuredSchema = config.grails.plugin.databasemigration.schema
String argSchema = argsMap.schema
String effectiveSchema = argSchema ?: configuredSchema ?: defaultSchema
def realDatabase
boolean add = false // booleanArg('add')
String filename = null // argsList[0]
try {
printMessage "Starting $hyphenatedScriptName"
ByteArrayOutputStream baos = new ByteArrayOutputStream()
def baosOut = new PrintStream(baos)
ScriptUtils.executeAndWrite filename, add, dsName, { PrintStream out ->
MigrationUtils.executeInSession(dsName) {
realDatabase = MigrationUtils.getDatabase(effectiveSchema, dsName)
def gormDatabase = ScriptUtils.createGormDatabase(dataSourceSuffix, config, appCtx, realDatabase, effectiveSchema)
ScriptUtils.createAndPrintFixedDiff(gormDatabase, realDatabase, realDatabase, appCtx, diffTypes, baosOut)
}
}
String xml = new String(baos.toString('UTF-8'))
def ChangelogXml2Groovy = classLoader.loadClass('grails.plugin.databasemigration.ChangelogXml2Groovy')
String groovy = ChangelogXml2Groovy.convert(xml)
// do something with the groovy or xml here
printMessage "Finished $hyphenatedScriptName"
}
catch (e) {
ScriptUtils.printStackTrace e
exit 1
}
finally {
ScriptUtils.closeConnection realDatabase
}
}
setDefaultTarget foo