SlideShare une entreprise Scribd logo
1  sur  80
Télécharger pour lire hors ligne
@oldJavaGuy#VoxxedBerlin
Enhancing Code at build or runtime
Sean Patrick Floyd
Hacking Java
@oldJavaGuy#VoxxedBerlin
FOUNDED IN 2008
+10.000 EMPLOYEES
7 TECH HUBS
+1000 TECHNOLOGISTS
15 COUNTRIES
14 MILLION APP DOWNLOADS
+17 MILLION CUSTOMERS
@oldJavaGuy#VoxxedBerlin
Sean Patrick Floyd
• Search Engineer @ Zalando
• ~20 years experience
• Java, Scala, Groovy
• Twitter: @oldJavaGuy
@oldJavaGuy#VoxxedBerlin
Scope of this talk
• Overview of non-standard techniques
• Grouped by use case
• Some techniques are more mature than others
• Code samples and unit tests
@oldJavaGuy#VoxxedBerlin
Use Cases
• Value Objects
• Third party library patching
• Code defect analysis
• More ideas?
@oldJavaGuy#VoxxedBerlin
Github Project
• https://github.com/mostlymagic/hacking-java 

http://bit.ly/hackingJava
• Organized as Multi-Module Maven project
• Grouped by use case
• Sample code and unit tests for every technique
@oldJavaGuy#VoxxedBerlin
Use Case: Value Objects
• Standard, well defined behavior
(equals(), hashCode(),
toString()) according to
Effective Java
• Mutable or immutable, with
constructors, factory methods
or builders
• Java Boilerplate very verbose
and error-prone
@oldJavaGuy#VoxxedBerlin
The Pain
public class MyValueObject {
private final String property1; private final boolean property2;
public MyValueObject(String property1, boolean property2) {
this.property1 = property1; this.property2 = property2;
}
public String getProperty1() { return property1; }
public boolean isProperty2() { return property2; }
@Override public int hashCode() { return Objects.hash(property1, property2); }
@Override public boolean equals(Object obj) {
if (this == obj) { return true; }
else if (obj instanceof MyValueObject) {
MyValueObject other = (MyValueObject) obj;
return Objects.equals(this.property1, other.property1)
&& Objects.equals(this.property2, other.property2);
} else { return false; } }
@Override public String toString() {
return MoreObjects.toStringHelper(this).add("property1", property1).add("property2", property2).toString();
}
}
@oldJavaGuy#VoxxedBerlin
For comparison: Scala
case class MyValueObject(property1: String, property2: Boolean) {}
@oldJavaGuy#VoxxedBerlin
For comparison: Scala
case class MyValueObject(property1: String, property2: Boolean) {}
Source: http://onlyinamericablogging.blogspot.com/2014/11/move-along-now-nothing-to-see-here.html
@oldJavaGuy#VoxxedBerlin
Boilerplate
• equals() / hashCode() / toString() /
getters / setters / constructors /
compareTo()
• IDEs offer generation, but in a static way
• potential bugs: adding or removing fields
• Boilerplate (n.): newspaper [and IT]
slang for "unit of writing that can be
used over and over without change,"
1893, from a literal meaning (1840)
"metal rolled in large, flat plates for use
in making steam boilers."

Source: http://www.etymonline.com/ https://en.wikipedia.org/wiki/Boilerplate_(robot)
@oldJavaGuy#VoxxedBerlin
Plan
• Let’s look at technologies that let us define value objects in a
less-verbose way
• But with the full functionality (a test suite will monitor
correct working of getters, equals, hashCode and toString)
• Different approaches: build-time, compile-time, run-time
@oldJavaGuy#VoxxedBerlin
Testing
• Expected Data structure (mutable or immutable):

User
- firstName (String)
- lastName (String)
- birthDate (LocalDate)
- addresses (List[Address])
Address
- street (String)
- zipCode (int)
- city (String)
@oldJavaGuy#VoxxedBerlin
Test suite for value objects
public abstract class BaseUserTest {
protected static final String FIRST_NAME = "Fred"; // etc.
@Test public void equalsAndHashCodeAreSymmetrical() {
Object user1 = createUser(); Object user2 = createUser();
assertThat(user1, is(equalTo(user2))); assertThat(user2, is(equalTo(user1)));
assertThat(user1.hashCode(), is(equalTo(user2.hashCode()))); }
@Test public void toStringIsConsistent() {
assertThat(createUser().toString(), is(equalTo(createUser().toString())));
String s = createUser().toString();
assertThat(s, containsString(FIRST_NAME)); /* etc. */ }
@SuppressWarnings({"unchecked", "rawtypes"}) @Test public void compareToIsSymmetrical() {
Object l = createUser(), r = createUser();
assertThat(l, instanceOf(Comparable.class));
assertThat(r, instanceOf(Comparable.class));
assertThat(((Comparable) l).compareTo(r), equalTo(((Comparable) r).compareTo(l))); }
}
@oldJavaGuy#VoxxedBerlin
Test suite (continued)
@Test public void propertyMapHasCorrectValues() {
Object instance = createUser();
Map<String, Object> map = getPropertyMap(instance);
assertThat(map, hasEntry("firstName", FIRST_NAME)); // etc.
}
private static Map<String, Object> getPropertyMap(Object instance) {
final Map<String, Object> map = new TreeMap<>();
try { Arrays.stream(Introspector.getBeanInfo(instance.getClass(), Object.class)
.getPropertyDescriptors()).filter((it) -> it.getReadMethod() != null)
.forEach((pD) -> { Method m = propertyDescriptor.getReadMethod();
try { Object o = m.invoke(instance); map.put(pD.getName(), o);
} catch (IllegalAccessException | ... e) { throw new ISE(e); }});
} catch (IntrospectionException e) { throw new IllegalStateException(e); }
return propertyMap;
}
protected abstract Object createUser();
@oldJavaGuy#VoxxedBerlin
Alternative Test Suite
• Guava’s AbstractPackageSanityTests

( http://bit.ly/AbstractPackageSanityTests )
• Automatically runs sanity checks against top level classes in
the same package of the test that extends
AbstractPackageSanityTests. Currently sanity checks include
NullPointerTester, EqualsTester and SerializableTester.
• Nice, but not a perfect match for this use case
@oldJavaGuy#VoxxedBerlin
Annotation Processing
• JSR 269, pluggable annotation processing:

Separate compiler lifecycle, well suited for code generation
• Service auto-discovery through ServiceLoader:

/META-INF/services/javax.annotation.processing.Processor

contains qualified processor names ( http://bit.ly/srvcLdr )
• Docs: http://bit.ly/annotationProcessing (Oracle JavaDocs)
@oldJavaGuy#VoxxedBerlin
Project Lombok
• Name: Lombok is an Indonesian Island
neighboring Java (“it’s not quite Java, but
almost”)
• Project Lombok uses Annotation Processing
to extend the AST. It uses internal compiler
APIs (Javac and Eclipse)
• Advantages: Little code, lots of power, no
runtime dependencies
• Disadvantages: Relying on undocumented
internal APIs, adds code that is not reflected
in sources (inconsistent)
Source: http://bit.ly/1lOfPbC
@oldJavaGuy#VoxxedBerlin
Lombok: mutable example
@Data
public class MyValueObject {
private String property1;
private boolean property2;
}
• Generates getters, setters, equals, hashCode, toString
• Additional fine-tuning annotations are available
@oldJavaGuy#VoxxedBerlin
Lombok: immutable example
@Data
public class MyValueObject {
private final String property1;
private final boolean property2;
}
• Generates constructor, getters, equals, hashCode, toString
• Builder version also available
@oldJavaGuy#VoxxedBerlin
Google AutoValue
• https://github.com/google/auto/tree/master/value
• “AutoValue […] is actually a great tool for eliminating the
drudgery of writing mundane value classes in Java. It
encapsulates much of the advice in Effective Java […].The
resulting program is likely to be shorter, clearer, and freer of
bugs.” -- Joshua Bloch, author, Effective Java
• Advantages: Only public APIs used, no runtime dependencies
• Disadvantages: Less power and flexibility, only immutable
types supported (or is that an advantage?)
@oldJavaGuy#VoxxedBerlin
AutoValue Sample code
@AutoValue // class needs to be abstract
public abstract class MyValueObject {
// use JavaBeans property names or simple field names
public abstract String getProperty1();
public abstract boolean isProperty2();
// factory method for instantiation
static MyValueObject create(String p1, boolean p2){
return new AutoValue_MyValueObject(p1, p2);
//"AutoValue_" + abstract class name
}
}
@oldJavaGuy#VoxxedBerlin
CGLib BeanGenerator
• https://github.com/cglib/cglib
• CGLib is a “high level” byte code manipulation framework
• Widely used in production code, mostly by IOC and ORM
frameworks (Spring, Guice etc)
• BeanGenerator is a playground feature that can create value
types on the fly
• Works on Byte Code:

https://en.wikipedia.org/wiki/Java_bytecode
@oldJavaGuy#VoxxedBerlin
Caveat
• BeanGenerator generates field + getter + setter (no immutable types,
no equals(), hashCode(), toString())
• To solve these issues, I am creating a dynamic proxy around the
generated class, which in turn uses a full JavaBeans property map for all
operations.
• /* This is O(scary), but seems quick enough in practice. */
( Source: http://bit.ly/bigOScary )
• There is probably no sane use case for this
@oldJavaGuy#VoxxedBerlin
Generating types on the fly
public static Class<?> createBeanClass(
final String className, // qualified type
Map<String, Class<?>> props) {
BeanGenerator beanGenerator = new BeanGenerator();
/* use our own hard coded class name */
beanGenerator.setNamingPolicy(new NamingPolicy() {
@Override public String getClassName(...) {
return className; }});
BeanGenerator.addProperties(beanGenerator, props);
return (Class<?>) beanGenerator.createClass();
}
@oldJavaGuy#VoxxedBerlin
Instantiating dynamic types
public static Object instantiateDto(Class<?> type,
Map<String, Object> data) {
try { Object dto = type.newInstance();
assignProperties(map, dto); // JavaBeans magic
return createProxy(dto);
} catch (InstantiationException | ... e) {
throw new IllegalStateException(e); } }
private static Object createProxy(Object dto) {
final Enhancer e = new Enhancer();
e.setSuperclass(dto.getClass());
e.setCallback(new HandlerBasedMethodInterceptor(dto));
return e.create(); }
@oldJavaGuy#VoxxedBerlin
MethodInterceptor
class HandlerBasedInterceptor implements MethodInterceptor,
Supplier<Object> {
private final Object dto;
public HandlerBasedInterceptor(Object dto) { this.dto = dto; }
public Object intercept(... , Object[] args, MethodProxy p) {
for (MethodHandler h : MethodHandler.values()) {
// equals / hashCode / toString handled by proxy
if (h.matches(method)) return h.invoke(dto, args);
}
return method.invoke(dto, args);
// getters / setters handled by original
}
public Object get() { return dto; } }
@oldJavaGuy#VoxxedBerlin
MethodHandler
enum MethodHandler {
EQUALS {
boolean matches(final Method candidate) {
return candidate.getName().equals("equals")
&& Arrays.equals(candidate.getParameterTypes(),
new Class[]{ Object.class} );
}
Object invoke(final Object o, final Object[] args) {
return o.getClass().isInstance(args[0])
&& DtoFactory.readProperties(o).equals(
DtoFactory.readProperties(args[0]));
}
} }; // + hashCode() + toString()
@oldJavaGuy#VoxxedBerlin
Don’t try this at home!
• There is (almost) no sane
reason for generating value
classes at runtime
• One possible scenario would
be a CMS that lets you add new
content types at runtime and is
backed by a class-based ORM
Source: http://www.richardbealblog.com/dont-try-this-at-home/
@oldJavaGuy#VoxxedBerlin
Code Generation
• Techniques for creating source code dynamically at build time
before compiling
• For those of us who like to “write code that writes code”
@oldJavaGuy#VoxxedBerlin
Code Generation
• Techniques for creating source code dynamically at build time
before compiling
• For those of us who like to “write code that writes code”
Source: http://xkcd.com/1629/
@oldJavaGuy#VoxxedBerlin
Code Generation
• Techniques for creating source code dynamically at build time
before compiling
• For those of us who like to “write code that writes code”
Source: http://xkcd.com/1629/ Source: http://www.memes.com/meme/487047
@oldJavaGuy#VoxxedBerlin
Why code generation?
• Everybody else is “doing it wrong”.

Style wars: e.g. instanceof vs getClass()
• Custom requirements: Serializable, Jackson or JPA
annotations
• Good news: you can do almost anything
• Bad news: you have to do almost everything yourself
• Most users will prefer “standard” solutions like Lombok or
AutoValue
@oldJavaGuy#VoxxedBerlin
Adding code generation to the Maven Build
• Maven has two dedicated lifecycle phases for code
generation,“generate-sources” and “generate-test-sources”
• The easiest way to achieve code generation is to execute a
Groovy script in one of these phases, using the Groovy
Maven Plugin (http://bit.ly/groovyMavenPlugin )
• By convention, Code generation uses the output folder
target/generated-sources/{generator-name}
• Never generate code to src folder!
@oldJavaGuy#VoxxedBerlin
JCodeModel
• https://github.com/phax/jcodemodel
• Fork of Sun’s abandoned JCodeModel project, which did
code generation for JAXB
• Programmatic Java AST generation and source code
generation
• Friendly, understandable API
@oldJavaGuy#VoxxedBerlin
Maven Setup
<plugin><groupId>org.codehaus.gmaven</groupId>
<artifactId>groovy-maven-plugin</artifactId>
<executions><execution>
<id>generate-dtos</id>
<goals><goal>execute</goal></goals>
<phase>generate-sources</phase>
<configuration> <classpathScope>compile</classpathScope>
<scriptpath><path>${project.basedir}/src/…
</path></scriptpath>
<source>import Wrapper
new Wrapper().exec(project)</source>
</configuration>
</execution></executions></plugin>
@oldJavaGuy#VoxxedBerlin
Groovy helper types
class Dto { def packageName; def className; def properties = [:]; }
class GenericType { def baseType; def params = [] }
def userDto = new Dto( // example usage
packageName: "com.myapp", className: "User",
properties: [
"firstName": "String", "lastName" : "String",
"birthDate": "java.time.LocalDate",
"addresses": new GenericType(
baseType: "java.util.List",
params: ["com.myapp.Address"])
])
@oldJavaGuy#VoxxedBerlin
CODE GENERATION WITH JCODEMODEL
class DtoGenerator {
def codeModel = new JCodeModel()
public generate(List<Dto> dtos, File targetDir) {
dtos.each {
def clazz = codeModel._package(it.packageName)
._class(JMod.PUBLIC, it.className)
defineConstructor(clazz, it.properties)
defineGetters(clazz, it.properties)
defineEqualsMethod(clazz, it.properties)
defineHashCodeMethod(clazz, it.properties)
defineToStringMethod(clazz, it.properties)
defineCompareToMethod(clazz, it.comparableProperties)
}
targetDir.mkdirs(); codeModel.build(targetDir) }}
@oldJavaGuy#VoxxedBerlin
Resolving types
class DtoGenerator { // continued
private AbstractJClass genType(GenericType type) {
def baseType = codeModel.ref(type.baseType)
def params = type.params.collect { resolveType(it) }
return baseType.narrow( params as AbstractJClass[] )
}
private AbstractJType resolveType(type) {
if (GenericType.class.isInstance(type)) return genType(type)
else return codeModel.ref(type)
}
} // continued
@oldJavaGuy#VoxxedBerlin
GETTERS
class DtoGenerator { // continued
private defineGetters(JDefinedClass c, Map fields) {
fields.each { e ->
def m = c.method(PUBLIC, resolveType(e.value),
e.value == 'boolean' ? 'is' : 'get'
+ LOWER_CAMEL.to(UPPER_CAMEL, e.key))
// return [property]
m.body()._return(THIS.ref(e.key))
}
}
} // continued
@oldJavaGuy#VoxxedBerlin
Constructor and Fields
class DtoGenerator { // continued
private defineConstructor(JDefinedClass c,
Map fields) {
def con = c.constructor(JMod.PUBLIC)
def body = con.body()
fields.each { e ->
def type = resolveType(e.value)
def f = c.field(PRIVATE | FINAL, type, e.key)
def param = con.param(type, e.key)
body.assign(THIS.ref(f), param)
} } } // continued
@oldJavaGuy#VoxxedBerlin
toString()
class DtoGenerator { // continued
private defineToStringMethod(JDefinedClass clazz, Map fields) {
def method = clazz.method(JMod.PUBLIC, String.class, "toString")
method.annotate(Override.class)
def body = method.body()
def invocation = codeModel.ref(MoreObjects.class)
.staticInvoke("toStringHelper").arg(THIS)
def current = invocation
fields.keySet().each { f ->
current = current.invoke(“add").arg(JExpr.lit(f))
.arg(THIS.ref(f)) }
body._return(current.invoke("toString"))
} } // continued
@oldJavaGuy#VoxxedBerlin
Value Objects (what else)
• Techniques I haven’t explored yet, but might in future:
• More efficient byte code generation (ByteBuddy)
• Interface-based proxies
• JEP 169 (http://openjdk.java.net/jeps/169)
@oldJavaGuy#VoxxedBerlin
Use Case: Library Patching
• Scenario:You have to use a third party library, which has
known defects
• Best choice: file an issue and / or submit a Pull Request to
the library owner
• I’ll assume this didn’t work, you are dependent on the library,
but you don’t want to fork it, since you want to stay current
• Techniques that will let you patch lib in an automated way
(ideally in a CI / CD pipeline)
@oldJavaGuy#VoxxedBerlin
Options
• If you have access to the source code, use a source code
parser and dynamically patch the sources
• Otherwise, manipulate byte code (change or intercept
existing code). Can be done statically at build time or
dynamically at class load time
• Create a static patch and write tests which make sure the
patch worked (I won’t explore this option)
@oldJavaGuy#VoxxedBerlin
Baseline
• As example I have written a trivial example class, with some
pretty stupid, but harmless bugs:
public class FicticiousExample {
public Integer yUNoReuseInteger(final int value) {
System.setProperty(RAN_BUGGY_CODE, TRUE);
return new Integer(value); }
public String yUStringConcatInLoop(Iterable<String> data, String delim) {
System.setProperty(RAN_BUGGY_CODE, TRUE);
String value = "";
final Iterator<String> iterator = data.iterator();
if (iterator.hasNext()) { value += iterator.next(); }
while (iterator.hasNext()) { value += delim + iterator.next(); }
return value;
}
}
@oldJavaGuy#VoxxedBerlin
Test suite
• In the test, I call the methods and assert that they work
correctly and that the system properties weren’t set
public abstract class FicticiousExamplePatchTest {
private FicticiousExample fe;
@After public void checkForEvilSystemProperty() {
assertThat(System.getProperty(RAN_BUGGY_CODE), nullValue()); }
@Before public void initObject() { fe = new FicticiousExample(); }
@Test public void assertCorrectIntegerBehavior() {
assertThat(fe.yUNoReuseInteger(123), is(sameInstance(123)));
assertThat(fe.yUNoReuseInteger(1234), not(sameInstance(1234)));
}
@Test public void assertCorrectStringBehavior() { // etc.
@oldJavaGuy#VoxxedBerlin
AspectJ
• https://eclipse.org/aspectj/
• Aspect-oriented language

(.aj format or Java with @AspectJ

annotations)
• ajc = AspectJ Compiler (after javac,
instead of javac)
• static compilation or load time
weaving through agent
• Docs are ancient / non-existent, use
mailing list or read the book:

http://bit.ly/ajInAction
@oldJavaGuy#VoxxedBerlin
Patching Aspect
public aspect FicticiousExamplePatch{
public pointcut integerMethodCalled(int value) :
execution(* FicticiousExample.yUNoReuseInteger(..)) && args(value);
public pointcut stringMethodCalled(Iterable<String> it, String s):
execution(* FicticiousExample.yUStringConcatInLoop(..)) && args(it, s);
Integer around(int i) : integerMethodCalled(i){ return Integer.valueOf(i); }
String around(Iterable<String> it, String s) : stringMethodCalled(it, s){
java.util.Iterator<String> iterator = it.iterator();
StringBuilder sb = new StringBuilder();
if(iterator.hasNext()){
sb.append(iterator.next());
while(iterator.hasNext()){ sb.append(s).append(iterator.next()); }
}
return sb.toString(); }
}
@oldJavaGuy#VoxxedBerlin
AspectJ capabilities
• Extending / replacing / enriching code
• Inter-type declarations (~= Traits)
• Policy Enforcement (define custom compiler errors)
• Very flexible, can work on source or byte code
• Standard usage: Cross-cutting concerns (security, logging etc.)
@oldJavaGuy#VoxxedBerlin
Source Code Manipulation in the Build
Process
• Extract sources to generated-sources directory
• Parse and manipulate the sources (non-idempotent!?)
• Compile sources and distribute your library
@oldJavaGuy#VoxxedBerlin
JavaParser
• http://javaparser.github.io/javaparser/
• Parsers generated from JavaCC (Java Compiler Compiler) ->
complete Java 1.8 grammar support
• API not as friendly as JCodeModel (no management of types
and references etc.), but the only available fully functional Java
source code parser
• Again, I’ll be using Groovy to do the actual work. It could be
done in Java too, but the build setup would be more complex
@oldJavaGuy#VoxxedBerlin
Groovy Wrapper
public class Wrapper {
public exec(project, dir) {
def source = new File(dir, "...Example.java")
def compilationUnit = JavaParser.parse(source)
applyPatch(compilationUnit)
sourceFile.text = compilationUnit.toString()
project.compileSourceRoots.add(baseDir); }
private applyPatch(CompilationUnit unit) {
unit.accept(new PatchVisitor(), null) }
}
@oldJavaGuy#VoxxedBerlin
PatchVisitor
class PatchVisitor extends VoidVisitorAdapter<Void> {
public void visit(MethodDeclaration n, Object arg) {
if (n.name == "yUStringConcatInLoop") {
n.body = new BlockStmt() // delete existing body
patchStringMethod(n.body, n.parameters[0],
n.parameters[1])
} else if (n.name == "yUNoReuseInteger") {
n.body = new BlockStmt() // delete existing body
patchIntegerMethod(n.body, n.parameters[0])
} else super.visit(n, arg) }
@oldJavaGuy#VoxxedBerlin
PatchVisitor (Integer method)
private patchIntegerMethod(
BlockStmt b, Parameter p) {
def t = new ClassOrInterfaceType("Integer");
def tExpr = new TypeExpr(); tExpr.type = t
def mc = new MethodCallExpr(tExpr, "valueOf")
mc.args.add(new NameExpr(p.id.name))
b.getStmts().add(new ReturnStmt(mc))
}
// all this for
// return Integer.valueOf(i);
@oldJavaGuy#VoxxedBerlin
PatchVisitor (String method)
class PatchVisitor extends VoidVisitorAdapter<Void> { // continued
private patchStringMethod(BlockStmt blockStatement, Parameter iterable, Parameter delim) {
def sbType = new ClassOrInterfaceType("StringBuilder")
def sbId = new VariableDeclaratorId("sb")
def sbDecl = new VariableDeclarationExpr(sbType, [
new VariableDeclarator(sbId, new ObjectCreationExpr(null, sbType, []))
])
def itType = new ClassOrInterfaceType("Iterator<String>")
def itCall = new MethodCallExpr(new NameExpr(iterable.id.name), "iterator")
def itId = new VariableDeclaratorId("iterator")
def itDecl = new VariableDeclarationExpr(itType, [new VariableDeclarator(itId, itCall)])
def itExpr = new NameExpr(itId.name); def sbExpr = new NameExpr(sbId.name)
blockStatement.stmts.addAll([
new ExpressionStmt(sbDecl), new ExpressionStmt(itDecl),
new IfStmt(
new MethodCallExpr(itExpr, "hasNext"),
new BlockStmt([
new ExpressionStmt(new MethodCallExpr(sbExpr, "append", [new MethodCallExpr(itExpr, "next")])),
new WhileStmt(new MethodCallExpr(itExpr, "hasNext"), new BlockStmt([
new ExpressionStmt(new MethodCallExpr(
new MethodCallExpr(sbExpr, "append", [new NameExpr(delim.id.name)]),
"append", [new MethodCallExpr(itExpr, "next")]
))
]))
]),
null // <-- no else block
),
new ReturnStmt(new MethodCallExpr(sbExpr, "toString"))
])
}
}
@oldJavaGuy#VoxxedBerlin
PatchVisitor (String method)
class PatchVisitor extends VoidVisitorAdapter<Void> { // continued
private patchStringMethod(BlockStmt blockStatement, Parameter iterable, Parameter delim) {
def sbType = new ClassOrInterfaceType("StringBuilder")
def sbId = new VariableDeclaratorId("sb")
def sbDecl = new VariableDeclarationExpr(sbType, [
new VariableDeclarator(sbId, new ObjectCreationExpr(null, sbType, []))
])
def itType = new ClassOrInterfaceType("Iterator<String>")
def itCall = new MethodCallExpr(new NameExpr(iterable.id.name), "iterator")
def itId = new VariableDeclaratorId("iterator")
def itDecl = new VariableDeclarationExpr(itType, [new VariableDeclarator(itId, itCall)])
def itExpr = new NameExpr(itId.name); def sbExpr = new NameExpr(sbId.name)
blockStatement.stmts.addAll([
new ExpressionStmt(sbDecl), new ExpressionStmt(itDecl),
new IfStmt(
new MethodCallExpr(itExpr, "hasNext"),
new BlockStmt([
new ExpressionStmt(new MethodCallExpr(sbExpr, "append", [new MethodCallExpr(itExpr, "next")])),
new WhileStmt(new MethodCallExpr(itExpr, "hasNext"), new BlockStmt([
new ExpressionStmt(new MethodCallExpr(
new MethodCallExpr(sbExpr, "append", [new NameExpr(delim.id.name)]),
"append", [new MethodCallExpr(itExpr, "next")]
))
]))
]),
null // <-- no else block
),
new ReturnStmt(new MethodCallExpr(sbExpr, "toString"))
])
}
}
Source: http://www.memecenter.com/fun/1467675/my-eyes
@oldJavaGuy#VoxxedBerlin
The generated code, for comparison
public String yUStringConcatInLoop(
final Iterable<String> data, final String delim) {
StringBuilder sb = new StringBuilder();
Iterator<String> it = data.iterator();
if (it.hasNext()) {
sb.append(it.next());
while (it.hasNext()) {
sb.append(delim).append(it.next());
}
}
return sb.toString(); }
@oldJavaGuy#VoxxedBerlin
Tradeoff
• AST manipulation is hard, very
verbose and error prone
• But: It’s still better than trying to
do it with Regex (insert obligatory
Regex Cthulhu link here)
• friendlier Alternative:
walkmod.com (built on JavaParser,
“walkmod is an open source tool
to apply and share your own code
conventions.”)
Source: http://subgenius.wikia.com/wiki/Cthulhu
@oldJavaGuy#VoxxedBerlin
Use Case: Defect Analysis
• Identify bug patterns, reject
them at compile time
• Not mentioning the “good old”
tools: CheckStyle, PMD,
FindBugs.They are best used in
a separate CI build
• Focus on tools that hook
directly into the compile
process
Source: http://sequart.org/magazine/59883/cult-classics-starship-troopers/
@oldJavaGuy#VoxxedBerlin
Test Harness
public abstract class AbstractDefectDetectionTest extends AbstractCompilerTest {
private DefectAnalysisEngine engine;
@Before public void setupEngine() { this.engine = instantiateEngine(); }
@Test public void detectWellBehavedClass() {
CompilationResult result = engine.compile(sourceFileFor(wellBehavedClass()));
assertThat(result, isSuccess());
}
@Test public void detectIllBehavedClass() {
CompilationResult result = engine.compile(sourceFileFor(illBehavedClass()));
assertThat(result, isFailureWithExpectedMessage(expectedErrorMessage()));
}
protected abstract DefectAnalysisEngine instantiateEngine();
protected abstract Class<? extends WellBehaved> wellBehavedClass();
protected abstract Class<? extends IllBehaved> illBehavedClass();
protected abstract String expectedErrorMessage();
}
@oldJavaGuy#VoxxedBerlin
ForkedRun
• Helper class that calls a Java class in a separate process,
copying the Classpath from the local context, abstracting the
output away to a CompilationResult class, that we can run
matchers against.
• Fluent API with matchers for converting classpath to
bootclasspath and other nice features
• http://bit.ly/forkedRun
@oldJavaGuy#VoxxedBerlin
ForkedRun (sample)
public final CompilationResult run() {
final Set<String> classPath = new LinkedHashSet<>();
final Set<String> bootPath = new LinkedHashSet<>();
try {
dispatchClassPath(classPathElements, bootPathElements);
String javaExe = getExecutable();
List<String> cmds = buildCommands(classPath, bootPath, javaExe);
ProcessBuilder processBuilder = new ProcessBuilder().command(cmds);
if (outputCommand) System.out.println(Joiner.on(' ').join(commands));
Process proc = processBuilder.start();
List<String> errorMsg = gatherOutput(proc);
int status = proc.waitFor();
return new CompilationResult(status == 0, errorMsg);
} catch (InterruptedException | IOException | URISyntaxException e) {
throw new IllegalStateException(e); } }
@oldJavaGuy#VoxxedBerlin
False positives
• Example: Immutability (impossible to reliably detect, by any
technology known to me)
public final class ImmutableUser{
private final String name; private final Date birthDate;
private final List<String> nickNames;
public ImmutableUser(String name, List<String> nickNames, Date birthDate) {
this.name = name;
this.nickNames = ImmutableList.copyOf(nickNames);
this.birthDate = new Date(birthDate.getTime()); }
public String getName() { return name; }
public List<String> getNickNames() { return ImmutableList.copyOf(nickNames); }
public Date getBirthDate() { return new Date(birthDate.getTime()); }
}
@oldJavaGuy#VoxxedBerlin
What can we detect?
• Forbidden classes, erroneous usages
• Package-level architecture restrictions

(MicroServices or OSGI are a better solution)
• Implementation inconsistent with annotations or interface
(e.g. Nullness)
@oldJavaGuy#VoxxedBerlin
Google Error Prone
• http://errorprone.info/
• Google 20% project
• Wrapper around javac, with compatible API
• Many defined bug-patterns, most specific to Google (Android,
Protobuf, Gwt, Guice)
• New Bug Patterns can only be added via Pull Request to
Google :-(
• Integrated with Maven, Gradle,Ant etc.
@oldJavaGuy#VoxxedBerlin
Obscure bug patterns
• Example: Regex bug detection
public class IllBehavedRegex {
static List<String> splitByAlpha(String s) {
return Arrays.asList(
s.split("[a-z")); // bad pattern
}
}
@oldJavaGuy#VoxxedBerlin
Calling the engine in a test
public class ErrorProneEngine implements DefectAnalysisEngine {
private final Set<String> checks;
public ErrorProneEngine(Set<String> checks) { this.checks = checks; }
@Override public CompilationResult compile(final File sourceFile) {
return new ForkedRun(ErrorProneAnalysisEngine.class)
.withAdditionalClassLoaderFromClass(AbstractUser.class)
.withBootClassPathMatcher("com", "google", "errorprone")
.withBootClassPathMatcher("org", "checkerframework")
.withArg(ErrorProneCompiler.class)
.withArg("-d").tempDirAsArg()
.withArg("-Xep:" + Joiner.on(',').join(this.checks))
.withArg(sourceFile.getAbsolutePath())
.run();
} }
@oldJavaGuy#VoxxedBerlin
Checker Framework
• http://types.cs.washington.edu/checker-framework/
• The Checker Framework enhances Java’s type system to make
it more powerful and useful.This lets software developers
detect and prevent errors in their Java programs.The Checker
Framework includes compiler plug-ins ("checkers") that find
bugs or verify their absence. It also permits you to write your
own compiler plug-ins.
• Disadvantage: needs to be installed separately, hard to
integrate in a build system
• But: checkers can be used as plain annotation processors
@oldJavaGuy#VoxxedBerlin
Java 8 Type Annotations
• As of the Java SE 8 release,
annotations can also be applied to
any type use […] A few examples
of where types are used are class
instance creation expressions
(new), casts, implements clauses,
and throws clauses.

http://bit.ly/typeAnnotations
• The Checker Framework
embraces these new annotation
usages
Source: http://memegenerator.net/
@oldJavaGuy#VoxxedBerlin
Checker Framework Engine
public class CheckerFrameworkAnalysisEngine implements DefectAnalysisEngine {
private final Class<? extends BaseTypeChecker> checkerClass;
public CheckerFrameworkAnalysisEngine(Class<? extends BaseTypeChecker> c) {
this.checkerClass = c;
}
@Override public CompilationResult compile(final File sourceFile) {
return new ForkedRun(CheckerFrameworkAnalysisEngine.class)
.withAdditionalClassLoaderFromClass(AbstractUser.class)
.withJavaC() // javac, not java
.withArg("-d").tempDirAsArg()
.withArg("-processor").withArg(checkerClass)
.withArg(sourceFile.getAbsolutePath())
.run(); }
}
@oldJavaGuy#VoxxedBerlin
Example: Nullness
public class WellbehavedNullness {
@PolyNull String nullInOut(@PolyNull String s) {
if (s == null) return s;
return s.toUpperCase().trim();
}
@MonotonicNonNull private String initallyNull;
public void assignField(@NonNull String value) {
initallyNull = checkNotNull(value);
}
@Nonnull public String getValue() {
if (initallyNull == null)
initallyNull = "wasNull";
return initallyNull;
} }
@oldJavaGuy#VoxxedBerlin
Example: Nullness
public class WellbehavedNullness {
@PolyNull String nullInOut(@PolyNull String s) {
if (s == null) return s;
return s.toUpperCase().trim();
}
@MonotonicNonNull private String initallyNull;
public void assignField(@NonNull String value) {
initallyNull = checkNotNull(value);
}
@Nonnull public String getValue() {
if (initallyNull == null)
initallyNull = "wasNull";
return initallyNull;
} }
Source: https://imgflip.com/memegenerator
@oldJavaGuy#VoxxedBerlin
AspectJ (again)
• AspectJ allows custom compiler
errors, according to static checks
• Architecture checks: package a may
not access package b
• Forbidden usage patterns: e.g. Spring
MVC controller may not take
OutputStream param
• Forbidden classes: Enforce
deprecation of legacy classes
• Most of these problems can be solved
differently (MicroServices etc.)
Source: http://www.someecards.com/usercards/viewcard/MjAxMy1jZWRhODllNTI3YWI4Yjkz
@oldJavaGuy#VoxxedBerlin
Forbid usage of deprecated types
public aspect PolicyEnforcementAspect{
pointcut badCall() : call(* Hashtable.*(..))
|| call(Hashtable.new(..))
|| call(* Vector.*(..))
|| call(Vector.new(..));
declare error : badCall()
"Hashtable and Vector are deprecated!";
}
@oldJavaGuy#VoxxedBerlin
AspectJ testing engine
public class AspectJPolicyEnforcementEngine
implements DefectAnalysisEngine {
@Override public CompilationResult compile(File sourceFile) {
final File aspectFile = findAspectFile(sourceFile);
return new ForkedRun(AspectJPolicyEnforcementEngine.class)
.withArg(Main.class /* ajc */).withArg("-1.8")
.withArg("-target").withArg("1.8")
.withArg("-d").tempDirAsArg()
.withArg(sourceFile.getAbsolutePath())
.withArg(aspectFile.getAbsolutePath())
.run(); }
@oldJavaGuy#VoxxedBerlin
Overview of discussed techniques
• Obviously, this is an opinion, not advice. Use at your own risk.
• Author is not affiliated with any of the above libraries.
Technique Have I used in real applications? Do I recommend technique?
Lombok yes maybe
AutoValue yes yes
CGLib BeanGenerator no no
JCodeModel yes yes
AspectJ (patching) yes maybe
JavaParser yes maybe
ErrorProne no maybe
Checker Framework no yes
AspectJ (policy enforcement) yes yes
@oldJavaGuy#VoxxedBerlin
Where to go from here
• Look at the github code, send me a PR if you have
improvements
• Contact me if you have questions
• Suggest more techniques / use cases
@oldJavaGuy#VoxxedBerlin
Source: http://www.gapingvoidart.com/gallery/any-questions/
@oldJavaGuy#VoxxedBerlin
Source: http://www.gapingvoidart.com/gallery/any-questions/

Contenu connexe

Tendances

Григорий Шехет "Treasure hunt in the land of Reactive frameworks"
Григорий Шехет "Treasure hunt in the land of Reactive frameworks"Григорий Шехет "Treasure hunt in the land of Reactive frameworks"
Григорий Шехет "Treasure hunt in the land of Reactive frameworks"Fwdays
 
Qt test framework
Qt test frameworkQt test framework
Qt test frameworkICS
 
Mastering Java Bytecode - JAX.de 2012
Mastering Java Bytecode - JAX.de 2012Mastering Java Bytecode - JAX.de 2012
Mastering Java Bytecode - JAX.de 2012Anton Arhipov
 
Reactor grails realtime web devoxx 2013
Reactor grails realtime web   devoxx 2013Reactor grails realtime web   devoxx 2013
Reactor grails realtime web devoxx 2013Stéphane Maldini
 
Alberto Maria Angelo Paro - Isomorphic programming in Scala and WebDevelopmen...
Alberto Maria Angelo Paro - Isomorphic programming in Scala and WebDevelopmen...Alberto Maria Angelo Paro - Isomorphic programming in Scala and WebDevelopmen...
Alberto Maria Angelo Paro - Isomorphic programming in Scala and WebDevelopmen...Codemotion
 
Implementing a JavaScript Engine
Implementing a JavaScript EngineImplementing a JavaScript Engine
Implementing a JavaScript EngineKris Mok
 
A Taste of Pharo 7.0
A Taste of Pharo 7.0A Taste of Pharo 7.0
A Taste of Pharo 7.0ESUG
 
2013.02.02 지앤선 테크니컬 세미나 - Xcode를 활용한 디버깅 팁(OSXDEV)
2013.02.02 지앤선 테크니컬 세미나 - Xcode를 활용한 디버깅 팁(OSXDEV)2013.02.02 지앤선 테크니컬 세미나 - Xcode를 활용한 디버깅 팁(OSXDEV)
2013.02.02 지앤선 테크니컬 세미나 - Xcode를 활용한 디버깅 팁(OSXDEV)JiandSon
 
Ruby in office time reboot
Ruby in office time rebootRuby in office time reboot
Ruby in office time rebootKentaro Goto
 
Road to sbt 1.0: Paved with server (2015 Amsterdam)
Road to sbt 1.0: Paved with server (2015 Amsterdam)Road to sbt 1.0: Paved with server (2015 Amsterdam)
Road to sbt 1.0: Paved with server (2015 Amsterdam)Eugene Yokota
 
(COSCUP 2015) A Beginner's Journey to Mozilla SpiderMonkey JS Engine
(COSCUP 2015) A Beginner's Journey to Mozilla SpiderMonkey JS Engine(COSCUP 2015) A Beginner's Journey to Mozilla SpiderMonkey JS Engine
(COSCUP 2015) A Beginner's Journey to Mozilla SpiderMonkey JS EngineZongXian Shen
 
Programming iOS in Lua - A bridge story
Programming iOS in Lua - A bridge storyProgramming iOS in Lua - A bridge story
Programming iOS in Lua - A bridge storyjljumpertz
 
Connecting the Worlds of Java and Ruby with JRuby
Connecting the Worlds of Java and Ruby with JRubyConnecting the Worlds of Java and Ruby with JRuby
Connecting the Worlds of Java and Ruby with JRubyNick Sieger
 
Need for Async: Hot pursuit for scalable applications
Need for Async: Hot pursuit for scalable applicationsNeed for Async: Hot pursuit for scalable applications
Need for Async: Hot pursuit for scalable applicationsKonrad Malawski
 
CodeFlow, an advanced IDE for Lua
CodeFlow, an advanced IDE for LuaCodeFlow, an advanced IDE for Lua
CodeFlow, an advanced IDE for Luajljumpertz
 
How the HotSpot and Graal JVMs execute Java Code
How the HotSpot and Graal JVMs execute Java CodeHow the HotSpot and Graal JVMs execute Java Code
How the HotSpot and Graal JVMs execute Java CodeJim Gough
 
Rubykaigi 2017-nishimotz-v6
Rubykaigi 2017-nishimotz-v6Rubykaigi 2017-nishimotz-v6
Rubykaigi 2017-nishimotz-v6Takuya Nishimoto
 

Tendances (20)

Григорий Шехет "Treasure hunt in the land of Reactive frameworks"
Григорий Шехет "Treasure hunt in the land of Reactive frameworks"Григорий Шехет "Treasure hunt in the land of Reactive frameworks"
Григорий Шехет "Treasure hunt in the land of Reactive frameworks"
 
Ratpack JVM_MX Meetup February 2016
Ratpack JVM_MX Meetup February 2016Ratpack JVM_MX Meetup February 2016
Ratpack JVM_MX Meetup February 2016
 
Turbo charging v8 engine
Turbo charging v8 engineTurbo charging v8 engine
Turbo charging v8 engine
 
Qt test framework
Qt test frameworkQt test framework
Qt test framework
 
Mastering Java Bytecode - JAX.de 2012
Mastering Java Bytecode - JAX.de 2012Mastering Java Bytecode - JAX.de 2012
Mastering Java Bytecode - JAX.de 2012
 
Reactor grails realtime web devoxx 2013
Reactor grails realtime web   devoxx 2013Reactor grails realtime web   devoxx 2013
Reactor grails realtime web devoxx 2013
 
Alberto Maria Angelo Paro - Isomorphic programming in Scala and WebDevelopmen...
Alberto Maria Angelo Paro - Isomorphic programming in Scala and WebDevelopmen...Alberto Maria Angelo Paro - Isomorphic programming in Scala and WebDevelopmen...
Alberto Maria Angelo Paro - Isomorphic programming in Scala and WebDevelopmen...
 
Implementing a JavaScript Engine
Implementing a JavaScript EngineImplementing a JavaScript Engine
Implementing a JavaScript Engine
 
Node.js code tracing
Node.js code tracingNode.js code tracing
Node.js code tracing
 
A Taste of Pharo 7.0
A Taste of Pharo 7.0A Taste of Pharo 7.0
A Taste of Pharo 7.0
 
2013.02.02 지앤선 테크니컬 세미나 - Xcode를 활용한 디버깅 팁(OSXDEV)
2013.02.02 지앤선 테크니컬 세미나 - Xcode를 활용한 디버깅 팁(OSXDEV)2013.02.02 지앤선 테크니컬 세미나 - Xcode를 활용한 디버깅 팁(OSXDEV)
2013.02.02 지앤선 테크니컬 세미나 - Xcode를 활용한 디버깅 팁(OSXDEV)
 
Ruby in office time reboot
Ruby in office time rebootRuby in office time reboot
Ruby in office time reboot
 
Road to sbt 1.0: Paved with server (2015 Amsterdam)
Road to sbt 1.0: Paved with server (2015 Amsterdam)Road to sbt 1.0: Paved with server (2015 Amsterdam)
Road to sbt 1.0: Paved with server (2015 Amsterdam)
 
(COSCUP 2015) A Beginner's Journey to Mozilla SpiderMonkey JS Engine
(COSCUP 2015) A Beginner's Journey to Mozilla SpiderMonkey JS Engine(COSCUP 2015) A Beginner's Journey to Mozilla SpiderMonkey JS Engine
(COSCUP 2015) A Beginner's Journey to Mozilla SpiderMonkey JS Engine
 
Programming iOS in Lua - A bridge story
Programming iOS in Lua - A bridge storyProgramming iOS in Lua - A bridge story
Programming iOS in Lua - A bridge story
 
Connecting the Worlds of Java and Ruby with JRuby
Connecting the Worlds of Java and Ruby with JRubyConnecting the Worlds of Java and Ruby with JRuby
Connecting the Worlds of Java and Ruby with JRuby
 
Need for Async: Hot pursuit for scalable applications
Need for Async: Hot pursuit for scalable applicationsNeed for Async: Hot pursuit for scalable applications
Need for Async: Hot pursuit for scalable applications
 
CodeFlow, an advanced IDE for Lua
CodeFlow, an advanced IDE for LuaCodeFlow, an advanced IDE for Lua
CodeFlow, an advanced IDE for Lua
 
How the HotSpot and Graal JVMs execute Java Code
How the HotSpot and Graal JVMs execute Java CodeHow the HotSpot and Graal JVMs execute Java Code
How the HotSpot and Graal JVMs execute Java Code
 
Rubykaigi 2017-nishimotz-v6
Rubykaigi 2017-nishimotz-v6Rubykaigi 2017-nishimotz-v6
Rubykaigi 2017-nishimotz-v6
 

En vedette

Making Java more dynamic: runtime code generation for the JVM
Making Java more dynamic: runtime code generation for the JVMMaking Java more dynamic: runtime code generation for the JVM
Making Java more dynamic: runtime code generation for the JVMRafael Winterhalter
 
Trends and future of java
Trends and future of javaTrends and future of java
Trends and future of javaCsaba Toth
 
Дмитрий Контрерас «Back to the future: the evolution of the Java Type System»
Дмитрий Контрерас «Back to the future: the evolution of the Java Type System»Дмитрий Контрерас «Back to the future: the evolution of the Java Type System»
Дмитрий Контрерас «Back to the future: the evolution of the Java Type System»Anna Shymchenko
 
Java World, Java Trends, Java 8 and Beyond (iForum - 2014)
Java World, Java Trends, Java 8 and Beyond (iForum - 2014)Java World, Java Trends, Java 8 and Beyond (iForum - 2014)
Java World, Java Trends, Java 8 and Beyond (iForum - 2014)Olena Syrota
 
BarcelonaJUG2016: walkmod: how to run and design code transformations
BarcelonaJUG2016: walkmod: how to run and design code transformationsBarcelonaJUG2016: walkmod: how to run and design code transformations
BarcelonaJUG2016: walkmod: how to run and design code transformationswalkmod
 
Type Annotations in Java 8
Type Annotations in Java 8 Type Annotations in Java 8
Type Annotations in Java 8 FinLingua, Inc.
 
Mathematical Logic Part 2
Mathematical Logic Part 2Mathematical Logic Part 2
Mathematical Logic Part 2blaircomp2003
 
Controlling Technical Debt with Continuous Delivery
Controlling Technical Debt with Continuous DeliveryControlling Technical Debt with Continuous Delivery
Controlling Technical Debt with Continuous Deliverywalkmod
 

En vedette (8)

Making Java more dynamic: runtime code generation for the JVM
Making Java more dynamic: runtime code generation for the JVMMaking Java more dynamic: runtime code generation for the JVM
Making Java more dynamic: runtime code generation for the JVM
 
Trends and future of java
Trends and future of javaTrends and future of java
Trends and future of java
 
Дмитрий Контрерас «Back to the future: the evolution of the Java Type System»
Дмитрий Контрерас «Back to the future: the evolution of the Java Type System»Дмитрий Контрерас «Back to the future: the evolution of the Java Type System»
Дмитрий Контрерас «Back to the future: the evolution of the Java Type System»
 
Java World, Java Trends, Java 8 and Beyond (iForum - 2014)
Java World, Java Trends, Java 8 and Beyond (iForum - 2014)Java World, Java Trends, Java 8 and Beyond (iForum - 2014)
Java World, Java Trends, Java 8 and Beyond (iForum - 2014)
 
BarcelonaJUG2016: walkmod: how to run and design code transformations
BarcelonaJUG2016: walkmod: how to run and design code transformationsBarcelonaJUG2016: walkmod: how to run and design code transformations
BarcelonaJUG2016: walkmod: how to run and design code transformations
 
Type Annotations in Java 8
Type Annotations in Java 8 Type Annotations in Java 8
Type Annotations in Java 8
 
Mathematical Logic Part 2
Mathematical Logic Part 2Mathematical Logic Part 2
Mathematical Logic Part 2
 
Controlling Technical Debt with Continuous Delivery
Controlling Technical Debt with Continuous DeliveryControlling Technical Debt with Continuous Delivery
Controlling Technical Debt with Continuous Delivery
 

Similaire à Hacking Java - Enhancing Java Code at Build or Runtime

The Why and How of Scala at Twitter
The Why and How of Scala at TwitterThe Why and How of Scala at Twitter
The Why and How of Scala at TwitterAlex Payne
 
Rapid Web API development with Kotlin and Ktor
Rapid Web API development with Kotlin and KtorRapid Web API development with Kotlin and Ktor
Rapid Web API development with Kotlin and KtorTrayan Iliev
 
Scala at Treasure Data
Scala at Treasure DataScala at Treasure Data
Scala at Treasure DataTaro L. Saito
 
New types of tests for Java projects
New types of tests for Java projectsNew types of tests for Java projects
New types of tests for Java projectsVincent Massol
 
Advanced Java Testing @ POSS 2019
Advanced Java Testing @ POSS 2019Advanced Java Testing @ POSS 2019
Advanced Java Testing @ POSS 2019Vincent Massol
 
New types of tests for Java projects
New types of tests for Java projectsNew types of tests for Java projects
New types of tests for Java projectsVincent Massol
 
SE2016 - Java EE revisits design patterns 2016
SE2016 - Java EE revisits design patterns 2016SE2016 - Java EE revisits design patterns 2016
SE2016 - Java EE revisits design patterns 2016Alex Theedom
 
Java EE revisits design patterns
Java EE revisits design patterns Java EE revisits design patterns
Java EE revisits design patterns Alex Theedom
 
Java EE Revisits Design Patterns
Java EE Revisits Design PatternsJava EE Revisits Design Patterns
Java EE Revisits Design PatternsAlex Theedom
 
Игорь Фесенко "Direction of C# as a High-Performance Language"
Игорь Фесенко "Direction of C# as a High-Performance Language"Игорь Фесенко "Direction of C# as a High-Performance Language"
Игорь Фесенко "Direction of C# as a High-Performance Language"Fwdays
 
Ractor's speed is not light-speed
Ractor's speed is not light-speedRactor's speed is not light-speed
Ractor's speed is not light-speedSATOSHI TAGOMORI
 
Projects Valhalla, Loom and GraalVM at JUG Mainz
Projects Valhalla, Loom and GraalVM at JUG MainzProjects Valhalla, Loom and GraalVM at JUG Mainz
Projects Valhalla, Loom and GraalVM at JUG MainzVadym Kazulkin
 
WTF is Twisted?
WTF is Twisted?WTF is Twisted?
WTF is Twisted?hawkowl
 
Afstuderen bij Sogeti Java
Afstuderen bij Sogeti JavaAfstuderen bij Sogeti Java
Afstuderen bij Sogeti Javaerwindeg
 
CBDW2014 - MockBox, get ready to mock your socks off!
CBDW2014 - MockBox, get ready to mock your socks off!CBDW2014 - MockBox, get ready to mock your socks off!
CBDW2014 - MockBox, get ready to mock your socks off!Ortus Solutions, Corp
 
Migration strategies 4
Migration strategies 4Migration strategies 4
Migration strategies 4Wenhua Wang
 
Google App Engine Java, Groovy and Gaelyk
Google App Engine Java, Groovy and GaelykGoogle App Engine Java, Groovy and Gaelyk
Google App Engine Java, Groovy and GaelykGuillaume Laforge
 
Java EE revisits design patterns
Java EE revisits design patternsJava EE revisits design patterns
Java EE revisits design patternsAlex Theedom
 
Genomic Computation at Scale with Serverless, StackStorm and Docker Swarm
Genomic Computation at Scale with Serverless, StackStorm and Docker SwarmGenomic Computation at Scale with Serverless, StackStorm and Docker Swarm
Genomic Computation at Scale with Serverless, StackStorm and Docker SwarmDmitri Zimine
 

Similaire à Hacking Java - Enhancing Java Code at Build or Runtime (20)

The Why and How of Scala at Twitter
The Why and How of Scala at TwitterThe Why and How of Scala at Twitter
The Why and How of Scala at Twitter
 
Rapid Web API development with Kotlin and Ktor
Rapid Web API development with Kotlin and KtorRapid Web API development with Kotlin and Ktor
Rapid Web API development with Kotlin and Ktor
 
Advanced Java Testing
Advanced Java TestingAdvanced Java Testing
Advanced Java Testing
 
Scala at Treasure Data
Scala at Treasure DataScala at Treasure Data
Scala at Treasure Data
 
New types of tests for Java projects
New types of tests for Java projectsNew types of tests for Java projects
New types of tests for Java projects
 
Advanced Java Testing @ POSS 2019
Advanced Java Testing @ POSS 2019Advanced Java Testing @ POSS 2019
Advanced Java Testing @ POSS 2019
 
New types of tests for Java projects
New types of tests for Java projectsNew types of tests for Java projects
New types of tests for Java projects
 
SE2016 - Java EE revisits design patterns 2016
SE2016 - Java EE revisits design patterns 2016SE2016 - Java EE revisits design patterns 2016
SE2016 - Java EE revisits design patterns 2016
 
Java EE revisits design patterns
Java EE revisits design patterns Java EE revisits design patterns
Java EE revisits design patterns
 
Java EE Revisits Design Patterns
Java EE Revisits Design PatternsJava EE Revisits Design Patterns
Java EE Revisits Design Patterns
 
Игорь Фесенко "Direction of C# as a High-Performance Language"
Игорь Фесенко "Direction of C# as a High-Performance Language"Игорь Фесенко "Direction of C# as a High-Performance Language"
Игорь Фесенко "Direction of C# as a High-Performance Language"
 
Ractor's speed is not light-speed
Ractor's speed is not light-speedRactor's speed is not light-speed
Ractor's speed is not light-speed
 
Projects Valhalla, Loom and GraalVM at JUG Mainz
Projects Valhalla, Loom and GraalVM at JUG MainzProjects Valhalla, Loom and GraalVM at JUG Mainz
Projects Valhalla, Loom and GraalVM at JUG Mainz
 
WTF is Twisted?
WTF is Twisted?WTF is Twisted?
WTF is Twisted?
 
Afstuderen bij Sogeti Java
Afstuderen bij Sogeti JavaAfstuderen bij Sogeti Java
Afstuderen bij Sogeti Java
 
CBDW2014 - MockBox, get ready to mock your socks off!
CBDW2014 - MockBox, get ready to mock your socks off!CBDW2014 - MockBox, get ready to mock your socks off!
CBDW2014 - MockBox, get ready to mock your socks off!
 
Migration strategies 4
Migration strategies 4Migration strategies 4
Migration strategies 4
 
Google App Engine Java, Groovy and Gaelyk
Google App Engine Java, Groovy and GaelykGoogle App Engine Java, Groovy and Gaelyk
Google App Engine Java, Groovy and Gaelyk
 
Java EE revisits design patterns
Java EE revisits design patternsJava EE revisits design patterns
Java EE revisits design patterns
 
Genomic Computation at Scale with Serverless, StackStorm and Docker Swarm
Genomic Computation at Scale with Serverless, StackStorm and Docker SwarmGenomic Computation at Scale with Serverless, StackStorm and Docker Swarm
Genomic Computation at Scale with Serverless, StackStorm and Docker Swarm
 

Dernier

call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️Delhi Call girls
 
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerHow To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerThousandEyes
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providermohitmore19
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...panagenda
 
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AISyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AIABDERRAOUF MEHENNI
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...ICS
 
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...OnePlan Solutions
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsAndolasoft Inc
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...MyIntelliSource, Inc.
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxComplianceQuest1
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Modelsaagamshah0812
 
Diamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionDiamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionSolGuruz
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVshikhaohhpro
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...Health
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Steffen Staab
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdfWave PLM
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfkalichargn70th171
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsJhone kinadey
 

Dernier (20)

call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerHow To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
 
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AISyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
 
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICECHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
 
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.js
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docx
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
Diamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionDiamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with Precision
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTV
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
Microsoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdfMicrosoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdf
 

Hacking Java - Enhancing Java Code at Build or Runtime

  • 1. @oldJavaGuy#VoxxedBerlin Enhancing Code at build or runtime Sean Patrick Floyd Hacking Java
  • 2. @oldJavaGuy#VoxxedBerlin FOUNDED IN 2008 +10.000 EMPLOYEES 7 TECH HUBS +1000 TECHNOLOGISTS 15 COUNTRIES 14 MILLION APP DOWNLOADS +17 MILLION CUSTOMERS
  • 3. @oldJavaGuy#VoxxedBerlin Sean Patrick Floyd • Search Engineer @ Zalando • ~20 years experience • Java, Scala, Groovy • Twitter: @oldJavaGuy
  • 4. @oldJavaGuy#VoxxedBerlin Scope of this talk • Overview of non-standard techniques • Grouped by use case • Some techniques are more mature than others • Code samples and unit tests
  • 5. @oldJavaGuy#VoxxedBerlin Use Cases • Value Objects • Third party library patching • Code defect analysis • More ideas?
  • 6. @oldJavaGuy#VoxxedBerlin Github Project • https://github.com/mostlymagic/hacking-java 
 http://bit.ly/hackingJava • Organized as Multi-Module Maven project • Grouped by use case • Sample code and unit tests for every technique
  • 7. @oldJavaGuy#VoxxedBerlin Use Case: Value Objects • Standard, well defined behavior (equals(), hashCode(), toString()) according to Effective Java • Mutable or immutable, with constructors, factory methods or builders • Java Boilerplate very verbose and error-prone
  • 8. @oldJavaGuy#VoxxedBerlin The Pain public class MyValueObject { private final String property1; private final boolean property2; public MyValueObject(String property1, boolean property2) { this.property1 = property1; this.property2 = property2; } public String getProperty1() { return property1; } public boolean isProperty2() { return property2; } @Override public int hashCode() { return Objects.hash(property1, property2); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } else if (obj instanceof MyValueObject) { MyValueObject other = (MyValueObject) obj; return Objects.equals(this.property1, other.property1) && Objects.equals(this.property2, other.property2); } else { return false; } } @Override public String toString() { return MoreObjects.toStringHelper(this).add("property1", property1).add("property2", property2).toString(); } }
  • 9. @oldJavaGuy#VoxxedBerlin For comparison: Scala case class MyValueObject(property1: String, property2: Boolean) {}
  • 10. @oldJavaGuy#VoxxedBerlin For comparison: Scala case class MyValueObject(property1: String, property2: Boolean) {} Source: http://onlyinamericablogging.blogspot.com/2014/11/move-along-now-nothing-to-see-here.html
  • 11. @oldJavaGuy#VoxxedBerlin Boilerplate • equals() / hashCode() / toString() / getters / setters / constructors / compareTo() • IDEs offer generation, but in a static way • potential bugs: adding or removing fields • Boilerplate (n.): newspaper [and IT] slang for "unit of writing that can be used over and over without change," 1893, from a literal meaning (1840) "metal rolled in large, flat plates for use in making steam boilers."
 Source: http://www.etymonline.com/ https://en.wikipedia.org/wiki/Boilerplate_(robot)
  • 12. @oldJavaGuy#VoxxedBerlin Plan • Let’s look at technologies that let us define value objects in a less-verbose way • But with the full functionality (a test suite will monitor correct working of getters, equals, hashCode and toString) • Different approaches: build-time, compile-time, run-time
  • 13. @oldJavaGuy#VoxxedBerlin Testing • Expected Data structure (mutable or immutable):
 User - firstName (String) - lastName (String) - birthDate (LocalDate) - addresses (List[Address]) Address - street (String) - zipCode (int) - city (String)
  • 14. @oldJavaGuy#VoxxedBerlin Test suite for value objects public abstract class BaseUserTest { protected static final String FIRST_NAME = "Fred"; // etc. @Test public void equalsAndHashCodeAreSymmetrical() { Object user1 = createUser(); Object user2 = createUser(); assertThat(user1, is(equalTo(user2))); assertThat(user2, is(equalTo(user1))); assertThat(user1.hashCode(), is(equalTo(user2.hashCode()))); } @Test public void toStringIsConsistent() { assertThat(createUser().toString(), is(equalTo(createUser().toString()))); String s = createUser().toString(); assertThat(s, containsString(FIRST_NAME)); /* etc. */ } @SuppressWarnings({"unchecked", "rawtypes"}) @Test public void compareToIsSymmetrical() { Object l = createUser(), r = createUser(); assertThat(l, instanceOf(Comparable.class)); assertThat(r, instanceOf(Comparable.class)); assertThat(((Comparable) l).compareTo(r), equalTo(((Comparable) r).compareTo(l))); } }
  • 15. @oldJavaGuy#VoxxedBerlin Test suite (continued) @Test public void propertyMapHasCorrectValues() { Object instance = createUser(); Map<String, Object> map = getPropertyMap(instance); assertThat(map, hasEntry("firstName", FIRST_NAME)); // etc. } private static Map<String, Object> getPropertyMap(Object instance) { final Map<String, Object> map = new TreeMap<>(); try { Arrays.stream(Introspector.getBeanInfo(instance.getClass(), Object.class) .getPropertyDescriptors()).filter((it) -> it.getReadMethod() != null) .forEach((pD) -> { Method m = propertyDescriptor.getReadMethod(); try { Object o = m.invoke(instance); map.put(pD.getName(), o); } catch (IllegalAccessException | ... e) { throw new ISE(e); }}); } catch (IntrospectionException e) { throw new IllegalStateException(e); } return propertyMap; } protected abstract Object createUser();
  • 16. @oldJavaGuy#VoxxedBerlin Alternative Test Suite • Guava’s AbstractPackageSanityTests
 ( http://bit.ly/AbstractPackageSanityTests ) • Automatically runs sanity checks against top level classes in the same package of the test that extends AbstractPackageSanityTests. Currently sanity checks include NullPointerTester, EqualsTester and SerializableTester. • Nice, but not a perfect match for this use case
  • 17. @oldJavaGuy#VoxxedBerlin Annotation Processing • JSR 269, pluggable annotation processing:
 Separate compiler lifecycle, well suited for code generation • Service auto-discovery through ServiceLoader:
 /META-INF/services/javax.annotation.processing.Processor
 contains qualified processor names ( http://bit.ly/srvcLdr ) • Docs: http://bit.ly/annotationProcessing (Oracle JavaDocs)
  • 18. @oldJavaGuy#VoxxedBerlin Project Lombok • Name: Lombok is an Indonesian Island neighboring Java (“it’s not quite Java, but almost”) • Project Lombok uses Annotation Processing to extend the AST. It uses internal compiler APIs (Javac and Eclipse) • Advantages: Little code, lots of power, no runtime dependencies • Disadvantages: Relying on undocumented internal APIs, adds code that is not reflected in sources (inconsistent) Source: http://bit.ly/1lOfPbC
  • 19. @oldJavaGuy#VoxxedBerlin Lombok: mutable example @Data public class MyValueObject { private String property1; private boolean property2; } • Generates getters, setters, equals, hashCode, toString • Additional fine-tuning annotations are available
  • 20. @oldJavaGuy#VoxxedBerlin Lombok: immutable example @Data public class MyValueObject { private final String property1; private final boolean property2; } • Generates constructor, getters, equals, hashCode, toString • Builder version also available
  • 21. @oldJavaGuy#VoxxedBerlin Google AutoValue • https://github.com/google/auto/tree/master/value • “AutoValue […] is actually a great tool for eliminating the drudgery of writing mundane value classes in Java. It encapsulates much of the advice in Effective Java […].The resulting program is likely to be shorter, clearer, and freer of bugs.” -- Joshua Bloch, author, Effective Java • Advantages: Only public APIs used, no runtime dependencies • Disadvantages: Less power and flexibility, only immutable types supported (or is that an advantage?)
  • 22. @oldJavaGuy#VoxxedBerlin AutoValue Sample code @AutoValue // class needs to be abstract public abstract class MyValueObject { // use JavaBeans property names or simple field names public abstract String getProperty1(); public abstract boolean isProperty2(); // factory method for instantiation static MyValueObject create(String p1, boolean p2){ return new AutoValue_MyValueObject(p1, p2); //"AutoValue_" + abstract class name } }
  • 23. @oldJavaGuy#VoxxedBerlin CGLib BeanGenerator • https://github.com/cglib/cglib • CGLib is a “high level” byte code manipulation framework • Widely used in production code, mostly by IOC and ORM frameworks (Spring, Guice etc) • BeanGenerator is a playground feature that can create value types on the fly • Works on Byte Code:
 https://en.wikipedia.org/wiki/Java_bytecode
  • 24. @oldJavaGuy#VoxxedBerlin Caveat • BeanGenerator generates field + getter + setter (no immutable types, no equals(), hashCode(), toString()) • To solve these issues, I am creating a dynamic proxy around the generated class, which in turn uses a full JavaBeans property map for all operations. • /* This is O(scary), but seems quick enough in practice. */ ( Source: http://bit.ly/bigOScary ) • There is probably no sane use case for this
  • 25. @oldJavaGuy#VoxxedBerlin Generating types on the fly public static Class<?> createBeanClass( final String className, // qualified type Map<String, Class<?>> props) { BeanGenerator beanGenerator = new BeanGenerator(); /* use our own hard coded class name */ beanGenerator.setNamingPolicy(new NamingPolicy() { @Override public String getClassName(...) { return className; }}); BeanGenerator.addProperties(beanGenerator, props); return (Class<?>) beanGenerator.createClass(); }
  • 26. @oldJavaGuy#VoxxedBerlin Instantiating dynamic types public static Object instantiateDto(Class<?> type, Map<String, Object> data) { try { Object dto = type.newInstance(); assignProperties(map, dto); // JavaBeans magic return createProxy(dto); } catch (InstantiationException | ... e) { throw new IllegalStateException(e); } } private static Object createProxy(Object dto) { final Enhancer e = new Enhancer(); e.setSuperclass(dto.getClass()); e.setCallback(new HandlerBasedMethodInterceptor(dto)); return e.create(); }
  • 27. @oldJavaGuy#VoxxedBerlin MethodInterceptor class HandlerBasedInterceptor implements MethodInterceptor, Supplier<Object> { private final Object dto; public HandlerBasedInterceptor(Object dto) { this.dto = dto; } public Object intercept(... , Object[] args, MethodProxy p) { for (MethodHandler h : MethodHandler.values()) { // equals / hashCode / toString handled by proxy if (h.matches(method)) return h.invoke(dto, args); } return method.invoke(dto, args); // getters / setters handled by original } public Object get() { return dto; } }
  • 28. @oldJavaGuy#VoxxedBerlin MethodHandler enum MethodHandler { EQUALS { boolean matches(final Method candidate) { return candidate.getName().equals("equals") && Arrays.equals(candidate.getParameterTypes(), new Class[]{ Object.class} ); } Object invoke(final Object o, final Object[] args) { return o.getClass().isInstance(args[0]) && DtoFactory.readProperties(o).equals( DtoFactory.readProperties(args[0])); } } }; // + hashCode() + toString()
  • 29. @oldJavaGuy#VoxxedBerlin Don’t try this at home! • There is (almost) no sane reason for generating value classes at runtime • One possible scenario would be a CMS that lets you add new content types at runtime and is backed by a class-based ORM Source: http://www.richardbealblog.com/dont-try-this-at-home/
  • 30. @oldJavaGuy#VoxxedBerlin Code Generation • Techniques for creating source code dynamically at build time before compiling • For those of us who like to “write code that writes code”
  • 31. @oldJavaGuy#VoxxedBerlin Code Generation • Techniques for creating source code dynamically at build time before compiling • For those of us who like to “write code that writes code” Source: http://xkcd.com/1629/
  • 32. @oldJavaGuy#VoxxedBerlin Code Generation • Techniques for creating source code dynamically at build time before compiling • For those of us who like to “write code that writes code” Source: http://xkcd.com/1629/ Source: http://www.memes.com/meme/487047
  • 33. @oldJavaGuy#VoxxedBerlin Why code generation? • Everybody else is “doing it wrong”.
 Style wars: e.g. instanceof vs getClass() • Custom requirements: Serializable, Jackson or JPA annotations • Good news: you can do almost anything • Bad news: you have to do almost everything yourself • Most users will prefer “standard” solutions like Lombok or AutoValue
  • 34. @oldJavaGuy#VoxxedBerlin Adding code generation to the Maven Build • Maven has two dedicated lifecycle phases for code generation,“generate-sources” and “generate-test-sources” • The easiest way to achieve code generation is to execute a Groovy script in one of these phases, using the Groovy Maven Plugin (http://bit.ly/groovyMavenPlugin ) • By convention, Code generation uses the output folder target/generated-sources/{generator-name} • Never generate code to src folder!
  • 35. @oldJavaGuy#VoxxedBerlin JCodeModel • https://github.com/phax/jcodemodel • Fork of Sun’s abandoned JCodeModel project, which did code generation for JAXB • Programmatic Java AST generation and source code generation • Friendly, understandable API
  • 37. @oldJavaGuy#VoxxedBerlin Groovy helper types class Dto { def packageName; def className; def properties = [:]; } class GenericType { def baseType; def params = [] } def userDto = new Dto( // example usage packageName: "com.myapp", className: "User", properties: [ "firstName": "String", "lastName" : "String", "birthDate": "java.time.LocalDate", "addresses": new GenericType( baseType: "java.util.List", params: ["com.myapp.Address"]) ])
  • 38. @oldJavaGuy#VoxxedBerlin CODE GENERATION WITH JCODEMODEL class DtoGenerator { def codeModel = new JCodeModel() public generate(List<Dto> dtos, File targetDir) { dtos.each { def clazz = codeModel._package(it.packageName) ._class(JMod.PUBLIC, it.className) defineConstructor(clazz, it.properties) defineGetters(clazz, it.properties) defineEqualsMethod(clazz, it.properties) defineHashCodeMethod(clazz, it.properties) defineToStringMethod(clazz, it.properties) defineCompareToMethod(clazz, it.comparableProperties) } targetDir.mkdirs(); codeModel.build(targetDir) }}
  • 39. @oldJavaGuy#VoxxedBerlin Resolving types class DtoGenerator { // continued private AbstractJClass genType(GenericType type) { def baseType = codeModel.ref(type.baseType) def params = type.params.collect { resolveType(it) } return baseType.narrow( params as AbstractJClass[] ) } private AbstractJType resolveType(type) { if (GenericType.class.isInstance(type)) return genType(type) else return codeModel.ref(type) } } // continued
  • 40. @oldJavaGuy#VoxxedBerlin GETTERS class DtoGenerator { // continued private defineGetters(JDefinedClass c, Map fields) { fields.each { e -> def m = c.method(PUBLIC, resolveType(e.value), e.value == 'boolean' ? 'is' : 'get' + LOWER_CAMEL.to(UPPER_CAMEL, e.key)) // return [property] m.body()._return(THIS.ref(e.key)) } } } // continued
  • 41. @oldJavaGuy#VoxxedBerlin Constructor and Fields class DtoGenerator { // continued private defineConstructor(JDefinedClass c, Map fields) { def con = c.constructor(JMod.PUBLIC) def body = con.body() fields.each { e -> def type = resolveType(e.value) def f = c.field(PRIVATE | FINAL, type, e.key) def param = con.param(type, e.key) body.assign(THIS.ref(f), param) } } } // continued
  • 42. @oldJavaGuy#VoxxedBerlin toString() class DtoGenerator { // continued private defineToStringMethod(JDefinedClass clazz, Map fields) { def method = clazz.method(JMod.PUBLIC, String.class, "toString") method.annotate(Override.class) def body = method.body() def invocation = codeModel.ref(MoreObjects.class) .staticInvoke("toStringHelper").arg(THIS) def current = invocation fields.keySet().each { f -> current = current.invoke(“add").arg(JExpr.lit(f)) .arg(THIS.ref(f)) } body._return(current.invoke("toString")) } } // continued
  • 43. @oldJavaGuy#VoxxedBerlin Value Objects (what else) • Techniques I haven’t explored yet, but might in future: • More efficient byte code generation (ByteBuddy) • Interface-based proxies • JEP 169 (http://openjdk.java.net/jeps/169)
  • 44. @oldJavaGuy#VoxxedBerlin Use Case: Library Patching • Scenario:You have to use a third party library, which has known defects • Best choice: file an issue and / or submit a Pull Request to the library owner • I’ll assume this didn’t work, you are dependent on the library, but you don’t want to fork it, since you want to stay current • Techniques that will let you patch lib in an automated way (ideally in a CI / CD pipeline)
  • 45. @oldJavaGuy#VoxxedBerlin Options • If you have access to the source code, use a source code parser and dynamically patch the sources • Otherwise, manipulate byte code (change or intercept existing code). Can be done statically at build time or dynamically at class load time • Create a static patch and write tests which make sure the patch worked (I won’t explore this option)
  • 46. @oldJavaGuy#VoxxedBerlin Baseline • As example I have written a trivial example class, with some pretty stupid, but harmless bugs: public class FicticiousExample { public Integer yUNoReuseInteger(final int value) { System.setProperty(RAN_BUGGY_CODE, TRUE); return new Integer(value); } public String yUStringConcatInLoop(Iterable<String> data, String delim) { System.setProperty(RAN_BUGGY_CODE, TRUE); String value = ""; final Iterator<String> iterator = data.iterator(); if (iterator.hasNext()) { value += iterator.next(); } while (iterator.hasNext()) { value += delim + iterator.next(); } return value; } }
  • 47. @oldJavaGuy#VoxxedBerlin Test suite • In the test, I call the methods and assert that they work correctly and that the system properties weren’t set public abstract class FicticiousExamplePatchTest { private FicticiousExample fe; @After public void checkForEvilSystemProperty() { assertThat(System.getProperty(RAN_BUGGY_CODE), nullValue()); } @Before public void initObject() { fe = new FicticiousExample(); } @Test public void assertCorrectIntegerBehavior() { assertThat(fe.yUNoReuseInteger(123), is(sameInstance(123))); assertThat(fe.yUNoReuseInteger(1234), not(sameInstance(1234))); } @Test public void assertCorrectStringBehavior() { // etc.
  • 48. @oldJavaGuy#VoxxedBerlin AspectJ • https://eclipse.org/aspectj/ • Aspect-oriented language
 (.aj format or Java with @AspectJ
 annotations) • ajc = AspectJ Compiler (after javac, instead of javac) • static compilation or load time weaving through agent • Docs are ancient / non-existent, use mailing list or read the book:
 http://bit.ly/ajInAction
  • 49. @oldJavaGuy#VoxxedBerlin Patching Aspect public aspect FicticiousExamplePatch{ public pointcut integerMethodCalled(int value) : execution(* FicticiousExample.yUNoReuseInteger(..)) && args(value); public pointcut stringMethodCalled(Iterable<String> it, String s): execution(* FicticiousExample.yUStringConcatInLoop(..)) && args(it, s); Integer around(int i) : integerMethodCalled(i){ return Integer.valueOf(i); } String around(Iterable<String> it, String s) : stringMethodCalled(it, s){ java.util.Iterator<String> iterator = it.iterator(); StringBuilder sb = new StringBuilder(); if(iterator.hasNext()){ sb.append(iterator.next()); while(iterator.hasNext()){ sb.append(s).append(iterator.next()); } } return sb.toString(); } }
  • 50. @oldJavaGuy#VoxxedBerlin AspectJ capabilities • Extending / replacing / enriching code • Inter-type declarations (~= Traits) • Policy Enforcement (define custom compiler errors) • Very flexible, can work on source or byte code • Standard usage: Cross-cutting concerns (security, logging etc.)
  • 51. @oldJavaGuy#VoxxedBerlin Source Code Manipulation in the Build Process • Extract sources to generated-sources directory • Parse and manipulate the sources (non-idempotent!?) • Compile sources and distribute your library
  • 52. @oldJavaGuy#VoxxedBerlin JavaParser • http://javaparser.github.io/javaparser/ • Parsers generated from JavaCC (Java Compiler Compiler) -> complete Java 1.8 grammar support • API not as friendly as JCodeModel (no management of types and references etc.), but the only available fully functional Java source code parser • Again, I’ll be using Groovy to do the actual work. It could be done in Java too, but the build setup would be more complex
  • 53. @oldJavaGuy#VoxxedBerlin Groovy Wrapper public class Wrapper { public exec(project, dir) { def source = new File(dir, "...Example.java") def compilationUnit = JavaParser.parse(source) applyPatch(compilationUnit) sourceFile.text = compilationUnit.toString() project.compileSourceRoots.add(baseDir); } private applyPatch(CompilationUnit unit) { unit.accept(new PatchVisitor(), null) } }
  • 54. @oldJavaGuy#VoxxedBerlin PatchVisitor class PatchVisitor extends VoidVisitorAdapter<Void> { public void visit(MethodDeclaration n, Object arg) { if (n.name == "yUStringConcatInLoop") { n.body = new BlockStmt() // delete existing body patchStringMethod(n.body, n.parameters[0], n.parameters[1]) } else if (n.name == "yUNoReuseInteger") { n.body = new BlockStmt() // delete existing body patchIntegerMethod(n.body, n.parameters[0]) } else super.visit(n, arg) }
  • 55. @oldJavaGuy#VoxxedBerlin PatchVisitor (Integer method) private patchIntegerMethod( BlockStmt b, Parameter p) { def t = new ClassOrInterfaceType("Integer"); def tExpr = new TypeExpr(); tExpr.type = t def mc = new MethodCallExpr(tExpr, "valueOf") mc.args.add(new NameExpr(p.id.name)) b.getStmts().add(new ReturnStmt(mc)) } // all this for // return Integer.valueOf(i);
  • 56. @oldJavaGuy#VoxxedBerlin PatchVisitor (String method) class PatchVisitor extends VoidVisitorAdapter<Void> { // continued private patchStringMethod(BlockStmt blockStatement, Parameter iterable, Parameter delim) { def sbType = new ClassOrInterfaceType("StringBuilder") def sbId = new VariableDeclaratorId("sb") def sbDecl = new VariableDeclarationExpr(sbType, [ new VariableDeclarator(sbId, new ObjectCreationExpr(null, sbType, [])) ]) def itType = new ClassOrInterfaceType("Iterator<String>") def itCall = new MethodCallExpr(new NameExpr(iterable.id.name), "iterator") def itId = new VariableDeclaratorId("iterator") def itDecl = new VariableDeclarationExpr(itType, [new VariableDeclarator(itId, itCall)]) def itExpr = new NameExpr(itId.name); def sbExpr = new NameExpr(sbId.name) blockStatement.stmts.addAll([ new ExpressionStmt(sbDecl), new ExpressionStmt(itDecl), new IfStmt( new MethodCallExpr(itExpr, "hasNext"), new BlockStmt([ new ExpressionStmt(new MethodCallExpr(sbExpr, "append", [new MethodCallExpr(itExpr, "next")])), new WhileStmt(new MethodCallExpr(itExpr, "hasNext"), new BlockStmt([ new ExpressionStmt(new MethodCallExpr( new MethodCallExpr(sbExpr, "append", [new NameExpr(delim.id.name)]), "append", [new MethodCallExpr(itExpr, "next")] )) ])) ]), null // <-- no else block ), new ReturnStmt(new MethodCallExpr(sbExpr, "toString")) ]) } }
  • 57. @oldJavaGuy#VoxxedBerlin PatchVisitor (String method) class PatchVisitor extends VoidVisitorAdapter<Void> { // continued private patchStringMethod(BlockStmt blockStatement, Parameter iterable, Parameter delim) { def sbType = new ClassOrInterfaceType("StringBuilder") def sbId = new VariableDeclaratorId("sb") def sbDecl = new VariableDeclarationExpr(sbType, [ new VariableDeclarator(sbId, new ObjectCreationExpr(null, sbType, [])) ]) def itType = new ClassOrInterfaceType("Iterator<String>") def itCall = new MethodCallExpr(new NameExpr(iterable.id.name), "iterator") def itId = new VariableDeclaratorId("iterator") def itDecl = new VariableDeclarationExpr(itType, [new VariableDeclarator(itId, itCall)]) def itExpr = new NameExpr(itId.name); def sbExpr = new NameExpr(sbId.name) blockStatement.stmts.addAll([ new ExpressionStmt(sbDecl), new ExpressionStmt(itDecl), new IfStmt( new MethodCallExpr(itExpr, "hasNext"), new BlockStmt([ new ExpressionStmt(new MethodCallExpr(sbExpr, "append", [new MethodCallExpr(itExpr, "next")])), new WhileStmt(new MethodCallExpr(itExpr, "hasNext"), new BlockStmt([ new ExpressionStmt(new MethodCallExpr( new MethodCallExpr(sbExpr, "append", [new NameExpr(delim.id.name)]), "append", [new MethodCallExpr(itExpr, "next")] )) ])) ]), null // <-- no else block ), new ReturnStmt(new MethodCallExpr(sbExpr, "toString")) ]) } } Source: http://www.memecenter.com/fun/1467675/my-eyes
  • 58. @oldJavaGuy#VoxxedBerlin The generated code, for comparison public String yUStringConcatInLoop( final Iterable<String> data, final String delim) { StringBuilder sb = new StringBuilder(); Iterator<String> it = data.iterator(); if (it.hasNext()) { sb.append(it.next()); while (it.hasNext()) { sb.append(delim).append(it.next()); } } return sb.toString(); }
  • 59. @oldJavaGuy#VoxxedBerlin Tradeoff • AST manipulation is hard, very verbose and error prone • But: It’s still better than trying to do it with Regex (insert obligatory Regex Cthulhu link here) • friendlier Alternative: walkmod.com (built on JavaParser, “walkmod is an open source tool to apply and share your own code conventions.”) Source: http://subgenius.wikia.com/wiki/Cthulhu
  • 60. @oldJavaGuy#VoxxedBerlin Use Case: Defect Analysis • Identify bug patterns, reject them at compile time • Not mentioning the “good old” tools: CheckStyle, PMD, FindBugs.They are best used in a separate CI build • Focus on tools that hook directly into the compile process Source: http://sequart.org/magazine/59883/cult-classics-starship-troopers/
  • 61. @oldJavaGuy#VoxxedBerlin Test Harness public abstract class AbstractDefectDetectionTest extends AbstractCompilerTest { private DefectAnalysisEngine engine; @Before public void setupEngine() { this.engine = instantiateEngine(); } @Test public void detectWellBehavedClass() { CompilationResult result = engine.compile(sourceFileFor(wellBehavedClass())); assertThat(result, isSuccess()); } @Test public void detectIllBehavedClass() { CompilationResult result = engine.compile(sourceFileFor(illBehavedClass())); assertThat(result, isFailureWithExpectedMessage(expectedErrorMessage())); } protected abstract DefectAnalysisEngine instantiateEngine(); protected abstract Class<? extends WellBehaved> wellBehavedClass(); protected abstract Class<? extends IllBehaved> illBehavedClass(); protected abstract String expectedErrorMessage(); }
  • 62. @oldJavaGuy#VoxxedBerlin ForkedRun • Helper class that calls a Java class in a separate process, copying the Classpath from the local context, abstracting the output away to a CompilationResult class, that we can run matchers against. • Fluent API with matchers for converting classpath to bootclasspath and other nice features • http://bit.ly/forkedRun
  • 63. @oldJavaGuy#VoxxedBerlin ForkedRun (sample) public final CompilationResult run() { final Set<String> classPath = new LinkedHashSet<>(); final Set<String> bootPath = new LinkedHashSet<>(); try { dispatchClassPath(classPathElements, bootPathElements); String javaExe = getExecutable(); List<String> cmds = buildCommands(classPath, bootPath, javaExe); ProcessBuilder processBuilder = new ProcessBuilder().command(cmds); if (outputCommand) System.out.println(Joiner.on(' ').join(commands)); Process proc = processBuilder.start(); List<String> errorMsg = gatherOutput(proc); int status = proc.waitFor(); return new CompilationResult(status == 0, errorMsg); } catch (InterruptedException | IOException | URISyntaxException e) { throw new IllegalStateException(e); } }
  • 64. @oldJavaGuy#VoxxedBerlin False positives • Example: Immutability (impossible to reliably detect, by any technology known to me) public final class ImmutableUser{ private final String name; private final Date birthDate; private final List<String> nickNames; public ImmutableUser(String name, List<String> nickNames, Date birthDate) { this.name = name; this.nickNames = ImmutableList.copyOf(nickNames); this.birthDate = new Date(birthDate.getTime()); } public String getName() { return name; } public List<String> getNickNames() { return ImmutableList.copyOf(nickNames); } public Date getBirthDate() { return new Date(birthDate.getTime()); } }
  • 65. @oldJavaGuy#VoxxedBerlin What can we detect? • Forbidden classes, erroneous usages • Package-level architecture restrictions
 (MicroServices or OSGI are a better solution) • Implementation inconsistent with annotations or interface (e.g. Nullness)
  • 66. @oldJavaGuy#VoxxedBerlin Google Error Prone • http://errorprone.info/ • Google 20% project • Wrapper around javac, with compatible API • Many defined bug-patterns, most specific to Google (Android, Protobuf, Gwt, Guice) • New Bug Patterns can only be added via Pull Request to Google :-( • Integrated with Maven, Gradle,Ant etc.
  • 67. @oldJavaGuy#VoxxedBerlin Obscure bug patterns • Example: Regex bug detection public class IllBehavedRegex { static List<String> splitByAlpha(String s) { return Arrays.asList( s.split("[a-z")); // bad pattern } }
  • 68. @oldJavaGuy#VoxxedBerlin Calling the engine in a test public class ErrorProneEngine implements DefectAnalysisEngine { private final Set<String> checks; public ErrorProneEngine(Set<String> checks) { this.checks = checks; } @Override public CompilationResult compile(final File sourceFile) { return new ForkedRun(ErrorProneAnalysisEngine.class) .withAdditionalClassLoaderFromClass(AbstractUser.class) .withBootClassPathMatcher("com", "google", "errorprone") .withBootClassPathMatcher("org", "checkerframework") .withArg(ErrorProneCompiler.class) .withArg("-d").tempDirAsArg() .withArg("-Xep:" + Joiner.on(',').join(this.checks)) .withArg(sourceFile.getAbsolutePath()) .run(); } }
  • 69. @oldJavaGuy#VoxxedBerlin Checker Framework • http://types.cs.washington.edu/checker-framework/ • The Checker Framework enhances Java’s type system to make it more powerful and useful.This lets software developers detect and prevent errors in their Java programs.The Checker Framework includes compiler plug-ins ("checkers") that find bugs or verify their absence. It also permits you to write your own compiler plug-ins. • Disadvantage: needs to be installed separately, hard to integrate in a build system • But: checkers can be used as plain annotation processors
  • 70. @oldJavaGuy#VoxxedBerlin Java 8 Type Annotations • As of the Java SE 8 release, annotations can also be applied to any type use […] A few examples of where types are used are class instance creation expressions (new), casts, implements clauses, and throws clauses.
 http://bit.ly/typeAnnotations • The Checker Framework embraces these new annotation usages Source: http://memegenerator.net/
  • 71. @oldJavaGuy#VoxxedBerlin Checker Framework Engine public class CheckerFrameworkAnalysisEngine implements DefectAnalysisEngine { private final Class<? extends BaseTypeChecker> checkerClass; public CheckerFrameworkAnalysisEngine(Class<? extends BaseTypeChecker> c) { this.checkerClass = c; } @Override public CompilationResult compile(final File sourceFile) { return new ForkedRun(CheckerFrameworkAnalysisEngine.class) .withAdditionalClassLoaderFromClass(AbstractUser.class) .withJavaC() // javac, not java .withArg("-d").tempDirAsArg() .withArg("-processor").withArg(checkerClass) .withArg(sourceFile.getAbsolutePath()) .run(); } }
  • 72. @oldJavaGuy#VoxxedBerlin Example: Nullness public class WellbehavedNullness { @PolyNull String nullInOut(@PolyNull String s) { if (s == null) return s; return s.toUpperCase().trim(); } @MonotonicNonNull private String initallyNull; public void assignField(@NonNull String value) { initallyNull = checkNotNull(value); } @Nonnull public String getValue() { if (initallyNull == null) initallyNull = "wasNull"; return initallyNull; } }
  • 73. @oldJavaGuy#VoxxedBerlin Example: Nullness public class WellbehavedNullness { @PolyNull String nullInOut(@PolyNull String s) { if (s == null) return s; return s.toUpperCase().trim(); } @MonotonicNonNull private String initallyNull; public void assignField(@NonNull String value) { initallyNull = checkNotNull(value); } @Nonnull public String getValue() { if (initallyNull == null) initallyNull = "wasNull"; return initallyNull; } } Source: https://imgflip.com/memegenerator
  • 74. @oldJavaGuy#VoxxedBerlin AspectJ (again) • AspectJ allows custom compiler errors, according to static checks • Architecture checks: package a may not access package b • Forbidden usage patterns: e.g. Spring MVC controller may not take OutputStream param • Forbidden classes: Enforce deprecation of legacy classes • Most of these problems can be solved differently (MicroServices etc.) Source: http://www.someecards.com/usercards/viewcard/MjAxMy1jZWRhODllNTI3YWI4Yjkz
  • 75. @oldJavaGuy#VoxxedBerlin Forbid usage of deprecated types public aspect PolicyEnforcementAspect{ pointcut badCall() : call(* Hashtable.*(..)) || call(Hashtable.new(..)) || call(* Vector.*(..)) || call(Vector.new(..)); declare error : badCall() "Hashtable and Vector are deprecated!"; }
  • 76. @oldJavaGuy#VoxxedBerlin AspectJ testing engine public class AspectJPolicyEnforcementEngine implements DefectAnalysisEngine { @Override public CompilationResult compile(File sourceFile) { final File aspectFile = findAspectFile(sourceFile); return new ForkedRun(AspectJPolicyEnforcementEngine.class) .withArg(Main.class /* ajc */).withArg("-1.8") .withArg("-target").withArg("1.8") .withArg("-d").tempDirAsArg() .withArg(sourceFile.getAbsolutePath()) .withArg(aspectFile.getAbsolutePath()) .run(); }
  • 77. @oldJavaGuy#VoxxedBerlin Overview of discussed techniques • Obviously, this is an opinion, not advice. Use at your own risk. • Author is not affiliated with any of the above libraries. Technique Have I used in real applications? Do I recommend technique? Lombok yes maybe AutoValue yes yes CGLib BeanGenerator no no JCodeModel yes yes AspectJ (patching) yes maybe JavaParser yes maybe ErrorProne no maybe Checker Framework no yes AspectJ (policy enforcement) yes yes
  • 78. @oldJavaGuy#VoxxedBerlin Where to go from here • Look at the github code, send me a PR if you have improvements • Contact me if you have questions • Suggest more techniques / use cases

Notes de l'éditeur

  1. reference to ByteBuddy
  2. chained invocations
  3. Code against original type
  4. Pointcut vs advice
  5. ANTLR
  6. delete body method not available
  7. reference: custom Maven version
  8. Classpath vs BootClassPath
  9. Annotation Processing
  10. Compatible with any annotation processor
  11. Ask me about working for Zalando