import org.apache.log4j.*
environments {
development {
log4j = {
appenders {
file name: 'grailsfile', file: 'target/grails.log', layout: pattern(conversionPattern: "[%d{ISO8601}]\t%p\t%c{2}\t%m%n")
file name: 'rootlog', file: 'target/root.log', layout: pattern(conversionPattern: "[%d{ISO8601}]\t%p\t%c{2}\t%m%n")
file name: 'devfile', file: 'target/development.log', layout: pattern(conversionPattern: "[%d{ISO8601}]\t%p\t%c{2}\t%m%n")
}
root { error 'stdout', 'rootlog' }
info additivity: false, grailsfile: 'org.codehaus.groovy.grails, com.orientechnologies'//.commons'
all additivity: false, devfile: [
'grails.app.controllers.my.package',
'grails.app.domain.my.package',
'grails.app.services.my.package',
'grails.app.taglib.my.package',
'grails.app.conf.my.package',
'grails.app.filters.my.package',
'my.package'
]
}
}
test {
log4j = {
appenders {
file name: 'grailsfile', file: 'target/grails.log', layout: pattern(conversionPattern: "[%d{ISO8601}]\t%p\t%c{2}\t%m%n")
file name: 'rootlog', file: 'target/root.log', layout: pattern(conversionPattern: "[%d{ISO8601}]\t%p\t%c{2}\t%m%n")
file name: 'testfile', file: 'target/test.log', layout: pattern(conversionPattern: "[%d{ISO8601}]\t%p\t%c{2}\t%m%n")
}
root { error 'stdout', 'rootlog' }
info additivity: false, grailsfile: 'org.codehaus.groovy.grails'
all additivity: false, testfile: [
'grails.app.controllers.my.package',
'grails.app.domain.my.package',
'grails.app.services.my.package',
'grails.app.taglib.my.package',
'grails.app.conf.my.package',
'grails.app.filters.my.package',
'my.package'
]
}
}
production {
grails.logging.jul.usebridge = false
log4j = {
appenders {
console name: 'console', layout: pattern(conversionPattern: "[%d{ISO8601}]\t%p\t%c{2}\t%m%n")
appender new org.apache.log4j.DailyRollingFileAppender(
name:"logfile", fileName:"./my.application.log",
layout:pattern(conversionPattern: '[%d{ISO8601}]\t%p\t%c{2}\t%m%n')
)
}
// set this to debug if debugging on a dev server
info 'grails.app.controllers.my.package',
'grails.app.domain.my.package',
'grails.app.services.my.package',
'grails.app.taglib.my.package',
'grails.app.conf.my.package',
'grails.app.filters.my.package',
'my.package'
info 'org.codehaus.groovy.grails.commons', 'com.orientechnologies'
root {
error 'logfile'
additivity = true
}
}
}
}
About digital things I find interesting. Like internet, software development, mobile phones, social media
About Me
- Olaf
- Freelancing software developer / architect, mobile geek. Topics: Software technologies (java, groovy, agile), politics, web, science, green, mobile, android.
Homepage: www.tutaona.com
"The absence of limitations is the enemy of art." - Orson Welles
Posts mit dem Label grails werden angezeigt. Alle Posts anzeigen
Posts mit dem Label grails werden angezeigt. Alle Posts anzeigen
Mittwoch, 27. Juni 2012
Logging Config for Grails 2.0
Finally I developed a good logging config. It debug logs your classes to development.log, grails stuff info logging to grails.log. Test logging goes into test.log and for production it logs nothing to the console (= catalina.out) and info logging to a daily rolling logfile.
Labels:
grails
Montag, 25. Juni 2012
Access Grails Config in Non-DI Classes
ApplicationContext context = servletContextHolder.servletContext.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT) as ApplicationContext ConfigObject grailsConfig = context.getBean(GrailsApplication).config
Labels:
grails
Freitag, 22. Juni 2012
Using SQL Views With Grails
The Problem
In a recent grails backend project for a conference management system I had a complex synchronization where the initial sync could be quite large. As the clients were iPads and could have connection and bandwidth constraints so the huge initial sync must happen in chunks. So if a client due to connectivity problems fails to receive a chunk it can request it again and not too much of data has to be transferred again. As the iPad knows about it's connection quality the app could even adjust the chunk size to its needs. Large chunks (causing less overhead) when the connection is good and smaller chunks (less data to resend) when the connection is poor. The idea was to use SQL views to create a snapshot of the database and use HQL queries against the views with all it's paging convenience for simple chunk handling code.
The problem was that GORM has no support for views. If you query a view GORM cannot map it to anything as there is no mapping defined. Moreover as the views must have different names for each sync and GORM's mapping of domain classes to tables is defined static but I needed the mapping to be dynamic. That was a hard one to solve.
Creating SQL Views in Grails
That was rather something simple as groovy has support for SQL. So I just take a client ID to make the view's name unique.
The business logic (controller/service)
In a recent grails backend project for a conference management system I had a complex synchronization where the initial sync could be quite large. As the clients were iPads and could have connection and bandwidth constraints so the huge initial sync must happen in chunks. So if a client due to connectivity problems fails to receive a chunk it can request it again and not too much of data has to be transferred again. As the iPad knows about it's connection quality the app could even adjust the chunk size to its needs. Large chunks (causing less overhead) when the connection is good and smaller chunks (less data to resend) when the connection is poor. The idea was to use SQL views to create a snapshot of the database and use HQL queries against the views with all it's paging convenience for simple chunk handling code.
The problem was that GORM has no support for views. If you query a view GORM cannot map it to anything as there is no mapping defined. Moreover as the views must have different names for each sync and GORM's mapping of domain classes to tables is defined static but I needed the mapping to be dynamic. That was a hard one to solve.
Creating SQL Views in Grails
That was rather something simple as groovy has support for SQL. So I just take a client ID to make the view's name unique.
The business logic (controller/service)
def dataSource // the Spring-Bean "dataSource" is auto-injected
@Secured([Role.ROLE_RECEPTIONIST])
def syncUsers = {
def clientId = params.client
long syncpoint = params.syncpoint ? params.syncpoint : 0) as Long)
def roleAttendee = Role.findByAuthority(Role.ROLE_ATTENDEE)
def db = new Sql(dataSource)
def query = "CREATE VIEW syncview_user_"+clientId+" \
AS SELECT u.* FROM user AS u, user_role AS ur \
WHERE ur.user_id = u.id AND ur.role_id = $roleAttendee.id"
db.execute(query)
}
The hibernate interceptor (src/java)
import org.hibernate.EmptyInterceptor
import org.springframework.web.context.request.RequestContextHolder
public class DynamicTableNameInterceptor extends EmptyInterceptor {
public String onPrepareStatement(String sql) {
if(RequestContextHolder.getRequestAttributes()) {
def params = RequestContextHolder.currentRequestAttributes().params
println ">>>> $sql"
println params
//params.each { println it}
sql = sql.replaceAll("from user ", "from syncview_user_"+params.client+" ")
println sql
println "------"
}
return sql //.replaceAll('_SCHEMA_', schema)
}
}
resources.groovy:
beans = {
entityInterceptor(DynamicTableNameInterceptor)
}
Labels:
grails
List Test-App Execution Order
Neat little bash magic
function grailsTestOrder() { grep testsuite target/test-reports/TESTS-TestSuites.xml | grep -v testsuites | cut -d\ -f8-9 | sed -E 's/name="(.*)" package="(.*)"/\2.\1/' | grep . } alias testorder=grailsTestOrder
Labels:
grails
Donnerstag, 10. Mai 2012
Simple Webservices Client
import grails.converters.*;
def url = new URL("http://www.your-needed-webservice-with-a-json-response.com")
def response = JSON.parse(url.newReader())
Labels:
grails
Executing a SQL Script on Startup
import groovy.sql.Sql
import org.codehaus.groovy.grails.commons.ConfigurationHolder as CH
String sqlFilePath = ApplicationHolder.application.parentContext.servletContext.getRealPath("/data/table_dump.sql")
String sqlString = new File(sqlFilePath).text
Sql sql = Sql.newInstance(CH.config.dataSource.url, CH.config.dataSource.username, CH.config.dataSource.password, CH.config.dataSource.driverClassName)
sql.execute(sqlString)
Labels:
grails
Montag, 14. November 2011
Grails logging how I like it
I was fiddling with grails logging for a long time and I was doing it again and again and again... not very DRY. So here is how it's good for me for most projects. To the console are errors only, in dev mode a simple log file at debug level and in production a daily rolling log file at info level.
// log4j configuration
log4j = {
appenders {
console name:'console', threshold:org.apache.log4j.Level.ERROR
file name:'file', file: './myapp.log', threshold: org.apache.log4j.Level.DEBUG, layout: pattern(conversionPattern: "%d{ISO8601}\t%p\t%c:%L\t%m%n")
}
environments {
production {
appenders {
console name:'console', threshold: org.apache.log4j.Level.ERROR
appender new org.apache.log4j.DailyRollingFileAppender(name:"file", fileName:"/var/log/myapp.log",
datePattern: '\'_\'yyyy-MM-dd', layout:pattern(conversionPattern: '%d{ISO8601}\t%p\t%c:%L\t%m%n'),
threshold:org.apache.log4j.Level.INFO)
}
}
}
// trace "org.hibernate.SQL",
// "org.hibernate.type"
debug 'grails.app.service',
'grails.app.controller'
'grails.app.tagLib'
error 'org.codehaus.groovy.grails.web.servlet', // controllers
'org.codehaus.groovy.grails.web.pages', // GSP
'org.codehaus.groovy.grails.web.sitemesh', // layouts
'org.codehaus.groovy.grails.web.mapping.filter', // URL mapping
'org.codehaus.groovy.grails.web.mapping', // URL mapping
'org.codehaus.groovy.grails.commons', // core / classloading
'org.codehaus.groovy.grails.plugins', // plugins
'org.codehaus.groovy.grails.orm.hibernate', // hibernate integration
'org.springframework',
'org.hibernate',
'org.apache.commons.digester',
'net.sf.ehcache.hibernate',
'org.apache.tomcat',
'org.apache.catalina',
'net.sf.ehcache',
'org.apache.coyote',
'org.apache.commons.beanutils'
warn 'org.mortbay.log'
root {
debug 'console','file'
additivity = false
}
}
// log4j configuration
log4j = {
appenders {
console name:'console', threshold:org.apache.log4j.Level.ERROR
file name:'file', file: './myapp.log', threshold: org.apache.log4j.Level.DEBUG, layout: pattern(conversionPattern: "%d{ISO8601}\t%p\t%c:%L\t%m%n")
}
environments {
production {
appenders {
console name:'console', threshold: org.apache.log4j.Level.ERROR
appender new org.apache.log4j.DailyRollingFileAppender(name:"file", fileName:"/var/log/myapp.log",
datePattern: '\'_\'yyyy-MM-dd', layout:pattern(conversionPattern: '%d{ISO8601}\t%p\t%c:%L\t%m%n'),
threshold:org.apache.log4j.Level.INFO)
}
}
}
// trace "org.hibernate.SQL",
// "org.hibernate.type"
debug 'grails.app.service',
'grails.app.controller'
'grails.app.tagLib'
error 'org.codehaus.groovy.grails.web.servlet', // controllers
'org.codehaus.groovy.grails.web.pages', // GSP
'org.codehaus.groovy.grails.web.sitemesh', // layouts
'org.codehaus.groovy.grails.web.mapping.filter', // URL mapping
'org.codehaus.groovy.grails.web.mapping', // URL mapping
'org.codehaus.groovy.grails.commons', // core / classloading
'org.codehaus.groovy.grails.plugins', // plugins
'org.codehaus.groovy.grails.orm.hibernate', // hibernate integration
'org.springframework',
'org.hibernate',
'org.apache.commons.digester',
'net.sf.ehcache.hibernate',
'org.apache.tomcat',
'org.apache.catalina',
'net.sf.ehcache',
'org.apache.coyote',
'org.apache.commons.beanutils'
warn 'org.mortbay.log'
root {
debug 'console','file'
additivity = false
}
}
Labels:
grails
Montag, 22. August 2011
Grails jquery autocompleter dialog
<script>
function show_scenarioOpenDialog() {
$('#openScenarioDialog').dialog('open');
return false;
}
$(function() {
$('#openScenarioDialog').dialog({
autoOpen: false,
modal: true,
width: 600,
buttons: {
<g:if test="${!isReadOnly}">
'${message(code:"button.form.submit")}': function() {
$('#openScenarioForm').submit();
},
</g:if>
'${message(code:"button.form.cancel")}': function() {
$( this ).dialog( "close" ); // der bleibt tatsaechlich erhalten
}
}
});
$("#scenarioName").autocomplete({
source: function(request, response) {
$.ajax({
url: "${createLink(base: '.', action: 'findByNameLike', controller: 'scenario')}",
dataType: "json",
data: {
name: request.term
},
success: function(data) {
response($.map(data, function(item) {
return {
label: item.name,
value: item.name,
title: item.uid
}
}))
}
})
},
minLength: 1,
select: function(event, ui) {
$("#scenarioUid").val(ui.item.title);
}
});
});
</script>
<div id="openScenarioDialog" class="popupForm" title="Open Scenario">
<g:form target="_top" base="." action="openScenarioSubmit" controller="workspace" name="openScenarioForm">
<div class="search form-row ui-widget">
<label for="scenarioName">Scenario Name: </label>
<input type="text" id="scenarioName" name="scenarioName" />
<input type="hidden" id="scenarioUid" name="scenarioUid"/>
</div>
</g:form>
</div>
function show_scenarioOpenDialog() {
$('#openScenarioDialog').dialog('open');
return false;
}
$(function() {
$('#openScenarioDialog').dialog({
autoOpen: false,
modal: true,
width: 600,
buttons: {
<g:if test="${!isReadOnly}">
'${message(code:"button.form.submit")}': function() {
$('#openScenarioForm').submit();
},
</g:if>
'${message(code:"button.form.cancel")}': function() {
$( this ).dialog( "close" ); // der bleibt tatsaechlich erhalten
}
}
});
$("#scenarioName").autocomplete({
source: function(request, response) {
$.ajax({
url: "${createLink(base: '.', action: 'findByNameLike', controller: 'scenario')}",
dataType: "json",
data: {
name: request.term
},
success: function(data) {
response($.map(data, function(item) {
return {
label: item.name,
value: item.name,
title: item.uid
}
}))
}
})
},
minLength: 1,
select: function(event, ui) {
$("#scenarioUid").val(ui.item.title);
}
});
});
</script>
<div id="openScenarioDialog" class="popupForm" title="Open Scenario">
<g:form target="_top" base="." action="openScenarioSubmit" controller="workspace" name="openScenarioForm">
<div class="search form-row ui-widget">
<label for="scenarioName">Scenario Name: </label>
<input type="text" id="scenarioName" name="scenarioName" />
<input type="hidden" id="scenarioUid" name="scenarioUid"/>
</div>
</g:form>
</div>
Labels:
grails
Abonnieren
Posts (Atom)