úterý 19. května 2015

Log4net Configuration for Desktop Application

I have configured log4net for out current project, a plain Windows Desktop application. I wanted log to rolling file into the AppData\Local folder. Not the Roaming folder, since log are specific to the machine and it is waste of resources to let them roam around. Also I wanted to see logs in console, but in DEBUG mode only, not in production. First problem to find the AppData\Local\MyAppName has been solved by log4net.Util.PatternString class. It parses a special %envFolderPath and %appdomain patterns. The second problem, to configure ConsoleAppender just in DEBUG mode has several solutions. It is possible to load different configs, or to configure ConsoleAppender programatically. I have decided to create own DebugFilter, which denies all log in production mode. This way I have just one log4net config file. The cons is that it is very slightly less efficient to have configured an Appender which denies all log, but it's OK for most of applications.
<log4net>
  <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
    <file type="log4net.Util.PatternString" value="%envFolderPath{LocalApplicationData}\%appdomain\log.txt" />
    <appendToFile value="true" />
    <maxSizeRollBackups value="2" />
    <maximumFileSize value="10MB" />
    <rollingStyle value="Size" />
    <staticLogFileName value="true" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
    </layout>
  </appender>
  <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
    </layout>
    <filter type="cvtBackend.Logging.DebugFilter" />
  </appender>
  <!-- Setup the root category, add the appenders and set the default level -->
  <root>
    <level value="INFO" />
    <appender-ref ref="RollingLogFileAppender" />
    <appender-ref ref="ConsoleAppender" />
  </root>
</log4net>
And the DebugFilter:
    /// 
    /// log4net filter which allows logging in DEBUG mode only.
    /// 
    public class DebugFilter : FilterSkeleton
    {
        public override FilterDecision Decide(LoggingEvent loggingEvent)
        {
#if DEBUG
            return FilterDecision.Neutral;
#else
            return FilterDecision.Deny;
#endif
        }
    }

pátek 13. února 2015

Java: fast date formatting in web containers

Java SimpleDateFormat is not thread safe. When it is used in multithreaded applications, like Java web containers, it's forbidden use it as a static constant
public static final DateFormat DATE_FORMAT = new SimpleDateFormat('YYYY.MM.dd'); // DON'T!
Most developers properly constructs new SimpleDateFormat everytime they need
DateFormat dateFormat = new SimpleDateFormat('YYYY.MM.dd');
String str = dateFormat.format(date);
However, constructing new SimpleDateFormat is not so cheap operation. So, how to safe CPU and memory like in static final pattern in a multithreaded environment?

Solution 1: clone


Fortunatelly, SimpleDateFormat implements clone() method, which is fasters than constructing a new one form a String:
private static final DateFormat DATE_FORMAT_TEMPLATE = new SimpleDateFormat('YYYY.MM.dd'); // Do not use directly

/**
 * @return every time a new SimpleDateFormat.
 */
public static DateFormat getDateFormat() {
 return DATE_FORMAT_TEMPLATE.clone();
}

Solution 2: ThreadLocal


A general solution for sharing non-thread safe objects is to have one copy for each thread. Java has a very nice ThreadLocal. Together with lazy initialization we got the proper thread safe and fast solution:
private static final ThreadLocal DATE_FORMAT_TL = new ThreadLocal() {
 protected DateFormat initialValue() {
  return new SimpleDateFormat('YYYY.MM.dd');
 };
};

/**
 * @return SimpleDateFormat, new one for every thread.
 */
public static DateFormat getDateFormat() {
 return DATE_FORMAT_TL .get();
}

/**
 * Simple, optimized Date formatting.
 */
public static String format(Date d) {
 return format == null ? null : getDateFormat().format(d);
}
This solution has advantage over clone() method, that calling getDateFormat() multiple times in one thread (e.g. inside a loop) does not create a new object. I like to add format() to simplify usage and handle null values. Note: ThreadLocal may cause redeploy memory leaks when it's value from a WAR class. E.g. see Java Web Container: Hunting Redeploy Memory Leaks

pátek 12. prosince 2014

Git error: short SHA1 is ambiguous

Git allows using shortened SHA1 hashes. Default is to use 7 characters (see core.abbrev config option). But sooner or later, you may hit problem, that short hashes are ambiguous. E.g.
$ git log 0ed98e5
...
error: short SHA1 0ed98e5 is ambiguous

To find, what Git objects are referenced by the same short has, use
$git rev-parse --disambiguate=0ed98e | git cat-file --batch-check
0ed98e56dd43b172d438dba0aa6ea9ebed0554c7 commit 307
0ed98e50771521f9ec27314d49286fd54e989c87 blob 1478
0ed98e50771521f9ec27314d49286fd54e989c87 blob 1478

In this case, only the first hash (0ed98e56) refers to a commit. The second hash (0ed98e50) refers to a blob - file. So, Git does check SHA1 for all it's objects, not only for commits, although the git log command uses just commits SHA1 for it's arguments.

