pátek 16. října 2009

Eclipse: Java Decompiler - Really Nice Plugin

Why the method throws an exception? What happens when I pass a null parameter? Hot the hell is the class designed? These question I have often asked when I had was working with a library without sources. But now I have found a really nice Eclipse plugin http://java.decompiler.free.fr/jd-eclipse/. The update site is http://java.decompiler.free.fr/jd-eclipse/update.

Now I may press F3 button and see the decompiled source immediately. I just miss a possibility to debug the code without source.

středa 16. září 2009

Linux: How to Convert Binary File into Byte Array

Sometimes, a programmer needs to embed a content of a binary file into a program, usually as an array of bytes. For instance, I needed to embed an icon into a Java code, to be really sure the icon is always available for the program. So I have saved an icon in a GIF format and then used the od command. But the result was an unusable list of octal numbers. After a minute with playing, I have found a feasible solution
od -A n -v -t d1 image.gif | sed -e 's/^ *//g' -e 's/  */,/g' -e 's/$/,/g'
I have copied a result into my Java code as
private static final byte[] MISSING_ICON_BYTES = new byte[] { /* copy result here */ };
public static final ImageIcon MISSING_ICON = new ImageIcon(MISSING_ICON_BYTES);

úterý 15. září 2009

Ant: Removing a File from ZIP or JAR

I was facing a common problem with building a Java project by Ant: how to remove one file, and how to replace another file in a JAR (or ZIP) file. Many folks on the web, e.g. here and here, advice to unzip the JAR, delete and replace what's needed, and then create JAR again. But I have found out a simpler solution using a <zipfileset> resource pattern:
<delete file="new.jar">
<zip file="new.jar">
  <zipfileset src="old.jar">
    <exclude name="META-INF/replace.properties"/>
    <exclude name="META-INF/remove.properties"/>
  </zipfileset>
  <zipfileset file="file-to-replace.properties" fullpath="source-file-to-be-replaced.properties"/>
</zip>
You can use any of Ant resources. I personally like zipfileset, because it allows to specify the fullpath attribute, which is the simple way how to change a name of a source file. The <exclude> element is like "delete file" command in this example.

The Ant jar task can be used instead of the zip task, too. But then it has to be specified the META-INF/MANIFEST.MF also, otherwise the jar task creates a new one.

The zip task has one unpleasant "feature". It does not create the file new.jar, if it is newer (has greater timestamp) than the file old.jar. So, I recommend to delete the new.jar file first.

čtvrtek 27. srpna 2009

Java Swing: Flexible Message Dialog

JOptionPane makes life of a developer much easier when one need to show simple message or input dialogs. However, when a huge String or a String with many line breaks is passed to it's showMessageDialog() method, then the message dialog may the screen. For the user, it's better, when the very large messages are wrapped in an JScrollPane. It is possible to pass JScrollPane as an message parameter into to JOptionPane, so far so good. Unfotunatelly, it's not possible to limit the maximum size of the dialog. Finally, I have finished with this solution:
JTextArea textArea = new JTextArea();
textArea.setEditable(false);
// better to keep it true, so the user is able to copy the message to the clipboard
//textArea.setFocusable(false);
textArea.setOpaque(false);
textArea.setText(message);
textArea.setCaretPosition(0); // jump to the beginning
JScrollPane scrollPane = new JScrollPane(textArea);
scrollPane.setBorder(null); // no border, please
Dimension scrollPaneSize = scrollPane.getPreferredSize();
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
scrollPaneSize.width = Math.min(scrollPaneSize.width, Math.max(screen.width / 2, 260));
scrollPaneSize.height = Math.min(scrollPaneSize.height, Math.max(screen.height / 2, 125));
scrollPane.setPreferredSize(scrollPaneSize);
JOptionPane jOptionPane = new JOptionPane(scrollPane, JOptionPane.ERROR_MESSAGE);
JDialog dialog = jOptionPane.createDialog(parent, "Error");
// it could be nice to let the user resize the dialog, but then the scrollPane always shows the scroll bar
//dialog.setResizable(true);
dialog.setVisible(true);
dialog.dispose();
The size of the scrollPane grows or shrinks. It's maximum is screen size divided by two, or (260, 125) points (just in case the screen is too small :-)). I have found out, that (260, 125) is roughly the size of the empty massage dialog.

pátek 26. června 2009

Eclipse: WTP Plugin and Name of Deployment

I have a WTP project in Eclipse called in like myproject_war. When I have deployed it into the JBoss server, then the deployment name was myproject_war.war. I wanted to deploy it as myproject.war only. I could not find any property in the "Properties" dialogue to change the deploument name, so I have searched my filesystem for "myproject_war" string. So, I have discovered the file
.settings/org.eclipse.wst.common.component in the project directory. I have changed the attribute name of the wb-module module there, from
<wb-module name="myproject_war">
to
<wb-module name="myproject">
Now, the project is deployed as myproject.war.

