Workaround for Batik’s NoClassDefFoundError/ClassNotFoundException: TruncatedFileException

Batik’s JPEGRegistryEntry that is repsonsible for handling inline JPEGs in SVGs contains a reference to TruncatedFileException, which is specific to the Sun/Oracle JDK (as well as other classes used in the same code).
If you’re running e.g. on OpenJDK, you’ll get a NoClassDefFoundError or ClassNotFoundException: com/sun/image/codec/jpeg/TruncatedFileException (the problem is mentioned but not resolved on the mailing list)

You have several options:

  • use the Sun/Oracle JDK
  • copy the class from the JDK and include it with your application (which is ugly)
  • you override the broken class in batik with a patched one as outlined below

//add this code before you use batik (make sure is runs only once)
//via the lower priority this subclass is registered before JPEGRegistryEntry
//and prevents JPEGRegistryEntry.handleStream from breaking when used on a non Sun/Oracle JDK
JPEGRegistryEntry entry = new JPEGRegistryEntry() {
 
  public float  getPriority() {
      //higher than that of JPEGRegistryEntry (which is 1000)
                return 500;
  }
 
 
  public Filter handleStream(InputStream inIS, ...
      //code from org.apache.batik.ext.awt.image.codec.jpeg.JPEGRegistryEntry#handleStream, replace the code that reads the
      //bufferedImage with BufferedImage image = ImageIO.read(is);
   }
}
 
ImageTagRegistry.getRegistry().register(entry);

Posted in Software | Leave a comment

RuntimeException: No suitable ClassLoader found for grab – Grab with main method in IntelliJ

When running a Groovy class with a main method from IntelliJ (12.1.5) that also uses grape @Grab, you get a

Exception in thread "main" java.lang.ExceptionInInitializerError
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:188)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:113)
Caused by: java.lang.RuntimeException: No suitable ClassLoader found for grab

The easiest solution is to just run the code as a groovy script instead of the main method (e.g. by naming the main method differently and invoking it directly from the script).

Posted in Software | Leave a comment

Grails Start – Error initializing the application: null

When you encounter this error, try grails clean. That helps in my case (the error occurs sometimes when running grails run-app from the command line, Grails 2.2.4)

| Error [localhost-startStop-1] ERROR context.GrailsContextLoader  - Error initializing the application: null

Message: null

    Line | Method

->>   41 | reload       in grails.plugin.cache.ConfigLoader

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

|    193 | reloadCaches in CacheGrailsPlugin
Posted in Software | Leave a comment

Grails Log to Database with Custom Log4J Appender

When you move to more asynchronous processing with Grails (e.g. as with quartz jobs), there’s no user-visible error when something goes wrong. There’s only the logs, and those aren’t the most prominent place for putting failures of mission-cricital jobs.

In a nutshell

  • there’s a Grails domain that holds the log data
  • a custom log4j appender writes to that domain
  • the appender is configured in Config.groovy
  • logging to the database like this only works after Bootstrap.init has run

So here’s the code for directly logging to the database with Grails. For data storage, we use a Grails domain which holds the data (you could use log4j’s JDBCAppender as well, but we’re in the Grails world …)

/**
 * Contains log entries that the tech staff should have a look at
 */
class EventLog {
 
    final static int SOURCE_MAXSIZE = 255
    final static int MESSAGE_MAXSIZE = 1000
    final static int DETAILS_MAXSIZE = 4000
 
    Date dateCreated
 
    String message
    String details
    String source
 
    // did someone look at this error?
    boolean cleared = false
 
    static constraints = {
        source(blank: false, nullable: false, maxSize: SOURCE_MAXSIZE)
        message(blank: false, nullable: false, maxSize: MESSAGE_MAXSIZE)
        details(blank: true, nullable: true, maxSize: DETAILS_MAXSIZE)
    }
 
    static mapping = {
        sort "dateCreated"
    }
}

This EventLog is written to by a custom log4 appender, the EventLogAppender.
Because Grails’ database access is only available from a certain point after the start of the application, database logging is only attempted when the application is initialized.

/**
 * Log4j appender that writes its entries to the EventLog
 */
class EventLogAppender extends org.apache.log4j.AppenderSkeleton
implements org.apache.log4j.Appender {
 
    static appInitialized = false
 
    String source
 
    @Override
    protected void append(LoggingEvent event) {
        if (appInitialized) {
            //copied from Log4J's JDBCAppender
            event.getNDC();
            event.getThreadName();
            // Get a copy of this thread's MDC.
            event.getMDCCopy();
            event.getLocationInformation();
            event.getRenderedMessage();
            event.getThrowableStrRep();
 
            def limit = { string, maxLength -> string.substring(0, Math.min(string.length(), maxLength))}
 
            String logStatement = getLayout().format(event);
            // use new transaction so that the log entry will be written even if the currently running transaction is rolled back
            EventLog.withNewTransaction {
                EventLog eventLog = new EventLog()
                eventLog.message = "Log4 Error Log"
                eventLog.details = limit((logStatement ?: "Not details available, something is wrong"), EventLog.DETAILS_MAXSIZE)
                eventLog.source = limit(source ?: "Source not set", EventLog.SOURCE_MAXSIZE)
                eventLog.save()
            }
        }
    }
 
    /**
     * Set the source value for the logger (e.g. which application the logger belongs to)
     * @param source
     */
    public void setSource(String source) {
        this.source = source
    }
 
    @Override
    void close() {
        //noop
    }
 
    @Override
    boolean requiresLayout() {
        return true
    }
}

When the init closure in Bootstrap.groovy runs, you can be sure that you can write to the database, thus enable the logger there.

class BootStrap {
 
    def init = { servletContext ->
        EventLogAppender.appInitialized = true
        //...
    }
}

Last but not least, you need to enable the custom logger in Config.groovy:

log4j = {
 
    appenders {
        //EnhancedPatternLayout is needed in order to support the %throwable logging of stacktraces
        appender new EventLogAppender(source:'YourApp', name: 'eventLogAppender', layout:new EnhancedPatternLayout(conversionPattern: '%d{DATE} %5p %c{1}:%L - %m%n %throwable{500}'), threshold: org.apache.log4j.Level.ERROR)
        console name:'stdout'
    }
 
    root {
        error 'eventLogAppender'
        info 'stdout'
    }
    // ... more logging config
}

Now you’re up and running with a custom database logger.

Posted in Software | 14 Comments

Resize and Crop All Files to Fixed Size with Imagemagick Convert

Here’s the imagemagick command (in a loop in a Windwos batch file) for doing this to all files:

  • resize to fixed size 100×100, with each dimension being at least 100 (regardless of the aspect ratio, but preserving it)
  • crop the part of the image that exceeds 100×100
for %%f in (*.png) do ( convert %%f -resize "100x100^" -gravity center -crop 100x100+0+0 +repage %%f )
Posted in Software | Leave a comment