Date post: | 27-Aug-2014 |
Category: |
Software |
Upload: | antoine-sabot-durand |
View: | 719 times |
Download: | 0 times |
Let’s Invoke Dynamite in Java EE with
InvokeDynamic
Antoine Sabot-Durand Senior Software Engineer
Red Hat @antoine_sd / #IndyEE
http://www.revistashqiptare.com/wp-content/uploads/2013/10/prishje-me-C4.jpg
Antoine Sabot-Durand
• Senior Software Engineer @Red Hat
!
• Java & OSS :
• CDI co-spec lead
• CDI community development
• Tech Lead on Agorava
!
• @antoine_sd
2
My support team
Agenda
• Back in time
• Proxies - the sweet poison
• The Invoke Dynamic antidote
• @Inject Demo
• To be done
• Q&A
4
Disclaimer
5
Disclaimer
5
I’M NOT AN INVOKEDYNAMIC, BYTECODE OR ASM EXPERT, ONLY
A USER. FOR ADVANCED QUESTION, ASK
THOSE GUYS
Disclaimer
5
I’M NOT AN INVOKEDYNAMIC, BYTECODE OR ASM EXPERT, ONLY
A USER. FOR ADVANCED QUESTION, ASK
THOSE GUYS
Back in time
https://www.flickr.com/photos/revolweb/3985108034
public void ship (String productId, String orderId, int quantity) { try { makeConnection(); con.setAutoCommit(false); updateOrderItem(productId, orderId); updateInventory(productId, quantity); con.commit(); } catch (Exception ex) { try { con.rollback(); throw new EJBException("Transaction failed: " + ex.getMessage()); } catch (SQLException sqx) { throw new EJBException("Rollback failed: " + sqx.getMessage()); } } finally { releaseConnection(); }}
Long, long time ago…
7
public void ship (String productId, String orderId, int quantity) { try { makeConnection(); con.setAutoCommit(false); updateOrderItem(productId, orderId); updateInventory(productId, quantity); con.commit(); } catch (Exception ex) { try { con.rollback(); throw new EJBException("Transaction failed: " + ex.getMessage()); } catch (SQLException sqx) { throw new EJBException("Rollback failed: " + sqx.getMessage()); } } finally { releaseConnection(); }}
Long, long time ago…
7
ALL TRANSVERSE CODE HAD TO BE
DUPLICATED EVERYWHERE
Then AOP was invented
• All cross-cutting concerns was extracted in one place
• Initiated at Xerox PARC in 1996
• AspectJ is released in 2001.
• Spring is released in 2004.
• Java EE adopted it in 2006 with Java EE 5
• In Java you can do AOP with code weaving (Open JPA) or theoretically with reflexion
• But the most popular tools to do AOP are… 8
@Transactional public void ship (String productId, String orderId, int quantity) { updateOrderItem(productId, orderId); updateInventory(productId, quantity); }
Proxies
https://www.flickr.com/photos/mhall209/3411159477
Proxies
https://www.flickr.com/photos/hape_gera/3281625420/sizes/o/in/photostream/
What’s a proxy
• At the beginning it’s a design pattern in OOP :
!
!
!
• It can be implemented in static code with an interface and a « client » class wrapping another class doing the actual work
• In Java EE implementations the proxy class is dynamically created at runtime
11
« proxy provides a surrogate or a placeholder for another object to control access to it. »
Sincerely yours, The Gang Of Four
private Class<T> createProxyClass(String proxyClassName) throws Exception { ArraySet<Class<?>> specialInterfaces = new ArraySet<Class<?>>(); addAdditionalInterfaces(specialInterfaces); ClassFile proxyClassType = null; if (getBeanType().isInterface()) { proxyClassType = new ClassFile(proxyClassName, Object.class.getName()); proxyClassType.addInterface(getBeanType().getName()); } else { proxyClassType = new ClassFile(proxyClassName, getBeanType().getName()); } for (Class<?> clazz : additionalInterfaces) { proxyClassType.addInterface(clazz.getName()); } List<DeferredBytecode> initialValueBytecode = new ArrayList<DeferredBytecode>(); ClassMethod staticConstructor = proxyClassType.addMethod(AccessFlag.PUBLIC, "<clinit>", "V"); addFields(proxyClassType, initialValueBytecode); addConstructors(proxyClassType, initialValueBytecode); addMethods(proxyClassType, staticConstructor); staticConstructor.getCodeAttribute().returnInstruction(); Class<T> proxyClass = cast(ClassFileUtils.toClass(proxyClassType, classLoader, domain)); BeanLogger.LOG.createdProxyClass(proxyClass, Arrays.toString(proxyClass.getInterfaces())); return proxyClass;}
Example from Weld
12
What are the issues with proxies
• Need a engine for object creation : The container proliferation
• Make a lot of boilerplate code in frameworks
• Long and weird stack traces
• Two objects when one could enough
• JIT compiler cannot optimize this runtime generated code
• Costly in CPU and memory
• And strange impact on specification…
13
Proxies tautologyhttp://www.rcgroups.com/forums/attachment.php?attachmentid=4004213
Meet Indy
http://commons.wikimedia.org/wiki/File:Bullwhip_and_IJ_hat.jpg
InvokeDynamic the good parts
• Is a JDK built-in solution to do linking at runtime
• Introduced in Java 7 with JSR 292
• Designed to implement dynamic language on JVM (JRuby, Groovy, Golo, Nashorn)
• Better than standard reflection
• Type Safe and not « interpreted »
• Better than standard bytecode manipulation
• Nearly everything is in JDK package java.lang.invoke
• Better than proxies
• Resolve all proxies issues 16
InvokeDynamic the WTF part
17
http://upload.wikimedia.org/wikipedia/commons/1/14/Rubber_Duck_(8374802487).jpg
InvokeDynamic the WTF part
17
http://upload.wikimedia.org/wikipedia/commons/1/14/Rubber_Duck_(8374802487).jpg
InvokeDynamic the WTF part
There’s no java instruction to
produce InvokeDynamic Bytecode
instruction.
!
So we have to do byte code
manipulation to add it
17
http://upload.wikimedia.org/wikipedia/commons/1/14/Rubber_Duck_(8374802487).jpg
Indy : the usual suspects
• The InvokeDynamic instruction
• It contains a reference to the bootstrap method
• The Boostrap method which does the dynamic linking
• Called once for a given indy instruction
• Return a Callsite containing the actual code
• The CallSite Object
• Contains the handle to the method to call
• Can be be mutable (we won’t show that here)
• The MethodeHandle
• Handle to the final method18
http://cache.reelz.com/assets/content/repFrame/65555/indiana.jpg
Invoke dynamic call
Bootstrap method
Callsite
MethodHandle
Demo
Proof by stacktrace
21
Code and Readme are here : https://github.com/antoinesd/weld-invokedynamic
@ApplicationScopedpublic class SecondBean { @Inject FirstBean bean; public void doSservice() { System.out.println("Service in SecondBean calling firstBean"); bean.doSomeWork(); } @PostConstruct public void init() { System.out.println("Contructing Second Bean"); }}
SecondBean Source
22
public class org/jboss/weld/test/SecondBean {!! @Ljavax/enterprise/context/ApplicationScoped;()!! Lorg/jboss/weld/test/FirstBean; bean! @Ljavax/inject/Inject;()!! public doSservice()V! L0! LINENUMBER 18 L0! GETSTATIC java/lang/System.out : Ljava/io/PrintStream;! LDC "Service in SecondBean calling firstBean"! INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V! L1! LINENUMBER 19 L1! ALOAD 0! GETFIELD org/jboss/weld/test/SecondBean.bean : Lorg/jboss/weld/test/FirstBean;! INVOKEVIRTUAL org/jboss/weld/test/FirstBean.doSomeWork ()V! L2! LINENUMBER 21 L2! RETURN!}
SecondBean standard bytecode
23
public class org/jboss/weld/test/SecondBean {!! @Ljavax/enterprise/context/ApplicationScoped;()!! Lorg/jboss/weld/test/FirstBean; bean! @Ljavax/inject/Inject;()!! public doSservice()V! L0! LINENUMBER 18 L0! GETSTATIC java/lang/System.out : Ljava/io/PrintStream;! LDC "Service in SecondBean calling firstBean"! INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V! L1! LINENUMBER 19 L1! ALOAD 0! INVOKEDYNAMIC bean(Lorg/jboss/weld/test/SecondBean;)Lorg/jboss/weld/test/FirstBean; [! // handle kind 0x6 : INVOKESTATIC! org/jboss/weld/invokedynamic/Bootstraper.bootstrapGetBean(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;)Ljava/lang/invoke/CallSite;! // arguments:! "bean"! ]! INVOKEDYNAMIC doSomeWork(Lorg/jboss/weld/test/FirstBean;)V [! // handle kind 0x6 : INVOKESTATIC! org/jboss/weld/invokedynamic/Bootstraper.bootstrapCallBeanMethod(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;! // arguments:! // handle kind 0x5 : INVOKEVIRTUAL! org/jboss/weld/test/FirstBean.doSomeWork()V, ! "bean"! ]! L2! LINENUMBER 21 L2! RETURN!}
SecondBean Indy bytecode
24
To be done…
• Implement @Inject on constructor and setter
• Manage the use case of field containing bean without Injection Point
• Add Indy to Instance<> methods
• Implement interceptors
• Implement decorators
• Remove all proxies from Weld
25
Q&A