pátek 19. června 2009

Java: Standalone Pattern and Lazy Initialization with Concurrent Access

Whats wrong with this piece of code?
public class MyClass {
  private static MyClass instance = null;
  public static MyClass getInstance() {
    if (instance == null) instance = new MyClass();
    return instance;
  }
}

Nothing, if you have single thread application only. Otherwise add a magic word synchronized:
public class MyClass {
  private static MyClass instance = null;
  public static synchronized MyClass getInstance() {
    if (instance == null) instance = new MyClass();
    return instance;
  }
}

Now, it's more safe, but a little bit slower. I was thinking about speeding it up by double check locking:
public class MyClass {
  private static MyClass instance = null;
  public static MyClass getInstance() {
    if (instance != null) return instance;
    synchronized (MyClass.class) {
      // yes, one more check in synchronized block
      if (instance == null) instance = new MyClass();
    }
    return instance;
  }
}

Danger! There's hidden snag in this code. Fortunately, I have stumbled upon the See Bill Pugh's page http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html explaining what's wrong with this piece of the code. Briefly, the construction of the object instance = new MyClass() is not an atomic operation. So a thread can get the instance variable while some other thread is still processing the constructor of the same instance object. You can find some old web pages advising to use double check locking in a wrong way, e.g. http://www.javaworld.com/javaworld/javatips/jw-javatip67.html. Bill Pugh's research has changed the memory model in Java 1.5, so there is a new keyword volatile which solves the hidden snag:
public class MyClass {
  private static volatile MyClass instance = null;
  public static MyClass getInstance() {
    if (instance != null) return instance;
    synchronized (MyClass.class) {
      // yes, one more check in synchronized block
      if (instance == null) instance = new MyClass();
    }
    return instance;
  }
}

Java has some nice semantic and syntactic sugar for multi-threaded programming. But this simple example shows, that one should not venture into the Java multi-threaded programming without deeper study of the Java internals and common concurrent programming principles.

čtvrtek 21. května 2009

Java logging.properties in the Classpath

Java utils logging (JUL) has been included in Java since version 1.4. Since then, one does not need to ship an extra JAR (log4j) with an application. It is possible to initialize JUL by a standard properties file logging.properties. I do not like, that
  • the user of the application has to provide a "hard to remember" parameter -Djava.util.logging.config.file=logging.properties to the JRE
  • and some default logging.properties cannot be packed into the JAR of the application.
Compare it with a very flexible initialization of JNDI:
The JNDI automatically reads the application resource files from all components in the applications' classpaths and JAVA_HOME/lib/jndi.properties, ...

I have solved this issues with just a few lines of code. The code has to be invoked before the logging is first used in the application. So, the most suitable place for this code is in the static constructor (initializer) of the class, where is main() method. Then you may pack a default logging.properties file into the JAR with your application (the same JAR where is the class with this code). And the user may still use the -Djava.util.logging.config.file=logging.properties property to use her/his own logging configuration.
/**
 * Should be called before the first use of the JDK logging. The best place is
 * in the static initializer of the class, which has the {@code main()} method.
 */
static {
  String file = System.getProperty("java.util.logging.config.file");
  if (loggingfile == null || loggingfile.isEmpty()) {
    URL url = null;
    try {
      url = MyClass.class.getClassLoader().getResource("logging.properties");
      if (url == null) {
        System.err.println("Cannot find logging.properties.");
      } else {
      LogManager.getLogManager().readConfiguration(url.openStream());
      }
    } catch (Exception e) {
    System.err.println("Error reading logging.properties from '" + url + "': " + e);
    }
  }
}

public static void main(String[] args) { ... }
If you need something more robust, then look into the Java sources for the aforementioned JNDI initialization :-).

středa 25. února 2009

Ze C# na Javu

Tak jsem konečně přešel ze C# na Javu. I kdy6 se mi C# a Visula Studio líbily, vadil mi především uzavřený a překotný vývoj těchto produktů. Společnost Microsoft bohužel moc neřeší známé bugy ve starších verzích .NET a navíc mám pocit, že se i k novým bugům má pštrosí chování -- když bug nevidíme, tak neexistuje. Navíc některá řešení dodávaná s .NET, jako třeba lokalizace, jsou udělána jen napůl. A kdo s tím má problém, tak ať si přikoupí komerční knihovny. Na tom není nic špatného, kdyby aspoň v dokumentaci byly upozornění na tyto nedostatky. Ale bohužel většinou se člověk jen dočte, jak je všechnu super.

Tak mám práci v Javě a Eclipse, ale stále bohužel pracovní prostředí MS Windows. Až se zapracuji, tak si nainstaluji svůj oblíbený Linux. I když musím uznat, že co se týká uživatelské přívětivosti, je na tom Java hůře nž .NET. Například taková prkotina, jako seznam přechozích hodnot v input boxu mi občas znatelně chybí. A i v Eclipse se najdou chybky. Ale aspoň je to zdarma a open source.