Top Banner
Little Did He Know ... Burt Beckwith
49
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 2: Little Did He Know ...
Page 3: Little Did He Know ...

"Little did he know that this simple, seemingly innocuous act would result in his imminent death."

Page 4: Little Did He Know ...

Did you say 'Little did he know ...'?

Dear god, I've written papers on 'Little did he know …'

I used to teach a class based on 'Little did he know ...'

I mean, I once gave an entire seminar on 'Little did he know ...'

Page 5: Little Did He Know ...

The Groovy “Map” Constructor

Page 6: Little Did He Know ...

The Groovy “Map” Constructor

● Not a real constructor (i.e. it's not in the bytecode)

Page 7: Little Did He Know ...

The Groovy “Map” Constructor

● Not a real constructor (i.e. it's not in the bytecode)

● Implemented in

MetaClassImpl.invokeConstructor() or

o.c.g.runtime.callsite.ConstructorSite.

callConstructor()

Page 8: Little Did He Know ...

The Groovy “Map” Constructor

● Not a real constructor (i.e. it's not in the bytecode)

● Implemented in

MetaClassImpl.invokeConstructor() or

o.c.g.runtime.callsite.ConstructorSite.

callConstructor()

● Calls the default constructor, then calls

MetaClassImpl.setProperty() for each map

key/value pair to invoke setters

Page 9: Little Did He Know ...

The Groovy “Map” Constructor

● Depends on an existing default (no-arg) constructor,

which is always created by javac/groovyc if

there are no constructors in the code

Page 10: Little Did He Know ...

The Groovy “Map” Constructor

● Depends on an existing default (no-arg) constructor,

which is always created by javac/groovyc if

there are no constructors in the code

● It's a bit more complicated in Grails domain classes

Page 11: Little Did He Know ...

The Groovy “Map” Constructor

● Depends on an existing default (no-arg) constructor,

which is always created by javac/groovyc if

there are no constructors in the code

● It's a bit more complicated in Grails domain classes

● Grails uses an AST transformation to add a default

constructor to support dependency injection

Page 12: Little Did He Know ...

The Groovy “Map” Constructor

● So what if I want to add a parameterized

constructor in a domain class?

package little

class ConstructedDomain { String name}

Page 13: Little Did He Know ...

The Groovy “Map” Constructor

● So what if I want to add a parameterized

constructor in a domain class?

package little

class ConstructedDomain { String name

ConstructedDomain(String name) { this.name = name }}

Page 14: Little Did He Know ...

The Groovy “Map” Constructor

● So what if I want to add a parameterized

constructor in a domain class?

package little

class ConstructedDomain { String name

ConstructedDomain(String name) { this.name = name }

ConstructedDomain() { // default }}

Need to explicitlyadd a no-arg

constructor forthe Map

constructor, right?

Page 15: Little Did He Know ...

The Groovy “Map” Constructor

● So what if I want to add a parameterized

constructor in a domain class?

package little

class ConstructedDomain { String name

ConstructedDomain(String name) { this() this.name = name }}

Not in domainclasses - the AST

adds a no-argconstructor!

So call this() toretain DI

Page 16: Little Did He Know ...

new Date().getTime()

● Please stop using new Date().getTime() to get the

current time in milliseconds, e.g.

long start = new Date().getTime()// do some timed stufflong end = new Date().getTime()long timeDelta = end - start

Page 17: Little Did He Know ...

new Date().getTime()

● This isn't particularly expensive - creating and discarding

a Date instance is lightweight, but it's best to use

System.currentTimeMillis() (which is where Date

gets its value btw):

long start = System.currentTimeMillis()// do some timed stufflong end = System.currentTimeMillis()long timeDelta = end - start

Page 18: Little Did He Know ...

fooId

● If you have a many-to-one in a domain class, e.g.

there is an authorId dynamic property that can be

used to access the id of the Author instance without

retrieving the instance

class Book { String title Date published Author author}

Page 19: Little Did He Know ...

fooId● Run this with SQL logging enabled:

new Author(name: 'a1').save()new Author(name: 'a2').save()new Book(author: Author.load(2), published: new Date(), title: 'b1').save()

Book.withSession {it.flush()it.clear()

}

def b = Book.get(1)println b.titleprintln b.publishedprintln b.authorIdprintln "calling b.author.id"println b.author.id

Page 20: Little Did He Know ...

Don't do this:

if (foo.validate() && foo.save()) { ...}

Page 21: Little Did He Know ...

Primitives in domain classes

● Think before using primitive types (boolean, int,

long, etc.)

● They are initialized to 0 for numbers and false for

boolean

● This means that it can be difficult to know if a

user chose that value, or if they didn't make a

choice at all

Page 22: Little Did He Know ...

Primitives in domain classes

● If the database column is nullable, expect

NullPointerExceptions

● Your business rules may consider null and 0 (or

false) to be equivalent, this is not a general

default – null means no value and 0 and false

are values

Page 23: Little Did He Know ...

Primitives in domain classes

● Hibernate proactively tries to keep you from

shooting yourself in the foot and always forces

database columns for primitives to be not-null

● This is great, but you end up with an inconsistency

between your constraints and the database

● So use the wrapper classes (Integer, Long,

Boolean, etc.) to detect that no value was chosen

or to properly support nullable columns

Page 24: Little Did He Know ...

findById vs get

● Both seem equivalent, but they are not

● get is a special query method to retrieve a single

instance by id, but findById is a dynamic finder

● They are cached very differently, and query caching

can be quite brittle – see

Hibernate query cache considered harmful?

● There doesn't appear to be any reason to ever use

findById over get

● But findByIdAndSomeOtherProperty can be

useful

Page 25: Little Did He Know ...

License weirdness

● Very interesting Twitter thread about iText and Craig

Burke's Groovy Document Builder here

● It turns out that you cannot use MPL/LGPL libraries

in an ASL2-licenced project, which sounds crazy, but

it's very true:

● https://www.apache.org/legal/resolved.html#cate

gory-x

Page 26: Little Did He Know ...

License weirdness

● “We avoid GPLv3 software because merely linking to

it is considered by the GPLv3 authors to create a

derivative work” -

https://www.apache.org/licenses/GPL-compatibility

.html● Bruno Lowagie (iText author): It's not even ok to use

pre-AGPL versions:

https://stackoverflow.com/questions/25696851/can-

itext-2-1-7-or-earlier-can-be-used-commercially

Page 27: Little Did He Know ...

Log4j vs Slf4j

● Slf4j is an excellent logger API wrapper library

● It's a good idea especially in plugins to use Slf4j in

case the containing app switched to Logback/Log4j

2/etc.

● Unfortunately the type of the message in Slf4j

methods is String, whereas it's Object in Log4j

Page 29: Little Did He Know ...

Log4j vs Slf4j

● Why is that a problem? GStrings:

● log.debug(“The huge collection

contains ${things}”)

● In Log4j, if the logger level is higher than DEBUG,

the message isn't logged, and there's ~0 cost

● But in Slf4j, the GString is coerced to a String

(expensive for large embedded expressions) and

then discarded

Page 30: Little Did He Know ...

Log4j vs Slf4j

● The solution is to use the Slf4j placeholder support:

● log.debug(“The huge collection

contains {}”, things)

Page 31: Little Did He Know ...

Use “public” for constants

● The public keyword is usually unnecessary noise in

Groovy classes – it's the default scope

● But weirdness happens when you omit it for static

properties, e.g. constants

● static final String FOO = 'FOO'

● public static final String BAR = 'BAR'

Page 32: Little Did He Know ...

Use “public” for constants

● Groovy auto-creates getters and setters for

properties, both instance and static (although no

setters for final properties)

● This means that you'll have a getFOO method:

private static final String FOO = "FOO";public static final String BAR = "BAR";

public static final String getFOO() {return FOO;

}

Hat tip to Ken Kousenfor originally pointing

this out

Page 33: Little Did He Know ...

Domain class transient methods

● Is the transients property needed in this domain?

class Thing { String name

int getClickCount() { … }

static transients = ['clickCount']}

Page 34: Little Did He Know ...

Domain class transient methods

● How about now?

class Thing { String name

void setClickCount(int count) { … }

static transients = ['clickCount']}

Page 35: Little Did He Know ...

Domain class transient methods

● And now?

class Thing { String name

int getClickCount() { … } void setClickCount(int count) { … }

static transients = ['clickCount']}

Page 36: Little Did He Know ...

Domain class transient methods

● It's only needed when there is a matched

getter/setter pair

● This is because the pair creates a property

● Recall that Groovy generates a getter and setter for

unscoped properties and converts the declared

property to a private field

Page 37: Little Did He Know ...

Domain class transient methods

● So for this domain class (or any POGO)

If you decompile the .class file you'll see code like

class Thing { String name}

class Thing { private String name public String() getName() { this.name } public void setName(String s) { name = s}}

Page 38: Little Did He Know ...

Domain class transient methods

● Hibernate knows nothing about Groovy, and Grails

doesn't auto-register declared properties as persistent –

Hibernate sees the getter and setter and considers it a

property

● So whether groovyc creates the getter/setter pair or you

do, either way it's a property

→ only add the property name to transients for the

third example

Page 39: Little Did He Know ...

Custom application templates

● We all know that it's easy to customize artifact

templates by running grails install-

templates and editing the files under

src/templates

● But what about application templates

(BuildConfig.groovy, DataSource.groovy,

etc.)?

Page 40: Little Did He Know ...

Custom application templates

● It's possible for Grails 2.3/2.4/2.5 – see

this StackOverflow answer for details

● Basically it involves extracting the jars from

dist/grails-resources-x.x.x.jar and

extracting the templates from those jars, editing,

then repackaging and storing in the correct

$HOME/.grails subfolder

Page 41: Little Did He Know ...

Exceptions are very expensive

● Don't be careless about exceptions

● We've all seen Groovy stacktraces as tall as Mount

Everest – there's a nontrivial cost to fill in the stack

● Use them for exceptional cases

● When a user makes a mistake and there's a

validation problem, that's not exceptional – it's

entirely expected

Page 42: Little Did He Know ...

Exceptions are very expensive

● This implies that save(failOnError:true) is a

really really bad idea except for rare cases

● This includes test data and creating instances in

BootStrap.groovy; in both cases you're hard-

coding values that you expect to be correct – use

failOnError only here, as a fail-safe

● In general do not use failOnError in app code

Page 43: Little Did He Know ...

Exceptions are very expensive

● Consider failOnError vs. checking hasErrors():

def thing = new Thing(foo: 'bar')thing.save()if (thing.hasErrors()) { // handle errors}else { // handle success}

def thing = new Thing(foo: 'bar')try { thing.save(failOnError: true) // handle success}catch (ValidationException e) { // handle errors}

Pretty similar –can someone tellme the value of

failOnError?Munchausen by

Proxy Syndrome?

Page 44: Little Did He Know ...

Exceptions are very expensive

● Let's stop sabotaging the performance of our

Groovy-based applications by throwing or triggering

dumb exceptions, ok?

● The Spring Security plugin has a no-stack Exception:

package grails.plugin.springsecurity.userdetails;

public class NoStackUsernameNotFoundException ... { ... @Override public synchronized Throwable fillInStackTrace() { // do nothing return this; }}

Page 45: Little Did He Know ...

Exceptions are very expensive

● As of Java 7 you can also call this subclass

constructor from yours:

protected Exception(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace);}

Page 46: Little Did He Know ...

Exceptions are very expensive

● This trick was in the comments of this very informative

blog post:

The hidden performance costs of instantiating Throwables

● Also see “The Exceptional Performance of Lil' Exception”

Page 48: Little Did He Know ...

¡Gracias!

Page 49: Little Did He Know ...

http://cuteoverload.files.wordpress.com/2014/03/cute-smiling-animals-251.jpg