Download - I Know Kung Fu - Juggling Java Bytecode
I Know Kung Fu Writing Java Bytecode
Alexander Shopov
[ash@edge ~]$ whoami
By day: Software Engineer at CiscoBy night: OSS contributorCoordinator of Bulgarian Gnome TP
Contacts:E-mail: [email protected]:[email protected]: http://www.linkedin.com/in/alshopovWeb:Just search al_shopov
Please Learn And Share
License: CC-BY v3.0Creative Commons Attribution v3.0
Disclaimer
My opinions, knowledge and experience!Not my employer's.
Plan
What is bytecode and how to juggle it
Why do it, why not do it
How to do itClass file structure
Manipulation with ASM
How to consume your own dog foodClassloaders
Proxies
Agents
What it is
Bytecodes are the assembler of JVM. By writing new bytecode sequences or changing existing ones we can program the JVM directly outside of the domain of the Java Programming Language;
Lower level than writing Java - JVM assembler;
Much easier than writing a whole (optimizing) compiler;
Harder than using a compiler;
Similar to the Jedi Dark Side pathway to many abilities some consider to be unnatural.
Why do it?
Makes impossible things possible &
Makes hard things attainable &
Makes long tasks shorter &
Makes slow things faster &
Makes repetition go away &
Makes your head blow away!!!
Common use cases
Program analysis find bugs, unused code paths, reverse engineering, metrics, etc.
Program generation compilers, stub/skeleton compilers, fast layers without reflection.
Transformation optimize, deoptimize/obfuscate, aspect weaving, performance monitoring.
Why not do it?
Source stops being close to executable &
Executable stops being close to any source &
There can be better or easier ways of doing things &
Fewer people knowing what happens means no one can help you &
Problems will be your fault! Unit test ! &
You take the red pill and find out the rabbit hole goes deep, deep, deep EOT
Common misuse cases
I write it, you maintain it.
Do it because it is cool or others are doing it.
Over engineering / over generalization.
Solution in a search of a problem.
Preferring NIH to a popular solution.
Bytecode crash course in 1-2-3
Thread AF0F1F2F3F4Thread BF0F1F2Thread CF0F1Thread DF0F1F2F3
Bytecode crash course in 1-2-3
JVM (heap)F0
Class
Pool of constants0123456
Local variables
Stack
Method codePCClass
Bytecode crash course in 1-2-3
Bytecodes retrieve things from class bytestream, constant pool, local variables array
Perform computations only on stack
Store temporary results in local variables array
Enter frame by method call
Return from frame with a possible result (top thing on stack), throw top of stack
JVM can also throw and unroll frames
If you need more information
Check my other presentation:
Lifting the Veil Reading Java Bytecode
How to do it
Many ways with different pluses and minuses
Easiest way to write byte code use the Java compiler.javac you all know that
java compiler api since 1.6
But more of that wait for the end
Until then
Serp
BCELCGlibJCF EditorAspectJJavaAssistRooSootRetroweaverJiapiTea Trove
jclasslibjReloaderjDeccojen
gnu.bytecode
Serp
BCELCGlibJCF EditorAspectJJavaAssistRooSootRetroweaverJiapiTea Trove
jclasslibjReloaderjDeccojen
gnu.bytecodeOne of the older frameworks,
Bytecode manipulation
Not constrained by Java
Not constrained by compiler
Not constrained by source availability
Still constrained by JVM
Generate or change bytecode to the limits of JVM
Classfile Structure, 0xcafebabe
Modifiers, name, super class, interfaces
Constant pool: numeric, string and type constants
Source file name (optional)
Enclosing class reference
Annotation*
Attribute*
Inner class*Name
Field*Modifiers, name, type
Annotation*
Attribute*
Method*
Modifiers, name, return and parameter types
Annotation*
Attribute*
Compiled code
What is ASM
Extremely well designed bytecode manipulation library
Modular, small and fast pick all
Events based visitor (+SAX), additionally has tree API (DOM like)
Provides a small bytecode generation variant
Minimal just transformation, can stack transformations, no classloading
Timeline
1.5 30 Aug 2004 up to JDK 1.6 incl.
2.0 17 May 2005
3.0 01 Nov 2006
4.0 29 Oct 2011 framework for compatibility
Who uses ASM?
Languages and AOP tools: AspectJ, BeanShell, CGLIB, Clojure, Groovy, Jruby, Jython, Coroutines
Tools and frameworks: Fractal, Terracotta, Javeleon, JRebel
Persistence: OpenEJB, Oracle BerkleyDB, EclipseLink
Monitoring: WebLogic, JiP
Testing and code analysis: Cobertura, Eclipse
Most JDKs Use It!
OracleSun HotSpot JDK, OpenJDK
BEA/Appeal VM JRockit
IBM J9
Azul Zing$JAVA_HOME/jre/lib/rt.jar
com.sun.xml.internal.ws.org.objectweb.asm
com.sun.xml.internal.ws.model.WrapperBeanGenerator RuntimeModeler Web Services implementation
Read and explore
ClassReaderClassVisitor
ClassReaderClassVisitorClassVisitorClassWriterRead, transform, write
ClassReaderClassVisitorClassVisitorClassVisitorClassVisitorClassVisitorClassVisitorClassWriterI know what I am doing
ClassReaderClassVisitorClassVisitorClassVisitorClassWriterClassVisitorClassVisitorClassVisitorClassWriterClassReaderClassVisitorClassVisitorClassVisitorClassVisitorI believe I can fly, I believe I can touch the sky
ClassReaderClassVisitorClassVisitorClassVisitorClassVisitorClassWriterClassVisitorClassVisitorClassVisitorClassWriter
ClassVisitor
ClassVisitor
Basic Pattern Of Transformation
Read bytestream of a class
Generate events from that
Make changes to the events
Receive the events and serialize to bytestream
Lather, rinse, repeat
**visitvisitSourcevisitOuterClassvisitAnnotationvisitAttributevisitInnerClassvisitFiledvistiMethodvisitEndBasic diagram of class visiting
UserClassVisitor
Order is important
Bold return other visitors
* marks repeat
*visitvisitEnumvisitAnnotationBasic diagram of annotation visiting
UserAnnotationVisitor
Order is important
Bold return other visitors
* marks repeat
visitArrayvisitEnd
*visitAnnotationvisitAttributevisitEndBasic diagram of field visiting
UserFieldVisitor
Order is important
Bold return other visitors
* marks repeat
*
*visitAnnotationDefaultvisitAnnotationvisitParameterAnnotationvisitAttributevisitCodevisitFramevisitXInsnvisitLabelvisitTryCatchBlockvisitLocalVariablevisitLineNumbervisitMaxsvisitEndBasic diagram of method visiting
UserMethodVisitor
Order is important
Bold return other visitors
* marks repeat
package org.kambanaria.writebytecode.asm;
public class Zombunny { //
public Integer getVersion() {
return Integer.valueOf(1);
}
}
Our humble beginning
public class StupidClassLoader extends ClassLoader {
private Map bytes = new HashMap();
public StupidClassLoader() {
super(StupidClassLoader.class.getClassLoader());
}
public void provide(String className, byte[] classBytes) {
bytes.put(className, classBytes);
}
@Override
public Class loadClass(String name) throws ClassNotFoundException
{
byte[] classBytes = bytes.get(name);
Class loaded;
if (classBytes != null) {
loaded = defineClass(name, classBytes, 0, classBytes.length);
} else {
ClassLoader parent = getParent();
if (null == parent) {
parent = ClassLoader.getSystemClassLoader();
}
loaded = parent.loadClass(name);
}
return loaded;
}
}Our building blocks
public final class Utilities {
private Utilities() { }
public static final String CLASS_NAME =
"org.kambanaria.writebytecode.asm.Zombunny";
public static final String METHOD_NAME = "getVersion";
public static String toClassPathResourceName(String clazz) {
return clazz.replace('.', '/') + ".class";
}
public static byte[] retrieveBytesFromClassPath(String className)
throws IOException {
String path = toClassPathResourceName(className);
InputStream is =
Utilities.class.getClassLoader().getResourceAsStream(path);
InputStream classBytes = new BufferedInputStream(is);
return is2bytes(classBytes);
}
public static byte[] patch(byte[] bytes, DemoClassAdapter adapter)
{
ClassReader cr = new ClassReader(bytes);
cr.accept(adapter, ClassReader.SKIP_FRAMES);
return adapter.getCw().toByteArray();
}
public static byte[] is2bytes(InputStream is) throws IOException
{
int max = 1024 * 1024;
byte[] bytes = new byte[max]; // 1MB
int read = is.read(bytes); // Don't do that
byte[] result = new byte[read];
System.arraycopy(bytes, 0, result, 0, result.length);
return result;
}
public static Object call0ArgsMethodOn(Object o, String methodName)
//
throws ReflectiveOperationException {
Class c = o.getClass();
Method m = c.getDeclaredMethod(methodName, (Class[]) null);
m.setAccessible(true);
return m.invoke(o, (Object[]) null);
}
}Our building blocks
public class DemoClassAdapter extends ClassVisitor {
public DemoClassAdapter(ClassVisitor cv) {
super(Opcodes.ASM4, cv);
}
public ClassWriter getCw() {
return (ClassWriter) cv;
}
}Our building blocks
public class Rename extends DemoClassAdapter {
private String suffix;
public Rename(ClassVisitor cv, String suffix) {
super(cv);
this.suffix = suffix;
}
@Override
public void visit(int version, int access, String name, //
String signature, String superName, String[] interfaces) {
cv.visit(version, access, name + suffix, signature, superName,
interfaces);
}
}Rename class
package org.kambanaria.writebytecode.asm;
public class ZombunnySUFFIX { //
public Integer getVersion() {
return Integer.valueOf(1);
}
}
Result is as if
public class AddField extends DemoClassAdapter {
public AddField(ClassVisitor cv) {
super(cv);
}
@Override
public void visitEnd() {
cv.visitField(ACC_PRIVATE, "_version", //
Type.getDescriptor(Integer.class), null, null);
cv.visitEnd();
}}Add field
public class Zombunny { //
private Integer _version;
public Integer getVersion() {
return Integer.valueOf(1);
}
}Add field
public class ChangeMethod extends DemoClassAdapter {
public ChangeMethod(ClassVisitor cv) {
super(cv);
}
@Override
public MethodVisitor visitMethod(int access, String name, String
desc, //
String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, desc, signature,
exceptions);
if (Utilities.METHOD_NAME.equals(name) &&
"()Ljava/lang/Integer;".equals(desc)) {
return new DemoMethodVisitor(Opcodes.ASM4, mv);
} else {
return mv;
}
}
class DemoMethodVisitor extends MethodVisitor {
public DemoMethodVisitor(int version, MethodVisitor mv) {
super(version, mv);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD,
"org/kambanaria/writebytecode/asm/Zombunny", //
"_version", "Ljava/lang/Integer;");
mv.visitInsn(ARETURN);
mv.visitEnd();
}
}
}Change method
public class Zombunny { //
private Integer _version;
public Integer getVersion() {
return _version;
}
}And change method
public class ManipulateConstructors extends DemoClassAdapter
{
public ManipulateConstructors(ClassVisitor cv) {
super(cv);
}
@Override
public MethodVisitor visitMethod(int access, String name, String
desc, //
String signature, String[] exceptions) {
if ("".equals(name)) {
MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "", "()V", null,
null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "",
"()V");
mv.visitVarInsn(ALOAD, 0);
mv.visitTypeInsn(NEW, "java/lang/Integer");
mv.visitInsn(DUP);
mv.visitInsn(ICONST_2);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Integer", "",
"(I)V");
mv.visitFieldInsn(PUTFIELD,
"org/kambanaria/writebytecode/asm/Zombunny", //
"_version", "Ljava/lang/Integer;");
mv.visitInsn(RETURN);
mv.visitMaxs(4, 1);
mv.visitEnd();
return mv;
} else {
return cv.visitMethod(access, name, desc, signature,
exceptions);
}
}
@Override
public void visitEnd() {
MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "",
"(Ljava/lang/Integer;)V", //
null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "",
"()V");
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitFieldInsn(PUTFIELD,
"org/kambanaria/writebytecode/asm/Zombunny", //
"_version", "Ljava/lang/Integer;");
mv.visitInsn(RETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
cv.visitEnd();
}
}Fix constructors
public class Zombunny { //
private Integer _version;
public Zombunny() {
_version = new Integer(2);
}
public Zombunny(Integer version) {
_version = version;
}
public Integer getVersion() {
return _version;
}
}And fix constructors
public class AddInterface extends DemoClassAdapter {
private static final String COMPARABLE =
"java/lang/Comparable";
public AddInterface(ClassVisitor cv) {
super(cv);
}
@Override
public void visit(int version, int access, String name, String
signature,//
String superName, String[] interfaces) {
if (Arrays.asList(interfaces).contains(COMPARABLE)) {
super.visit(version, access, name, signature, superName,
interfaces);
} else {
int l = interfaces.length;
String[] newInterfaces = new String[l + 1];
System.arraycopy(interfaces, 0, newInterfaces, 0, l);
newInterfaces[l] = COMPARABLE;
super.visit(version, access, name, signature, superName,
newInterfaces);
}
}
}Add interface
public class Zombunny implements Comparable { //
private Integer _version;
public Zombunny() {
_version = new Integer(2);
}
public Zombunny(Integer version) {
_version = version;
}
public Integer getVersion() {
return _version;
}
}And add interface
public class AddMethod extends DemoClassAdapter {
public AddMethod(ClassVisitor cv) {super(cv);}
@Override
public void visitEnd() {
MethodVisitor compareToObject = cv.visitMethod(ACC_PUBLIC,
"compareTo", //
"(Ljava/lang/Object;)I", null, null);
compareToObject.visitCode();
compareToObject.visitVarInsn(ALOAD, 1);
compareToObject.visitTypeInsn(INSTANCEOF,
"org/kambanaria/writebytecode/asm/Zombunny");
Label lbl0 = new Label();
compareToObject.visitJumpInsn(IFNE, lbl0);
compareToObject.visitTypeInsn(NEW,
"java/lang/ClassCastException");
compareToObject.visitInsn(DUP);
compareToObject.visitMethodInsn(INVOKESPECIAL,
"java/lang/ClassCastException", //
"", "()V");
compareToObject.visitInsn(ATHROW);
compareToObject.visitLabel(lbl0);
compareToObject.visitVarInsn(ALOAD, 0);
compareToObject.visitVarInsn(ALOAD, 1);
compareToObject.visitTypeInsn(CHECKCAST,
"org/kambanaria/writebytecode/asm/Zombunny");
compareToObject.visitMethodInsn(INVOKEVIRTUAL,
"org/kambanaria/writebytecode/asm/Zombunny", //
"compareTo",
"(Lorg/kambanaria/writebytecode/asm/Zombunny;)I");
compareToObject.visitInsn(IRETURN);
compareToObject.visitMaxs(2, 2);
compareToObject.visitEnd();
MethodVisitor compareToZombunny = cv.visitMethod(ACC_PUBLIC,
"compareTo", //
"(Lorg/kambanaria/writebytecode/asm/Zombunny;)I", null,
null);
compareToZombunny.visitCode();
compareToZombunny.visitVarInsn(ALOAD, 0);
compareToZombunny.visitFieldInsn(GETFIELD,
"org/kambanaria/writebytecode/asm/Zombunny", //
"_version", "Ljava/lang/Integer;");
compareToZombunny.visitVarInsn(ALOAD, 1);
compareToZombunny.visitFieldInsn(GETFIELD,
"org/kambanaria/writebytecode/asm/Zombunny", //
"_version", "Ljava/lang/Integer;");
compareToZombunny.visitMethodInsn(INVOKEVIRTUAL,
"java/lang/Integer", "compareTo", //
"(Ljava/lang/Integer;)I");
compareToZombunny.visitInsn(IRETURN);
compareToZombunny.visitMaxs(2, 2);
compareToZombunny.visitEnd();
cv.visitEnd();
}
}Add interface implementation
public class Zombunny implements Comparable { //
private Integer _version;
public Zombunny() {
_version = new Integer(2);
}
public Zombunny(Integer version) {
_version = version;
}
public Integer getVersion() {
return _version;
}
public int compareTo(Object o) {
if (o instanceof Zombunny) {
return compareTo((Zombunny) o);
} else {
throw new ClassCastException();
}
}
public int compareTo(Zombunny o) {
return _version.compareTo(o._version);
}
}And add interface
package org.kambanaria.writebytecode.asm;
public class Version {
private Integer _version;
public Version(Integer version) {
_version = version;
}
public Integer getVersion() {
return _version;
}
@Override
public String toString() {
return "Version: " + _version;
}
}The class we get method from
public class Chimerize extends DemoClassAdapter {
protected ClassNode twig;
protected MethodNode nm;
private static final String TWIG_NAME =
"org.kambanaria.writebytecode.asm.Version";
public Chimerize(ClassVisitor cv) throws IOException {
super(cv);
ClassReader rdr = new ClassReader(TWIG_NAME);
twig = new ClassNode();
rdr.accept(twig, 0);
for (Object o : twig.methods) {
MethodNode method = (MethodNode) o;
if (method.name.equals("toString")) {
// Guess what is missing?
nm = method;
}
}
}
@Override
public void visitEnd() {
MethodVisitor chimeric =cv.visitMethod(nm.access, nm.name,
nm.desc,
nm.signature, null);
nm.instructions.resetLabels();
Remapper remapper = new Remapper() {
@Override
public String map(String name) {
return name.replace("Version", "Zombunny");
}
};
nm.accept(new RemappingMethodAdapter(nm.access, nm.desc, chimeric,
remapper));
cv.visitEnd();
}
}Chimerization
public class Zombunny implements Comparable { //
private Integer _version;
public Zombunny() {
_version = new Integer(2);
}
public Zombunny(Integer version) {
_version = version;
}
public Integer getVersion() {
return _version;
}
public int compareTo(Object o) {
if (o instanceof Zombunny) {
return compareTo((Zombunny) o);
} else {
throw new ClassCastException();
}
}
public int compareTo(Zombunny o) {
return _version.compareTo(o._version);
}
@Override
public String toString(){
return "Version: " + _version;
}
}Finally
public class ChimerizeTest {
Comparable sut;
@Before
public void setUp() throws IOException,
ReflectiveOperationException {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES +
//
ClassWriter.COMPUTE_MAXS);
ClassVisitor cv = new AddField(new ManipulateConstructors( //
new AddMethod(new AddInterface(new Chimerize(cw)))));
ClassReader rdr = new ClassReader(Utilities.CLASS_NAME);
rdr.accept(cv, 0);
byte[] newClassBytes = cw.toByteArray();
StupidClassLoader ldr = new StupidClassLoader();
ldr.provide(Utilities.CLASS_NAME, newClassBytes);
Class newClass = ldr.loadClass(Utilities.CLASS_NAME);
Constructor constructor =
newClass.getDeclaredConstructor(Integer.class);
sut = (Comparable) constructor.newInstance(new Integer(42));
}
}Peek at test construction chain
Utilities
Type
Generate the visitors with: Asmfierjava -classpath
asm.jar:asm-util.jar:OUR_CP \
org.objectweb.asm.util.ASMifier CLASS
TraceClassVisitor
CheckClassAdapter
Additional APIs
org.objectweb.asm.commons commonly needed adaptors
org.objectweb.asm.xml bridge to SAX 2.0, manipulate via XSLT, XQuery
org.objectweb.asm.tree deserialize to tree
Classloaders
Dynamically load software components for Java platform
Lazy loaded on demand, as late as possible
Type-safe linkage must not violate type safety, no runtime checks
User-defined extensibility normal, user controlled objects
Multiple communicating namespaces types determined by class name and classloader
Classloader Chain
Bootstrap CLprimordial, nativejre/lib/*.jarExtension CLjre/lib/ext/*.jar-Djava.ext.dirsSystem CL$CLASSPATH-Djava.class.path
Applicationclassloader
Programmerclassloader
Classloader Hierarchy
Bootstrap CLprimordial, nativejre/lib/*.jarExtension CLjre/lib/ext/*.jar-Djava.ext.dirsSystem CL$CLASSPATH-Djava.class.path
Applicationclassloader
ApplicationclassloaderApplicationclassloader
ProgrammerclassloaderProgrammerclassloaderProgrammerclassloader
Enterprise Classloader Hierarchy
Bootstrap CLprimordial, nativejre/lib/*.jarExtension CLjre/lib/ext/*.jar-Djava.ext.dirsSystem CL$CLASSPATH-Djava.class.path
Applicationclassloader
ApplicationclassloaderApplicationclassloaderProgrammerclassloader
Programmerclassloader
Programmerclassloader
Programmerclassloader
Programmerclassloader
Programmerclassloader
Class Loader API
public abstract class ClassLoader {
protected ClassLoader(ClassLoader parent);
protected ClassLoader();
protected Class loadClass(String name, boolean resolve);
protected Class findClass(String name);
protected final Class defineClass(String name, byte[] b,
int off, int len);
protected final void resolveClass(Class c);
public URL getResource(String name);
public Enumeration getResources(String name);
public final MyClassLoader getParent();
public void setDefaultAssertionStatus(boolean);
public void setPackageAssertionStatus(String packageName,boolean
enabled);
public void setClassAssertionStatus(String className, boolean
enabled);
public void clearAssertionStatus();
}
When are class loaded?
Statically:Instance creation: new Integer(42);
Reference to static field or method: System.out;
Dynamically:Class.forName("java.lang.HashMap");
Class.forName("java.lang.HashMap",
boolean initialize,
ClassLoader loader);
Delegating Classloaders Standard
Plugin or Web Classloaders
OSGI & others
OMG!!!
Type Compatibility
A classloader can see and use (with exact type) instances of classes loaded by the ancestral chain and the classloader itself
Instances of classes loaded by sibling or descendant classloaders are invisible, they are just java.lang.Object
Object a; "SomeClass".equals(a.getClass().getName()); a instanceof Object; (SomeClass)a -> ClassCastExceptionUse reflection
Proxies
Dynamic proxy acts as a pass through/router to the real objectruntime implementations of interfaces
public, final and not abstract
extend java.lang.reflect.Proxy
Proxys behaviour is determined by an implementation of java.lang.reflect.InvocationHandler
Square peg in a non square hole
Some objectSome interface
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[]
args)
throws Throwable;
}Fit it with an InvocationHandler
Object
Proxy
public class MyInvocationHandler implements InvocationHandler
{
private Object delegate;
public MyInvocationHandler(Object... params) {
delegate = makeDelegate();
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
String methodName = method.getName();
Class[] types = method.getParameterTypes();
Method m = reachMethod(methodName, types);
Object result;
try {
result = m.invoke(delegate, args);
} catch (InvocationTargetException e) {
throw e.getCause();
}
return result;
}
private static Method reachMethod(String name, Class... parTypes)
{
Method m = null;
/* Logic to determine method */
return m;
}
private static Object makeDelegate(Object... params) {
/* Produce real object instead */
return new Object();
}
}
Java agents
Package java.lang.instrument - allow Java programming language agents to instrument programs running on the JVM.
java ... -javaagent:jarpath[=options]
Manifest attributes
byte[] -> byte[]
Almost Real Example
Action takes place here
A planet, an icy atmosphere
This country
Outsources to this country
They deliver cheaper than the world, faster than the speed of thought
The entrepreneurial spirit of that country
Makes them sell the software to this country
Where it fails spectacularly while clearly working in the other two ones
Investigation takes place
Finds the culprit
SimpleDateFormat frmt =
new SimpleDateFormat("E MM/dd/yyyy");
publicSimpleDateFormat(String pattern)Constructs a SimpleDateFormat using thegiven pattern and the default date formatsymbols for the default locale.Note: This constructor may not support allLocales. For full coverage, use the factorymethods in the DateFormat class.Parameters:pattern - the pattern describing the date andtime format Throws: NullPointerException -if the given pattern is null IllegalArgumentException - if the given patternis invalid
SimpleDateFormat
Could this have been avoided?
LANG=de_DE.UTF-8 java -jar SimpleDateFormat.jar Freitag 11/16/2012LANG=en_US.UTF-8 java -jar SimpleDateFormat.jar Friday 11/16/2012LANG=hi_IN.UTF-8 java -jar SimpleDateFormat.jar //LANG=bn_IN.UTF-8 java -jar SimpleDateFormat.jar Friday 11/16/2012LANG=bg_BG.UTF-8 java -jar SimpleDateFormat.jar 11/16/2012Hindi(300106, 4th)ShukravrBengali(200106, 7th)Shukrobar
India proposes:
UseEnglishLocale
USA retransmits:
UseEnglish Locale
Germany says:
No!
USA try in German:
VerwendenSieEnglischeLocale
Germany says:
Nein!
USA try manners:
Bitte?
Germany says:
NEIN!
USA checks what they said with Google translate:
mv.visitMethodInsn(INVOKESPECIAL, "java/text/SimpleDateFormat",
//
"", "(Ljava/lang/String;)V");mv.visitFieldInsn(GETSTATIC,
"java/util/Locale", //
"US", "Ljava/util/Locale;");
mv.visitMethodInsn(INVOKESPECIAL, "java/text/SimpleDateFormat",
//
"", "(Ljava/lang/String;Ljava/util/Locale;)V");
Compiler API
import java.util.Random;
public class I {
public boolean singOutOfTune() {
return new Random().nextBoolean();
}
}
import java.util.Random;
public class I {
public boolean singOutOfTune() {
return new Random().nextBoolean();
}
}public class You {
public static String DID = "StandUp&WalkOutOnMe";
public static String DIDNOT = "LendMeAnEar";
public String wouldDo(boolean iF) {
return iF ? "StandUp&WalkOutOnMe" : "LendMeAnEar";
}
}
import java.util.Random;
public class I {
public boolean singOutOfTune() {
return new Random().nextBoolean();
}
}public class You {
public static String DID = "StandUp&WalkOutOnMe";
public static String DIDNOT = "LendMeAnEar";
public String wouldDo(boolean iF) {
return iF ? "StandUp&WalkOutOnMe" : "LendMeAnEar";
}
}public class ExistingClassesTest {
@Test
public void test1000times() {
int times = 1000;
do {
boolean didI;
I i = new I();
You you = new You();
String what = you.wouldDo(didI = i.singOutOfTune());
assertEquals(what, didI ? You.DID : You.DIDNOT);
} while (--times > 0);
}
}
import java.util.Random;
public class I {
public boolean singOutOfTune() {
return new Random().nextBoolean();
}
}public class You {
public static String DID = "StandUp&WalkOutOnMe";
public static String DIDNOT = "LendMeAnEar";
public String wouldDo(boolean iF) {
return iF ? "StandUp&WalkOutOnMe" : "LendMeAnEar";
}
}public class ExistingClassesTest {
@Test
public void test1000times() {
int times = 1000;
do {
boolean didI;
I i = new I();
You you = new You();
String what = you.wouldDo(didI = i.singOutOfTune());
assertEquals(what, didI ? You.DID : You.DIDNOT);
} while (--times > 0);
// What would you do if I sang out of tune ? The Beatles
}
}
public class SourceStrings {
private SourceStrings() }
public final static String I = " "
+ "import java.util.Random; "
+ "public class I { "
+ " public boolean singOutOfTune() { "
+ " return new Random().nextBoolean(); "
+ " } "
+ "} ";
public final static String YOU = " "
+ "public class You { "
+ " public static String DID = \"StandUp&WalkOutOnMe\"; "
+ " public static String DIDNOT = \"LendMeAnEar\"; "
+ " public String wouldDo(boolean iF) { "
+ " return iF ? \"StandUp&WalkOutOnMe\" :
\"LendMeAnEar\";"
+ " } "
+ "} ";
}class StringSourceCodeObject extends SimpleJavaFileObject {
final String _source;
public StringSourceCodeObject(String fqName, String source) {
super(URI.create("string:///" + fqName.replaceAll("\\.", "/")
//
+ Kind.SOURCE.extension), Kind.SOURCE);
_source = source;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors)
{
return _source;
}
public class CompilerAPI {
public static void main(String args[]) throws Exception {
/* Creating dynamic java source code file object */
JavaFileObject iObject = new StringSourceCodeObject("I",
SourceStrings.I);
JavaFileObject youObject = new StringSourceCodeObject("You",
SourceStrings.YOU);
JavaFileObject jfObjects[] = new JavaFileObject[]{iObject,
youObject};
/* Units to compile */
Iterable units = Arrays.asList(jfObjects);
/* Instantiating the java compiler */
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
/* Get compiler file manager to show what to read. */
// (DEFAULT LISTENER, Locale.getDefault(), Charset.defaultCharset()
)
JavaFileManager manager = compiler.getStandardFileManager(null,
null, null);
/* Compilation options - here: place in target directory */
String[] compileOptions = new String[]{"-d",
"target/classes"};
Iterable options = Arrays.asList(compileOptions);
/* Diagnostic placeholder */
DiagnosticCollector sink = new DiagnosticCollector();
/* 1st null: where to write (default), 2nd null: no annotations
processed */
CompilationTask task = compiler.getTask(null, manager, sink,
options, null, units);
/* Go, go, go */
boolean status = task.call();
if (!status) {
for (Diagnostic