How to fix the error? Use longer prefix of the SHA1 hash. The bulletproof solution is to use all 40 characters. But if you still like abbreviates, then you can find the safe minimum length of SHA1 prefix for your project by Josh Stone's solution:
$git rev-list --all --abbrev=0 --abbrev-commit | wc -L

čtvrtek 15. května 2014

Simple console.log wrapper hangs IE10 and Chrome

Many programmers write a wrapper around console.log to use logging only when it's available. I have reassigned log to my own variable this.log = console.log; and it has worked fine in Firefox with Firebug plugin. However, it has caused Javascript to stop working in Internet Explorer 10 (IE10) and Chrome. Strange thing is, IE10 hangs only when it has closed the developer tools F12. When I have opened the developer tools to see what's going on, the IE10 has proceeded well.

var mylog = {
    log : this._logNull,
    init: function() {
        if (typeof console == 'object' && typeof console.log == 'function') {
            console.log(this);
            this.log = console.log; // IE10 hangs bug
            //this.log = this._log; // IE10 ok
        }
    },
    _logNull : function() {},
    _log : function() { console.log.apply(console, arguments); }
    
};
Try the code at JSFiddle.

After writing a proper wrapper function _log, the code goes well even in IE10 with closed developer tools.

Note, this is just proof of concept code for IE10. The real console.log wrapper may become more complicated when considering various browser like Chrome, Safari, etc.

pondělí 21. října 2013

Java Swing: Change Global Action Key

Java Swing Look and Feel defines it's own actions and register them globally on key strokes. It may happen, such a global keystroke is the same you need to use for own action. E.g. you define an action hooked on F8 key in the JPanel. But when the user has focused a JTable inside the JPanel and hit F8, then it still triggers the "focusHeader" action instead your action. One solution is to put your action on every component in the JPanel. Or you can change default keys globally:

private static void reregisterGlobalKeys() {
 // F8 to Ctrl+F8
 reregisterGlobalKey("Table.ancestorInputMap", KeyStroke.getKeyStroke(KeyEvent.VK_F8), KeyStroke.getKeyStroke(KeyEvent.VK_F8, KeyEvent.CTRL_DOWN_MASK));
 reregisterGlobalKey("SplitPane.ancestorInputMap", KeyStroke.getKeyStroke(KeyEvent.VK_F8), KeyStroke.getKeyStroke(KeyEvent.VK_F8, KeyEvent.CTRL_DOWN_MASK));
}

private static void reregisterGlobalKey(String uiManagerKey, KeyStroke oldKeyStroke, KeyStroke newKeyStroke) {
 Object inputMapObj = UIManager.get(uiManagerKey);
 if (inputMapObj == null || !(inputMapObj instanceof InputMap)) {
  System.err.println("reregisterGlobalKey inputMap not found: " + uiManagerKey);
  return;
 }
 InputMap inputMap = (InputMap) inputMapObj;
 String actionCommand = (String) inputMap.get(oldKeyStroke);
 if (actionCommand == null) {
  System.err.println("reregisterGlobalKey actionCommand not found for inputMap: " + uiManagerKey + " keyStroke: " + oldKeyStroke);
  return;
 }
 inputMap.remove(oldKeyStroke);
 if (newKeyStroke != null) {
  inputMap.put(newKeyStroke, actionCommand);
 }
}

Keep in mind that these uiManagerKeys and key strokes are Look and Feel dependend.

čtvrtek 25. dubna 2013

Guava ClassPath Suite

Sometimes, one needs to run just a subset of JUnit test. The very good library ClassPathSuite (cpsuite) address this problem and is able to select test based on a class name, or another class properties (annotation, super class, etc.)

In our project, we have needed a little bit more complicated filter conditions than it offers. Afters studying ClassPathSuite and  Google Guava internals, I just figure out, some kind of more flexible ClassPathSuite can be created using Guava Predicate and ClassPath classes. So I have coded just one core file, GuavaClassPathSuite which does what we need - scans the class path for JUnit tests and filter them by user defined Predicates. I have pushed the solution to GitHub as gcpsuite, Guava ClassPath Suite, project. (Not very distinct name, but points to the Guava ClassPath). Either use it as a library, or simply copy&paste the GuavaClassPathSuite.java, write your predicates and enjoy!

čtvrtek 13. září 2012

Java Swing: MigLayout, JScrollPane and JTextField Showing Horizontal Scrollbar

MigLayout is a great layout manager for Java Swing. However, it needs some time to discover it's nooks and crannies. One common problem is with components inside JScrollPane.

We place all components inside the JPanel, which has a JScrollPane as parent. And we use MigLayout mostly with fill and grow parameters. Such layout ensures that a user can access all components by scrolling when she has a small screen.

But I have a faces a problem with JTextField containing a very long text. I usually use MigLayuot

growx, width 50::

constraints for text fields. However, when a text files contains a very long String, then it forces JScrollPane to to enlarge it's width and show the horizontal scrollbar. The user usually need the text field to be as long as possible, but not to overflow the screen width, if possible. After a while, I have found a proper and simple solution, just set the preferred size as minimum:

growx, width 50:50:

Note: I have described the problem using component constraints, the solution is similar with row constraints

fill, grow, 50:50: