SlideShare a Scribd company logo
Annotation Processing
& Code Gen
@kojilin
2015/05/30@TWJUG
Outline
•My requirements
•Annotation Processing
•Code gen
My Requirements
Kaif Android Client
•Using REST client to fetch article and
debates
•Need stable connection
Kaif Android Client
•Using REST client to fetch article and
debates
•Need stable connection
•If not, stale data is better than no data
•Local cache Database
•Http cache response
Rest Http Client
•Retrofit from Square
•A type-safe REST client for Android
and Java
•http://square.github.io/retrofit/
public interface GitHubService {
@GET("/users/{user}/repos")
List<Repo> listRepos(@Path("user") String user);
}
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("https://api.github.com")
.build();
GitHubService service =

restAdapter.create(GitHubService.class);
List<Repo> repos = service.listRepos("octocat");
Header
@Headers("Cache-Control: max-stale=86400")
@GET("/users/{user}/repos")
List<Repo> listRepos$$RetryStale(@Path("user") 

String user);
How to retry ?
try {
// normal access
} catch (Exception e) {
// force local cache if network error
}
How to retry ?
try {
service.listRepos("koji");
} catch (Exception e) {
if (e instanceof RetrofitError
&& isNetworkError(e)) {
service.listRepos$$RetryStale("koji");
}
}
If we do it manually
•Every service access need previous try
& catch code.
try {
service.listRepos("koji");
} catch (Exception e) {
if (e instanceof RetrofitError
&& isNetworkError(e)) {
service.listRepos$$RetryStale("koji");
}

}
If we do it manually
•Every service access need previous try
& catch code.
•Using Proxy pattern
service.listRepos("koji");
service = 

Proxy.newProxyInstance(serviceClass.getClassLoader(),

new Class[] { serviceClass },

new RetryStaleHandler(restAdapter.create(

Class.forName(serviceClass.getName()

+ "$$RetryStale"))));
@Override 

public Object invoke(Object proxy, Method 

method, Object[] args) throws Throwable {

...

if(method isGet and canRetry) {

try & catch block
}

...

}
RetryStaleHandler.java
If we do it manually
•Every service access need previous try
& catch code.
•Using Proxy pattern
•Almost every GET method need 

$$RetryStale version.
public interface GitHubService {
@GET("/users/{user}/repos")

List<Repo> listRepos(@Path("user") String user);



@GET("/users/{user}/repos")

@Headers("Cache-Control: max-stale=86400")

List<Repo> listRepos$$RetryStale(@Path("user") String user);



@GET("/repos/{owner}/{repo}/contributors")

List<Contributor> contributors(@Path("owner") String owner, 

@Path("repo") String repo);



@GET("/repos/{owner}/{repo}/contributors")

@Headers("Cache-Control: max-stale=86400")

List<Contributor> contributors$$RetryStale(@Path("owner") String 

owner, @Path("repo") String repo);
}
If we do it manually
•Every service access need previous try
& catch code.
•Using Proxy pattern.
•Almost every GET method need 

$$RetryStale.
•Generating boilerplate code during
build.
How ?
•Scan all interface has @GET method.
•Generate new interface has both non-
cache and cache method.
When scan?
•Compile time.
•Annotation Processing.
•Run time.
•Reflection.
•Slow and feedback at runtime
•Do we know the concrete class we want
to generate when we are writing ?
Generate code
•bytecode
•.java
Generate bytecode
•Can do more than what java source can.
•Bytecode is difficult to read and write.
•ASM
•JarJar
•Dexmaker for android
Generate .java
•Readable and maintainable.
•JavaPoet from Square
•Successor of JavaWriter
•https://github.com/square/javapoet
public interface GitHubService {
@GET("/users/{user}/repos")

List<Repo> listRepos(@Path("user") String user);



@GET("/repos/{owner}/{repo}/contributors")

List<Contributor> contributors(@Path("owner") String owner, 

@Path("repo") String repo);

}
public interface GitHubService$$RetryStale {
@GET("/users/{user}/repos")

List<Repo> listRepos(@Path("user") String user);



@GET("/users/{user}/repos")

@Headers("Cache-Control: max-stale=86400")

List<Repo> listRepos$$RetryStale(@Path("user") String user);



@GET("/repos/{owner}/{repo}/contributors")

List<Contributor> contributors(@Path("owner") String owner, 

@Path("repo") String repo);



@GET("/repos/{owner}/{repo}/contributors")

@Headers("Cache-Control: max-stale=86400")

List<Contributor> contributors$$RetryStale(@Path("owner") String 

owner, @Path("repo") String repo);
}
AutoValue
@AutoValue

public abstract class Foo {
public abstract String name();
public abstract int age();
}


final class AutoValue_Foo extends Foo {

private final String name;

private final int age;



AutoValue_Foo(

String name,

int age) {

if (name == null) {

throw new 

NullPointerException("Null name");

}

this.name = name;

this.age = age;

}



@Override

public String name() {

return name;

}



@Override

public int age() {

return age;

}


@Override

public String toString() {

return "Foo{"

+ "name=" + name

+ ", age=" + age

+ "}";

}



@Override

public boolean equals(Object o) {

if (o == this) {

return true;

}

if (o instanceof Foo) {

Foo that = (Foo) o;

return 

(this.name.equals(that.name()))

&& (this.age == that.age());

}

return false;

}



@Override

public int hashCode() {

int h = 1;

h *= 1000003;

h ^= name.hashCode();

h *= 1000003;

h ^= age;

return h;

}

}
Butter Knife


@InjectView(R.id.content)

public TextView content;

@InjectView(R.id.last_update_time)

public TextView lastUpdateTime;

@InjectView(R.id.vote_score)

public TextView voteScore;

@InjectView(R.id.debater_name)

public TextView debaterName;

@InjectView(R.id.debate_actions)

public DebateActions debateActions;



public DebateViewHolder(View itemView) {

super(itemView);

ButterKnife.inject(this, itemView);

content.setOnTouchListener(new 

ClickableSpanTouchListener());

}


