Dynamic code in Java (a.k.a. "eval in Java")
I really don't like eval-like structures. They are evil. It is a headache to test, makes code ugly, hard to optimize, etc. I simply don't like them.
But I finally found a use to them. It's very common to use scripting to create a flexible system. I have a ticket filter that uses Spring and EL to create filtering rules in a "building blocks"-way. It is cool, easy to maintain, but it is not optimized: hundreds of instances in a Composite Pattern (with some expressions) must be navigated to find a simple boolean answer: keep or discard?
Java 6 has a not-so-secret feature that allows invoking the javac using Java code: you pass the files to the framework, it builds compiled classes. You can find how in the JavaCompiler's API. It mentions an example of a JavaSourceFromString class that you can implement and give the compiler java code stored in String. Very useful!
But, if you try and run a test (like this one), you will receive a nice class file. But that's not what I want. I want it loaded and ready-to-go. How to interface with this new class when it is loaded? Reflection? Maybe, but I prefer another way: interfaces. I already have this simple one:
package pkg; public interface Filter { boolean filter(VO vo); }
It is the base of my filter infrastructure. If I change JavaBeat test code to something that "implements pkg.Filter", my whole problem is solved, with minimal changes on the rest of the code.
Nice! We can interface with the class, but it is not loaded! I could read the class file and pass it to the ClassLoader, but I don't want to create a file. This class does the trick:
class JavaObjectToByteArray extends SimpleJavaFileObject { private ByteArrayOutputStream output = new ByteArrayOutputStream(); public JavaObjectToByteArray(String className, Kind kind) throws URISyntaxException { super(new URI(className), kind); } @Override public OutputStream openOutputStream() throws IOException { return output; } }
You can pass this object by overriding ForwardingJavaFileManager.getJavaFileForOutput:
fileManager = new ForwardingJavaFileManager(fileManager) { @Override public JavaFileObject getJavaFileForOutput(Location location, String clazz, Kind kind, FileObject sibling) throws IOException { try { JavaObjectToByteArray jfo = new JavaObjectToByteArray(clazz, kind); cl = new ByteArrayClassLoader(className, jfo.output); return jfo; } catch (Exception ex) { throw new IOException(ex); } } };
ByteArrayClassLoader is:
class ByteArrayClassLoader extends ClassLoader { private String clazz; private ByteArrayOutputStream baos; public ByteArrayClassLoader(String clazz, ByteArrayOutputStream baos) { this.baos = baos; this.className = className; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { byte[] data = baos.toByteArray(); return defineClass(name, data, 0, data.length); } public Class<?> loadClass() throws ClassNotFoundException { return loadClass(className); } }
The "loadClass" method is a convenience method, since this class loader only loads one class. With one line of code, I can use my brand-new-dynamic-class:
Class<?> clz = cl.loadClass(); Filter f = (Filter) clz.newInstance(); System.out.println(f.filter(vo));
BTW, there's only one small change in JavaBeat's code. I need a "public" class, and this means the java file must be named "MyClass.java" (not "MyClass"). You only need to change JavaObjectFromString constructor from:
super(new URI(className), Kind.SOURCE);
to:
super(new URI(className + ".java"), Kind.SOURCE);
Easy, huh? It is not like "eval" in JavaScript, but it is better, since this class will be JIT'ed.
BufferedImage and ByteBuffers
Direct ByteBuffer are really cool, because they can allocate and wrap native memory outside the JVM heap. This is very useful with JNI or with large files. I'm playing with OpenGL and PCX files (this is evil, I know). Since I want to keep a small footprint, I want to keep only a pointer to PCX raw data (run-lenght encoded), and decompress it into a RGBA ByteBuffer as needed. This, in theory, allows me to share the same code with OpenGL and BufferedImage - since I'm bound only to a pointer of RGBA data. The former is easy: TextureData did the dirt job. The latter... Well... Not that easy. If you try something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | final IntBuffer buf = myPCXLoader.getImage(); DataBuffer dataBuffer = new DataBuffer( DataBuffer.TYPE_INT, buf.limit()) { @Override public int getElem(int bank, int i) { return buf.get(i); } @Override public void setElem(int bank, int i, int val) { } }; SampleModel sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_INT, w, h, new int[] { 0xFF000000, 0xFF0000, 0xFF00, 0xFF }); WritableRaster raster = Raster.createWritableRaster( sm, dataBuffer, null); BufferedImage img = new BufferedImage( new DirectColorModel(32, 0xFF000000, 0xFF0000, 0xFF00, 0xFF), raster, false, null); |
Line 15 leads me to an ugly exception:
java.awt.image.RasterFormatException: IntegerComponentRasters must haveinteger DataBuffers at sun.awt.image.IntegerComponentRaster.(IntegerComponentRaster.java:155) at sun.awt.image.IntegerInterleavedRaster. (IntegerInterleavedRaster.java:111) at sun.awt.image.IntegerInterleavedRaster. (IntegerInterleavedRaster.java:78) at java.awt.image.Raster.createWritableRaster(Raster.java:994)
Asking Google, I found that ByteComponentRaster checks if DataBuffer is a instanceof ByteDataBuffer (instead of checking its type). Disgusting... Looks like Sun hired some kind of Baby Junior Programmer taking his first cup of Java to make that part of java.awt...
I also found this 3-years-old-and-not-solved-yet bug, with an workaround:
WritableRaster raster = new WritableRaster(sm, dataBuffer, new java.awt.Point()) {};
Works like a charm!
Sun is really disappointing me. There are a lot of small, simple-to-solve, really old bugs and seems like nobody cares about them. I think this is why they are opening the source of their major products: let the community clean the mess...
IOException when loading a sound/midi from a JAR/ZIP archive
I have a MIDI file in a JAR archive. If I try to play it with the Sequencer class, I got an exception: "java.io.IOException: mark/reset not supported". I've found this related bug. I don't like workarounds (all bugs must die), but I developed this one:
public static InputStream loadStream(InputStream in) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] buf = new byte[1024]; int c; while ((c = in.read(buf)) != -1) { bos.write(buf, 0, c); } return new ByteArrayInputStream(bos.toByteArray()); }
With this method, I encapsulate the "unresetable" stream into a "resetable" ByteArrayInputStream.
Java2D Optimization (a.k.a "Java is NOT slow")
Playing around with Java2D, I created a little application that paints a lot of PNG images on screen (about 200). I use ImageIO to load them, but the first prototype was VERY slow (3fps maximum).
After reading about fullscreen exclusive mode API and some tips on graphics performance, I've created a BufferStrategy using the same snippet from its API. I got no improvement, but the code became a lot cleaner.
Then, I remembered my ancient days as a C programmer, where I always need to convert the image color format to the screen color format. Since I'm programming in Java, I created this snippet:
private static BufferedImage createCompatibleImage(BufferedImage img, boolean translucent) { GraphicsEnvironment e = GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsDevice d = e.getDefaultScreenDevice(); GraphicsConfiguration c = d.getDefaultConfiguration(); int t = translucent ? Transparency.TRANSLUCENT : Transparency.BITMASK; BufferedImage ret = c.createCompatibleImage(img.getWidth(), img.getHeight(), t); Graphics2D g = ret.createGraphics(); g.drawImage(img, 0, 0, null); g.dispose(); return ret; }
Maybe there's a better way to copy the image, but the important part is to create an image with GraphicsConfiguration.
After this little trick, I got an impressive speed boost: from the 2fps-slow-motion-turtle to ludicrous 170fps (yes, one hundred seventy). I feel like saying: "It's over nine thousand!"
Configuring proxy in Java applications
If you have a Java application that needs to connect to Internet, and you are behind a proxy, you have two options:
- Add this to the application command line parameters:
-Dhttp.proxyHost=[host.of.proxy] -Dhttp.proxyPort=[port.of.proxy]
- Or edit the net.properties file in the lib folder of your java home. It is self-explanatory, since you have to edit one or two lines and there's about 70 lines of comments on that file.
The bad news is if your proxy requires authentication. This means you will have to do something like:
Authenticator.setDefault(new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication("username", new char[] { 49, 50, 51, 52, 53, 54 } ); } });
You can, of course, create a more complex Authenticator subclass, that will open a Popup asking for the proxy user and password.
Tooltip on JTable cells (take 2)
In a previous post, I explain how to add tooltips to JTable cells. Armand Bendananff told, in a comment, that a better way is to override the table's getToolTipText method as in:
public String getToolTipText(MouseEvent event) { Point p = event.getPoint(); // Locate the renderer under the event location int colIndex = columnAtPoint(p); int rowIndex = rowAtPoint(p); String tooltip = "some value based on the row/col"; return tooltip; }
Long lines in Java Tooltips (or multiline tooltips)
Do you want to show a tooltip with more than one line? Are your tooltips too large to fit in one line? The solution is easier than you think. Tooltips, in Java, accept HTML code:
jcomponent.setToolTipText("HTML tooltip");
It isn't a Firefox, but is very powerful. And, when I said HTML, I also think about CSS. So, you can set your tooltips using this evil cheat:
<html><body> <strong><div style="width: 300px; text-justification: justify;"></strong> Blah Blah Blah (repeat "blah" 100 times) </div> </html></body>
Have you seen automatic line breaks and the justification? Oh, yeah!
BTW, do you remember old days, when you couldn't believe WordPad didn't have justified text?
Copying file in Java – NIO version
In old days, copying a file in Java wasn't easy: you had to create a array buffer to read from source and write to destination. Now, with NIO from JDK, it's a matter of creating the streams and let the API do the dirty job.
FileChannel src = new FileInputStream(srcFile).getChannel(); FileChannel dst = new FileOutputStream(dstFile).getChannel(); try { dst.transferFrom(src, 0, src.size()); } finally { src.close(); dst.close(); }
Tooltip on JTable cells
This is a easy one (but it is not obvious): how to define tooltips in JTable's cells. Just create a class like this one:
public class ToolTipTable extends JTable { @Override public Component prepareRenderer(TableCellRenderer renderer, int row, int column) { Component c = super.prepareRenderer(renderer, row, column); if (c instanceof JComponent) { JComponent jc = (JComponent) c; jc.setToolTipText((String) getValueAt(row, column)); } return c; } }
This example will set cells' tooltip to its own content. Very useful if you have large contents on small cells.
Blackmagic with NetBeans and OpenOffice
This one is one of the weirdest (and useful) things I ever seen in 16 years of my life as a developer (I'm 26, BTW)... OpenOffice is a very powerful Office Automation solution... NetBeans Plaform is a very powerful application platform... Can you imagine what you get when you mix both together? Yup, this is possible. With NB and OOo installed, you just need to create a NB Module with following JARs from OOo:
- juh.jar
- jurt.jar
- officebean.jar
- ridl.jar
- unoil.jar
I created another module with a single TopComponent, and add this snippet:
private OOoBean oo; @Override public void componentOpened() { add(oo = new OOoBean()); oo.loadFromURL("private:factory/swriter", null); oo.aquireSystemWindow(); }
But, this will NOT work... If you try to run it, an exception will be thrown, because OOo libs (.DLL/.SO) will not be found. They only exists in OOo install folder, and are automagically found by OOo classes - only if they aren't moved from the "classes" folder in OOo installation.
The workaround is a couple of classes in OOo SDK and The Ultimate Blackmagic(TM) - patent pending:
- Grab odk / source / com / sun / star / lib / loader from OOo's CVS and add it to integration module.
- Add unowinreg.dll to Windows Path (if you are on Linux, skip this step).
- We need to add "officebean" to classpath. Here lies OOoBean, and is optional if you are only using UNO interfaces. Other JARs are correctly listed by UnoInfo. Alter Loader.java and add this after line 205 (after "UnoInfo" call):
vec.add(new File(fClassesDir, "officebean.jar").toURL());
- Now, the ugliest part of the evil potion: create a Facade. Using OOoBean directly will lead to some ugly ClassLoader conflicts. I've no time to fix this, but the following works:
public static Container buildOOoBean() { return (Container) Loader.getCustomLoader(). loadClass("com.sun.star.comp.beans.OOoBean"). newInstance(); } public static void showOOoBean(Container oo) { Class c = oo.getClass(). getClassLoader(). loadClass("com.sun.star.beans.PropertyValue"); Class ca = Array.newInstance(c, 0).getClass(); Method m = oo.getClass(). getMethod("loadFromURL", String.class, ca); m.invoke(oo, "private:factory/swriter", null); oo.getClass(). getMethod("aquireSystemWindow").invoke(oo); }
- Back to the TC, I use "buildOOoBean" in ComponentOpened and "showOOoBean" AFTER the component is shown (does not work otherwise).
This level of indirection is mandatory, because client module's ClassLoader is different than integration module's. If I do "OOoBean oo = new OOoBean()", the declaration and instantiation will try to use the JARs bundled. Since they don't lie on OOo install folder, the libraries will not be found. If I use "oo = (OOoBean) Loader.getCustomLoader().getClass("...").newInstance()", the "OOoBean" class from declaration will NOT be the same as the class instantiated. If you ever worked with JavaEE 4 and JSF/Struts/etc, you know that same classes from different ClassLoaders throws ClassCastException.
Yes, it is the purest evilness, but works (with some bugs):
OOo works really well with Java, but NetBeans ClassLoader is "self-contained" (i.e. your bundled application must include EVERYTHING you use). This is a very nice architecture, but does not help if you if you need to interface with external applications.