@Override public void inject(final Finder finder, final T target, Object
source) {

View view;

view = finder.findRequiredView(source, 2131296343, "field 'content'");

target.content = finder.castView(view, 2131296343, "field 'content'");

view = 

finder.findRequiredView(source, 2131296375, "field 'lastUpdateTime'");

target.lastUpdateTime = 

finder.castView(view, 2131296375, "field 'lastUpdateTime'");

view = finder.findRequiredView(source, 2131296374, "field 'voteScore'");

target.voteScore = finder.castView(view, 2131296374, "field
'voteScore'");

view = finder.findRequiredView(source, 2131296373, "field
'debaterName'");

target.debaterName =
finder.castView(view, 2131296373, "field 'debaterName'");

view = finder.findRequiredView(source, 2131296370, "field
'debateActions'");

target.debateActions = 

finder.castView(view, 2131296370, "field 'debateActions'");

}



@Override public void reset(T target) {

target.content = null;

target.lastUpdateTime = null;

target.voteScore = null;

target.debaterName = null;

target.debateActions = null;

}
JPA metamodel
@Entity

public class Pet {
@Id

Long id;
String name;
}
@StaticMetaModel(Pet.class)

public class Pet_ {
public static volatile SingularAttribute<Person, 

Long> id;
public static volatile SingularAttribute<Person, 

String> name;
}
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Order> cq = cb.createQuery(Pet.class);
cq.where(cb.equal(itemNode.get(Pet_.id), 5))

.distinct(true);
Annotation Processing
•JSR 269
•Pluggable Annotation Processing API
•Run automatically when javac runs
•Using ServiceLoader to find
•META-INF/services/
javax.annotation.processing.Processor
•Running inside JVM
@SupportedSourceVersion(...)

@SupportedAnnotationTypes( 

"retrofit.http.GET")

public class MyProcessor extends 

AbstractProcessor {
@Override public synchronized void 

init(ProcessingEnvironment env){ }
@Override public boolean process(

Set<? extends TypeElement> annotations, 

RoundEnvironment env) { }

}
@SupportedSourceVersion(...)

@SupportedAnnotationTypes( 

"retrofit.http.GET")

public class MyProcessor extends 

AbstractProcessor {
@Override public synchronized void 

init(ProcessingEnvironment env){ }
@Override public boolean process(

Set<? extends TypeElement> annotations, 

RoundEnvironment env) { }

}
@SupportedSourceVersion(...)

@SupportedAnnotationTypes(...)

public class MyProcessor extends 

AbstractProcessor {
@Override public synchronized void 

init(ProcessingEnvironment env){ }
@Override public boolean process(

Set<? extends TypeElement> annotations, 

RoundEnvironment env) { }

}
ProcessingEnvironment
•Types
•ElementUtils
•Filer
•Messager
Elements
package foo.bar;



public class Foo { // Type Element
String bar; // Variable Element
public void hoge() { // Executable Element

System.out.println("Hello!");

}

}
Types
package foo.bar;



public class Foo {
String bar;
public void hoge() { 

System.out.println("Hello!");

}

}
@SupportedSourceVersion(...)

@SupportedAnnotationTypes(...)

public class MyProcessor extends 

AbstractProcessor {
@Override public synchronized void 

init(ProcessingEnvironment env){ }
@Override public boolean process(

Set<? extends TypeElement> annotations, 

RoundEnvironment env) { }

}
@Override public boolean process(

Set<? extends TypeElement> annotations, 

RoundEnvironment env) {
Set<? extends Element> elements = 

env.getElementsAnnotatedWith(GET.cl ass);
// generating code for elements
}

JavaPoet
package foo.bar;



public final class Foo {

public static void main(String[] args) {

System.out.println("Hello, JavaPoet!");

}

}
MethodSpec main = MethodSpec.methodBuilder("main")

.addModifiers(Modifier.PUBLIC, Modifier.STATIC)

.returns(Void.class)

.addParameter(String[].class, "args")

.addStatement("$T.out.println($S)", System.class, 

"Hello, JavaPoet!")

.build();



TypeSpec helloWorld = 

TypeSpec.classBuilder("HelloWorld")

.addModifiers(Modifier.PUBLIC, Modifier.FINAL)

.addMethod(main)

.build();



JavaFile javaFile = 

JavaFile.builder("foo.bar", helloWorld).build();



javaFile.writeTo(System.out);
Element to JavaPoet
•AnnotationSpec#get
•AnnotationMirror to AnnotationSpec
•MethodSpect#overriding
•Override MethodElement
public interface Foo$$RetryStale {
@GET("/foo/{user}")

List<Foo> foo(@Path("user") String user);



@GET("/foo/{user}")

@Headers("Cache-Control: max-stale=86400")

List<Foo> foo$$RetryStale(@Path("user") String user);



}
TypeSpec.Builder typeSpecBuilder = TypeSpec.interfaceBuilder(

classElement.getSimpleName() + "$RetryStale")

.addModifiers(Modifier.PUBLIC);
Interface
public interface Foo$$RetryStale
Method


MethodSpec.Builder builder = 

MethodSpec.methodBuilder("foo$RetryStale");

builder.addModifiers(Modifier.ABSTRACT)

.addModifiers(Modifier.PUBLIC);



methodElement.getParameters().stream().map(variableElement -> {



ParameterSpec.Builder parameterBuilder = 

ParameterSpec.builder(TypeName.get(variableElement.asType()),

variableElement.getSimpleName().toString());



variableElement.getAnnotationMirrors().stream()
.map(AnnotationSpecUtil::generate)

.forEach(parameterBuilder::addAnnotation);



return parameterBuilder.build();



}).forEach(builder::addParameter);
List<Foo> foo(@Path("user") String user)
Method
methodElement.getAnnotationMirrors()

.stream()

.map(AnnotationSpecUtil::generate)

.forEach(builder::addAnnotation);

@GET("/foo/{user}")
Convenient Libraries
•Google Auto-Service
•Truth
•Making your tests and their error
messages more readable and
discoverable
•Google Compile testing
AutoService
@AutoService(Processor.class)

public class MyProcessor extends AbstracProcessor {
...
}
•Generating META-INF/services/
javax.annotation.processing.Processor
Truth and Compile testing
ASSERT.about(javaSources())

.that(ImmutableList.of(inputFile))

.processedWith(new MyProcessor())

.compilesWithoutError()

.and()

.generatesSources(outputFile);
References
•ANNOTATION PROCESSING 101

http://hannesdorfmann.com/annotation-processing/
annotationprocessing101/
•Annotation Processing Boilerplate
Destruction

https://speakerdeck.com/jakewharton/annotation-
processing-boilerplate-destruction-square-waterloo-2014

More Related Content

What's hot (18)

PDF
Stubる - Mockingjayを使ったHTTPクライアントのテスト -
Kenji Tanaka
 
PDF
Little Big Ruby
LittleBIGRuby
 
PDF
Http4s, Doobie and Circe: The Functional Web Stack
GaryCoady
 
PDF
OneRing @ OSCamp 2010
Qiangning Hong
 
KEY
Python在豆瓣的应用
Qiangning Hong
 
PDF
Writing native bindings to node.js in C++
nsm.nikhil
 
PDF
RestMQ - HTTP/Redis based Message Queue
Gleicon Moraes
 
PDF
Rhebok, High Performance Rack Handler / Rubykaigi 2015
Masahiro Nagano
 
PPTX
Down to Stack Traces, up from Heap Dumps
Andrei Pangin
 
PDF
Shrimp: A Rather Practical Example Of Application Development With RESTinio a...
Yauheni Akhotnikau
 
PDF
Using Ruby on Rails with legacy Oracle databases
Raimonds Simanovskis
 
PPT
Hector v2: The Second Version of the Popular High-Level Java Client for Apach...
zznate
 
PDF
Designing a Functional GraphQL Library
Pierre Ricadat
 
PDF
JavaScript Unit Testing with Jasmine
Raimonds Simanovskis
 
ODP
Intravert Server side processing for Cassandra
Edward Capriolo
 
PDF
Scala for scripting
day
 
PDF
Scala4sling
day
 
PDF
Jasmine - why JS tests don't smell fishy
Igor Napierala
 
Stubる - Mockingjayを使ったHTTPクライアントのテスト -
Kenji Tanaka
 
Little Big Ruby
LittleBIGRuby
 
Http4s, Doobie and Circe: The Functional Web Stack
GaryCoady
 
OneRing @ OSCamp 2010
Qiangning Hong
 
Python在豆瓣的应用
Qiangning Hong
 
Writing native bindings to node.js in C++
nsm.nikhil
 
RestMQ - HTTP/Redis based Message Queue
Gleicon Moraes
 
Rhebok, High Performance Rack Handler / Rubykaigi 2015
Masahiro Nagano
 
Down to Stack Traces, up from Heap Dumps
Andrei Pangin
 
Shrimp: A Rather Practical Example Of Application Development With RESTinio a...
Yauheni Akhotnikau
 
Using Ruby on Rails with legacy Oracle databases
Raimonds Simanovskis
 
Hector v2: The Second Version of the Popular High-Level Java Client for Apach...
zznate
 
Designing a Functional GraphQL Library
Pierre Ricadat
 
JavaScript Unit Testing with Jasmine
Raimonds Simanovskis
 
Intravert Server side processing for Cassandra
Edward Capriolo
 
Scala for scripting
day
 
Scala4sling
day
 
Jasmine - why JS tests don't smell fishy
Igor Napierala
 

Similar to Annotation processing and code gen (20)

PDF
API first with Swagger and Scala by Slava Schmidt
JavaDayUA
 
PDF
Marvel of Annotation Preprocessing in Java by Alexey Buzdin
Java User Group Latvia
 
PDF
QA Lab: тестирование ПО. Станислав Шмидт: "Self-testing REST APIs with API Fi...
GeeksLab Odessa
 
PPT
Slides
Videoguy
 
PDF
Annotation Processing
Jintin Lin
 
PDF
Java Annotation Processing: A Beginner Walkthrough
Mahfuz Islam Bhuiyan
 
PPTX
Cutting through the fog of microservices: lightsabers optional
Graham Charters
 
PPTX
Making Java more dynamic: runtime code generation for the JVM
Rafael Winterhalter
 
PPTX
Android dev toolbox
Shem Magnezi
 
PDF
Beautiful code
Peter Hilton
 
PDF
How to code to code less
Anton Novikau
 
PPTX
Increase your performance and code quality
Dusko Vesin
 
PPTX
Increase your quality and performance
Dusko Vesin
 
PDF
Write code that writes code! A beginner's guide to Annotation Processing - Ja...
DroidConTLV
 
PDF
Write code that writes code!
Jason Feinstein
 
PDF
JEEConf 2017 - Having fun with Javassist
Anton Arhipov
 
KEY
Curator intro
Jordan Zimmerman
 
PDF
Ow2 Utilities - The Swiss Army Knife Of Ow2 Projects
Guillaume Sauthier
 
PDF
JavaFest. Cedrick Lunven. Build APIS with SpringBoot - REST, GRPC, GRAPHQL wh...
FestGroup
 
PDF
Spring MVC Annotations
Jordan Silva
 
API first with Swagger and Scala by Slava Schmidt
JavaDayUA
 
Marvel of Annotation Preprocessing in Java by Alexey Buzdin
Java User Group Latvia
 
QA Lab: тестирование ПО. Станислав Шмидт: "Self-testing REST APIs with API Fi...
GeeksLab Odessa
 
Slides
Videoguy
 
Annotation Processing
Jintin Lin
 
Java Annotation Processing: A Beginner Walkthrough
Mahfuz Islam Bhuiyan
 
Cutting through the fog of microservices: lightsabers optional
Graham Charters
 
Making Java more dynamic: runtime code generation for the JVM
Rafael Winterhalter
 
Android dev toolbox
Shem Magnezi
 
Beautiful code
Peter Hilton
 
How to code to code less
Anton Novikau
 
Increase your performance and code quality
Dusko Vesin
 
Increase your quality and performance
Dusko Vesin
 
Write code that writes code! A beginner's guide to Annotation Processing - Ja...
DroidConTLV
 
Write code that writes code!
Jason Feinstein
 
JEEConf 2017 - Having fun with Javassist
Anton Arhipov
 
Curator intro
Jordan Zimmerman
 
Ow2 Utilities - The Swiss Army Knife Of Ow2 Projects
Guillaume Sauthier
 
JavaFest. Cedrick Lunven. Build APIS with SpringBoot - REST, GRPC, GRAPHQL wh...
FestGroup
 
Spring MVC Annotations
Jordan Silva
 
Ad

More from koji lin (18)

PDF
サーバーサイドでの非同期処理で色々やったよ
koji lin
 
PPTX
G1GC
koji lin
 
PDF
Using armeria to write your RPC
koji lin
 
PDF
使用 Java 上的 future/promise API
koji lin
 
PDF
Jcconf
koji lin
 
PDF
Use Lambdas in Android
koji lin
 
PDF
docker intro
koji lin
 
PDF
Java8 time
koji lin
 
PDF
Java8 stream
koji lin
 
PDF
Java8 lambda
koji lin
 
PDF
Idea13
koji lin
 
ODP
CompletableFuture
koji lin
 
ODP
Raspberry Pi with Java
koji lin
 
PDF
Services you can use to monitor and analyze mobile app
koji lin
 
PDF
Programming with Threads in Java
koji lin
 
PDF
JQuery
koji lin
 
PPT
山頂洞人日記 - 回歸到最純樸的開發
koji lin
 
ODP
Android Location-based應用開發分享
koji lin
 
サーバーサイドでの非同期処理で色々やったよ
koji lin
 
G1GC
koji lin
 
Using armeria to write your RPC
koji lin
 
使用 Java 上的 future/promise API
koji lin
 
Jcconf
koji lin
 
Use Lambdas in Android
koji lin
 
docker intro
koji lin
 
Java8 time
koji lin
 
Java8 stream
koji lin
 
Java8 lambda
koji lin
 
Idea13
koji lin
 
CompletableFuture
koji lin
 
Raspberry Pi with Java
koji lin
 
Services you can use to monitor and analyze mobile app
koji lin
 
Programming with Threads in Java
koji lin
 
JQuery
koji lin
 
山頂洞人日記 - 回歸到最純樸的開發
koji lin
 
Android Location-based應用開發分享
koji lin
 
Ad

Recently uploaded (20)

PPTX
WooCommerce Workshop: Bring Your Laptop
Laura Hartwig
 
PDF
Go Concurrency Real-World Patterns, Pitfalls, and Playground Battles.pdf
Emily Achieng
 
PPTX
From Sci-Fi to Reality: Exploring AI Evolution
Svetlana Meissner
 
DOCX
Python coding for beginners !! Start now!#
Rajni Bhardwaj Grover
 
PDF
Jak MŚP w Europie Środkowo-Wschodniej odnajdują się w świecie AI
dominikamizerska1
 
PDF
POV_ Why Enterprises Need to Find Value in ZERO.pdf
darshakparmar
 
PDF
Transforming Utility Networks: Large-scale Data Migrations with FME
Safe Software
 
PPTX
AI Penetration Testing Essentials: A Cybersecurity Guide for 2025
defencerabbit Team
 
PDF
"AI Transformation: Directions and Challenges", Pavlo Shaternik
Fwdays
 
PDF
Transcript: New from BookNet Canada for 2025: BNC BiblioShare - Tech Forum 2025
BookNet Canada
 
PPTX
Building Search Using OpenSearch: Limitations and Workarounds
Sease
 
PDF
Newgen 2022-Forrester Newgen TEI_13 05 2022-The-Total-Economic-Impact-Newgen-...
darshakparmar
 
PDF
Using FME to Develop Self-Service CAD Applications for a Major UK Police Force
Safe Software
 
PPTX
COMPARISON OF RASTER ANALYSIS TOOLS OF QGIS AND ARCGIS
Sharanya Sarkar
 
PPTX
The Project Compass - GDG on Campus MSIT
dscmsitkol
 
PDF
The Rise of AI and IoT in Mobile App Tech.pdf
IMG Global Infotech
 
PDF
Building Real-Time Digital Twins with IBM Maximo & ArcGIS Indoors
Safe Software
 
PPTX
Q2 FY26 Tableau User Group Leader Quarterly Call
lward7
 
PDF
LOOPS in C Programming Language - Technology
RishabhDwivedi43
 
PDF
Agentic AI lifecycle for Enterprise Hyper-Automation
Debmalya Biswas
 
WooCommerce Workshop: Bring Your Laptop
Laura Hartwig
 
Go Concurrency Real-World Patterns, Pitfalls, and Playground Battles.pdf
Emily Achieng
 
From Sci-Fi to Reality: Exploring AI Evolution
Svetlana Meissner
 
Python coding for beginners !! Start now!#
Rajni Bhardwaj Grover
 
Jak MŚP w Europie Środkowo-Wschodniej odnajdują się w świecie AI
dominikamizerska1
 
POV_ Why Enterprises Need to Find Value in ZERO.pdf
darshakparmar
 
Transforming Utility Networks: Large-scale Data Migrations with FME
Safe Software
 
AI Penetration Testing Essentials: A Cybersecurity Guide for 2025
defencerabbit Team
 
"AI Transformation: Directions and Challenges", Pavlo Shaternik
Fwdays
 
Transcript: New from BookNet Canada for 2025: BNC BiblioShare - Tech Forum 2025
BookNet Canada
 
Building Search Using OpenSearch: Limitations and Workarounds
Sease
 
Newgen 2022-Forrester Newgen TEI_13 05 2022-The-Total-Economic-Impact-Newgen-...
darshakparmar
 
Using FME to Develop Self-Service CAD Applications for a Major UK Police Force
Safe Software
 
COMPARISON OF RASTER ANALYSIS TOOLS OF QGIS AND ARCGIS
Sharanya Sarkar
 
The Project Compass - GDG on Campus MSIT
dscmsitkol
 
The Rise of AI and IoT in Mobile App Tech.pdf
IMG Global Infotech
 
Building Real-Time Digital Twins with IBM Maximo & ArcGIS Indoors
Safe Software
 
Q2 FY26 Tableau User Group Leader Quarterly Call
lward7
 
LOOPS in C Programming Language - Technology
RishabhDwivedi43
 
Agentic AI lifecycle for Enterprise Hyper-Automation
Debmalya Biswas
 

Annotation processing and code gen

  • 1. Annotation Processing & Code Gen @kojilin 2015/05/30@TWJUG
  • 4. Kaif Android Client •Using REST client to fetch article and debates •Need stable connection
  • 5. Kaif Android Client •Using REST client to fetch article and debates •Need stable connection •If not, stale data is better than no data •Local cache Database •Http cache response
  • 6. Rest Http Client •Retrofit from Square •A type-safe REST client for Android and Java •http://square.github.io/retrofit/
  • 7. public interface GitHubService { @GET("/users/{user}/repos") List<Repo> listRepos(@Path("user") String user); }
  • 8. RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://api.github.com") .build(); GitHubService service =
 restAdapter.create(GitHubService.class);
  • 9. List<Repo> repos = service.listRepos("octocat");
  • 11. How to retry ? try { // normal access } catch (Exception e) { // force local cache if network error }
  • 12. How to retry ? try { service.listRepos("koji"); } catch (Exception e) { if (e instanceof RetrofitError && isNetworkError(e)) { service.listRepos$$RetryStale("koji"); } }
  • 13. If we do it manually •Every service access need previous try & catch code. try { service.listRepos("koji"); } catch (Exception e) { if (e instanceof RetrofitError && isNetworkError(e)) { service.listRepos$$RetryStale("koji"); }
 }
  • 14. If we do it manually •Every service access need previous try & catch code. •Using Proxy pattern
  • 15. service.listRepos("koji"); service = 
 Proxy.newProxyInstance(serviceClass.getClassLoader(),
 new Class[] { serviceClass },
 new RetryStaleHandler(restAdapter.create(
 Class.forName(serviceClass.getName()
 + "$$RetryStale")))); @Override 
 public Object invoke(Object proxy, Method 
 method, Object[] args) throws Throwable {
 ...
 if(method isGet and canRetry) {
 try & catch block }
 ...
 } RetryStaleHandler.java
  • 16. If we do it manually •Every service access need previous try & catch code. •Using Proxy pattern •Almost every GET method need 
 $$RetryStale version.
  • 17. public interface GitHubService { @GET("/users/{user}/repos")
 List<Repo> listRepos(@Path("user") String user);
 
 @GET("/users/{user}/repos")
 @Headers("Cache-Control: max-stale=86400")
 List<Repo> listRepos$$RetryStale(@Path("user") String user);
 
 @GET("/repos/{owner}/{repo}/contributors")
 List<Contributor> contributors(@Path("owner") String owner, 
 @Path("repo") String repo);
 
 @GET("/repos/{owner}/{repo}/contributors")
 @Headers("Cache-Control: max-stale=86400")
 List<Contributor> contributors$$RetryStale(@Path("owner") String 
 owner, @Path("repo") String repo); }
  • 18. If we do it manually •Every service access need previous try & catch code. •Using Proxy pattern. •Almost every GET method need 
 $$RetryStale. •Generating boilerplate code during build.
  • 19. How ? •Scan all interface has @GET method. •Generate new interface has both non- cache and cache method.
  • 20. When scan? •Compile time. •Annotation Processing. •Run time. •Reflection. •Slow and feedback at runtime •Do we know the concrete class we want to generate when we are writing ?
  • 22. Generate bytecode •Can do more than what java source can. •Bytecode is difficult to read and write. •ASM •JarJar •Dexmaker for android
  • 23. Generate .java •Readable and maintainable. •JavaPoet from Square •Successor of JavaWriter •https://github.com/square/javapoet
  • 24. public interface GitHubService { @GET("/users/{user}/repos")
 List<Repo> listRepos(@Path("user") String user);
 
 @GET("/repos/{owner}/{repo}/contributors")
 List<Contributor> contributors(@Path("owner") String owner, 
 @Path("repo") String repo);
 }
  • 25. public interface GitHubService$$RetryStale { @GET("/users/{user}/repos")
 List<Repo> listRepos(@Path("user") String user);
 
 @GET("/users/{user}/repos")
 @Headers("Cache-Control: max-stale=86400")
 List<Repo> listRepos$$RetryStale(@Path("user") String user);
 
 @GET("/repos/{owner}/{repo}/contributors")
 List<Contributor> contributors(@Path("owner") String owner, 
 @Path("repo") String repo);
 
 @GET("/repos/{owner}/{repo}/contributors")
 @Headers("Cache-Control: max-stale=86400")
 List<Contributor> contributors$$RetryStale(@Path("owner") String 
 owner, @Path("repo") String repo); }
  • 26. AutoValue @AutoValue
 public abstract class Foo { public abstract String name(); public abstract int age(); }
  • 27. 
 final class AutoValue_Foo extends Foo {
 private final String name;
 private final int age;
 
 AutoValue_Foo(
 String name,
 int age) {
 if (name == null) {
 throw new 
 NullPointerException("Null name");
 }
 this.name = name;
 this.age = age;
 }
 
 @Override
 public String name() {
 return name;
 }
 
 @Override
 public int age() {
 return age;
 } 
 @Override
 public String toString() {
 return "Foo{"
 + "name=" + name
 + ", age=" + age
 + "}";
 }
 
 @Override
 public boolean equals(Object o) {
 if (o == this) {
 return true;
 }
 if (o instanceof Foo) {
 Foo that = (Foo) o;
 return 
 (this.name.equals(that.name()))
 && (this.age == that.age());
 }
 return false;
 }
 
 @Override
 public int hashCode() {
 int h = 1;
 h *= 1000003;
 h ^= name.hashCode();
 h *= 1000003;
 h ^= age;
 return h;
 }
 }
  • 28. Butter Knife 
 @InjectView(R.id.content)
 public TextView content;
 @InjectView(R.id.last_update_time)
 public TextView lastUpdateTime;
 @InjectView(R.id.vote_score)
 public TextView voteScore;
 @InjectView(R.id.debater_name)
 public TextView debaterName;
 @InjectView(R.id.debate_actions)
 public DebateActions debateActions;
 
 public DebateViewHolder(View itemView) {
 super(itemView);
 ButterKnife.inject(this, itemView);
 content.setOnTouchListener(new 
 ClickableSpanTouchListener());
 }
  • 29. 
 @Override public void inject(final Finder finder, final T target, Object source) {
 View view;
 view = finder.findRequiredView(source, 2131296343, "field 'content'");
 target.content = finder.castView(view, 2131296343, "field 'content'");
 view = 
 finder.findRequiredView(source, 2131296375, "field 'lastUpdateTime'");
 target.lastUpdateTime = 
 finder.castView(view, 2131296375, "field 'lastUpdateTime'");
 view = finder.findRequiredView(source, 2131296374, "field 'voteScore'");
 target.voteScore = finder.castView(view, 2131296374, "field 'voteScore'");
 view = finder.findRequiredView(source, 2131296373, "field 'debaterName'");
 target.debaterName = finder.castView(view, 2131296373, "field 'debaterName'");
 view = finder.findRequiredView(source, 2131296370, "field 'debateActions'");
 target.debateActions = 
 finder.castView(view, 2131296370, "field 'debateActions'");
 }
 
 @Override public void reset(T target) {
 target.content = null;
 target.lastUpdateTime = null;
 target.voteScore = null;
 target.debaterName = null;
 target.debateActions = null;
 }
  • 30. JPA metamodel @Entity
 public class Pet { @Id
 Long id; String name; }
  • 31. @StaticMetaModel(Pet.class)
 public class Pet_ { public static volatile SingularAttribute<Person, 
 Long> id; public static volatile SingularAttribute<Person, 
 String> name; } CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery<Order> cq = cb.createQuery(Pet.class); cq.where(cb.equal(itemNode.get(Pet_.id), 5))
 .distinct(true);
  • 32. Annotation Processing •JSR 269 •Pluggable Annotation Processing API •Run automatically when javac runs •Using ServiceLoader to find •META-INF/services/ javax.annotation.processing.Processor •Running inside JVM
  • 33. @SupportedSourceVersion(...)
 @SupportedAnnotationTypes( 
 "retrofit.http.GET")
 public class MyProcessor extends 
 AbstractProcessor { @Override public synchronized void 
 init(ProcessingEnvironment env){ } @Override public boolean process(
 Set<? extends TypeElement> annotations, 
 RoundEnvironment env) { }
 }
  • 34. @SupportedSourceVersion(...)
 @SupportedAnnotationTypes( 
 "retrofit.http.GET")
 public class MyProcessor extends 
 AbstractProcessor { @Override public synchronized void 
 init(ProcessingEnvironment env){ } @Override public boolean process(
 Set<? extends TypeElement> annotations, 
 RoundEnvironment env) { }
 }
  • 35. @SupportedSourceVersion(...)
 @SupportedAnnotationTypes(...)
 public class MyProcessor extends 
 AbstractProcessor { @Override public synchronized void 
 init(ProcessingEnvironment env){ } @Override public boolean process(
 Set<? extends TypeElement> annotations, 
 RoundEnvironment env) { }
 }
  • 37. Elements package foo.bar;
 
 public class Foo { // Type Element String bar; // Variable Element public void hoge() { // Executable Element
 System.out.println("Hello!");
 }
 }
  • 38. Types package foo.bar;
 
 public class Foo { String bar; public void hoge() { 
 System.out.println("Hello!");
 }
 }
  • 39. @SupportedSourceVersion(...)
 @SupportedAnnotationTypes(...)
 public class MyProcessor extends 
 AbstractProcessor { @Override public synchronized void 
 init(ProcessingEnvironment env){ } @Override public boolean process(
 Set<? extends TypeElement> annotations, 
 RoundEnvironment env) { }
 }
  • 40. @Override public boolean process(
 Set<? extends TypeElement> annotations, 
 RoundEnvironment env) { Set<? extends Element> elements = 
 env.getElementsAnnotatedWith(GET.cl ass); // generating code for elements }

  • 41. JavaPoet package foo.bar;
 
 public final class Foo {
 public static void main(String[] args) {
 System.out.println("Hello, JavaPoet!");
 }
 }
  • 42. MethodSpec main = MethodSpec.methodBuilder("main")
 .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
 .returns(Void.class)
 .addParameter(String[].class, "args")
 .addStatement("$T.out.println($S)", System.class, 
 "Hello, JavaPoet!")
 .build();
 
 TypeSpec helloWorld = 
 TypeSpec.classBuilder("HelloWorld")
 .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
 .addMethod(main)
 .build();
 
 JavaFile javaFile = 
 JavaFile.builder("foo.bar", helloWorld).build();
 
 javaFile.writeTo(System.out);
  • 43. Element to JavaPoet •AnnotationSpec#get •AnnotationMirror to AnnotationSpec •MethodSpect#overriding •Override MethodElement
  • 44. public interface Foo$$RetryStale { @GET("/foo/{user}")
 List<Foo> foo(@Path("user") String user);
 
 @GET("/foo/{user}")
 @Headers("Cache-Control: max-stale=86400")
 List<Foo> foo$$RetryStale(@Path("user") String user);
 
 }
  • 45. TypeSpec.Builder typeSpecBuilder = TypeSpec.interfaceBuilder(
 classElement.getSimpleName() + "$RetryStale")
 .addModifiers(Modifier.PUBLIC); Interface public interface Foo$$RetryStale
  • 46. Method 
 MethodSpec.Builder builder = 
 MethodSpec.methodBuilder("foo$RetryStale");
 builder.addModifiers(Modifier.ABSTRACT)
 .addModifiers(Modifier.PUBLIC);
 
 methodElement.getParameters().stream().map(variableElement -> {
 
 ParameterSpec.Builder parameterBuilder = 
 ParameterSpec.builder(TypeName.get(variableElement.asType()),
 variableElement.getSimpleName().toString());
 
 variableElement.getAnnotationMirrors().stream() .map(AnnotationSpecUtil::generate)
 .forEach(parameterBuilder::addAnnotation);
 
 return parameterBuilder.build();
 
 }).forEach(builder::addParameter); List<Foo> foo(@Path("user") String user)
  • 48. Convenient Libraries •Google Auto-Service •Truth •Making your tests and their error messages more readable and discoverable •Google Compile testing
  • 49. AutoService @AutoService(Processor.class)
 public class MyProcessor extends AbstracProcessor { ... } •Generating META-INF/services/ javax.annotation.processing.Processor
  • 50. Truth and Compile testing ASSERT.about(javaSources())
 .that(ImmutableList.of(inputFile))
 .processedWith(new MyProcessor())
 .compilesWithoutError()
 .and()
 .generatesSources(outputFile);
  • 51. References •ANNOTATION PROCESSING 101
 http://hannesdorfmann.com/annotation-processing/ annotationprocessing101/ •Annotation Processing Boilerplate Destruction
 https://speakerdeck.com/jakewharton/annotation- processing-boilerplate-destruction-square-waterloo-2014